Merge branch 'develop' of https://github.com/PrismLauncher/PrismLauncher into visit_mod_page

Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
This commit is contained in:
Trial97 2023-06-21 09:14:58 +03:00
commit ba609f3600
No known key found for this signature in database
GPG Key ID: 55EF5DA53DB36318
42 changed files with 437 additions and 113 deletions

View File

@ -191,7 +191,7 @@ jobs:
if: runner.os == 'Linux' if: runner.os == 'Linux'
run: | run: |
sudo apt-get -y update sudo apt-get -y update
sudo apt-get -y install ninja-build extra-cmake-modules scdoc sudo apt-get -y install ninja-build extra-cmake-modules scdoc appstream
- name: Install Dependencies (macOS) - name: Install Dependencies (macOS)
if: runner.os == 'macOS' if: runner.os == 'macOS'
@ -250,6 +250,7 @@ jobs:
wget "https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/continuous/linuxdeploy-plugin-qt-x86_64.AppImage" wget "https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/continuous/linuxdeploy-plugin-qt-x86_64.AppImage"
${{ github.workspace }}/.github/scripts/prepare_JREs.sh ${{ github.workspace }}/.github/scripts/prepare_JREs.sh
sudo apt install libopengl0
- name: Add QT_HOST_PATH var (Windows MSVC arm64) - name: Add QT_HOST_PATH var (Windows MSVC arm64)
if: runner.os == 'Windows' && matrix.architecture == 'arm64' if: runner.os == 'Windows' && matrix.architecture == 'arm64'
@ -467,7 +468,8 @@ jobs:
shell: bash shell: bash
run: | run: |
cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_APPIMAGE_DIR }}/usr cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_APPIMAGE_DIR }}/usr
mv ${{ env.INSTALL_APPIMAGE_DIR }}/usr/share/metainfo/org.prismlauncher.PrismLauncher.metainfo.xml ${{ env.INSTALL_APPIMAGE_DIR }}/usr/share/metainfo/org.prismlauncher.PrismLauncher.appdata.xml
export "NO_APPSTREAM=1" # we have to skip appstream checking because appstream on ubuntu 20.04 is outdated
export OUTPUT="PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage" export OUTPUT="PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage"
chmod +x linuxdeploy-*.AppImage chmod +x linuxdeploy-*.AppImage
@ -482,7 +484,8 @@ jobs:
cp -r /home/runner/work/PrismLauncher/Qt/${{ matrix.qt_version }}/gcc_64/plugins/iconengines/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/plugins/iconengines cp -r /home/runner/work/PrismLauncher/Qt/${{ matrix.qt_version }}/gcc_64/plugins/iconengines/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/plugins/iconengines
cp /usr/lib/x86_64-linux-gnu/libcrypto.so.1.1 ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/ cp /usr/lib/x86_64-linux-gnu/libcrypto.so.1.1 ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/
cp /usr/lib/x86_64-linux-gnu/libssl.so.1.1 ${{ env.INSTALL_APPIMAGE_DIR }}//usr/lib/ cp /usr/lib/x86_64-linux-gnu/libssl.so.1.1 ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/
cp /usr/lib/x86_64-linux-gnu/libOpenGL.so.0* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/
LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib" LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib"
LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-8-openjdk/lib/amd64/server" LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-8-openjdk/lib/amd64/server"
@ -599,7 +602,7 @@ jobs:
submodules: 'true' submodules: 'true'
- name: Install nix - name: Install nix
if: inputs.build_type == 'Debug' if: inputs.build_type == 'Debug'
uses: cachix/install-nix-action@v21 uses: cachix/install-nix-action@v22
with: with:
install_url: https://nixos.org/nix/install install_url: https://nixos.org/nix/install
extra_nix_config: | extra_nix_config: |

View File

@ -138,7 +138,7 @@ set(Launcher_NEWS_OPEN_URL "https://prismlauncher.org/news" CACHE STRING "URL th
set(Launcher_HELP_URL "https://prismlauncher.org/wiki/help-pages/%1" CACHE STRING "URL (with arg %1 to be substituted with page-id) that gets opened when the user requests help") set(Launcher_HELP_URL "https://prismlauncher.org/wiki/help-pages/%1" CACHE STRING "URL (with arg %1 to be substituted with page-id) that gets opened when the user requests help")
######## Set version numbers ######## ######## Set version numbers ########
set(Launcher_VERSION_MAJOR 7) set(Launcher_VERSION_MAJOR 8)
set(Launcher_VERSION_MINOR 0) set(Launcher_VERSION_MINOR 0)
set(Launcher_VERSION_NAME "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}") set(Launcher_VERSION_NAME "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}")

View File

@ -82,6 +82,7 @@ Config::Config()
{ {
GIT_REFSPEC = "refs/heads/stable"; GIT_REFSPEC = "refs/heads/stable";
GIT_TAG = versionString(); GIT_TAG = versionString();
GIT_COMMIT = "";
} }
if (GIT_REFSPEC.startsWith("refs/heads/")) if (GIT_REFSPEC.startsWith("refs/heads/"))

View File

@ -102,7 +102,7 @@ namespace fs = ghc::filesystem;
#include <linux/fs.h> #include <linux/fs.h>
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <unistd.h> #include <unistd.h>
#elif defined(Q_OS_MACOS) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD) #elif defined(Q_OS_MACOS) || defined(Q_OS_OPENBSD)
#include <sys/attr.h> #include <sys/attr.h>
#include <sys/clonefile.h> #include <sys/clonefile.h>
#elif defined(Q_OS_WIN) #elif defined(Q_OS_WIN)
@ -1151,7 +1151,7 @@ bool clone_file(const QString& src, const QString& dst, std::error_code& ec)
return false; return false;
} }
#elif defined(Q_OS_MACOS) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD) #elif defined(Q_OS_MACOS) || defined(Q_OS_OPENBSD)
if (!macos_bsd_clonefile(src_path, dst_path, ec)) { if (!macos_bsd_clonefile(src_path, dst_path, ec)) {
qDebug() << "failed macos_bsd_clonefile:"; qDebug() << "failed macos_bsd_clonefile:";
@ -1380,7 +1380,7 @@ bool linux_ficlone(const std::string& src_path, const std::string& dst_path, std
return true; return true;
} }
#elif defined(Q_OS_MACOS) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD) #elif defined(Q_OS_MACOS) || defined(Q_OS_OPENBSD)
bool macos_bsd_clonefile(const std::string& src_path, const std::string& dst_path, std::error_code& ec) bool macos_bsd_clonefile(const std::string& src_path, const std::string& dst_path, std::error_code& ec)
{ {

View File

@ -45,7 +45,10 @@ QString InstanceName::name() const
{ {
if (!m_modified_name.isEmpty()) if (!m_modified_name.isEmpty())
return modifiedName(); return modifiedName();
if (!m_original_version.isEmpty())
return QString("%1 %2").arg(m_original_name, m_original_version); return QString("%1 %2").arg(m_original_name, m_original_version);
return m_original_name;
} }
QString InstanceName::originalName() const QString InstanceName::originalName() const

View File

@ -1,7 +1,8 @@
// SPDX-License-Identifier: GPL-3.0-only // SPDX-License-Identifier: GPL-3.0-only
/* /*
* PolyMC - Minecraft Launcher * Prism Launcher - Minecraft Launcher
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> * Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
* Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me>
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@ -54,9 +55,14 @@ public:
bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
{ {
const auto &filters = m_parent->filters(); const auto &filters = m_parent->filters();
const QString &search = m_parent->search();
const QModelIndex idx = sourceModel()->index(source_row, 0, source_parent);
if (!search.isEmpty() && !sourceModel()->data(idx, BaseVersionList::VersionRole).toString().contains(search, Qt::CaseInsensitive))
return false;
for (auto it = filters.begin(); it != filters.end(); ++it) for (auto it = filters.begin(); it != filters.end(); ++it)
{ {
auto idx = sourceModel()->index(source_row, 0, source_parent);
auto data = sourceModel()->data(idx, it.key()); auto data = sourceModel()->data(idx, it.key());
auto match = data.toString(); auto match = data.toString();
if(!it.value()->accepts(match)) if(!it.value()->accepts(match))
@ -206,10 +212,6 @@ QVariant VersionProxyModel::data(const QModelIndex &index, int role) const
return tr("Latest"); return tr("Latest");
} }
} }
else if(index.row() == 0)
{
return tr("Latest");
}
} }
} }
default: default:
@ -239,10 +241,6 @@ QVariant VersionProxyModel::data(const QModelIndex &index, int role) const
return APPLICATION->getThemedIcon("bug"); return APPLICATION->getThemedIcon("bug");
} }
} }
else if(index.row() == 0)
{
return APPLICATION->getThemedIcon("bug");
}
QPixmap pixmap; QPixmap pixmap;
QPixmapCache::find("placeholder", &pixmap); QPixmapCache::find("placeholder", &pixmap);
if(!pixmap) if(!pixmap)
@ -431,6 +429,7 @@ QModelIndex VersionProxyModel::getVersion(const QString& version) const
void VersionProxyModel::clearFilters() void VersionProxyModel::clearFilters()
{ {
m_filters.clear(); m_filters.clear();
m_search.clear();
filterModel->filterChanged(); filterModel->filterChanged();
} }
@ -440,11 +439,21 @@ void VersionProxyModel::setFilter(const BaseVersionList::ModelRoles column, Filt
filterModel->filterChanged(); filterModel->filterChanged();
} }
void VersionProxyModel::setSearch(const QString &search) {
m_search = search;
filterModel->filterChanged();
}
const VersionProxyModel::FilterMap &VersionProxyModel::filters() const const VersionProxyModel::FilterMap &VersionProxyModel::filters() const
{ {
return m_filters; return m_filters;
} }
const QString &VersionProxyModel::search() const
{
return m_search;
}
void VersionProxyModel::sourceAboutToBeReset() void VersionProxyModel::sourceAboutToBeReset()
{ {
beginResetModel(); beginResetModel();

View File

@ -38,7 +38,9 @@ public:
virtual void setSourceModel(QAbstractItemModel *sourceModel) override; virtual void setSourceModel(QAbstractItemModel *sourceModel) override;
const FilterMap &filters() const; const FilterMap &filters() const;
const QString &search() const;
void setFilter(const BaseVersionList::ModelRoles column, Filter * filter); void setFilter(const BaseVersionList::ModelRoles column, Filter * filter);
void setSearch(const QString &search);
void clearFilters(); void clearFilters();
QModelIndex getRecommended() const; QModelIndex getRecommended() const;
QModelIndex getVersion(const QString & version) const; QModelIndex getVersion(const QString & version) const;
@ -59,6 +61,7 @@ private slots:
private: private:
QList<Column> m_columns; QList<Column> m_columns;
FilterMap m_filters; FilterMap m_filters;
QString m_search;
BaseVersionList::RoleList roles; BaseVersionList::RoleList roles;
VersionFilterModel * filterModel; VersionFilterModel * filterModel;
bool hasRecommended = false; bool hasRecommended = false;

View File

@ -1,7 +1,8 @@
// SPDX-License-Identifier: GPL-3.0-only // SPDX-License-Identifier: GPL-3.0-only
/* /*
* PolyMC - Minecraft Launcher * Prism Launcher - Minecraft Launcher
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> * Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
* Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me>
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@ -98,6 +99,8 @@ QVariant JavaInstallList::data(const QModelIndex &index, int role) const
auto version = std::dynamic_pointer_cast<JavaInstall>(m_vlist[index.row()]); auto version = std::dynamic_pointer_cast<JavaInstall>(m_vlist[index.row()]);
switch (role) switch (role)
{ {
case SortRole:
return -index.row();
case VersionPointerRole: case VersionPointerRole:
return QVariant::fromValue(m_vlist[index.row()]); return QVariant::fromValue(m_vlist[index.row()]);
case VersionIdRole: case VersionIdRole:

View File

@ -149,9 +149,10 @@ void MinecraftInstance::loadSpecificSettings()
// special! // special!
m_settings->registerPassthrough(global_settings->getSetting("JavaTimestamp"), javaOrLocation); m_settings->registerPassthrough(global_settings->getSetting("JavaTimestamp"), javaOrLocation);
m_settings->registerPassthrough(global_settings->getSetting("JavaVersion"), javaOrLocation);
m_settings->registerPassthrough(global_settings->getSetting("JavaArchitecture"), javaOrLocation); m_settings->registerPassthrough(global_settings->getSetting("JavaArchitecture"), javaOrLocation);
m_settings->registerPassthrough(global_settings->getSetting("JavaRealArchitecture"), javaOrLocation); m_settings->registerPassthrough(global_settings->getSetting("JavaRealArchitecture"), javaOrLocation);
m_settings->registerPassthrough(global_settings->getSetting("JavaVersion"), javaOrLocation);
m_settings->registerPassthrough(global_settings->getSetting("JavaVendor"), javaOrLocation);
// Window Size // Window Size
auto windowSetting = m_settings->registerSetting("OverrideWindow", false); auto windowSetting = m_settings->registerSetting("OverrideWindow", false);

View File

@ -333,13 +333,13 @@ QVariant AccountList::data(const QModelIndex &index, int role) const
case MigrationColumn: { case MigrationColumn: {
if(account->isMSA() || account->isOffline()) { if(account->isMSA() || account->isOffline()) {
return tr("N/A", "Can Migrate?"); return tr("N/A", "Can Migrate");
} }
if (account->canMigrate()) { if (account->canMigrate()) {
return tr("Yes", "Can Migrate?"); return tr("Yes", "Can Migrate");
} }
else { else {
return tr("No", "Can Migrate?"); return tr("No", "Can Migrate");
} }
} }

View File

@ -153,6 +153,9 @@ bool FlameCreationTask::updateInstance()
old_files.remove(file.key()); old_files.remove(file.key());
files_iterator = files.erase(files_iterator); files_iterator = files.erase(files_iterator);
if (files_iterator != files.begin())
files_iterator--;
} }
} }

View File

@ -50,7 +50,7 @@ INIFile::INIFile() {}
bool INIFile::saveFile(QString fileName) bool INIFile::saveFile(QString fileName)
{ {
if (!contains("ConfigVersion")) if (!contains("ConfigVersion"))
insert("ConfigVersion", "1.1"); insert("ConfigVersion", "1.2");
QSettings _settings_obj{ fileName, QSettings::Format::IniFormat }; QSettings _settings_obj{ fileName, QSettings::Format::IniFormat };
_settings_obj.setFallbacksEnabled(false); _settings_obj.setFallbacksEnabled(false);
@ -97,6 +97,20 @@ QString unescape(QString orig)
} }
return out; return out;
} }
QString unquote(QString str)
{
if ((str.contains(QChar(';')) || str.contains(QChar('=')) || str.contains(QChar(','))) && str.endsWith("\"") && str.startsWith("\"")) {
#if QT_VERSION < QT_VERSION_CHECK(6, 5, 0)
str = str.remove(0, 1);
str = str.remove(str.size() - 1, 1);
#else
str = str.removeFirst().removeLast();
#endif
}
return str;
}
bool parseOldFileFormat(QIODevice& device, QSettings::SettingsMap& map) bool parseOldFileFormat(QIODevice& device, QSettings::SettingsMap& map)
{ {
QTextStream in(device.readAll()); QTextStream in(device.readAll());
@ -124,7 +138,7 @@ bool parseOldFileFormat(QIODevice& device, QSettings::SettingsMap& map)
QString key = line.left(eqPos).trimmed(); QString key = line.left(eqPos).trimmed();
QString valueStr = line.right(line.length() - eqPos - 1).trimmed(); QString valueStr = line.right(line.length() - eqPos - 1).trimmed();
valueStr = unescape(valueStr); valueStr = unquote(unescape(valueStr));
QVariant value(valueStr); QVariant value(valueStr);
map.insert(key, value); map.insert(key, value);
@ -154,7 +168,17 @@ bool INIFile::loadFile(QString fileName)
file.close(); file.close();
for (auto&& key : map.keys()) for (auto&& key : map.keys())
insert(key, map.value(key)); insert(key, map.value(key));
insert("ConfigVersion", "1.1"); insert("ConfigVersion", "1.2");
} else if (_settings_obj.value("ConfigVersion").toString() == "1.1") {
for (auto&& key : _settings_obj.allKeys()) {
if (auto valueStr = _settings_obj.value(key).toString();
(valueStr.contains(QChar(';')) || valueStr.contains(QChar('=')) || valueStr.contains(QChar(','))) &&
valueStr.endsWith("\"") && valueStr.startsWith("\"")) {
insert(key, unquote(valueStr));
} else
insert(key, _settings_obj.value(key));
}
insert("ConfigVersion", "1.2");
} else } else
for (auto&& key : _settings_obj.allKeys()) for (auto&& key : _settings_obj.allKeys())
insert(key, _settings_obj.value(key)); insert(key, _settings_obj.value(key));

View File

@ -1234,6 +1234,12 @@ void MainWindow::on_actionViewInstanceFolder_triggered()
DesktopServices::openDirectory(str); DesktopServices::openDirectory(str);
} }
void MainWindow::on_actionViewLauncherRootFolder_triggered()
{
const QString dataPath = QDir::currentPath();
DesktopServices::openDirectory(dataPath);
}
void MainWindow::refreshInstances() void MainWindow::refreshInstances()
{ {
APPLICATION->instances()->loadList(); APPLICATION->instances()->loadList();

View File

@ -113,6 +113,8 @@ private slots:
void on_actionViewInstanceFolder_triggered(); void on_actionViewInstanceFolder_triggered();
void on_actionViewLauncherRootFolder_triggered();
void on_actionViewSelectedInstFolder_triggered(); void on_actionViewSelectedInstFolder_triggered();
void refreshInstances(); void refreshInstances();

View File

@ -187,6 +187,7 @@
<bool>true</bool> <bool>true</bool>
</property> </property>
<addaction name="actionViewInstanceFolder"/> <addaction name="actionViewInstanceFolder"/>
<addaction name="actionViewLauncherRootFolder"/>
<addaction name="actionViewCentralModsFolder"/> <addaction name="actionViewCentralModsFolder"/>
</widget> </widget>
<widget class="QMenu" name="accountsMenu"> <widget class="QMenu" name="accountsMenu">
@ -541,6 +542,18 @@
<string>Open the instance folder in a file browser.</string> <string>Open the instance folder in a file browser.</string>
</property> </property>
</action> </action>
<action name="actionViewLauncherRootFolder">
<property name="icon">
<iconset theme="viewfolder">
<normaloff>.</normaloff>.</iconset>
</property>
<property name="text">
<string>&amp;View Launcher Root Folder</string>
</property>
<property name="toolTip">
<string>Open the launcher's root folder in a file browser.</string>
</property>
</action>
<action name="actionViewCentralModsFolder"> <action name="actionViewCentralModsFolder">
<property name="icon"> <property name="icon">
<iconset theme="centralmods"> <iconset theme="centralmods">

View File

@ -1,4 +1,24 @@
/* Copyright 2013-2021 MultiMC Contributors // SPDX-License-Identifier: GPL-3.0-only
/*
* Prism Launcher - Minecraft Launcher
* Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me>
*
* 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/>.
*
* This file incorporates work covered by the following copyright and
* permission notice:
*
* Copyright 2013-2021 MultiMC Contributors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -22,15 +42,10 @@
#include <QtWidgets/QVBoxLayout> #include <QtWidgets/QVBoxLayout>
#include <QDebug> #include <QDebug>
#include "ui/dialogs/ProgressDialog.h"
#include "ui/widgets/VersionSelectWidget.h" #include "ui/widgets/VersionSelectWidget.h"
#include "ui/dialogs/CustomMessageBox.h"
#include "BaseVersion.h" #include "BaseVersion.h"
#include "BaseVersionList.h" #include "BaseVersionList.h"
#include "tasks/Task.h"
#include "Application.h"
#include "VersionProxyModel.h"
VersionSelectDialog::VersionSelectDialog(BaseVersionList *vlist, QString title, QWidget *parent, bool cancelable) VersionSelectDialog::VersionSelectDialog(BaseVersionList *vlist, QString title, QWidget *parent, bool cancelable)
: QDialog(parent) : QDialog(parent)
@ -40,7 +55,7 @@ VersionSelectDialog::VersionSelectDialog(BaseVersionList *vlist, QString title,
m_verticalLayout = new QVBoxLayout(this); m_verticalLayout = new QVBoxLayout(this);
m_verticalLayout->setObjectName(QStringLiteral("verticalLayout")); m_verticalLayout->setObjectName(QStringLiteral("verticalLayout"));
m_versionWidget = new VersionSelectWidget(parent); m_versionWidget = new VersionSelectWidget(true, parent);
m_verticalLayout->addWidget(m_versionWidget); m_verticalLayout->addWidget(m_versionWidget);
m_horizontalLayout = new QHBoxLayout(); m_horizontalLayout = new QHBoxLayout();

View File

@ -35,15 +35,16 @@
#pragma once #pragma once
#include <QString>
#include <QIcon> #include <QIcon>
#include <QString>
#include <functional>
#include <memory> #include <memory>
#include "BasePageContainer.h" #include "BasePageContainer.h"
class BasePage class BasePage {
{
public: public:
using updateExtraInfoFunc = std::function<void(QString)>;
virtual ~BasePage() {} virtual ~BasePage() {}
virtual QString id() const = 0; virtual QString id() const = 0;
virtual QString displayName() const = 0; virtual QString displayName() const = 0;
@ -63,15 +64,14 @@ public:
} }
virtual void openedImpl() {} virtual void openedImpl() {}
virtual void closedImpl() {} virtual void closedImpl() {}
virtual void setParentContainer(BasePageContainer * container) virtual void setParentContainer(BasePageContainer* container) { m_container = container; };
{
m_container = container;
};
virtual void retranslate() {} virtual void retranslate() {}
public: public:
int stackIndex = -1; int stackIndex = -1;
int listIndex = -1; int listIndex = -1;
updateExtraInfoFunc updateExtraInfo;
protected: protected:
BasePageContainer* m_container = nullptr; BasePageContainer* m_container = nullptr;
bool isOpened = false; bool isOpened = false;

View File

@ -172,7 +172,7 @@
<string>Disable using metadata provided by mod providers (like Modrinth or Curseforge) for mods.</string> <string>Disable using metadata provided by mod providers (like Modrinth or Curseforge) for mods.</string>
</property> </property>
<property name="text"> <property name="text">
<string>Disable using metadata for mods?</string> <string>Disable using metadata for mods</string>
</property> </property>
</widget> </widget>
</item> </item>
@ -307,21 +307,21 @@
<item> <item>
<widget class="QCheckBox" name="showConsoleCheck"> <widget class="QCheckBox" name="showConsoleCheck">
<property name="text"> <property name="text">
<string>Show console while the game is &amp;running?</string> <string>Show console while the game is &amp;running</string>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QCheckBox" name="autoCloseConsoleCheck"> <widget class="QCheckBox" name="autoCloseConsoleCheck">
<property name="text"> <property name="text">
<string>&amp;Automatically close console when the game quits?</string> <string>&amp;Automatically close console when the game quits</string>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QCheckBox" name="showConsoleErrorCheck"> <widget class="QCheckBox" name="showConsoleErrorCheck">
<property name="text"> <property name="text">
<string>Show console when the game &amp;crashes?</string> <string>Show console when the game &amp;crashes</string>
</property> </property>
</widget> </widget>
</item> </item>

View File

@ -51,7 +51,7 @@
<item> <item>
<widget class="QCheckBox" name="maximizedCheckBox"> <widget class="QCheckBox" name="maximizedCheckBox">
<property name="text"> <property name="text">
<string>Start Minecraft &amp;maximized?</string> <string>Start Minecraft &amp;maximized</string>
</property> </property>
</widget> </widget>
</item> </item>

View File

@ -1,3 +1,38 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* Prism Launcher - Minecraft Launcher
* Copyright (c) 2023 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/>.
*
* This file incorporates work covered by the following copyright and
* permission notice:
*
* Copyright 2013-2021 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "ExternalResourcesPage.h" #include "ExternalResourcesPage.h"
#include "ui/dialogs/CustomMessageBox.h" #include "ui/dialogs/CustomMessageBox.h"
#include "ui_ExternalResourcesPage.h" #include "ui_ExternalResourcesPage.h"
@ -9,6 +44,7 @@
#include <QKeyEvent> #include <QKeyEvent>
#include <QMenu> #include <QMenu>
#include <algorithm>
ExternalResourcesPage::ExternalResourcesPage(BaseInstance* instance, std::shared_ptr<ResourceFolderModel> model, QWidget* parent) ExternalResourcesPage::ExternalResourcesPage(BaseInstance* instance, std::shared_ptr<ResourceFolderModel> model, QWidget* parent)
: QMainWindow(parent), m_instance(instance), ui(new Ui::ExternalResourcesPage), m_model(model) : QMainWindow(parent), m_instance(instance), ui(new Ui::ExternalResourcesPage), m_model(model)
@ -43,6 +79,13 @@ ExternalResourcesPage::ExternalResourcesPage(BaseInstance* instance, std::shared
auto selection_model = ui->treeView->selectionModel(); auto selection_model = ui->treeView->selectionModel();
connect(selection_model, &QItemSelectionModel::currentChanged, this, &ExternalResourcesPage::current); connect(selection_model, &QItemSelectionModel::currentChanged, this, &ExternalResourcesPage::current);
auto updateExtra = [this]() {
if (updateExtraInfo)
updateExtraInfo(extraHeaderInfoString());
};
connect(selection_model, &QItemSelectionModel::selectionChanged, this, updateExtra);
connect(model.get(), &ResourceFolderModel::updateFinished, this, updateExtra);
connect(ui->filterEdit, &QLineEdit::textChanged, this, &ExternalResourcesPage::filterTextChanged); connect(ui->filterEdit, &QLineEdit::textChanged, this, &ExternalResourcesPage::filterTextChanged);
} }
@ -248,6 +291,15 @@ bool ExternalResourcesPage::onSelectionChanged(const QModelIndex& current, const
int row = sourceCurrent.row(); int row = sourceCurrent.row();
Resource const& resource = m_model->at(row); Resource const& resource = m_model->at(row);
ui->frame->updateWithResource(resource); ui->frame->updateWithResource(resource);
return true; return true;
} }
QString ExternalResourcesPage::extraHeaderInfoString()
{
if (ui && ui->treeView && ui->treeView->selectionModel()) {
auto selection = m_filterModel->mapSelectionToSource(ui->treeView->selectionModel()->selection()).indexes();
if (auto count = std::count_if(selection.cbegin(), selection.cend(), [](auto v) { return v.column() == 0; }); count != 0)
return tr(" (%1 installed, %2 selected)").arg(m_model->size()).arg(count);
}
return tr(" (%1 installed)").arg(m_model->size());
}

View File

@ -29,6 +29,7 @@ class ExternalResourcesPage : public QMainWindow, public BasePage {
virtual QString helpPage() const override = 0; virtual QString helpPage() const override = 0;
virtual bool shouldDisplay() const override = 0; virtual bool shouldDisplay() const override = 0;
QString extraHeaderInfoString();
void openedImpl() override; void openedImpl() override;
void closedImpl() override; void closedImpl() override;

View File

@ -269,7 +269,7 @@
<item> <item>
<widget class="QCheckBox" name="maximizedCheckBox"> <widget class="QCheckBox" name="maximizedCheckBox">
<property name="text"> <property name="text">
<string>Start Minecraft maximized?</string> <string>Start Minecraft maximized</string>
</property> </property>
</widget> </widget>
</item> </item>
@ -341,21 +341,21 @@
<item> <item>
<widget class="QCheckBox" name="showConsoleCheck"> <widget class="QCheckBox" name="showConsoleCheck">
<property name="text"> <property name="text">
<string>Show console while the game is running?</string> <string>Show console while the game is running</string>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QCheckBox" name="autoCloseConsoleCheck"> <widget class="QCheckBox" name="autoCloseConsoleCheck">
<property name="text"> <property name="text">
<string>Automatically close console when the game quits?</string> <string>Automatically close console when the game quits</string>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QCheckBox" name="showConsoleErrorCheck"> <widget class="QCheckBox" name="showConsoleErrorCheck">
<property name="text"> <property name="text">
<string>Show console when the game crashes?</string> <string>Show console when the game crashes</string>
</property> </property>
</widget> </widget>
</item> </item>

View File

@ -119,12 +119,8 @@ ModFolderPage::ModFolderPage(BaseInstance* inst, std::shared_ptr<ModFolderModel>
connect(mods.get(), &ModFolderModel::rowsRemoved, this, connect(mods.get(), &ModFolderModel::rowsRemoved, this,
[this, check_allow_update] { ui->actionUpdateItem->setEnabled(check_allow_update()); }); [this, check_allow_update] { ui->actionUpdateItem->setEnabled(check_allow_update()); });
connect(mods.get(), &ModFolderModel::updateFinished, this, [this, check_allow_update, mods] { connect(mods.get(), &ModFolderModel::updateFinished, this,
ui->actionUpdateItem->setEnabled(check_allow_update()); [this, check_allow_update] { ui->actionUpdateItem->setEnabled(check_allow_update()); });
// Prevent a weird crash when trying to open the mods page twice in a session o.O
disconnect(mods.get(), &ModFolderModel::updateFinished, this, 0);
});
connect(m_instance, &BaseInstance::runningStatusChanged, this, &ModFolderPage::runningStateChanged); connect(m_instance, &BaseInstance::runningStatusChanged, this, &ModFolderPage::runningStateChanged);
ModFolderPage::runningStateChanged(m_instance && m_instance->isRunning()); ModFolderPage::runningStateChanged(m_instance && m_instance->isRunning());

View File

@ -6,12 +6,14 @@
#include "minecraft/MinecraftInstance.h" #include "minecraft/MinecraftInstance.h"
#include "minecraft/PackProfile.h" #include "minecraft/PackProfile.h"
#include "minecraft/mod/ModFolderModel.h"
#include <QMessageBox> #include <QMessageBox>
#include <algorithm>
namespace ResourceDownload { namespace ResourceDownload {
ModModel::ModModel(BaseInstance const& base_inst, ResourceAPI* api) : ResourceModel(api), m_base_instance(base_inst) {} ModModel::ModModel(BaseInstance& base_inst, ResourceAPI* api) : ResourceModel(api), m_base_instance(base_inst) {}
/******** Make data requests ********/ /******** Make data requests ********/
@ -67,4 +69,14 @@ void ModModel::searchWithTerm(const QString& term, unsigned int sort, bool filte
refresh(); refresh();
} }
bool ModModel::isPackInstalled(ModPlatform::IndexedPack::Ptr pack) const
{
auto allMods = static_cast<MinecraftInstance&>(m_base_instance).loaderModList()->allMods();
return std::any_of(allMods.cbegin(), allMods.cend(), [pack](Mod* mod) {
if (auto meta = mod->metadata(); meta)
return meta->provider == pack->provider && meta->project_id == pack->addonId;
return false;
});
}
} // namespace ResourceDownload } // namespace ResourceDownload

View File

@ -24,7 +24,7 @@ class ModModel : public ResourceModel {
Q_OBJECT Q_OBJECT
public: public:
ModModel(const BaseInstance&, ResourceAPI* api); ModModel(BaseInstance&, ResourceAPI* api);
/* Ask the API for more information */ /* Ask the API for more information */
void searchWithTerm(const QString& term, unsigned int sort, bool filter_changed); void searchWithTerm(const QString& term, unsigned int sort, bool filter_changed);
@ -42,9 +42,10 @@ class ModModel : public ResourceModel {
protected: protected:
auto documentToArray(QJsonDocument& obj) const -> QJsonArray override = 0; auto documentToArray(QJsonDocument& obj) const -> QJsonArray override = 0;
virtual bool isPackInstalled(ModPlatform::IndexedPack::Ptr) const override;
protected: protected:
const BaseInstance& m_base_instance; BaseInstance& m_base_instance;
std::shared_ptr<ModFilterWidget::Filter> m_filter = nullptr; std::shared_ptr<ModFilterWidget::Filter> m_filter = nullptr;
}; };

View File

@ -77,6 +77,8 @@ auto ResourceModel::data(const QModelIndex& index, int role) const -> QVariant
return pack->description; return pack->description;
case UserDataTypes::SELECTED: case UserDataTypes::SELECTED:
return pack->isAnyVersionSelected(); return pack->isAnyVersionSelected();
case UserDataTypes::INSTALLED:
return this->isPackInstalled(pack);
default: default:
break; break;
} }
@ -95,6 +97,7 @@ QHash<int, QByteArray> ResourceModel::roleNames() const
roles[UserDataTypes::TITLE] = "title"; roles[UserDataTypes::TITLE] = "title";
roles[UserDataTypes::DESCRIPTION] = "description"; roles[UserDataTypes::DESCRIPTION] = "description";
roles[UserDataTypes::SELECTED] = "selected"; roles[UserDataTypes::SELECTED] = "selected";
roles[UserDataTypes::INSTALLED] = "installed";
return roles; return roles;
} }

View File

@ -116,6 +116,8 @@ class ResourceModel : public QAbstractListModel {
virtual void loadExtraPackInfo(ModPlatform::IndexedPack&, QJsonObject&); virtual void loadExtraPackInfo(ModPlatform::IndexedPack&, QJsonObject&);
virtual void loadIndexedPackVersions(ModPlatform::IndexedPack&, QJsonArray&); virtual void loadIndexedPackVersions(ModPlatform::IndexedPack&, QJsonArray&);
virtual bool isPackInstalled(ModPlatform::IndexedPack::Ptr) const { return false; }
protected: protected:
/* Basic search parameters */ /* Basic search parameters */
enum class SearchState { None, CanFetchMore, ResetRequested, Finished } m_search_state = SearchState::None; enum class SearchState { None, CanFetchMore, ResetRequested, Finished } m_search_state = SearchState::None;

View File

@ -60,6 +60,8 @@ QVariant ListModel::data(const QModelIndex& index, int role) const
return pack.description; return pack.description;
case UserDataTypes::SELECTED: case UserDataTypes::SELECTED:
return false; return false;
case UserDataTypes::INSTALLED:
return false;
default: default:
break; break;
} }

View File

@ -11,7 +11,7 @@
namespace ResourceDownload { namespace ResourceDownload {
FlameModModel::FlameModModel(BaseInstance const& base) : ModModel(base, new FlameAPI) {} FlameModModel::FlameModModel(BaseInstance& base) : ModModel(base, new FlameAPI) {}
void FlameModModel::loadIndexedPack(ModPlatform::IndexedPack& m, QJsonObject& obj) void FlameModModel::loadIndexedPack(ModPlatform::IndexedPack& m, QJsonObject& obj)
{ {

View File

@ -14,7 +14,7 @@ class FlameModModel : public ModModel {
Q_OBJECT Q_OBJECT
public: public:
FlameModModel(const BaseInstance&); FlameModModel(BaseInstance&);
~FlameModModel() override = default; ~FlameModModel() override = default;
private: private:

View File

@ -106,6 +106,8 @@ auto ModpackListModel::data(const QModelIndex& index, int role) const -> QVarian
return pack.description; return pack.description;
case UserDataTypes::SELECTED: case UserDataTypes::SELECTED:
return false; return false;
case UserDataTypes::INSTALLED:
return false;
default: default:
break; break;
} }

View File

@ -25,7 +25,7 @@
namespace ResourceDownload { namespace ResourceDownload {
ModrinthModModel::ModrinthModModel(BaseInstance const& base) : ModModel(base, new ModrinthAPI) {} ModrinthModModel::ModrinthModModel(BaseInstance& base) : ModModel(base, new ModrinthAPI) {}
void ModrinthModModel::loadIndexedPack(ModPlatform::IndexedPack& m, QJsonObject& obj) void ModrinthModModel::loadIndexedPack(ModPlatform::IndexedPack& m, QJsonObject& obj)
{ {

View File

@ -30,7 +30,7 @@ class ModrinthModModel : public ModModel {
Q_OBJECT Q_OBJECT
public: public:
ModrinthModModel(const BaseInstance&); ModrinthModModel(BaseInstance&);
~ModrinthModModel() override = default; ~ModrinthModModel() override = default;
private: private:

View File

@ -46,7 +46,7 @@ void JavaSettingsWidget::setupUi()
m_verticalLayout = new QVBoxLayout(this); m_verticalLayout = new QVBoxLayout(this);
m_verticalLayout->setObjectName(QStringLiteral("verticalLayout")); m_verticalLayout->setObjectName(QStringLiteral("verticalLayout"));
m_versionWidget = new VersionSelectWidget(this); m_versionWidget = new VersionSelectWidget(true, this);
m_verticalLayout->addWidget(m_versionWidget); m_verticalLayout->addWidget(m_versionWidget);
m_horizontalLayout = new QHBoxLayout(); m_horizontalLayout = new QHBoxLayout();

View File

@ -93,6 +93,10 @@ PageContainer::PageContainer(BasePageProvider *pageProvider, QString defaultId,
page->listIndex = counter; page->listIndex = counter;
page->setParentContainer(this); page->setParentContainer(this);
counter++; counter++;
page->updateExtraInfo = [this](QString info) {
if (m_currentPage)
m_header->setText(m_currentPage->displayName() + info);
};
} }
m_model->setPages(pages); m_model->setPages(pages);

View File

@ -64,6 +64,17 @@ void ProjectItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem& o
font.setBold(true); font.setBold(true);
font.setUnderline(true); font.setUnderline(true);
} }
if (index.data(UserDataTypes::INSTALLED).toBool()) {
auto hRect = opt.rect;
hRect.setX(hRect.x() + 1);
hRect.setY(hRect.y() + 1);
hRect.setHeight(hRect.height() - 2);
hRect.setWidth(hRect.width() - 2);
// Set nice font
font.setItalic(true);
font.setOverline(true);
painter->drawRect(hRect);
}
font.setPointSize(font.pointSize() + 2); font.setPointSize(font.pointSize() + 2);
painter->setFont(font); painter->setFont(font);

View File

@ -6,7 +6,8 @@
enum UserDataTypes { enum UserDataTypes {
TITLE = 257, // QString TITLE = 257, // QString
DESCRIPTION = 258, // QString DESCRIPTION = 258, // QString
SELECTED = 259 // bool SELECTED = 259, // bool
INSTALLED = 260 // bool
}; };
/** This is an item delegate composed of: /** This is an item delegate composed of:

View File

@ -1,7 +1,8 @@
// SPDX-License-Identifier: GPL-3.0-only // SPDX-License-Identifier: GPL-3.0-only
/* /*
* PolyMC - Minecraft Launcher * Prism Launcher - Minecraft Launcher
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> * Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
* Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me>
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@ -125,14 +126,9 @@ void VersionListView::paintEvent(QPaintEvent *event)
QString VersionListView::currentEmptyString() const QString VersionListView::currentEmptyString() const
{ {
if(m_itemCount) {
return QString();
}
switch(m_emptyMode) switch(m_emptyMode)
{ {
default: default:
case VersionListView::Empty:
return QString();
case VersionListView::String: case VersionListView::String:
return m_emptyString; return m_emptyString;
case VersionListView::ErrorString: case VersionListView::ErrorString:

View File

@ -1,15 +1,20 @@
#include "VersionSelectWidget.h" #include "VersionSelectWidget.h"
#include <QApplication>
#include <QEvent>
#include <QHeaderView>
#include <QKeyEvent>
#include <QProgressBar> #include <QProgressBar>
#include <QVBoxLayout> #include <QVBoxLayout>
#include <QHeaderView>
#include "VersionProxyModel.h" #include "VersionProxyModel.h"
#include "ui/dialogs/CustomMessageBox.h" #include "ui/dialogs/CustomMessageBox.h"
VersionSelectWidget::VersionSelectWidget(QWidget* parent) VersionSelectWidget::VersionSelectWidget(QWidget* parent) : VersionSelectWidget(false, parent) {}
: QWidget(parent)
VersionSelectWidget::VersionSelectWidget(bool focusSearch, QWidget* parent)
: QWidget(parent), focusSearch(focusSearch)
{ {
setObjectName(QStringLiteral("VersionSelectWidget")); setObjectName(QStringLiteral("VersionSelectWidget"));
verticalLayout = new QVBoxLayout(this); verticalLayout = new QVBoxLayout(this);
@ -30,6 +35,21 @@ VersionSelectWidget::VersionSelectWidget(QWidget* parent)
listView->setModel(m_proxyModel); listView->setModel(m_proxyModel);
verticalLayout->addWidget(listView); verticalLayout->addWidget(listView);
search = new QLineEdit(this);
search->setPlaceholderText(tr("Search"));
search->setClearButtonEnabled(true);
verticalLayout->addWidget(search);
connect(search, &QLineEdit::textEdited, [this](const QString& value) {
m_proxyModel->setSearch(value);
if (!value.isEmpty() || !listView->selectionModel()->hasSelection()) {
const QModelIndex first = listView->model()->index(0, 0);
listView->selectionModel()->setCurrentIndex(first, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows);
listView->scrollToTop();
} else
listView->scrollTo(listView->selectionModel()->currentIndex(), QAbstractItemView::PositionAtCenter);
});
search->installEventFilter(this);
sneakyProgressBar = new QProgressBar(this); sneakyProgressBar = new QProgressBar(this);
sneakyProgressBar->setObjectName(QStringLiteral("sneakyProgressBar")); sneakyProgressBar->setObjectName(QStringLiteral("sneakyProgressBar"));
sneakyProgressBar->setFormat(QStringLiteral("%p%")); sneakyProgressBar->setFormat(QStringLiteral("%p%"));
@ -72,6 +92,23 @@ void VersionSelectWidget::setResizeOn(int column)
listView->header()->setSectionResizeMode(resizeOnColumn, QHeaderView::Stretch); listView->header()->setSectionResizeMode(resizeOnColumn, QHeaderView::Stretch);
} }
bool VersionSelectWidget::eventFilter(QObject *watched, QEvent *event) {
if (watched == search && event->type() == QEvent::KeyPress) {
const QKeyEvent* keyEvent = (QKeyEvent*)event;
const bool up = keyEvent->key() == Qt::Key_Up;
const bool down = keyEvent->key() == Qt::Key_Down;
if (up || down) {
const QModelIndex index = listView->model()->index(listView->currentIndex().row() + (up ? -1 : 1), 0);
if (index.row() >= 0 && index.row() < listView->model()->rowCount()) {
listView->selectionModel()->setCurrentIndex(index, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows);
return true;
}
}
}
return QObject::eventFilter(watched, event);
}
void VersionSelectWidget::initialize(BaseVersionList *vlist) void VersionSelectWidget::initialize(BaseVersionList *vlist)
{ {
m_vlist = vlist; m_vlist = vlist;
@ -79,6 +116,9 @@ void VersionSelectWidget::initialize(BaseVersionList *vlist)
listView->header()->setSectionResizeMode(QHeaderView::ResizeToContents); listView->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
listView->header()->setSectionResizeMode(resizeOnColumn, QHeaderView::Stretch); listView->header()->setSectionResizeMode(resizeOnColumn, QHeaderView::Stretch);
if (focusSearch)
search->setFocus();
if (!m_vlist->isLoaded()) if (!m_vlist->isLoaded())
{ {
loadList(); loadList();

View File

@ -1,4 +1,24 @@
/* Copyright 2013-2021 MultiMC Contributors // SPDX-License-Identifier: GPL-3.0-only
/*
* Prism Launcher - Minecraft Launcher
* Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me>
*
* 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/>.
*
* This file incorporates work covered by the following copyright and
* permission notice:
*
* Copyright 2013-2021 MultiMC Contributors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -17,6 +37,7 @@
#include <QWidget> #include <QWidget>
#include <QSortFilterProxyModel> #include <QSortFilterProxyModel>
#include <QLineEdit>
#include "BaseVersionList.h" #include "BaseVersionList.h"
#include "VersionListView.h" #include "VersionListView.h"
@ -30,7 +51,8 @@ class VersionSelectWidget: public QWidget
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit VersionSelectWidget(QWidget *parent = 0); explicit VersionSelectWidget(QWidget *parent);
explicit VersionSelectWidget(bool focusSearch = false, QWidget *parent = 0);
~VersionSelectWidget(); ~VersionSelectWidget();
//! loads the list if needed. //! loads the list if needed.
@ -52,6 +74,7 @@ public:
void setEmptyErrorString(QString emptyErrorString); void setEmptyErrorString(QString emptyErrorString);
void setEmptyMode(VersionListView::EmptyMode mode); void setEmptyMode(VersionListView::EmptyMode mode);
void setResizeOn(int column); void setResizeOn(int column);
bool eventFilter(QObject* watched, QEvent* event) override;
signals: signals:
void selectedVersionChanged(BaseVersion::Ptr version); void selectedVersionChanged(BaseVersion::Ptr version);
@ -75,9 +98,10 @@ private:
int resizeOnColumn = 0; int resizeOnColumn = 0;
Task * loadTask; Task * loadTask;
bool preselectedAlready = false; bool preselectedAlready = false;
bool focusSearch;
private:
QVBoxLayout *verticalLayout = nullptr; QVBoxLayout *verticalLayout = nullptr;
VersionListView *listView = nullptr; VersionListView *listView = nullptr;
QLineEdit *search;
QProgressBar *sneakyProgressBar = nullptr; QProgressBar *sneakyProgressBar = nullptr;
}; };

View File

@ -37,7 +37,7 @@
nil nil
]; ];
inputsFrom = [self.packages.${system}.default]; inputsFrom = [self.packages.${system}.prismlauncher-unwrapped];
buildInputs = with pkgs; [ccache ninja]; buildInputs = with pkgs; [ccache ninja];
}; };

View File

@ -2,7 +2,10 @@
#include <settings/INIFile.h> #include <settings/INIFile.h>
#include <QList> #include <QList>
#include <QSettings>
#include <QTemporaryFile>
#include <QVariant> #include <QVariant>
#include "FileSystem.h"
#include <QVariantUtils.h> #include <QVariantUtils.h>
@ -72,32 +75,120 @@ class IniFileTest : public QObject {
QCOMPARE(out_list_numbers, list_numbers); QCOMPARE(out_list_numbers, list_numbers);
} }
void test_SaveAleardyExistingFile() void test_SaveAlreadyExistingFile()
{ {
QString fileName = "test_SaveAleardyExistingFile.ini";
QString fileContent = R"(InstanceType=OneSix QString fileContent = R"(InstanceType=OneSix
iconKey=vanillia_icon iconKey=vanillia_icon
name=Minecraft Vanillia name=Minecraft Vanillia
OverrideCommands=true OverrideCommands=true
PreLaunchCommand="$INST_JAVA" -jar packwiz-installer-bootstrap.jar link PreLaunchCommand="$INST_JAVA" -jar packwiz-installer-bootstrap.jar link
)"; Wrapperommand=)";
fileContent += "\"";
fileContent += +R"(\"$INST_JAVA\" -jar packwiz-installer-bootstrap.jar link =)";
fileContent += "\"\n";
#if defined(Q_OS_WIN)
QString fileName = "test_SaveAlreadyExistingFile.ini";
QFile file(fileName); QFile file(fileName);
QCOMPARE(file.open(QFile::WriteOnly | QFile::Text), true);
if (file.open(QFile::WriteOnly | QFile::Text)) { #else
QTemporaryFile file;
QCOMPARE(file.open(), true);
QCOMPARE(file.fileName().isEmpty(), false);
QString fileName = file.fileName();
#endif
QTextStream stream(&file); QTextStream stream(&file);
stream << fileContent; stream << fileContent;
file.close(); file.close();
}
// load // load
INIFile f1; INIFile f1;
f1.loadFile(fileName); f1.loadFile(fileName);
QCOMPARE(f1.get("PreLaunchCommand", "NOT SET").toString(), "\"$INST_JAVA\" -jar packwiz-installer-bootstrap.jar link"); QCOMPARE(f1.get("PreLaunchCommand", "NOT SET").toString(), "\"$INST_JAVA\" -jar packwiz-installer-bootstrap.jar link");
QCOMPARE(f1.get("Wrapperommand", "NOT SET").toString(), "\"$INST_JAVA\" -jar packwiz-installer-bootstrap.jar link =");
f1.saveFile(fileName); f1.saveFile(fileName);
INIFile f2; INIFile f2;
f2.loadFile(fileName); f2.loadFile(fileName);
QCOMPARE(f2.get("PreLaunchCommand", "NOT SET").toString(), "\"$INST_JAVA\" -jar packwiz-installer-bootstrap.jar link"); QCOMPARE(f2.get("PreLaunchCommand", "NOT SET").toString(), "\"$INST_JAVA\" -jar packwiz-installer-bootstrap.jar link");
QCOMPARE(f2.get("ConfigVersion", "NOT SET").toString(), "1.1"); QCOMPARE(f2.get("Wrapperommand", "NOT SET").toString(), "\"$INST_JAVA\" -jar packwiz-installer-bootstrap.jar link =");
QCOMPARE(f2.get("ConfigVersion", "NOT SET").toString(), "1.2");
#if defined(Q_OS_WIN)
FS::deletePath(fileName);
#endif
}
void test_SaveAlreadyExistingFileWithSpecialChars()
{
#if defined(Q_OS_WIN)
QString fileName = "test_SaveAlreadyExistingFileWithSpecialChars.ini";
#else
QTemporaryFile file;
QCOMPARE(file.open(), true);
QCOMPARE(file.fileName().isEmpty(), false);
QString fileName = file.fileName();
file.close();
#endif
QSettings settings{ fileName, QSettings::Format::IniFormat };
settings.setFallbacksEnabled(false);
settings.setValue("simple", "value1");
settings.setValue("withQuotes", R"("value2" with quotes)");
settings.setValue("withSpecialCharacters", "env mesa=true");
settings.setValue("withSpecialCharacters2", "1,2,3,4");
settings.setValue("withSpecialCharacters2", "1;2;3;4");
settings.setValue("withAll", "val=\"$INST_JAVA\" -jar; ls ");
settings.sync();
QCOMPARE(settings.status(), QSettings::Status::NoError);
// load
INIFile f1;
f1.loadFile(fileName);
for (auto key : settings.allKeys())
QCOMPARE(f1.get(key, "NOT SET").toString(), settings.value(key).toString());
f1.saveFile(fileName);
INIFile f2;
f2.loadFile(fileName);
for (auto key : settings.allKeys())
QCOMPARE(f2.get(key, "NOT SET").toString(), settings.value(key).toString());
QCOMPARE(f2.get("ConfigVersion", "NOT SET").toString(), "1.2");
#if defined(Q_OS_WIN)
FS::deletePath(fileName);
#endif
}
void test_SaveAlreadyExistingFileWithSpecialCharsV1()
{
QString fileContent = R"(InstanceType=OneSix
ConfigVersion=1.1
iconKey=vanillia_icon
name=Minecraft Vanillia
OverrideCommands=true
PreLaunchCommand=)";
fileContent += "\"\\\"env mesa=true\\\"\"\n";
#if defined(Q_OS_WIN)
QString fileName = "test_SaveAlreadyExistingFileWithSpecialCharsV1.ini";
QFile file(fileName);
QCOMPARE(file.open(QFile::WriteOnly | QFile::Text), true);
#else
QTemporaryFile file;
QCOMPARE(file.open(), true);
QCOMPARE(file.fileName().isEmpty(), false);
QString fileName = file.fileName();
#endif
QTextStream stream(&file);
stream << fileContent;
file.close();
// load
INIFile f1;
f1.loadFile(fileName);
QCOMPARE(f1.get("PreLaunchCommand", "NOT SET").toString(), "env mesa=true");
QCOMPARE(f1.get("ConfigVersion", "NOT SET").toString(), "1.2");
#if defined(Q_OS_WIN)
FS::deletePath(fileName);
#endif
} }
}; };