diff --git a/launcher/Application.cpp b/launcher/Application.cpp index a92c6b1e7..99b72870b 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -796,6 +796,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) // The cat m_settings->registerSetting("TheCat", false); m_settings->registerSetting("CatOpacity", 100); + m_settings->registerSetting("CatFit", "fit"); m_settings->registerSetting("StatusBarVisible", true); diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index 494efe9bb..23b2fbfad 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -893,6 +893,8 @@ SET(LAUNCHER_SOURCES ui/themes/ThemeManager.h ui/themes/CatPack.cpp ui/themes/CatPack.h + ui/themes/CatPainter.cpp + ui/themes/CatPainter.h # Processes LaunchController.h diff --git a/launcher/SysInfo.h b/launcher/SysInfo.h index f3688d60d..f6c04d702 100644 --- a/launcher/SysInfo.h +++ b/launcher/SysInfo.h @@ -1,3 +1,4 @@ +#pragma once #include namespace SysInfo { diff --git a/launcher/logs/LogParser.h b/launcher/logs/LogParser.h index 1a1d86dd1..aaf21e397 100644 --- a/launcher/logs/LogParser.h +++ b/launcher/logs/LogParser.h @@ -16,7 +16,7 @@ * along with this program. If not, see . * */ - +#pragma once #include #include #include diff --git a/launcher/minecraft/AssetsUtils.cpp b/launcher/minecraft/AssetsUtils.cpp index 4406d9b34..083924dc6 100644 --- a/launcher/minecraft/AssetsUtils.cpp +++ b/launcher/minecraft/AssetsUtils.cpp @@ -159,7 +159,7 @@ bool loadAssetsIndexJson(const QString& assetsId, const QString& path, AssetsInd if (key == "hash") { object.hash = value.toString(); } else if (key == "size") { - object.size = value.toDouble(); + object.size = value.toLongLong(); } } diff --git a/launcher/minecraft/Component.cpp b/launcher/minecraft/Component.cpp index ad7ef545c..5f114e942 100644 --- a/launcher/minecraft/Component.cpp +++ b/launcher/minecraft/Component.cpp @@ -462,7 +462,7 @@ void Component::waitLoadMeta() } } -void Component::setUpdateAction(UpdateAction action) +void Component::setUpdateAction(const UpdateAction& action) { m_updateAction = action; } diff --git a/launcher/minecraft/Component.h b/launcher/minecraft/Component.h index 203cc2241..eafdb8ed7 100644 --- a/launcher/minecraft/Component.h +++ b/launcher/minecraft/Component.h @@ -106,7 +106,7 @@ class Component : public QObject, public ProblemProvider { void waitLoadMeta(); - void setUpdateAction(UpdateAction action); + void setUpdateAction(const UpdateAction& action); void clearUpdateAction(); UpdateAction getUpdateAction(); diff --git a/launcher/minecraft/ComponentUpdateTask.cpp b/launcher/minecraft/ComponentUpdateTask.cpp index 36a07ee72..c77ed248d 100644 --- a/launcher/minecraft/ComponentUpdateTask.cpp +++ b/launcher/minecraft/ComponentUpdateTask.cpp @@ -570,7 +570,7 @@ void ComponentUpdateTask::performUpdateActions() component->setVersion(cv.targetVersion); component->waitLoadMeta(); }, - [&component, &instance](const UpdateActionLatestRecommendedCompatible lrc) { + [&component, &instance](const UpdateActionLatestRecommendedCompatible& lrc) { qCDebug(instanceProfileResolveC) << instance->name() << "|" << "UpdateActionLatestRecommendedCompatible" << component->getID() << ":" << component->getVersion() diff --git a/launcher/minecraft/auth/AccountData.cpp b/launcher/minecraft/auth/AccountData.cpp index 9dbe7ffc0..161fd968c 100644 --- a/launcher/minecraft/auth/AccountData.cpp +++ b/launcher/minecraft/auth/AccountData.cpp @@ -41,7 +41,7 @@ #include namespace { -void tokenToJSONV3(QJsonObject& parent, Token t, const char* tokenName) +void tokenToJSONV3(QJsonObject& parent, const Token& t, const char* tokenName) { if (!t.persistent) { return; diff --git a/launcher/minecraft/mod/tasks/GetModDependenciesTask.cpp b/launcher/minecraft/mod/tasks/GetModDependenciesTask.cpp index af00c3b9a..21e7c5a2a 100644 --- a/launcher/minecraft/mod/tasks/GetModDependenciesTask.cpp +++ b/launcher/minecraft/mod/tasks/GetModDependenciesTask.cpp @@ -87,7 +87,7 @@ ModPlatform::Dependency GetModDependenciesTask::getOverride(const ModPlatform::D { if (auto isQuilt = m_loaderType & ModPlatform::Quilt; isQuilt || m_loaderType & ModPlatform::Fabric) { auto overide = ModPlatform::getOverrideDeps(); - auto over = std::find_if(overide.cbegin(), overide.cend(), [dep, providerName, isQuilt](auto o) { + auto over = std::find_if(overide.cbegin(), overide.cend(), [dep, providerName, isQuilt](const auto& o) { return o.provider == providerName && dep.addonId == (isQuilt ? o.fabric : o.quilt); }); if (over != overide.cend()) { @@ -207,8 +207,9 @@ Task::Ptr GetModDependenciesTask::prepareDependencyTask(const ModPlatform::Depen if (!pDep->version.addonId.isValid()) { if (m_loaderType & ModPlatform::Quilt) { // falback for quilt auto overide = ModPlatform::getOverrideDeps(); - auto over = std::find_if(overide.cbegin(), overide.cend(), - [dep, provider](auto o) { return o.provider == provider.name && dep.addonId == o.quilt; }); + auto over = std::find_if(overide.cbegin(), overide.cend(), [dep, provider](const auto& o) { + return o.provider == provider.name && dep.addonId == o.quilt; + }); if (over != overide.cend()) { removePack(dep.addonId); addTask(prepareDependencyTask({ over->fabric, dep.type }, provider.name, level)); diff --git a/launcher/modplatform/atlauncher/ATLPackIndex.h b/launcher/modplatform/atlauncher/ATLPackIndex.h index 187bc05ec..0df5e237c 100644 --- a/launcher/modplatform/atlauncher/ATLPackIndex.h +++ b/launcher/modplatform/atlauncher/ATLPackIndex.h @@ -45,3 +45,4 @@ void loadIndexedPack(IndexedPack& m, QJsonObject& obj); } // namespace ATLauncher Q_DECLARE_METATYPE(ATLauncher::IndexedPack) +Q_DECLARE_METATYPE(QList) \ No newline at end of file diff --git a/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp b/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp index 042214c84..4f2cca80f 100644 --- a/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp +++ b/launcher/modplatform/atlauncher/ATLPackInstallTask.cpp @@ -391,7 +391,7 @@ QString PackInstallTask::getVersionForLoader(QString uid) return m_version.loader.version; } -QString PackInstallTask::detectLibrary(VersionLibrary library) +QString PackInstallTask::detectLibrary(const VersionLibrary& library) { // Try to detect what the library is if (!library.server.isEmpty() && library.server.split("/").length() >= 3) { diff --git a/launcher/modplatform/atlauncher/ATLPackInstallTask.h b/launcher/modplatform/atlauncher/ATLPackInstallTask.h index ce8bb636d..8024286e8 100644 --- a/launcher/modplatform/atlauncher/ATLPackInstallTask.h +++ b/launcher/modplatform/atlauncher/ATLPackInstallTask.h @@ -105,7 +105,7 @@ class PackInstallTask : public InstanceTask { private: QString getDirForModType(ModType type, QString raw); QString getVersionForLoader(QString uid); - QString detectLibrary(VersionLibrary library); + QString detectLibrary(const VersionLibrary& library); bool createLibrariesComponent(QString instanceRoot, std::shared_ptr profile); bool createPackComponent(QString instanceRoot, std::shared_ptr profile); diff --git a/launcher/modplatform/flame/FlamePackIndex.cpp b/launcher/modplatform/flame/FlamePackIndex.cpp index 8a7734be5..db2061d99 100644 --- a/launcher/modplatform/flame/FlamePackIndex.cpp +++ b/launcher/modplatform/flame/FlamePackIndex.cpp @@ -140,3 +140,11 @@ void Flame::loadIndexedPackVersions(Flame::IndexedPack& pack, QJsonArray& arr) pack.versions = unsortedVersions; pack.versionsLoaded = true; } + +auto Flame::getVersionDisplayString(const IndexedVersion& version) -> QString +{ + auto release_type = version.version_type.isValid() ? QString(" [%1]").arg(version.version_type.toString()) : ""; + auto mcVersion = + !version.mcVersion.isEmpty() && !version.version.contains(version.mcVersion) ? QObject::tr(" for %1").arg(version.mcVersion) : ""; + return QString("%1%2%3").arg(version.version, mcVersion, release_type); +} diff --git a/launcher/modplatform/flame/FlamePackIndex.h b/launcher/modplatform/flame/FlamePackIndex.h index 30391288b..22f7d648a 100644 --- a/launcher/modplatform/flame/FlamePackIndex.h +++ b/launcher/modplatform/flame/FlamePackIndex.h @@ -47,6 +47,9 @@ struct IndexedPack { void loadIndexedPack(IndexedPack& m, QJsonObject& obj); void loadIndexedInfo(IndexedPack&, QJsonObject&); void loadIndexedPackVersions(IndexedPack& m, QJsonArray& arr); + +auto getVersionDisplayString(const IndexedVersion&) -> QString; } // namespace Flame Q_DECLARE_METATYPE(Flame::IndexedPack) +Q_DECLARE_METATYPE(QList) diff --git a/launcher/modplatform/modrinth/ModrinthPackManifest.cpp b/launcher/modplatform/modrinth/ModrinthPackManifest.cpp index be565bf11..1e90f713e 100644 --- a/launcher/modplatform/modrinth/ModrinthPackManifest.cpp +++ b/launcher/modplatform/modrinth/ModrinthPackManifest.cpp @@ -183,4 +183,14 @@ auto loadIndexedVersion(QJsonObject& obj) -> ModpackVersion return file; } +auto getVersionDisplayString(const ModpackVersion& version) -> QString +{ + auto release_type = version.version_type.isValid() ? QString(" [%1]").arg(version.version_type.toString()) : ""; + auto mcVersion = !version.gameVersion.isEmpty() && !version.name.contains(version.gameVersion) + ? QObject::tr(" for %1").arg(version.gameVersion) + : ""; + auto versionStr = !version.name.contains(version.version) ? version.version : ""; + return QString("%1%2 — %3%4").arg(version.name, mcVersion, versionStr, release_type); +} + } // namespace Modrinth diff --git a/launcher/modplatform/modrinth/ModrinthPackManifest.h b/launcher/modplatform/modrinth/ModrinthPackManifest.h index 97b8ab712..a990e9a77 100644 --- a/launcher/modplatform/modrinth/ModrinthPackManifest.h +++ b/launcher/modplatform/modrinth/ModrinthPackManifest.h @@ -120,7 +120,10 @@ auto loadIndexedVersion(QJsonObject&) -> ModpackVersion; auto validateDownloadUrl(QUrl) -> bool; +auto getVersionDisplayString(const ModpackVersion&) -> QString; + } // namespace Modrinth Q_DECLARE_METATYPE(Modrinth::Modpack) Q_DECLARE_METATYPE(Modrinth::ModpackVersion) +Q_DECLARE_METATYPE(QList) diff --git a/launcher/ui/dialogs/ExportPackDialog.cpp b/launcher/ui/dialogs/ExportPackDialog.cpp index 15420616e..e6c17972d 100644 --- a/launcher/ui/dialogs/ExportPackDialog.cpp +++ b/launcher/ui/dialogs/ExportPackDialog.cpp @@ -102,9 +102,10 @@ ExportPackDialog::ExportPackDialog(MinecraftInstancePtr instance, QWidget* paren MinecraftInstance* mcInstance = dynamic_cast(instance.get()); if (mcInstance) { - for (auto& resourceModel : mcInstance->resourceLists()) - if (resourceModel->indexDir().exists()) + for (auto resourceModel : mcInstance->resourceLists()) { + if (resourceModel && resourceModel->indexDir().exists()) m_proxy->ignoreFilesWithPath().insert(instanceRoot.relativeFilePath(resourceModel->indexDir().absolutePath())); + } } m_ui->files->setModel(m_proxy); diff --git a/launcher/ui/dialogs/MSALoginDialog.cpp b/launcher/ui/dialogs/MSALoginDialog.cpp index 14ec672e0..9e2c3df02 100644 --- a/launcher/ui/dialogs/MSALoginDialog.cpp +++ b/launcher/ui/dialogs/MSALoginDialog.cpp @@ -139,6 +139,9 @@ void MSALoginDialog::onTaskFailed(QString reason) void MSALoginDialog::authorizeWithBrowser(const QUrl& url) { ui->stackedWidget2->setCurrentIndex(1); + ui->stackedWidget2->adjustSize(); + ui->stackedWidget2->updateGeometry(); + this->adjustSize(); ui->loginButton->setToolTip(QString("
%1
").arg(url.toString())); m_url = url; } @@ -173,6 +176,9 @@ void paintQR(QPainter& painter, const QSize sz, const QString& data, QColor fg) void MSALoginDialog::authorizeWithBrowserWithExtra(QString url, QString code, [[maybe_unused]] int expiresIn) { ui->stackedWidget->setCurrentIndex(1); + ui->stackedWidget->adjustSize(); + ui->stackedWidget->updateGeometry(); + this->adjustSize(); const auto linkString = QString("%2").arg(url, url); if (url == "https://www.microsoft.com/link" && !code.isEmpty()) { @@ -196,12 +202,18 @@ void MSALoginDialog::authorizeWithBrowserWithExtra(QString url, QString code, [[ void MSALoginDialog::onDeviceFlowStatus(QString status) { ui->stackedWidget->setCurrentIndex(0); + ui->stackedWidget->adjustSize(); + ui->stackedWidget->updateGeometry(); + this->adjustSize(); ui->status->setText(status); } void MSALoginDialog::onAuthFlowStatus(QString status) { ui->stackedWidget2->setCurrentIndex(0); + ui->stackedWidget2->adjustSize(); + ui->stackedWidget2->updateGeometry(); + this->adjustSize(); ui->status2->setText(status); } diff --git a/launcher/ui/instanceview/InstanceView.cpp b/launcher/ui/instanceview/InstanceView.cpp index 2349c684d..14d480e90 100644 --- a/launcher/ui/instanceview/InstanceView.cpp +++ b/launcher/ui/instanceview/InstanceView.cpp @@ -49,6 +49,7 @@ #include #include "VisualGroup.h" +#include "ui/themes/CatPainter.h" #include "ui/themes/ThemeManager.h" #include @@ -78,6 +79,9 @@ InstanceView::~InstanceView() { qDeleteAll(m_groups); m_groups.clear(); + if (m_cat) { + m_cat->deleteLater(); + } } void InstanceView::setModel(QAbstractItemModel* model) @@ -172,7 +176,7 @@ void InstanceView::updateScrollbar() void InstanceView::updateGeometries() { - geometryCache.clear(); + m_geometryCache.clear(); QMap cats; @@ -186,8 +190,8 @@ void InstanceView::updateGeometries() cat->update(); } else { auto cat = new VisualGroup(groupName, this); - if (fVisibility) { - cat->collapsed = fVisibility(groupName); + if (m_fVisibility) { + cat->collapsed = m_fVisibility(groupName); } cats.insert(groupName, cat); cat->update(); @@ -436,11 +440,15 @@ void InstanceView::mouseDoubleClickEvent(QMouseEvent* event) void InstanceView::setPaintCat(bool visible) { - m_catVisible = visible; - if (visible) - m_catPixmap.load(APPLICATION->themeManager()->getCatPack()); - else - m_catPixmap = QPixmap(); + if (m_cat) { + disconnect(m_cat, &CatPainter::updateFrame, this, nullptr); + delete m_cat; + m_cat = nullptr; + } + if (visible) { + m_cat = new CatPainter(APPLICATION->themeManager()->getCatPack(), this); + connect(m_cat, &CatPainter::updateFrame, this, [this] { viewport()->update(); }); + } } void InstanceView::paintEvent([[maybe_unused]] QPaintEvent* event) @@ -449,19 +457,8 @@ void InstanceView::paintEvent([[maybe_unused]] QPaintEvent* event) QPainter painter(this->viewport()); - if (m_catVisible) { - painter.setOpacity(APPLICATION->settings()->get("CatOpacity").toFloat() / 100); - int widWidth = this->viewport()->width(); - int widHeight = this->viewport()->height(); - if (m_catPixmap.width() < widWidth) - widWidth = m_catPixmap.width(); - if (m_catPixmap.height() < widHeight) - widHeight = m_catPixmap.height(); - auto pixmap = m_catPixmap.scaled(widWidth, widHeight, Qt::KeepAspectRatio, Qt::SmoothTransformation); - QRect rectOfPixmap = pixmap.rect(); - rectOfPixmap.moveBottomRight(this->viewport()->rect().bottomRight()); - painter.drawPixmap(rectOfPixmap.topLeft(), pixmap); - painter.setOpacity(1.0); + if (m_cat) { + m_cat->paint(&painter, this->viewport()->rect()); } QStyleOptionViewItem option; @@ -711,8 +708,8 @@ QRect InstanceView::geometryRect(const QModelIndex& index) const } int row = index.row(); - if (geometryCache.contains(row)) { - return *geometryCache[row]; + if (m_geometryCache.contains(row)) { + return *m_geometryCache[row]; } const VisualGroup* cat = category(index); @@ -727,7 +724,7 @@ QRect InstanceView::geometryRect(const QModelIndex& index) const out.setTop(cat->verticalPosition() + cat->headerHeight() + 5 + cat->rowTopOf(index)); out.setLeft(m_spacing + x * (itemWidth() + m_spacing)); out.setSize(itemDelegate()->sizeHint(option, index)); - geometryCache.insert(row, new QRect(out)); + m_geometryCache.insert(row, new QRect(out)); return out; } diff --git a/launcher/ui/instanceview/InstanceView.h b/launcher/ui/instanceview/InstanceView.h index dea8b1212..5d9dbf729 100644 --- a/launcher/ui/instanceview/InstanceView.h +++ b/launcher/ui/instanceview/InstanceView.h @@ -41,6 +41,7 @@ #include #include #include "VisualGroup.h" +#include "ui/themes/CatPainter.h" struct InstanceViewRoles { enum { GroupRole = Qt::UserRole, ProgressValueRole, ProgressMaximumRole }; @@ -56,7 +57,7 @@ class InstanceView : public QAbstractItemView { void setModel(QAbstractItemModel* model) override; using visibilityFunction = std::function; - void setSourceOfGroupCollapseStatus(visibilityFunction f) { fVisibility = f; } + void setSourceOfGroupCollapseStatus(visibilityFunction f) { m_fVisibility = f; } /// return geometry rectangle occupied by the specified model item QRect geometryRect(const QModelIndex& index) const; @@ -116,7 +117,7 @@ class InstanceView : public QAbstractItemView { friend struct VisualGroup; QList m_groups; - visibilityFunction fVisibility; + visibilityFunction m_fVisibility; // geometry int m_leftMargin = 5; @@ -127,9 +128,8 @@ class InstanceView : public QAbstractItemView { int m_itemWidth = 100; int m_currentItemsPerRow = -1; int m_currentCursorColumn = -1; - mutable QCache geometryCache; - bool m_catVisible = false; - QPixmap m_catPixmap; + mutable QCache m_geometryCache; + CatPainter* m_cat = nullptr; // point where the currently active mouse action started in geometry coordinates QPoint m_pressedPosition; diff --git a/launcher/ui/pages/instance/ManagedPackPage.cpp b/launcher/ui/pages/instance/ManagedPackPage.cpp index 3230268c4..a95762b5d 100644 --- a/launcher/ui/pages/instance/ManagedPackPage.cpp +++ b/launcher/ui/pages/instance/ManagedPackPage.cpp @@ -292,11 +292,8 @@ void ModrinthManagedPackPage::parseManagedPack() ui->versionsComboBox->clear(); ui->versionsComboBox->blockSignals(false); - for (auto version : m_pack.versions) { - QString name = version.version; - - if (!version.name.contains(version.version)) - name = QString("%1 — %2").arg(version.name, version.version); + for (const auto& version : m_pack.versions) { + QString name = Modrinth::getVersionDisplayString(version); // NOTE: the id from version isn't the same id in the modpack format spec... // e.g. HexMC's 4.4.0 has versionId 4.0.0 in the modpack index.............. @@ -489,8 +486,8 @@ void FlameManagedPackPage::parseManagedPack() ui->versionsComboBox->clear(); ui->versionsComboBox->blockSignals(false); - for (auto version : m_pack.versions) { - QString name = version.version; + for (const auto& version : m_pack.versions) { + QString name = Flame::getVersionDisplayString(version); if (version.fileId == m_inst->getManagedPackVersionID().toInt()) name = tr("%1 (Current)").arg(name); diff --git a/launcher/ui/pages/instance/McClient.h b/launcher/ui/pages/instance/McClient.h index 832b70d40..633e7aaed 100644 --- a/launcher/ui/pages/instance/McClient.h +++ b/launcher/ui/pages/instance/McClient.h @@ -1,3 +1,4 @@ +#pragma once #include #include #include diff --git a/launcher/ui/pages/instance/McResolver.cpp b/launcher/ui/pages/instance/McResolver.cpp index 2a769762c..5e2b8239c 100644 --- a/launcher/ui/pages/instance/McResolver.cpp +++ b/launcher/ui/pages/instance/McResolver.cpp @@ -37,9 +37,9 @@ void McResolver::pingWithDomainSRV(QString domain, int port) } const auto& firstRecord = records.at(0); - QString domain = firstRecord.target(); - int port = firstRecord.port(); - pingWithDomainA(domain, port); + QString newDomain = firstRecord.target(); + int newPort = firstRecord.port(); + pingWithDomainA(newDomain, newPort); }); lookup->lookup(); diff --git a/launcher/ui/pages/modplatform/atlauncher/AtlFilterModel.cpp b/launcher/ui/pages/modplatform/atlauncher/AtlFilterModel.cpp index dee3784e5..6868ce736 100644 --- a/launcher/ui/pages/modplatform/atlauncher/AtlFilterModel.cpp +++ b/launcher/ui/pages/modplatform/atlauncher/AtlFilterModel.cpp @@ -68,7 +68,10 @@ bool FilterModel::filterAcceptsRow(int sourceRow, const QModelIndex& sourceParen return true; } QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent); - ATLauncher::IndexedPack pack = sourceModel()->data(index, Qt::UserRole).value(); + QVariant raw = sourceModel()->data(index, Qt::UserRole); + Q_ASSERT(raw.canConvert()); + auto pack = raw.value(); + if (searchTerm.startsWith("#")) return QString::number(pack.id) == searchTerm.mid(1); return pack.name.contains(searchTerm, Qt::CaseInsensitive); @@ -76,8 +79,12 @@ bool FilterModel::filterAcceptsRow(int sourceRow, const QModelIndex& sourceParen bool FilterModel::lessThan(const QModelIndex& left, const QModelIndex& right) const { - ATLauncher::IndexedPack leftPack = sourceModel()->data(left, Qt::UserRole).value(); - ATLauncher::IndexedPack rightPack = sourceModel()->data(right, Qt::UserRole).value(); + QVariant leftRaw = sourceModel()->data(left, Qt::UserRole); + Q_ASSERT(leftRaw.canConvert()); + auto leftPack = leftRaw.value(); + QVariant rightRaw = sourceModel()->data(right, Qt::UserRole); + Q_ASSERT(rightRaw.canConvert()); + auto rightPack = rightRaw.value(); if (currentSorting == ByPopularity) { return leftPack.position > rightPack.position; diff --git a/launcher/ui/pages/modplatform/atlauncher/AtlPage.cpp b/launcher/ui/pages/modplatform/atlauncher/AtlPage.cpp index 7c69b315e..ad49d940e 100644 --- a/launcher/ui/pages/modplatform/atlauncher/AtlPage.cpp +++ b/launcher/ui/pages/modplatform/atlauncher/AtlPage.cpp @@ -143,7 +143,9 @@ void AtlPage::onSelectionChanged(QModelIndex first, [[maybe_unused]] QModelIndex return; } - selected = filterModel->data(first, Qt::UserRole).value(); + QVariant raw = filterModel->data(first, Qt::UserRole); + Q_ASSERT(raw.canConvert()); + selected = raw.value(); ui->packDescription->setHtml(StringUtils::htmlListPatch(selected.description.replace("\n", "
"))); diff --git a/launcher/ui/pages/modplatform/flame/FlameModel.cpp b/launcher/ui/pages/modplatform/flame/FlameModel.cpp index 20005a8b2..9255ccf1e 100644 --- a/launcher/ui/pages/modplatform/flame/FlameModel.cpp +++ b/launcher/ui/pages/modplatform/flame/FlameModel.cpp @@ -79,6 +79,7 @@ bool ListModel::setData(const QModelIndex& index, const QVariant& value, [[maybe if (pos >= modpacks.size() || pos < 0 || !index.isValid()) return false; + Q_ASSERT(value.canConvert()); modpacks[pos] = value.value(); return true; diff --git a/launcher/ui/pages/modplatform/flame/FlamePage.cpp b/launcher/ui/pages/modplatform/flame/FlamePage.cpp index b60e5800b..6563f3362 100644 --- a/launcher/ui/pages/modplatform/flame/FlamePage.cpp +++ b/launcher/ui/pages/modplatform/flame/FlamePage.cpp @@ -166,7 +166,9 @@ void FlamePage::onSelectionChanged(QModelIndex curr, [[maybe_unused]] QModelInde return; } - current = listModel->data(curr, Qt::UserRole).value(); + QVariant raw = listModel->data(curr, Qt::UserRole); + Q_ASSERT(raw.canConvert()); + current = raw.value(); if (!current.versionsLoaded || m_filterWidget->changed()) { qDebug() << "Loading flame modpack versions"; @@ -206,13 +208,8 @@ void FlamePage::onSelectionChanged(QModelIndex curr, [[maybe_unused]] QModelInde else ++it; #endif - for (auto version : current.versions) { - auto release_type = version.version_type.isValid() ? QString(" [%1]").arg(version.version_type.toString()) : ""; - auto mcVersion = !version.mcVersion.isEmpty() && !version.version.contains(version.mcVersion) - ? QString(" for %1").arg(version.mcVersion) - : ""; - ui->versionSelectionBox->addItem(QString("%1%2%3").arg(version.version, mcVersion, release_type), - QVariant(version.downloadUrl)); + for (const auto& version : current.versions) { + ui->versionSelectionBox->addItem(Flame::getVersionDisplayString(version), QVariant(version.downloadUrl)); } QVariant current_updated; diff --git a/launcher/ui/pages/modplatform/import_ftb/ImportFTBPage.cpp b/launcher/ui/pages/modplatform/import_ftb/ImportFTBPage.cpp index 15303bb22..35e1dc110 100644 --- a/launcher/ui/pages/modplatform/import_ftb/ImportFTBPage.cpp +++ b/launcher/ui/pages/modplatform/import_ftb/ImportFTBPage.cpp @@ -103,13 +103,16 @@ void ImportFTBPage::suggestCurrent() dialog->setSuggestedIconFromFile(FS::PathCombine(selected.path, "folder.jpg"), editedLogoName); } -void ImportFTBPage::onPublicPackSelectionChanged(QModelIndex now, QModelIndex prev) +void ImportFTBPage::onPublicPackSelectionChanged(QModelIndex now, QModelIndex) { if (!now.isValid()) { onPackSelectionChanged(); return; } - Modpack selectedPack = currentModel->data(now, Qt::UserRole).value(); + + QVariant raw = currentModel->data(now, Qt::UserRole); + Q_ASSERT(raw.canConvert()); + auto selectedPack = raw.value(); onPackSelectionChanged(&selectedPack); } diff --git a/launcher/ui/pages/modplatform/import_ftb/ListModel.cpp b/launcher/ui/pages/modplatform/import_ftb/ListModel.cpp index f99ac8d65..8d3beea01 100644 --- a/launcher/ui/pages/modplatform/import_ftb/ListModel.cpp +++ b/launcher/ui/pages/modplatform/import_ftb/ListModel.cpp @@ -143,8 +143,12 @@ FilterModel::FilterModel(QObject* parent) : QSortFilterProxyModel(parent) bool FilterModel::lessThan(const QModelIndex& left, const QModelIndex& right) const { - Modpack leftPack = sourceModel()->data(left, Qt::UserRole).value(); - Modpack rightPack = sourceModel()->data(right, Qt::UserRole).value(); + QVariant leftRaw = sourceModel()->data(left, Qt::UserRole); + Q_ASSERT(leftRaw.canConvert()); + auto leftPack = leftRaw.value(); + QVariant rightRaw = sourceModel()->data(right, Qt::UserRole); + Q_ASSERT(rightRaw.canConvert()); + auto rightPack = rightRaw.value(); if (m_currentSorting == Sorting::ByGameVersion) { Version lv(leftPack.mcVersion); @@ -166,7 +170,9 @@ bool FilterModel::filterAcceptsRow([[maybe_unused]] int sourceRow, [[maybe_unuse return true; } QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent); - Modpack pack = sourceModel()->data(index, Qt::UserRole).value(); + QVariant raw = sourceModel()->data(index, Qt::UserRole); + Q_ASSERT(raw.canConvert()); + auto pack = raw.value(); return pack.name.contains(m_searchTerm, Qt::CaseInsensitive); } diff --git a/launcher/ui/pages/modplatform/legacy_ftb/ListModel.cpp b/launcher/ui/pages/modplatform/legacy_ftb/ListModel.cpp index 9c5041126..efa9bd3bc 100644 --- a/launcher/ui/pages/modplatform/legacy_ftb/ListModel.cpp +++ b/launcher/ui/pages/modplatform/legacy_ftb/ListModel.cpp @@ -61,8 +61,12 @@ FilterModel::FilterModel(QObject* parent) : QSortFilterProxyModel(parent) bool FilterModel::lessThan(const QModelIndex& left, const QModelIndex& right) const { - Modpack leftPack = sourceModel()->data(left, Qt::UserRole).value(); - Modpack rightPack = sourceModel()->data(right, Qt::UserRole).value(); + QVariant leftRaw = sourceModel()->data(left, Qt::UserRole); + Q_ASSERT(leftRaw.canConvert()); + auto leftPack = leftRaw.value(); + QVariant rightRaw = sourceModel()->data(right, Qt::UserRole); + Q_ASSERT(rightRaw.canConvert()); + auto rightPack = rightRaw.value(); if (currentSorting == Sorting::ByGameVersion) { Version lv(leftPack.mcVersion); @@ -84,7 +88,9 @@ bool FilterModel::filterAcceptsRow([[maybe_unused]] int sourceRow, [[maybe_unuse return true; } QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent); - Modpack pack = sourceModel()->data(index, Qt::UserRole).value(); + QVariant raw = sourceModel()->data(index, Qt::UserRole); + Q_ASSERT(raw.canConvert()); + auto pack = raw.value(); if (searchTerm.startsWith("#")) return pack.packCode == searchTerm.mid(1); return pack.name.contains(searchTerm, Qt::CaseInsensitive); diff --git a/launcher/ui/pages/modplatform/legacy_ftb/Page.cpp b/launcher/ui/pages/modplatform/legacy_ftb/Page.cpp index 5752b6c61..1576f52fe 100644 --- a/launcher/ui/pages/modplatform/legacy_ftb/Page.cpp +++ b/launcher/ui/pages/modplatform/legacy_ftb/Page.cpp @@ -233,7 +233,9 @@ void Page::onPublicPackSelectionChanged(QModelIndex now, [[maybe_unused]] QModel onPackSelectionChanged(); return; } - Modpack selectedPack = publicFilterModel->data(now, Qt::UserRole).value(); + QVariant raw = publicFilterModel->data(now, Qt::UserRole); + Q_ASSERT(raw.canConvert()); + auto selectedPack = raw.value(); onPackSelectionChanged(&selectedPack); } @@ -243,7 +245,9 @@ void Page::onThirdPartyPackSelectionChanged(QModelIndex now, [[maybe_unused]] QM onPackSelectionChanged(); return; } - Modpack selectedPack = thirdPartyFilterModel->data(now, Qt::UserRole).value(); + QVariant raw = thirdPartyFilterModel->data(now, Qt::UserRole); + Q_ASSERT(raw.canConvert()); + auto selectedPack = raw.value(); onPackSelectionChanged(&selectedPack); } @@ -253,7 +257,9 @@ void Page::onPrivatePackSelectionChanged(QModelIndex now, [[maybe_unused]] QMode onPackSelectionChanged(); return; } - Modpack selectedPack = privateFilterModel->data(now, Qt::UserRole).value(); + QVariant raw = privateFilterModel->data(now, Qt::UserRole); + Q_ASSERT(raw.canConvert()); + auto selectedPack = raw.value(); onPackSelectionChanged(&selectedPack); } @@ -328,7 +334,9 @@ void Page::onTabChanged(int tab) currentList->selectionModel()->reset(); QModelIndex idx = currentList->currentIndex(); if (idx.isValid()) { - auto pack = currentModel->data(idx, Qt::UserRole).value(); + QVariant raw = currentModel->data(idx, Qt::UserRole); + Q_ASSERT(raw.canConvert()); + auto pack = raw.value(); onPackSelectionChanged(&pack); } else { onPackSelectionChanged(); diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp index 717dd359f..e84903cfd 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp @@ -121,6 +121,7 @@ bool ModpackListModel::setData(const QModelIndex& index, const QVariant& value, if (pos >= modpacks.size() || pos < 0 || !index.isValid()) return false; + Q_ASSERT(value.canConvert()); modpacks[pos] = value.value(); return true; @@ -137,7 +138,7 @@ void ModpackListModel::performPaginatedSearch() ResourceAPI::ProjectInfoCallbacks callbacks; callbacks.on_fail = [this](QString reason) { searchRequestFailed(reason); }; - callbacks.on_succeed = [this](auto& doc, auto& pack) { searchRequestForOneSucceeded(doc); }; + callbacks.on_succeed = [this](auto& doc, auto&) { searchRequestForOneSucceeded(doc); }; callbacks.on_abort = [this] { qCritical() << "Search task aborted by an unknown reason!"; searchRequestFailed("Aborted"); @@ -345,7 +346,7 @@ void ModpackListModel::searchRequestForOneSucceeded(QJsonDocument& doc) endInsertRows(); } -void ModpackListModel::searchRequestFailed(QString reason) +void ModpackListModel::searchRequestFailed(QString) { auto failed_action = dynamic_cast(jobPtr.get())->getFailedActions().at(0); if (failed_action->replyStatusCode() == -1) { diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp index 1b5b8d4de..4586bf70d 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp @@ -150,7 +150,9 @@ void ModrinthPage::onSelectionChanged(QModelIndex curr, [[maybe_unused]] QModelI return; } - current = m_model->data(curr, Qt::UserRole).value(); + QVariant raw = m_model->data(curr, Qt::UserRole); + Q_ASSERT(raw.canConvert()); + current = raw.value(); auto name = current.name; if (!current.extraInfoLoaded) { @@ -244,14 +246,8 @@ void ModrinthPage::onSelectionChanged(QModelIndex curr, [[maybe_unused]] QModelI else ++it; #endif - for (auto version : current.versions) { - auto release_type = version.version_type.isValid() ? QString(" [%1]").arg(version.version_type.toString()) : ""; - auto mcVersion = !version.gameVersion.isEmpty() && !version.name.contains(version.gameVersion) - ? QString(" for %1").arg(version.gameVersion) - : ""; - auto versionStr = !version.name.contains(version.version) ? version.version : ""; - ui->versionSelectionBox->addItem(QString("%1%2 — %3%4").arg(version.name, mcVersion, versionStr, release_type), - QVariant(version.id)); + for (const auto& version : current.versions) { + ui->versionSelectionBox->addItem(Modrinth::getVersionDisplayString(version), QVariant(version.id)); } QVariant current_updated; diff --git a/launcher/ui/pages/modplatform/technic/TechnicData.h b/launcher/ui/pages/modplatform/technic/TechnicData.h index 11d57f071..1049d1f2e 100644 --- a/launcher/ui/pages/modplatform/technic/TechnicData.h +++ b/launcher/ui/pages/modplatform/technic/TechnicData.h @@ -36,6 +36,7 @@ #pragma once #include +#include #include namespace Technic { diff --git a/launcher/ui/pages/modplatform/technic/TechnicPage.cpp b/launcher/ui/pages/modplatform/technic/TechnicPage.cpp index 63a51d363..7d999b31e 100644 --- a/launcher/ui/pages/modplatform/technic/TechnicPage.cpp +++ b/launcher/ui/pages/modplatform/technic/TechnicPage.cpp @@ -135,7 +135,9 @@ void TechnicPage::onSelectionChanged(QModelIndex first, [[maybe_unused]] QModelI return; } - current = model->data(first, Qt::UserRole).value(); + QVariant raw = model->data(first, Qt::UserRole); + Q_ASSERT(raw.canConvert()); + current = raw.value(); suggestCurrent(); } diff --git a/launcher/ui/themes/CatPainter.cpp b/launcher/ui/themes/CatPainter.cpp new file mode 100644 index 000000000..7ff24932b --- /dev/null +++ b/launcher/ui/themes/CatPainter.cpp @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2025 Trial97 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "ui/themes/CatPainter.h" +#include +#include "Application.h" + +CatPainter::CatPainter(const QString& path, QObject* parent) : QObject(parent) +{ + m_image = QPixmap(path); +} + +void CatPainter::paint(QPainter* painter, const QRect& viewport) +{ + QPixmap frame = m_image; + + auto fit = APPLICATION->settings()->get("CatFit").toString(); + painter->setOpacity(APPLICATION->settings()->get("CatOpacity").toFloat() / 100); + int widWidth = viewport.width(); + int widHeight = viewport.height(); + auto aspectMode = Qt::IgnoreAspectRatio; + if (fit == "fill") { + aspectMode = Qt::KeepAspectRatio; + } else if (fit == "fit") { + aspectMode = Qt::KeepAspectRatio; + if (frame.width() < widWidth) + widWidth = frame.width(); + if (frame.height() < widHeight) + widHeight = frame.height(); + } + auto pixmap = frame.scaled(widWidth, widHeight, aspectMode, Qt::SmoothTransformation); + QRect rectOfPixmap = pixmap.rect(); + rectOfPixmap.moveBottomRight(viewport.bottomRight()); + painter->drawPixmap(rectOfPixmap.topLeft(), pixmap); + painter->setOpacity(1.0); +}; diff --git a/launcher/ui/themes/CatPainter.h b/launcher/ui/themes/CatPainter.h new file mode 100644 index 000000000..3b790c640 --- /dev/null +++ b/launcher/ui/themes/CatPainter.h @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + * Prism Launcher - Minecraft Launcher + * Copyright (c) 2025 Trial97 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include +#include +#include +#include + +class CatPainter : public QObject { + Q_OBJECT + public: + CatPainter(const QString& path, QObject* parent = nullptr); + virtual ~CatPainter() = default; + void paint(QPainter*, const QRect&); + + signals: + void updateFrame(); + + private: + QPixmap m_image; +}; diff --git a/launcher/ui/widgets/AppearanceWidget.cpp b/launcher/ui/widgets/AppearanceWidget.cpp index ae9c6ee5e..cfe3b8c0d 100644 --- a/launcher/ui/widgets/AppearanceWidget.cpp +++ b/launcher/ui/widgets/AppearanceWidget.cpp @@ -97,22 +97,28 @@ void AppearanceWidget::applySettings() settings->set("ConsoleFont", consoleFontFamily); settings->set("ConsoleFontSize", m_ui->fontSizeBox->value()); settings->set("CatOpacity", m_ui->catOpacitySlider->value()); + auto catFit = m_ui->catFitComboBox->currentIndex(); + settings->set("CatFit", catFit == 0 ? "fit" : catFit == 1 ? "fill" : "strech"); } void AppearanceWidget::loadSettings() { - QString fontFamily = APPLICATION->settings()->get("ConsoleFont").toString(); + SettingsObjectPtr settings = APPLICATION->settings(); + QString fontFamily = settings->get("ConsoleFont").toString(); QFont consoleFont(fontFamily); m_ui->consoleFont->setCurrentFont(consoleFont); bool conversionOk = true; - int fontSize = APPLICATION->settings()->get("ConsoleFontSize").toInt(&conversionOk); + int fontSize = settings->get("ConsoleFontSize").toInt(&conversionOk); if (!conversionOk) { fontSize = 11; } m_ui->fontSizeBox->setValue(fontSize); - m_ui->catOpacitySlider->setValue(APPLICATION->settings()->get("CatOpacity").toInt()); + m_ui->catOpacitySlider->setValue(settings->get("CatOpacity").toInt()); + + auto catFit = settings->get("CatFit").toString(); + m_ui->catFitComboBox->setCurrentIndex(catFit == "fit" ? 0 : catFit == "fill" ? 1 : 2); } void AppearanceWidget::retranslateUi() @@ -245,9 +251,7 @@ void AppearanceWidget::updateConsolePreview() workCursor.insertBlock(); }; - print(QString("%1 version: %2\n") - .arg(BuildConfig.LAUNCHER_DISPLAYNAME, BuildConfig.printableVersionString()), - MessageLevel::Launcher); + print(QString("%1 version: %2\n").arg(BuildConfig.LAUNCHER_DISPLAYNAME, BuildConfig.printableVersionString()), MessageLevel::Launcher); QDate today = QDate::currentDate(); diff --git a/launcher/ui/widgets/AppearanceWidget.ui b/launcher/ui/widgets/AppearanceWidget.ui index c672279f0..99bf4a500 100644 --- a/launcher/ui/widgets/AppearanceWidget.ui +++ b/launcher/ui/widgets/AppearanceWidget.ui @@ -7,7 +7,7 @@ 0 0 600 - 700 + 711 @@ -203,6 +203,53 @@ + + + + + 0 + 0 + + + + Fit + + + + + + + + 0 + 0 + + + + + 77 + 30 + + + + 0 + + + + Fit + + + + + Fill + + + + + Stretch + + + + @@ -372,8 +419,7 @@ - - .. + true @@ -389,8 +435,7 @@ - - .. + true @@ -406,8 +451,7 @@ - - .. + true @@ -423,8 +467,7 @@ - - .. + true @@ -440,8 +483,7 @@ - - .. + true @@ -457,8 +499,7 @@ - - .. + true @@ -474,8 +515,7 @@ - - .. + true @@ -491,8 +531,7 @@ - - .. + true @@ -508,8 +547,7 @@ - - .. + true @@ -525,8 +563,7 @@ - - .. + true @@ -586,6 +623,7 @@ reloadThemesButton consoleFont fontSizeBox + catFitComboBox catOpacitySlider consolePreview diff --git a/launcher/ui/widgets/ProjectItem.cpp b/launcher/ui/widgets/ProjectItem.cpp index 03fa659c9..91cf0956f 100644 --- a/launcher/ui/widgets/ProjectItem.cpp +++ b/launcher/ui/widgets/ProjectItem.cpp @@ -16,13 +16,20 @@ void ProjectItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem& o QStyleOptionViewItem opt(option); initStyleOption(&opt, index); + auto isInstalled = index.data(UserDataTypes::INSTALLED).toBool(); + auto isChecked = opt.checkState == Qt::Checked; + auto isSelected = option.state & QStyle::State_Selected; + + if (!isSelected && !isChecked && isInstalled) { + painter->setOpacity(0.4); // Fade out the entire item + } const QStyle* style = opt.widget == nullptr ? QApplication::style() : opt.widget->style(); auto rect = opt.rect; style->drawPrimitive(QStyle::PE_PanelItemViewItem, &opt, painter, opt.widget); - if (option.state & QStyle::State_Selected && style->objectName() != "windowsvista") + if (isSelected && style->objectName() != "windowsvista") painter->setPen(opt.palette.highlightedText().color()); if (opt.features & QStyleOptionViewItem::HasCheckIndicator) { @@ -68,12 +75,16 @@ void ProjectItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem& o { // Title painting auto title = index.data(UserDataTypes::TITLE).toString(); - if (index.data(UserDataTypes::INSTALLED).toBool()) - title = tr("%1 [installed]").arg(title); - painter->save(); auto font = opt.font; + if (isChecked) { + font.setBold(true); + } + if (isInstalled) { + title = tr("%1 [installed]").arg(title); + } + font.setPointSize(font.pointSize() + 2); painter->setFont(font); diff --git a/tests/FileSystem_test.cpp b/tests/FileSystem_test.cpp index 050fafd3c..48da30a25 100644 --- a/tests/FileSystem_test.cpp +++ b/tests/FileSystem_test.cpp @@ -69,7 +69,9 @@ class LinkTask : public Task { } FS::create_link* m_lnk; - [[maybe_unused]] bool m_useHard = false; +#if defined Q_OS_WIN32 + bool m_useHard = false; +#endif bool m_linkRecursive = true; };