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'
run: |
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)
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"
${{ github.workspace }}/.github/scripts/prepare_JREs.sh
sudo apt install libopengl0
- name: Add QT_HOST_PATH var (Windows MSVC arm64)
if: runner.os == 'Windows' && matrix.architecture == 'arm64'
@ -467,7 +468,8 @@ jobs:
shell: bash
run: |
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"
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 /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/jvm/java-8-openjdk/lib/amd64/server"
@ -599,7 +602,7 @@ jobs:
submodules: 'true'
- name: Install nix
if: inputs.build_type == 'Debug'
uses: cachix/install-nix-action@v21
uses: cachix/install-nix-action@v22
with:
install_url: https://nixos.org/nix/install
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 version numbers ########
set(Launcher_VERSION_MAJOR 7)
set(Launcher_VERSION_MAJOR 8)
set(Launcher_VERSION_MINOR 0)
set(Launcher_VERSION_NAME "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}")

View File

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

View File

@ -102,7 +102,7 @@ namespace fs = ghc::filesystem;
#include <linux/fs.h>
#include <sys/ioctl.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/clonefile.h>
#elif defined(Q_OS_WIN)
@ -1151,7 +1151,7 @@ bool clone_file(const QString& src, const QString& dst, std::error_code& ec)
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)) {
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;
}
#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)
{

View File

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

View File

@ -1,7 +1,8 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* PolyMC - Minecraft Launcher
* Prism Launcher - Minecraft Launcher
* 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
* 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
{
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)
{
auto idx = sourceModel()->index(source_row, 0, source_parent);
auto data = sourceModel()->data(idx, it.key());
auto match = data.toString();
if(!it.value()->accepts(match))
@ -206,10 +212,6 @@ QVariant VersionProxyModel::data(const QModelIndex &index, int role) const
return tr("Latest");
}
}
else if(index.row() == 0)
{
return tr("Latest");
}
}
}
default:
@ -239,10 +241,6 @@ QVariant VersionProxyModel::data(const QModelIndex &index, int role) const
return APPLICATION->getThemedIcon("bug");
}
}
else if(index.row() == 0)
{
return APPLICATION->getThemedIcon("bug");
}
QPixmap pixmap;
QPixmapCache::find("placeholder", &pixmap);
if(!pixmap)
@ -431,6 +429,7 @@ QModelIndex VersionProxyModel::getVersion(const QString& version) const
void VersionProxyModel::clearFilters()
{
m_filters.clear();
m_search.clear();
filterModel->filterChanged();
}
@ -440,11 +439,21 @@ void VersionProxyModel::setFilter(const BaseVersionList::ModelRoles column, Filt
filterModel->filterChanged();
}
void VersionProxyModel::setSearch(const QString &search) {
m_search = search;
filterModel->filterChanged();
}
const VersionProxyModel::FilterMap &VersionProxyModel::filters() const
{
return m_filters;
}
const QString &VersionProxyModel::search() const
{
return m_search;
}
void VersionProxyModel::sourceAboutToBeReset()
{
beginResetModel();

View File

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

View File

@ -1,7 +1,8 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* PolyMC - Minecraft Launcher
* Prism Launcher - Minecraft Launcher
* 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
* 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()]);
switch (role)
{
case SortRole:
return -index.row();
case VersionPointerRole:
return QVariant::fromValue(m_vlist[index.row()]);
case VersionIdRole:

View File

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

View File

@ -333,13 +333,13 @@ QVariant AccountList::data(const QModelIndex &index, int role) const
case MigrationColumn: {
if(account->isMSA() || account->isOffline()) {
return tr("N/A", "Can Migrate?");
return tr("N/A", "Can Migrate");
}
if (account->canMigrate()) {
return tr("Yes", "Can Migrate?");
return tr("Yes", "Can Migrate");
}
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());
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)
{
if (!contains("ConfigVersion"))
insert("ConfigVersion", "1.1");
insert("ConfigVersion", "1.2");
QSettings _settings_obj{ fileName, QSettings::Format::IniFormat };
_settings_obj.setFallbacksEnabled(false);
@ -97,6 +97,20 @@ QString unescape(QString orig)
}
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)
{
QTextStream in(device.readAll());
@ -124,7 +138,7 @@ bool parseOldFileFormat(QIODevice& device, QSettings::SettingsMap& map)
QString key = line.left(eqPos).trimmed();
QString valueStr = line.right(line.length() - eqPos - 1).trimmed();
valueStr = unescape(valueStr);
valueStr = unquote(unescape(valueStr));
QVariant value(valueStr);
map.insert(key, value);
@ -154,7 +168,17 @@ bool INIFile::loadFile(QString fileName)
file.close();
for (auto&& key : map.keys())
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
for (auto&& key : _settings_obj.allKeys())
insert(key, _settings_obj.value(key));

View File

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

View File

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

View File

@ -187,6 +187,7 @@
<bool>true</bool>
</property>
<addaction name="actionViewInstanceFolder"/>
<addaction name="actionViewLauncherRootFolder"/>
<addaction name="actionViewCentralModsFolder"/>
</widget>
<widget class="QMenu" name="accountsMenu">
@ -541,6 +542,18 @@
<string>Open the instance folder in a file browser.</string>
</property>
</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">
<property name="icon">
<iconset theme="centralmods">

View File

@ -1,16 +1,36 @@
/* Copyright 2013-2021 MultiMC Contributors
// SPDX-License-Identifier: GPL-3.0-only
/*
* Prism Launcher - Minecraft Launcher
* Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me>
*
* 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
* 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.
*
* http://www.apache.org/licenses/LICENSE-2.0
* 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.
*
* 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.
* 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 "VersionSelectDialog.h"
@ -22,15 +42,10 @@
#include <QtWidgets/QVBoxLayout>
#include <QDebug>
#include "ui/dialogs/ProgressDialog.h"
#include "ui/widgets/VersionSelectWidget.h"
#include "ui/dialogs/CustomMessageBox.h"
#include "BaseVersion.h"
#include "BaseVersionList.h"
#include "tasks/Task.h"
#include "Application.h"
#include "VersionProxyModel.h"
VersionSelectDialog::VersionSelectDialog(BaseVersionList *vlist, QString title, QWidget *parent, bool cancelable)
: QDialog(parent)
@ -40,7 +55,7 @@ VersionSelectDialog::VersionSelectDialog(BaseVersionList *vlist, QString title,
m_verticalLayout = new QVBoxLayout(this);
m_verticalLayout->setObjectName(QStringLiteral("verticalLayout"));
m_versionWidget = new VersionSelectWidget(parent);
m_versionWidget = new VersionSelectWidget(true, parent);
m_verticalLayout->addWidget(m_versionWidget);
m_horizontalLayout = new QHBoxLayout();

View File

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

View File

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

View File

@ -51,7 +51,7 @@
<item>
<widget class="QCheckBox" name="maximizedCheckBox">
<property name="text">
<string>Start Minecraft &amp;maximized?</string>
<string>Start Minecraft &amp;maximized</string>
</property>
</widget>
</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 "ui/dialogs/CustomMessageBox.h"
#include "ui_ExternalResourcesPage.h"
@ -9,6 +44,7 @@
#include <QKeyEvent>
#include <QMenu>
#include <algorithm>
ExternalResourcesPage::ExternalResourcesPage(BaseInstance* instance, std::shared_ptr<ResourceFolderModel> model, QWidget* parent)
: 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();
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);
}
@ -248,6 +291,15 @@ bool ExternalResourcesPage::onSelectionChanged(const QModelIndex& current, const
int row = sourceCurrent.row();
Resource const& resource = m_model->at(row);
ui->frame->updateWithResource(resource);
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 bool shouldDisplay() const override = 0;
QString extraHeaderInfoString();
void openedImpl() override;
void closedImpl() override;

View File

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

View File

@ -119,12 +119,8 @@ ModFolderPage::ModFolderPage(BaseInstance* inst, std::shared_ptr<ModFolderModel>
connect(mods.get(), &ModFolderModel::rowsRemoved, this,
[this, check_allow_update] { ui->actionUpdateItem->setEnabled(check_allow_update()); });
connect(mods.get(), &ModFolderModel::updateFinished, this, [this, check_allow_update, mods] {
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(mods.get(), &ModFolderModel::updateFinished, this,
[this, check_allow_update] { ui->actionUpdateItem->setEnabled(check_allow_update()); });
connect(m_instance, &BaseInstance::runningStatusChanged, this, &ModFolderPage::runningStateChanged);
ModFolderPage::runningStateChanged(m_instance && m_instance->isRunning());

View File

@ -6,12 +6,14 @@
#include "minecraft/MinecraftInstance.h"
#include "minecraft/PackProfile.h"
#include "minecraft/mod/ModFolderModel.h"
#include <QMessageBox>
#include <algorithm>
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 ********/
@ -24,7 +26,7 @@ ResourceAPI::SearchArgs ModModel::createSearchArguments()
std::optional<std::list<Version>> versions{};
{ // Version filter
{ // Version filter
if (!m_filter->versions.empty())
versions = m_filter->versions;
}
@ -67,4 +69,14 @@ void ModModel::searchWithTerm(const QString& term, unsigned int sort, bool filte
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

View File

@ -24,7 +24,7 @@ class ModModel : public ResourceModel {
Q_OBJECT
public:
ModModel(const BaseInstance&, ResourceAPI* api);
ModModel(BaseInstance&, ResourceAPI* api);
/* Ask the API for more information */
void searchWithTerm(const QString& term, unsigned int sort, bool filter_changed);
@ -42,9 +42,10 @@ class ModModel : public ResourceModel {
protected:
auto documentToArray(QJsonDocument& obj) const -> QJsonArray override = 0;
virtual bool isPackInstalled(ModPlatform::IndexedPack::Ptr) const override;
protected:
const BaseInstance& m_base_instance;
BaseInstance& m_base_instance;
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;
case UserDataTypes::SELECTED:
return pack->isAnyVersionSelected();
case UserDataTypes::INSTALLED:
return this->isPackInstalled(pack);
default:
break;
}
@ -95,6 +97,7 @@ QHash<int, QByteArray> ResourceModel::roleNames() const
roles[UserDataTypes::TITLE] = "title";
roles[UserDataTypes::DESCRIPTION] = "description";
roles[UserDataTypes::SELECTED] = "selected";
roles[UserDataTypes::INSTALLED] = "installed";
return roles;
}

View File

@ -116,6 +116,8 @@ class ResourceModel : public QAbstractListModel {
virtual void loadExtraPackInfo(ModPlatform::IndexedPack&, QJsonObject&);
virtual void loadIndexedPackVersions(ModPlatform::IndexedPack&, QJsonArray&);
virtual bool isPackInstalled(ModPlatform::IndexedPack::Ptr) const { return false; }
protected:
/* Basic search parameters */
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;
case UserDataTypes::SELECTED:
return false;
case UserDataTypes::INSTALLED:
return false;
default:
break;
}

View File

@ -11,7 +11,7 @@
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)
{

View File

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

View File

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

View File

@ -25,7 +25,7 @@
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)
{

View File

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

View File

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

View File

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

View File

@ -64,6 +64,17 @@ void ProjectItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem& o
font.setBold(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);
painter->setFont(font);

View File

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

View File

@ -1,7 +1,8 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* PolyMC - Minecraft Launcher
* Prism Launcher - Minecraft Launcher
* 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
* 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
{
if(m_itemCount) {
return QString();
}
switch(m_emptyMode)
{
default:
case VersionListView::Empty:
return QString();
case VersionListView::String:
return m_emptyString;
case VersionListView::ErrorString:

View File

@ -1,15 +1,20 @@
#include "VersionSelectWidget.h"
#include <QApplication>
#include <QEvent>
#include <QHeaderView>
#include <QKeyEvent>
#include <QProgressBar>
#include <QVBoxLayout>
#include <QHeaderView>
#include "VersionProxyModel.h"
#include "ui/dialogs/CustomMessageBox.h"
VersionSelectWidget::VersionSelectWidget(QWidget* parent)
: QWidget(parent)
VersionSelectWidget::VersionSelectWidget(QWidget* parent) : VersionSelectWidget(false, parent) {}
VersionSelectWidget::VersionSelectWidget(bool focusSearch, QWidget* parent)
: QWidget(parent), focusSearch(focusSearch)
{
setObjectName(QStringLiteral("VersionSelectWidget"));
verticalLayout = new QVBoxLayout(this);
@ -30,6 +35,21 @@ VersionSelectWidget::VersionSelectWidget(QWidget* parent)
listView->setModel(m_proxyModel);
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->setObjectName(QStringLiteral("sneakyProgressBar"));
sneakyProgressBar->setFormat(QStringLiteral("%p%"));
@ -72,6 +92,23 @@ void VersionSelectWidget::setResizeOn(int column)
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)
{
m_vlist = vlist;
@ -79,6 +116,9 @@ void VersionSelectWidget::initialize(BaseVersionList *vlist)
listView->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
listView->header()->setSectionResizeMode(resizeOnColumn, QHeaderView::Stretch);
if (focusSearch)
search->setFocus();
if (!m_vlist->isLoaded())
{
loadList();

View File

@ -1,22 +1,43 @@
/* Copyright 2013-2021 MultiMC Contributors
// SPDX-License-Identifier: GPL-3.0-only
/*
* Prism Launcher - Minecraft Launcher
* Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me>
*
* 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
* 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.
*
* http://www.apache.org/licenses/LICENSE-2.0
* 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.
*
* 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.
* 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.
*/
#pragma once
#include <QWidget>
#include <QSortFilterProxyModel>
#include <QLineEdit>
#include "BaseVersionList.h"
#include "VersionListView.h"
@ -30,7 +51,8 @@ class VersionSelectWidget: public QWidget
{
Q_OBJECT
public:
explicit VersionSelectWidget(QWidget *parent = 0);
explicit VersionSelectWidget(QWidget *parent);
explicit VersionSelectWidget(bool focusSearch = false, QWidget *parent = 0);
~VersionSelectWidget();
//! loads the list if needed.
@ -52,6 +74,7 @@ public:
void setEmptyErrorString(QString emptyErrorString);
void setEmptyMode(VersionListView::EmptyMode mode);
void setResizeOn(int column);
bool eventFilter(QObject* watched, QEvent* event) override;
signals:
void selectedVersionChanged(BaseVersion::Ptr version);
@ -75,9 +98,10 @@ private:
int resizeOnColumn = 0;
Task * loadTask;
bool preselectedAlready = false;
bool focusSearch;
private:
QVBoxLayout *verticalLayout = nullptr;
VersionListView *listView = nullptr;
QLineEdit *search;
QProgressBar *sneakyProgressBar = nullptr;
};

View File

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

View File

@ -2,7 +2,10 @@
#include <settings/INIFile.h>
#include <QList>
#include <QSettings>
#include <QTemporaryFile>
#include <QVariant>
#include "FileSystem.h"
#include <QVariantUtils.h>
@ -72,32 +75,120 @@ class IniFileTest : public QObject {
QCOMPARE(out_list_numbers, list_numbers);
}
void test_SaveAleardyExistingFile()
void test_SaveAlreadyExistingFile()
{
QString fileName = "test_SaveAleardyExistingFile.ini";
QString fileContent = R"(InstanceType=OneSix
iconKey=vanillia_icon
name=Minecraft Vanillia
OverrideCommands=true
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);
if (file.open(QFile::WriteOnly | QFile::Text)) {
QTextStream stream(&file);
stream << fileContent;
file.close();
}
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(), "\"$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);
INIFile f2;
f2.loadFile(fileName);
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
}
};