diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index eeb141f6f..733088705 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -15,6 +15,7 @@ concurrency: env: C2_ENABLE_LTO: ${{ github.ref == 'refs/heads/master' }} CHATTERINO_REQUIRE_CLEAN_GIT: On + C2_BUILD_WITH_QT6: Off jobs: build: @@ -39,6 +40,11 @@ jobs: qt-version: 5.15.2 pch: true force-lto: false + # Ubuntu 22.04, Qt 6.2.4 + - os: ubuntu-22.04 + qt-version: 6.2.4 + pch: false + force-lto: false # Test for disabling Precompiled Headers & enabling link-time optimization - os: ubuntu-22.04 qt-version: 5.15.2 @@ -73,18 +79,34 @@ jobs: echo "vs_version=2022" >> "$GITHUB_ENV" shell: bash + - name: Set BUILD_WITH_QT6 + if: startsWith(matrix.qt-version, '6.') + run: | + echo "C2_BUILD_WITH_QT6=ON" >> "$GITHUB_ENV" + shell: bash + - uses: actions/checkout@v3 with: submodules: recursive fetch-depth: 0 # allows for tags access - - name: Install Qt + - name: Install Qt5 + if: startsWith(matrix.qt-version, '5.') uses: jurplel/install-qt-action@v3.0.0 with: cache: true cache-key-prefix: ${{ runner.os }}-QtCache-${{ matrix.qt-version }}-v2 version: ${{ matrix.qt-version }} + - name: Install Qt6 + if: startsWith(matrix.qt-version, '6.') + uses: jurplel/install-qt-action@v3.0.0 + with: + cache: true + cache-key-prefix: ${{ runner.os }}-QtCache-${{ matrix.qt-version }}-v2 + modules: qt5compat + version: ${{ matrix.qt-version }} + # WINDOWS - name: Cache conan packages part 1 if: startsWith(matrix.os, 'windows') @@ -131,6 +153,7 @@ jobs: -DCMAKE_TOOLCHAIN_FILE="conan_toolchain.cmake" ` -DBUILD_WITH_CRASHPAD="$Env:C2_ENABLE_CRASHPAD" ` -DCHATTERINO_LTO="$Env:C2_ENABLE_LTO" ` + -DBUILD_WITH_QT6="$Env:C2_BUILD_WITH_QT6" ` .. set cl=/MP nmake /S /NOLOGO @@ -216,6 +239,7 @@ jobs: -DUSE_PRECOMPILED_HEADERS=${{ matrix.pch }} \ -DCMAKE_EXPORT_COMPILE_COMMANDS=On \ -DCHATTERINO_LTO="$C2_ENABLE_LTO" \ + -DBUILD_WITH_QT6="$C2_BUILD_WITH_QT6" \ .. make -j"$(nproc)" shell: bash @@ -284,6 +308,7 @@ jobs: -DOPENSSL_ROOT_DIR=/usr/local/opt/openssl \ -DUSE_PRECOMPILED_HEADERS=${{ matrix.pch }} \ -DCHATTERINO_LTO="$C2_ENABLE_LTO" \ + -DBUILD_WITH_QT6="$C2_BUILD_WITH_QT6" \ .. make -j"$(sysctl -n hw.logicalcpu)" shell: bash diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e59edce7..82be7d5a7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## Unversioned +- Dev: Add capability to build Chatterino with Qt6. (#4393) - Dev: Fix homebrew update action. (#4394) ## 2.4.1 diff --git a/CMakeLists.txt b/CMakeLists.txt index 9dac811f4..68e07ca33 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -58,6 +58,13 @@ find_package(Qt${MAJOR_QT_VERSION} REQUIRED Concurrent ) +if (BUILD_WITH_QT6) + find_package(Qt${MAJOR_QT_VERSION} REQUIRED + COMPONENTS + Core5Compat + ) +endif () + message(STATUS "Qt version: ${Qt${MAJOR_QT_VERSION}_VERSION}") if (WIN32) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 5b8ad1bb8..fd705c125 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -620,6 +620,14 @@ target_link_libraries(${LIBRARY_PROJECT} LRUCache MagicEnum ) + +if (BUILD_WITH_QT6) + target_link_libraries(${LIBRARY_PROJECT} + PUBLIC + Qt${MAJOR_QT_VERSION}::Core5Compat + ) +endif () + if (BUILD_WITH_QTKEYCHAIN) target_link_libraries(${LIBRARY_PROJECT} PUBLIC diff --git a/src/PrecompiledHeader.hpp b/src/PrecompiledHeader.hpp index 0f7ac7643..a4a453954 100644 --- a/src/PrecompiledHeader.hpp +++ b/src/PrecompiledHeader.hpp @@ -105,7 +105,6 @@ # include # include # include -# include # include # include # include diff --git a/src/common/LinkParser.cpp b/src/common/LinkParser.cpp index 1b8c3f14d..7e82359b9 100644 --- a/src/common/LinkParser.cpp +++ b/src/common/LinkParser.cpp @@ -16,7 +16,12 @@ namespace { QFile file(":/tlds.txt"); file.open(QFile::ReadOnly); QTextStream stream(&file); + +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + // Default encoding of QTextStream is already UTF-8, at least in Qt6 +#else stream.setCodec("UTF-8"); +#endif int safetyMax = 20000; QSet set; diff --git a/src/controllers/commands/CommandController.cpp b/src/controllers/commands/CommandController.cpp index 06feedd96..42fa76e4f 100644 --- a/src/controllers/commands/CommandController.cpp +++ b/src/controllers/commands/CommandController.cpp @@ -3191,7 +3191,8 @@ QString CommandController::execCommand(const QString &textNoEmoji, } } - auto maxSpaces = std::min(this->maxSpaces_, words.length() - 1); + // We have checks to ensure words cannot be empty, so this can never wrap around + auto maxSpaces = std::min(this->maxSpaces_, (qsizetype)words.length() - 1); for (int i = 0; i < maxSpaces; ++i) { commandName += ' ' + words[i + 1]; diff --git a/src/controllers/commands/CommandController.hpp b/src/controllers/commands/CommandController.hpp index fcae24249..3816fa71d 100644 --- a/src/controllers/commands/CommandController.hpp +++ b/src/controllers/commands/CommandController.hpp @@ -62,7 +62,7 @@ private: // User-created commands QMap userCommands_; - int maxSpaces_ = 0; + qsizetype maxSpaces_ = 0; std::shared_ptr sm_; // Because the setting manager is not initialized until the initialize diff --git a/src/controllers/sound/SoundController.cpp b/src/controllers/sound/SoundController.cpp index 22c71a90c..be9313ef7 100644 --- a/src/controllers/sound/SoundController.cpp +++ b/src/controllers/sound/SoundController.cpp @@ -7,6 +7,7 @@ #define MINIAUDIO_IMPLEMENTATION #include +#include #include #include diff --git a/src/providers/bttv/liveupdates/BttvLiveUpdateSubscription.cpp b/src/providers/bttv/liveupdates/BttvLiveUpdateSubscription.cpp index cce726357..12b27a349 100644 --- a/src/providers/bttv/liveupdates/BttvLiveUpdateSubscription.cpp +++ b/src/providers/bttv/liveupdates/BttvLiveUpdateSubscription.cpp @@ -1,5 +1,6 @@ #include "providers/bttv/liveupdates/BttvLiveUpdateSubscription.hpp" +#include #include namespace chatterino { diff --git a/src/providers/irc/IrcServer.cpp b/src/providers/irc/IrcServer.cpp index b79f635bc..5ae01c56b 100644 --- a/src/providers/irc/IrcServer.cpp +++ b/src/providers/irc/IrcServer.cpp @@ -94,7 +94,8 @@ void IrcServer::initializeConnectionSignals(IrcConnection *connection, QObject::connect(connection, &Communi::IrcConnection::nickNameRequired, this, [](const QString &reserved, QString *result) { - *result = reserved + (std::rand() % 100); + *result = QString("%1%2").arg( + reserved, QString::number(std::rand() % 100)); }); QObject::connect(connection, &Communi::IrcConnection::noticeMessageReceived, diff --git a/src/providers/twitch/TwitchIrcServer.cpp b/src/providers/twitch/TwitchIrcServer.cpp index c608f9e0b..f2b30aef3 100644 --- a/src/providers/twitch/TwitchIrcServer.cpp +++ b/src/providers/twitch/TwitchIrcServer.cpp @@ -325,7 +325,7 @@ std::shared_ptr TwitchIrcServer::getChannelOrEmptyByID( continue; if (twitchChannel->roomId() == channelId && - twitchChannel->getName().splitRef(":").size() < 3) + twitchChannel->getName().count(':') < 2) { return twitchChannel; } diff --git a/src/providers/twitch/TwitchMessageBuilder.cpp b/src/providers/twitch/TwitchMessageBuilder.cpp index 7987f34bd..c8c4da409 100644 --- a/src/providers/twitch/TwitchMessageBuilder.cpp +++ b/src/providers/twitch/TwitchMessageBuilder.cpp @@ -821,14 +821,14 @@ void TwitchMessageBuilder::runIgnoreReplaces( }; auto addReplEmotes = [&twitchEmotes](const IgnorePhrase &phrase, - const QStringRef &midrepl, + const auto &midrepl, int startIndex) mutable { if (!phrase.containsEmote()) { return; } - QVector words = midrepl.split(' '); + auto words = midrepl.split(' '); int pos = 0; for (const auto &word : words) { @@ -843,7 +843,7 @@ void TwitchMessageBuilder::runIgnoreReplaces( } twitchEmotes.push_back(TwitchEmoteOccurrence{ startIndex + pos, - startIndex + pos + emote.first.string.length(), + startIndex + pos + (int)emote.first.string.length(), emote.second, emote.first, }); @@ -904,8 +904,13 @@ void TwitchMessageBuilder::runIgnoreReplaces( shiftIndicesAfter(from + len, midsize - len); +#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) + auto midExtendedRef = + QStringView{this->originalMessage_}.mid(pos1, pos2 - pos1); +#else auto midExtendedRef = this->originalMessage_.midRef(pos1, pos2 - pos1); +#endif for (auto &tup : vret) { @@ -969,8 +974,13 @@ void TwitchMessageBuilder::runIgnoreReplaces( shiftIndicesAfter(from + len, replacesize - len); +#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) + auto midExtendedRef = + QStringView{this->originalMessage_}.mid(pos1, pos2 - pos1); +#else auto midExtendedRef = this->originalMessage_.midRef(pos1, pos2 - pos1); +#endif for (auto &tup : vret) { diff --git a/src/util/Helpers.cpp b/src/util/Helpers.cpp index a913d4fc0..d4d4c6173 100644 --- a/src/util/Helpers.cpp +++ b/src/util/Helpers.cpp @@ -174,6 +174,12 @@ QString localizeNumbers(unsigned int number) return locale.toString(number); } +QString localizeNumbers(qsizetype number) +{ + QLocale locale; + return locale.toString(number); +} + QString kFormatNumbers(const int &number) { return QString("%1K").arg(number / 1000); diff --git a/src/util/Helpers.hpp b/src/util/Helpers.hpp index f16590282..34a374d43 100644 --- a/src/util/Helpers.hpp +++ b/src/util/Helpers.hpp @@ -74,6 +74,7 @@ QString shortenString(const QString &str, unsigned maxWidth = 50); QString localizeNumbers(const int &number); QString localizeNumbers(unsigned int number); +QString localizeNumbers(qsizetype number); QString kFormatNumbers(const int &number); diff --git a/src/widgets/Notebook.cpp b/src/widgets/Notebook.cpp index e797dca05..1d7671f7c 100644 --- a/src/widgets/Notebook.cpp +++ b/src/widgets/Notebook.cpp @@ -641,8 +641,8 @@ void Notebook::performLayout(bool animated) bool isLastColumn = col == columnCount - 1; auto largestWidth = 0; int tabStart = col * tabsPerColumn; - int tabEnd = - std::min((col + 1) * tabsPerColumn, this->items_.size()); + int tabEnd = std::min((col + 1) * tabsPerColumn, + (int)this->items_.size()); for (int i = tabStart; i < tabEnd; i++) { @@ -743,8 +743,8 @@ void Notebook::performLayout(bool animated) bool isLastColumn = col == columnCount - 1; auto largestWidth = 0; int tabStart = col * tabsPerColumn; - int tabEnd = - std::min((col + 1) * tabsPerColumn, this->items_.size()); + int tabEnd = std::min((col + 1) * tabsPerColumn, + (int)this->items_.size()); for (int i = tabStart; i < tabEnd; i++) { diff --git a/src/widgets/dialogs/SettingsDialog.cpp b/src/widgets/dialogs/SettingsDialog.cpp index d30365540..ebc33774a 100644 --- a/src/widgets/dialogs/SettingsDialog.cpp +++ b/src/widgets/dialogs/SettingsDialog.cpp @@ -22,6 +22,7 @@ #include "widgets/settingspages/NotificationPage.hpp" #include +#include #include namespace chatterino { diff --git a/src/widgets/helper/ResizingTextEdit.cpp b/src/widgets/helper/ResizingTextEdit.cpp index 5b0dcd861..9494a6cf1 100644 --- a/src/widgets/helper/ResizingTextEdit.cpp +++ b/src/widgets/helper/ResizingTextEdit.cpp @@ -62,7 +62,11 @@ QString ResizingTextEdit::textUnderCursor(bool *hadSpace) const auto textUpToCursor = currentText.left(tc.selectionStart()); +#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) + auto words = QStringView{textUpToCursor}.split(' '); +#else auto words = textUpToCursor.splitRef(' '); +#endif if (words.size() == 0) { return QString(); diff --git a/src/widgets/helper/SettingsDialogTab.cpp b/src/widgets/helper/SettingsDialogTab.cpp index ca8d7caa4..73014b990 100644 --- a/src/widgets/helper/SettingsDialogTab.cpp +++ b/src/widgets/helper/SettingsDialogTab.cpp @@ -54,7 +54,7 @@ void SettingsDialogTab::paintEvent(QPaintEvent *) QPainter painter(this); QStyleOption opt; - opt.init(this); + opt.initFrom(this); this->style()->drawPrimitive(QStyle::PE_Widget, &opt, &painter, this); diff --git a/src/widgets/settingspages/AboutPage.cpp b/src/widgets/settingspages/AboutPage.cpp index 773f5b8e9..712549e1c 100644 --- a/src/widgets/settingspages/AboutPage.cpp +++ b/src/widgets/settingspages/AboutPage.cpp @@ -8,6 +8,7 @@ #include "widgets/BasePopup.hpp" #include "widgets/helper/SignalLabel.hpp" +#include #include #include #include @@ -144,7 +145,11 @@ AboutPage::AboutPage() contributorsFile.open(QFile::ReadOnly); QTextStream stream(&contributorsFile); +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + // Default encoding of QTextStream is already UTF-8 +#else stream.setCodec("UTF-8"); +#endif QString line; diff --git a/src/widgets/settingspages/ModerationPage.cpp b/src/widgets/settingspages/ModerationPage.cpp index e306d2734..6dfd6f5f0 100644 --- a/src/widgets/settingspages/ModerationPage.cpp +++ b/src/widgets/settingspages/ModerationPage.cpp @@ -112,35 +112,36 @@ ModerationPage::ModerationPage() // Show how big (size-wise) the logs are auto logsPathSizeLabel = logs.emplace(); logsPathSizeLabel->setText(QtConcurrent::run([] { - return fetchLogDirectorySize(); - })); + return fetchLogDirectorySize(); + }).result()); // Select event - QObject::connect(selectDir.getElement(), &QPushButton::clicked, this, - [this, logsPathSizeLabel]() mutable { - auto dirName = - QFileDialog::getExistingDirectory(this); + QObject::connect( + selectDir.getElement(), &QPushButton::clicked, this, + [this, logsPathSizeLabel]() mutable { + auto dirName = QFileDialog::getExistingDirectory(this); - getSettings()->logPath = dirName; + getSettings()->logPath = dirName; - // Refresh: Show how big (size-wise) the logs are - logsPathSizeLabel->setText(QtConcurrent::run([] { - return fetchLogDirectorySize(); - })); - }); + // Refresh: Show how big (size-wise) the logs are + logsPathSizeLabel->setText(QtConcurrent::run([] { + return fetchLogDirectorySize(); + }).result()); + }); buttons->addSpacing(16); // Reset custom logpath - QObject::connect(resetDir.getElement(), &QPushButton::clicked, this, - [logsPathSizeLabel]() mutable { - getSettings()->logPath = ""; + QObject::connect( + resetDir.getElement(), &QPushButton::clicked, this, + [logsPathSizeLabel]() mutable { + getSettings()->logPath = ""; - // Refresh: Show how big (size-wise) the logs are - logsPathSizeLabel->setText(QtConcurrent::run([] { - return fetchLogDirectorySize(); - })); - }); + // Refresh: Show how big (size-wise) the logs are + logsPathSizeLabel->setText(QtConcurrent::run([] { + return fetchLogDirectorySize(); + }).result()); + }); QCheckBox *onlyLogListedChannels = this->createCheckBox("Only log channels listed below", diff --git a/src/widgets/splits/SplitInput.cpp b/src/widgets/splits/SplitInput.cpp index fa631fae9..252b308f9 100644 --- a/src/widgets/splits/SplitInput.cpp +++ b/src/widgets/splits/SplitInput.cpp @@ -699,7 +699,7 @@ void SplitInput::updateCompletionPopup() return; } - for (int i = clamp(position, 0, text.length() - 1); i >= 0; i--) + for (int i = clamp(position, 0, (int)text.length() - 1); i >= 0; i--) { if (text[i] == ' ') { @@ -766,7 +766,7 @@ void SplitInput::showCompletionPopup(const QString &text, bool emoteCompletion) popup->updateUsers(text, this->split_->getChannel()); } - auto pos = this->mapToGlobal({0, 0}) - QPoint(0, popup->height()) + + auto pos = this->mapToGlobal(QPoint{0, 0}) - QPoint(0, popup->height()) + QPoint((this->width() - popup->width()) / 2, 0); popup->move(pos); @@ -789,7 +789,7 @@ void SplitInput::insertCompletionText(const QString &input_) const auto text = edit.toPlainText(); auto position = edit.textCursor().position() - 1; - for (int i = clamp(position, 0, text.length() - 1); i >= 0; i--) + for (int i = clamp(position, 0, (int)text.length() - 1); i >= 0; i--) { bool done = false; if (text[i] == ':')