Merge branch 'develop' into launcher-log-page

This commit is contained in:
Yihe Li 2025-06-06 21:36:03 +08:00
commit 836c558c6c
No known key found for this signature in database
42 changed files with 332 additions and 120 deletions

View file

@ -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);

View file

@ -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

View file

@ -1,3 +1,4 @@
#pragma once
#include <QString>
namespace SysInfo {

View file

@ -16,7 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
#pragma once
#include <QAnyStringView>
#include <QDateTime>
#include <QList>

View file

@ -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();
}
}

View file

@ -462,7 +462,7 @@ void Component::waitLoadMeta()
}
}
void Component::setUpdateAction(UpdateAction action)
void Component::setUpdateAction(const UpdateAction& action)
{
m_updateAction = action;
}

View file

@ -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();

View file

@ -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()

View file

@ -41,7 +41,7 @@
#include <QUuid>
namespace {
void tokenToJSONV3(QJsonObject& parent, Token t, const char* tokenName)
void tokenToJSONV3(QJsonObject& parent, const Token& t, const char* tokenName)
{
if (!t.persistent) {
return;

View file

@ -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));

View file

@ -45,3 +45,4 @@ void loadIndexedPack(IndexedPack& m, QJsonObject& obj);
} // namespace ATLauncher
Q_DECLARE_METATYPE(ATLauncher::IndexedPack)
Q_DECLARE_METATYPE(QList<ATLauncher::IndexedVersion>)

View file

@ -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) {

View file

@ -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<PackProfile> profile);
bool createPackComponent(QString instanceRoot, std::shared_ptr<PackProfile> profile);

View file

@ -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);
}

View file

@ -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<Flame::IndexedVersion>)

View file

@ -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

View file

@ -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<Modrinth::ModpackVersion>)

View file

@ -102,9 +102,10 @@ ExportPackDialog::ExportPackDialog(MinecraftInstancePtr instance, QWidget* paren
MinecraftInstance* mcInstance = dynamic_cast<MinecraftInstance*>(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);

View file

@ -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("<div style='width: 200px;'>%1</div>").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("<a href=\"%1\">%2</a>").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);
}

View file

@ -49,6 +49,7 @@
#include <QtMath>
#include "VisualGroup.h"
#include "ui/themes/CatPainter.h"
#include "ui/themes/ThemeManager.h"
#include <Application.h>
@ -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<LocaleString, VisualGroup*> 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;
}

View file

@ -41,6 +41,7 @@
#include <QScrollBar>
#include <functional>
#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<bool(const QString&)>;
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<VisualGroup*> 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<int, QRect> geometryCache;
bool m_catVisible = false;
QPixmap m_catPixmap;
mutable QCache<int, QRect> m_geometryCache;
CatPainter* m_cat = nullptr;
// point where the currently active mouse action started in geometry coordinates
QPoint m_pressedPosition;

View file

@ -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);

View file

@ -1,3 +1,4 @@
#pragma once
#include <QFuture>
#include <QJsonDocument>
#include <QJsonObject>

View file

@ -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();

View file

@ -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<ATLauncher::IndexedPack>();
QVariant raw = sourceModel()->data(index, Qt::UserRole);
Q_ASSERT(raw.canConvert<ATLauncher::IndexedPack>());
auto pack = raw.value<ATLauncher::IndexedPack>();
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>();
ATLauncher::IndexedPack rightPack = sourceModel()->data(right, Qt::UserRole).value<ATLauncher::IndexedPack>();
QVariant leftRaw = sourceModel()->data(left, Qt::UserRole);
Q_ASSERT(leftRaw.canConvert<ATLauncher::IndexedPack>());
auto leftPack = leftRaw.value<ATLauncher::IndexedPack>();
QVariant rightRaw = sourceModel()->data(right, Qt::UserRole);
Q_ASSERT(rightRaw.canConvert<ATLauncher::IndexedPack>());
auto rightPack = rightRaw.value<ATLauncher::IndexedPack>();
if (currentSorting == ByPopularity) {
return leftPack.position > rightPack.position;

View file

@ -143,7 +143,9 @@ void AtlPage::onSelectionChanged(QModelIndex first, [[maybe_unused]] QModelIndex
return;
}
selected = filterModel->data(first, Qt::UserRole).value<ATLauncher::IndexedPack>();
QVariant raw = filterModel->data(first, Qt::UserRole);
Q_ASSERT(raw.canConvert<ATLauncher::IndexedPack>());
selected = raw.value<ATLauncher::IndexedPack>();
ui->packDescription->setHtml(StringUtils::htmlListPatch(selected.description.replace("\n", "<br>")));

View file

@ -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<Flame::IndexedPack>());
modpacks[pos] = value.value<Flame::IndexedPack>();
return true;

View file

@ -166,7 +166,9 @@ void FlamePage::onSelectionChanged(QModelIndex curr, [[maybe_unused]] QModelInde
return;
}
current = listModel->data(curr, Qt::UserRole).value<Flame::IndexedPack>();
QVariant raw = listModel->data(curr, Qt::UserRole);
Q_ASSERT(raw.canConvert<Flame::IndexedPack>());
current = raw.value<Flame::IndexedPack>();
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;

View file

@ -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<Modpack>();
QVariant raw = currentModel->data(now, Qt::UserRole);
Q_ASSERT(raw.canConvert<Modpack>());
auto selectedPack = raw.value<Modpack>();
onPackSelectionChanged(&selectedPack);
}

View file

@ -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>();
Modpack rightPack = sourceModel()->data(right, Qt::UserRole).value<Modpack>();
QVariant leftRaw = sourceModel()->data(left, Qt::UserRole);
Q_ASSERT(leftRaw.canConvert<Modpack>());
auto leftPack = leftRaw.value<Modpack>();
QVariant rightRaw = sourceModel()->data(right, Qt::UserRole);
Q_ASSERT(rightRaw.canConvert<Modpack>());
auto rightPack = rightRaw.value<Modpack>();
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<Modpack>();
QVariant raw = sourceModel()->data(index, Qt::UserRole);
Q_ASSERT(raw.canConvert<Modpack>());
auto pack = raw.value<Modpack>();
return pack.name.contains(m_searchTerm, Qt::CaseInsensitive);
}

View file

@ -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>();
Modpack rightPack = sourceModel()->data(right, Qt::UserRole).value<Modpack>();
QVariant leftRaw = sourceModel()->data(left, Qt::UserRole);
Q_ASSERT(leftRaw.canConvert<Modpack>());
auto leftPack = leftRaw.value<Modpack>();
QVariant rightRaw = sourceModel()->data(right, Qt::UserRole);
Q_ASSERT(rightRaw.canConvert<Modpack>());
auto rightPack = rightRaw.value<Modpack>();
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<Modpack>();
QVariant raw = sourceModel()->data(index, Qt::UserRole);
Q_ASSERT(raw.canConvert<Modpack>());
auto pack = raw.value<Modpack>();
if (searchTerm.startsWith("#"))
return pack.packCode == searchTerm.mid(1);
return pack.name.contains(searchTerm, Qt::CaseInsensitive);

View file

@ -233,7 +233,9 @@ void Page::onPublicPackSelectionChanged(QModelIndex now, [[maybe_unused]] QModel
onPackSelectionChanged();
return;
}
Modpack selectedPack = publicFilterModel->data(now, Qt::UserRole).value<Modpack>();
QVariant raw = publicFilterModel->data(now, Qt::UserRole);
Q_ASSERT(raw.canConvert<Modpack>());
auto selectedPack = raw.value<Modpack>();
onPackSelectionChanged(&selectedPack);
}
@ -243,7 +245,9 @@ void Page::onThirdPartyPackSelectionChanged(QModelIndex now, [[maybe_unused]] QM
onPackSelectionChanged();
return;
}
Modpack selectedPack = thirdPartyFilterModel->data(now, Qt::UserRole).value<Modpack>();
QVariant raw = thirdPartyFilterModel->data(now, Qt::UserRole);
Q_ASSERT(raw.canConvert<Modpack>());
auto selectedPack = raw.value<Modpack>();
onPackSelectionChanged(&selectedPack);
}
@ -253,7 +257,9 @@ void Page::onPrivatePackSelectionChanged(QModelIndex now, [[maybe_unused]] QMode
onPackSelectionChanged();
return;
}
Modpack selectedPack = privateFilterModel->data(now, Qt::UserRole).value<Modpack>();
QVariant raw = privateFilterModel->data(now, Qt::UserRole);
Q_ASSERT(raw.canConvert<Modpack>());
auto selectedPack = raw.value<Modpack>();
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<Modpack>();
QVariant raw = currentModel->data(idx, Qt::UserRole);
Q_ASSERT(raw.canConvert<Modpack>());
auto pack = raw.value<Modpack>();
onPackSelectionChanged(&pack);
} else {
onPackSelectionChanged();

View file

@ -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<Modrinth::Modpack>());
modpacks[pos] = value.value<Modrinth::Modpack>();
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<NetJob*>(jobPtr.get())->getFailedActions().at(0);
if (failed_action->replyStatusCode() == -1) {

View file

@ -150,7 +150,9 @@ void ModrinthPage::onSelectionChanged(QModelIndex curr, [[maybe_unused]] QModelI
return;
}
current = m_model->data(curr, Qt::UserRole).value<Modrinth::Modpack>();
QVariant raw = m_model->data(curr, Qt::UserRole);
Q_ASSERT(raw.canConvert<Modrinth::Modpack>());
current = raw.value<Modrinth::Modpack>();
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;

View file

@ -36,6 +36,7 @@
#pragma once
#include <QList>
#include <QMetaType>
#include <QString>
namespace Technic {

View file

@ -135,7 +135,9 @@ void TechnicPage::onSelectionChanged(QModelIndex first, [[maybe_unused]] QModelI
return;
}
current = model->data(first, Qt::UserRole).value<Technic::Modpack>();
QVariant raw = model->data(first, Qt::UserRole);
Q_ASSERT(raw.canConvert<Technic::Modpack>());
current = raw.value<Technic::Modpack>();
suggestCurrent();
}

View file

@ -0,0 +1,51 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* Prism Launcher - Minecraft Launcher
* Copyright (c) 2025 Trial97 <alexandru.tripon97@gmail.com>
*
* 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 <https://www.gnu.org/licenses/>.
*/
#include "ui/themes/CatPainter.h"
#include <QPixmap>
#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);
};

View file

@ -0,0 +1,38 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* Prism Launcher - Minecraft Launcher
* Copyright (c) 2025 Trial97 <alexandru.tripon97@gmail.com>
*
* 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 <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <QMovie>
#include <QPainter>
#include <QPixmap>
#include <QString>
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;
};

View file

@ -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();

View file

@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>600</width>
<height>700</height>
<height>711</height>
</rect>
</property>
<property name="minimumSize">
@ -203,6 +203,53 @@
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="catFitLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Fit</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="catFitComboBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>77</width>
<height>30</height>
</size>
</property>
<property name="currentIndex">
<number>0</number>
</property>
<item>
<property name="text">
<string>Fit</string>
</property>
</item>
<item>
<property name="text">
<string>Fill</string>
</property>
</item>
<item>
<property name="text">
<string>Stretch</string>
</property>
</item>
</widget>
</item>
<item>
<widget class="QLabel" name="catOpacityLabel">
<property name="text">
@ -372,8 +419,7 @@
<string/>
</property>
<property name="icon">
<iconset theme="new">
<normaloff>.</normaloff>.</iconset>
<iconset theme="new"/>
</property>
<property name="flat">
<bool>true</bool>
@ -389,8 +435,7 @@
<string/>
</property>
<property name="icon">
<iconset theme="centralmods">
<normaloff>.</normaloff>.</iconset>
<iconset theme="centralmods"/>
</property>
<property name="flat">
<bool>true</bool>
@ -406,8 +451,7 @@
<string/>
</property>
<property name="icon">
<iconset theme="viewfolder">
<normaloff>.</normaloff>.</iconset>
<iconset theme="viewfolder"/>
</property>
<property name="flat">
<bool>true</bool>
@ -423,8 +467,7 @@
<string/>
</property>
<property name="icon">
<iconset theme="launch">
<normaloff>.</normaloff>.</iconset>
<iconset theme="launch"/>
</property>
<property name="flat">
<bool>true</bool>
@ -440,8 +483,7 @@
<string/>
</property>
<property name="icon">
<iconset theme="copy">
<normaloff>.</normaloff>.</iconset>
<iconset theme="copy"/>
</property>
<property name="flat">
<bool>true</bool>
@ -457,8 +499,7 @@
<string/>
</property>
<property name="icon">
<iconset theme="export">
<normaloff>.</normaloff>.</iconset>
<iconset theme="export"/>
</property>
<property name="flat">
<bool>true</bool>
@ -474,8 +515,7 @@
<string/>
</property>
<property name="icon">
<iconset theme="delete">
<normaloff>.</normaloff>.</iconset>
<iconset theme="delete"/>
</property>
<property name="flat">
<bool>true</bool>
@ -491,8 +531,7 @@
<string/>
</property>
<property name="icon">
<iconset theme="about">
<normaloff>.</normaloff>.</iconset>
<iconset theme="about"/>
</property>
<property name="flat">
<bool>true</bool>
@ -508,8 +547,7 @@
<string/>
</property>
<property name="icon">
<iconset theme="settings">
<normaloff>.</normaloff>.</iconset>
<iconset theme="settings"/>
</property>
<property name="flat">
<bool>true</bool>
@ -525,8 +563,7 @@
<string/>
</property>
<property name="icon">
<iconset theme="cat">
<normaloff>.</normaloff>.</iconset>
<iconset theme="cat"/>
</property>
<property name="flat">
<bool>true</bool>
@ -586,6 +623,7 @@
<tabstop>reloadThemesButton</tabstop>
<tabstop>consoleFont</tabstop>
<tabstop>fontSizeBox</tabstop>
<tabstop>catFitComboBox</tabstop>
<tabstop>catOpacitySlider</tabstop>
<tabstop>consolePreview</tabstop>
</tabstops>

View file

@ -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);

View file

@ -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;
};