aboutsummaryrefslogtreecommitdiff
path: root/launcher/ui
diff options
context:
space:
mode:
Diffstat (limited to 'launcher/ui')
-rw-r--r--launcher/ui/MainWindow.cpp14
-rw-r--r--launcher/ui/dialogs/BlockedModsDialog.cpp70
-rw-r--r--launcher/ui/dialogs/BlockedModsDialog.h1
-rw-r--r--launcher/ui/dialogs/CopyInstanceDialog.cpp125
-rw-r--r--launcher/ui/dialogs/CopyInstanceDialog.h33
-rw-r--r--launcher/ui/dialogs/CopyInstanceDialog.ui358
-rw-r--r--launcher/ui/dialogs/ExportInstanceDialog.cpp5
-rw-r--r--launcher/ui/dialogs/NewInstanceDialog.cpp2
-rw-r--r--launcher/ui/dialogs/ProgressDialog.cpp136
-rw-r--r--launcher/ui/dialogs/ProgressDialog.h54
-rw-r--r--launcher/ui/dialogs/ProgressDialog.ui150
-rw-r--r--launcher/ui/pages/global/LauncherPage.cpp2
-rw-r--r--launcher/ui/pages/global/LauncherPage.ui62
-rw-r--r--launcher/ui/pages/global/MinecraftPage.cpp1
-rw-r--r--launcher/ui/pages/global/MinecraftPage.ui133
-rw-r--r--launcher/ui/pages/instance/VersionPage.cpp2
-rw-r--r--launcher/ui/pages/instance/WorldListPage.cpp1
-rw-r--r--launcher/ui/pages/modplatform/ftb/FtbFilterModel.cpp93
-rw-r--r--launcher/ui/pages/modplatform/ftb/FtbFilterModel.h51
-rw-r--r--launcher/ui/pages/modplatform/ftb/FtbListModel.cpp304
-rw-r--r--launcher/ui/pages/modplatform/ftb/FtbListModel.h83
-rw-r--r--launcher/ui/pages/modplatform/ftb/FtbPage.cpp199
-rw-r--r--launcher/ui/pages/modplatform/ftb/FtbPage.h105
-rw-r--r--launcher/ui/pages/modplatform/ftb/FtbPage.ui86
-rw-r--r--launcher/ui/themes/ITheme.cpp2
-rw-r--r--launcher/ui/themes/ITheme.h2
-rw-r--r--launcher/ui/themes/SystemTheme.cpp8
-rw-r--r--launcher/ui/themes/SystemTheme.h2
-rw-r--r--launcher/ui/themes/ThemeManager.cpp8
-rw-r--r--launcher/ui/themes/ThemeManager.h4
-rw-r--r--launcher/ui/widgets/ProgressWidget.cpp1
-rw-r--r--launcher/ui/widgets/SubTaskProgressBar.cpp58
-rw-r--r--launcher/ui/widgets/SubTaskProgressBar.h48
-rw-r--r--launcher/ui/widgets/SubTaskProgressBar.ui94
34 files changed, 1068 insertions, 1229 deletions
diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp
index ff7a12c2..cb194f66 100644
--- a/launcher/ui/MainWindow.cpp
+++ b/launcher/ui/MainWindow.cpp
@@ -1340,6 +1340,20 @@ void MainWindow::on_actionDeleteInstance_triggered()
if (response != QMessageBox::Yes)
return;
+ auto linkedInstances = APPLICATION->instances()->getLinkedInstancesById(id);
+ if (!linkedInstances.empty()) {
+ response = CustomMessageBox::selectable(
+ this, tr("There are linked instances"),
+ tr("The following instance(s) might reference files in this instance:\n\n"
+ "%1\n\n"
+ "Deleting it could break the other instance(s), \n\n"
+ "Do you wish to proceed?", nullptr, linkedInstances.count()).arg(linkedInstances.join("\n")),
+ QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::No
+ )->exec();
+ if (response != QMessageBox::Yes)
+ return;
+ }
+
if (APPLICATION->instances()->trashInstance(id)) {
ui->actionUndoTrashInstance->setEnabled(APPLICATION->instances()->trashedSomething());
return;
diff --git a/launcher/ui/dialogs/BlockedModsDialog.cpp b/launcher/ui/dialogs/BlockedModsDialog.cpp
index ff885f10..fdfae597 100644
--- a/launcher/ui/dialogs/BlockedModsDialog.cpp
+++ b/launcher/ui/dialogs/BlockedModsDialog.cpp
@@ -39,7 +39,6 @@
#include <QFileInfo>
#include <QMimeData>
#include <QPushButton>
-#include <QMimeData>
#include <QStandardPaths>
BlockedModsDialog::BlockedModsDialog(QWidget* parent, const QString& title, const QString& text, QList<BlockedMod>& mods)
@@ -89,11 +88,11 @@ void BlockedModsDialog::dragEnterEvent(QDragEnterEvent* e)
void BlockedModsDialog::dropEvent(QDropEvent* e)
{
for (QUrl& url : e->mimeData()->urls()) {
- if (url.scheme().isEmpty()) { // ensure isLocalFile() works correctly
+ if (url.scheme().isEmpty()) { // ensure isLocalFile() works correctly
url.setScheme("file");
}
- if (!url.isLocalFile()) { // can't drop external files here.
+ if (!url.isLocalFile()) { // can't drop external files here.
continue;
}
@@ -172,7 +171,7 @@ void BlockedModsDialog::update()
}
}
-/// @brief Signal fired when a watched direcotry has changed
+/// @brief Signal fired when a watched directory has changed
/// @param path the path to the changed directory
void BlockedModsDialog::directoryChanged(QString path)
{
@@ -186,8 +185,29 @@ void BlockedModsDialog::setupWatch()
{
const QString downloadsFolder = APPLICATION->settings()->get("DownloadsDir").toString();
const QString modsFolder = APPLICATION->settings()->get("CentralModsDir").toString();
- m_watcher.addPath(downloadsFolder);
- m_watcher.addPath(modsFolder);
+ const bool downloadsFolderWatchRecursive = APPLICATION->settings()->get("DownloadsDirWatchRecursive").toBool();
+ watchPath(downloadsFolder, downloadsFolderWatchRecursive);
+ watchPath(modsFolder, true);
+}
+
+void BlockedModsDialog::watchPath(QString path, bool watch_recursive)
+{
+ auto to_watch = QFileInfo(path);
+ auto to_watch_path = to_watch.canonicalFilePath();
+ if (m_watcher.directories().contains(to_watch_path))
+ return; // don't watch the same path twice (no loops!)
+
+ qDebug() << "[Blocked Mods Dialog] Adding Watch Path:" << path;
+ m_watcher.addPath(to_watch_path);
+
+ if (!to_watch.isDir() || !watch_recursive)
+ return;
+
+ QDirIterator it(to_watch_path, QDir::Filter::Dirs | QDir::Filter::NoDotAndDotDot, QDirIterator::NoIteratorFlags);
+ while (it.hasNext()) {
+ QString watch_dir = QDir(it.next()).canonicalPath(); // resolve symlinks and relative paths
+ watchPath(watch_dir, watch_recursive);
+ }
}
/// @brief scan all watched folder
@@ -221,7 +241,7 @@ void BlockedModsDialog::scanPath(QString path, bool start_task)
}
}
-/// @brief add a hashing task for the file located at path, add the path to the pending set if the hasing task is already running
+/// @brief add a hashing task for the file located at path, add the path to the pending set if the hashing task is already running
/// @param path the path to the local file being hashed
void BlockedModsDialog::addHashTask(QString path)
{
@@ -281,11 +301,35 @@ bool BlockedModsDialog::checkValidPath(QString path)
{
const QFileInfo file = QFileInfo(path);
const QString filename = file.fileName();
- QString laxFilename(filename);
- laxFilename.replace('+', ' ');
- auto compare = [](QString fsfilename, QString metadataFilename) {
- return metadataFilename.compare(fsfilename, Qt::CaseInsensitive) == 0;
+ auto compare = [](QString fsFilename, QString metadataFilename) {
+ return metadataFilename.compare(fsFilename, Qt::CaseInsensitive) == 0;
+ };
+
+ // super lax compare (but not fuzzy)
+ // convert to lowercase
+ // convert all speratores to whitespace
+ // simplify sequence of internal whitespace to a single space
+ // efectivly compare two strings ignoring all separators and case
+ auto laxCompare = [](QString fsfilename, QString metadataFilename) {
+ // allowed character seperators
+ QList<QChar> allowedSeperators = { '-', '+', '.' , '_'};
+
+ // copy in lowercase
+ auto fsName = fsfilename.toLower();
+ auto metaName = metadataFilename.toLower();
+
+ // replace all potential allowed seperatores with whitespace
+ for (auto sep : allowedSeperators) {
+ fsName = fsName.replace(sep, ' ');
+ metaName = metaName.replace(sep, ' ');
+ }
+
+ // remove extraneous whitespace
+ fsName = fsName.simplified();
+ metaName = metaName.simplified();
+
+ return fsName.compare(metaName) == 0;
};
for (auto& mod : m_mods) {
@@ -293,7 +337,7 @@ bool BlockedModsDialog::checkValidPath(QString path)
qDebug() << "[Blocked Mods Dialog] Name match found:" << mod.name << "| From path:" << path;
return true;
}
- if (compare(laxFilename, mod.name)) {
+ if (laxCompare(filename, mod.name)) {
qDebug() << "[Blocked Mods Dialog] Lax name match found:" << mod.name << "| From path:" << path;
return true;
}
@@ -328,7 +372,7 @@ void BlockedModsDialog::validateMatchedMods()
}
}
-/// @brief run hash task or mark a pending run if it is already runing
+/// @brief run hash task or mark a pending run if it is already running
void BlockedModsDialog::runHashTask()
{
if (!m_hashing_task->isRunning()) {
diff --git a/launcher/ui/dialogs/BlockedModsDialog.h b/launcher/ui/dialogs/BlockedModsDialog.h
index 014f488a..e3b7c975 100644
--- a/launcher/ui/dialogs/BlockedModsDialog.h
+++ b/launcher/ui/dialogs/BlockedModsDialog.h
@@ -79,6 +79,7 @@ class BlockedModsDialog : public QDialog {
void update();
void directoryChanged(QString path);
void setupWatch();
+ void watchPath(QString path, bool watch_recursive = false);
void scanPaths();
void scanPath(QString path, bool start_task);
void addHashTask(QString path);
diff --git a/launcher/ui/dialogs/CopyInstanceDialog.cpp b/launcher/ui/dialogs/CopyInstanceDialog.cpp
index 3f5122f6..d75bb5fe 100644
--- a/launcher/ui/dialogs/CopyInstanceDialog.cpp
+++ b/launcher/ui/dialogs/CopyInstanceDialog.cpp
@@ -37,18 +37,21 @@
#include <QPushButton>
#include "Application.h"
+#include "BuildConfig.h"
#include "CopyInstanceDialog.h"
#include "ui_CopyInstanceDialog.h"
#include "ui/dialogs/IconPickerDialog.h"
-#include "BaseVersion.h"
-#include "icons/IconList.h"
#include "BaseInstance.h"
+#include "BaseVersion.h"
+#include "DesktopServices.h"
+#include "FileSystem.h"
#include "InstanceList.h"
+#include "icons/IconList.h"
-CopyInstanceDialog::CopyInstanceDialog(InstancePtr original, QWidget *parent)
- :QDialog(parent), ui(new Ui::CopyInstanceDialog), m_original(original)
+CopyInstanceDialog::CopyInstanceDialog(InstancePtr original, QWidget* parent)
+ : QDialog(parent), ui(new Ui::CopyInstanceDialog), m_original(original)
{
ui->setupUi(this);
resize(minimumSizeHint());
@@ -71,8 +74,7 @@ CopyInstanceDialog::CopyInstanceDialog(InstancePtr original, QWidget *parent)
groupList.push_front("");
ui->groupBox->addItems(groupList);
int index = groupList.indexOf(APPLICATION->instances()->getInstanceGroup(m_original->id()));
- if(index == -1)
- {
+ if (index == -1) {
index = 0;
}
ui->groupBox->setCurrentIndex(index);
@@ -85,6 +87,35 @@ CopyInstanceDialog::CopyInstanceDialog(InstancePtr original, QWidget *parent)
ui->copyServersCheckbox->setChecked(m_selectedOptions.isCopyServersEnabled());
ui->copyModsCheckbox->setChecked(m_selectedOptions.isCopyModsEnabled());
ui->copyScreenshotsCheckbox->setChecked(m_selectedOptions.isCopyScreenshotsEnabled());
+
+ ui->symbolicLinksCheckbox->setChecked(m_selectedOptions.isUseSymLinksEnabled());
+ ui->hardLinksCheckbox->setChecked(m_selectedOptions.isUseHardLinksEnabled());
+
+ ui->recursiveLinkCheckbox->setChecked(m_selectedOptions.isLinkRecursivelyEnabled());
+ ui->dontLinkSavesCheckbox->setChecked(m_selectedOptions.isDontLinkSavesEnabled());
+
+ auto detectedFS = FS::statFS(m_original->instanceRoot()).fsType;
+
+ m_cloneSupported = FS::canCloneOnFS(detectedFS);
+ m_linkSupported = FS::canLinkOnFS(detectedFS);
+
+ if (m_cloneSupported) {
+ ui->cloneSupportedLabel->setText(tr("Reflinks are supported on %1").arg(FS::getFilesystemTypeName(detectedFS)));
+ } else {
+ ui->cloneSupportedLabel->setText(tr("Reflinks aren't supported on %1").arg(FS::getFilesystemTypeName(detectedFS)));
+ }
+
+#if defined(Q_OS_WIN)
+ ui->symbolicLinksCheckbox->setIcon(style()->standardIcon(QStyle::SP_VistaShield));
+ ui->symbolicLinksCheckbox->setToolTip(tr("Use symbolic links instead of copying files.") +
+ "\n" + tr("On Windows, symbolic links may require admin permission to create."));
+#endif
+
+ updateLinkOptions();
+ updateUseCloneCheckbox();
+
+ auto HelpButton = ui->buttonBox->button(QDialogButtonBox::Help);
+ connect(HelpButton, &QPushButton::clicked, this, &CopyInstanceDialog::help);
}
CopyInstanceDialog::~CopyInstanceDialog()
@@ -96,8 +127,7 @@ void CopyInstanceDialog::updateDialogState()
{
auto allowOK = !instName().isEmpty();
auto OkButton = ui->buttonBox->button(QDialogButtonBox::Ok);
- if(OkButton->isEnabled() != allowOK)
- {
+ if (OkButton->isEnabled() != allowOK) {
OkButton->setEnabled(allowOK);
}
}
@@ -105,8 +135,7 @@ void CopyInstanceDialog::updateDialogState()
QString CopyInstanceDialog::instName() const
{
auto result = ui->instNameTextBox->text().trimmed();
- if(result.size())
- {
+ if (result.size()) {
return result;
}
return QString();
@@ -127,6 +156,11 @@ const InstanceCopyPrefs& CopyInstanceDialog::getChosenOptions() const
return m_selectedOptions;
}
+void CopyInstanceDialog::help()
+{
+ DesktopServices::openUrl(QUrl(BuildConfig.HELP_URL.arg("instance-copy")));
+}
+
void CopyInstanceDialog::checkAllCheckboxes(const bool& b)
{
ui->keepPlaytimeCheckbox->setChecked(b);
@@ -147,20 +181,46 @@ void CopyInstanceDialog::updateSelectAllCheckbox()
ui->selectAllCheckbox->blockSignals(false);
}
+void CopyInstanceDialog::updateUseCloneCheckbox()
+{
+ ui->useCloneCheckbox->setEnabled(m_cloneSupported && !ui->symbolicLinksCheckbox->isChecked() && !ui->hardLinksCheckbox->isChecked());
+ ui->useCloneCheckbox->setChecked(m_cloneSupported && m_selectedOptions.isUseCloneEnabled() && !ui->symbolicLinksCheckbox->isChecked() &&
+ !ui->hardLinksCheckbox->isChecked());
+}
+
+void CopyInstanceDialog::updateLinkOptions()
+{
+ ui->symbolicLinksCheckbox->setEnabled(m_linkSupported && !ui->hardLinksCheckbox->isChecked() && !ui->useCloneCheckbox->isChecked());
+ ui->hardLinksCheckbox->setEnabled(m_linkSupported && !ui->symbolicLinksCheckbox->isChecked() && !ui->useCloneCheckbox->isChecked());
+
+ ui->symbolicLinksCheckbox->setChecked(m_linkSupported && m_selectedOptions.isUseSymLinksEnabled() &&
+ !ui->useCloneCheckbox->isChecked());
+ ui->hardLinksCheckbox->setChecked(m_linkSupported && m_selectedOptions.isUseHardLinksEnabled() && !ui->useCloneCheckbox->isChecked());
+
+ bool linksInUse = (ui->symbolicLinksCheckbox->isChecked() || ui->hardLinksCheckbox->isChecked());
+ ui->recursiveLinkCheckbox->setEnabled(m_linkSupported && linksInUse && !ui->hardLinksCheckbox->isChecked());
+ ui->dontLinkSavesCheckbox->setEnabled(m_linkSupported && linksInUse);
+ ui->recursiveLinkCheckbox->setChecked(m_linkSupported && linksInUse && m_selectedOptions.isLinkRecursivelyEnabled());
+ ui->dontLinkSavesCheckbox->setChecked(m_linkSupported && linksInUse && m_selectedOptions.isDontLinkSavesEnabled());
+
+#if defined(Q_OS_WIN)
+ auto OkButton = ui->buttonBox->button(QDialogButtonBox::Ok);
+ OkButton->setIcon(m_selectedOptions.isUseSymLinksEnabled() ? style()->standardIcon(QStyle::SP_VistaShield) : QIcon());
+#endif
+}
+
void CopyInstanceDialog::on_iconButton_clicked()
{
IconPickerDialog dlg(this);
dlg.execWithSelection(InstIconKey);
- if (dlg.result() == QDialog::Accepted)
- {
+ if (dlg.result() == QDialog::Accepted) {
InstIconKey = dlg.selectedIconKey;
ui->iconButton->setIcon(APPLICATION->icons()->getIcon(InstIconKey));
}
}
-
-void CopyInstanceDialog::on_instNameTextBox_textChanged(const QString &arg1)
+void CopyInstanceDialog::on_instNameTextBox_textChanged(const QString& arg1)
{
updateDialogState();
}
@@ -175,10 +235,10 @@ void CopyInstanceDialog::on_selectAllCheckbox_stateChanged(int state)
void CopyInstanceDialog::on_copySavesCheckbox_stateChanged(int state)
{
m_selectedOptions.enableCopySaves(state == Qt::Checked);
+ ui->dontLinkSavesCheckbox->setChecked((state == Qt::Checked) && ui->dontLinkSavesCheckbox->isChecked());
updateSelectAllCheckbox();
}
-
void CopyInstanceDialog::on_keepPlaytimeCheckbox_stateChanged(int state)
{
m_selectedOptions.enableKeepPlaytime(state == Qt::Checked);
@@ -220,3 +280,38 @@ void CopyInstanceDialog::on_copyScreenshotsCheckbox_stateChanged(int state)
m_selectedOptions.enableCopyScreenshots(state == Qt::Checked);
updateSelectAllCheckbox();
}
+
+void CopyInstanceDialog::on_symbolicLinksCheckbox_stateChanged(int state)
+{
+ m_selectedOptions.enableUseSymLinks(state == Qt::Checked);
+ updateUseCloneCheckbox();
+ updateLinkOptions();
+}
+
+void CopyInstanceDialog::on_hardLinksCheckbox_stateChanged(int state)
+{
+ m_selectedOptions.enableUseHardLinks(state == Qt::Checked);
+ if (state == Qt::Checked && !ui->recursiveLinkCheckbox->isChecked()) {
+ ui->recursiveLinkCheckbox->setChecked(true);
+ }
+ updateUseCloneCheckbox();
+ updateLinkOptions();
+}
+
+void CopyInstanceDialog::on_recursiveLinkCheckbox_stateChanged(int state)
+{
+ m_selectedOptions.enableLinkRecursively(state == Qt::Checked);
+ updateLinkOptions();
+}
+
+void CopyInstanceDialog::on_dontLinkSavesCheckbox_stateChanged(int state)
+{
+ m_selectedOptions.enableDontLinkSaves(state == Qt::Checked);
+}
+
+void CopyInstanceDialog::on_useCloneCheckbox_stateChanged(int state)
+{
+ m_selectedOptions.enableUseClone(m_cloneSupported && (state == Qt::Checked));
+ updateUseCloneCheckbox();
+ updateLinkOptions();
+}
diff --git a/launcher/ui/dialogs/CopyInstanceDialog.h b/launcher/ui/dialogs/CopyInstanceDialog.h
index 884501d1..698c6e93 100644
--- a/launcher/ui/dialogs/CopyInstanceDialog.h
+++ b/launcher/ui/dialogs/CopyInstanceDialog.h
@@ -16,22 +16,21 @@
#pragma once
#include <QDialog>
+#include "BaseInstance.h"
#include "BaseVersion.h"
#include "InstanceCopyPrefs.h"
class BaseInstance;
-namespace Ui
-{
+namespace Ui {
class CopyInstanceDialog;
}
-class CopyInstanceDialog : public QDialog
-{
+class CopyInstanceDialog : public QDialog {
Q_OBJECT
-public:
- explicit CopyInstanceDialog(InstancePtr original, QWidget *parent = 0);
+ public:
+ explicit CopyInstanceDialog(InstancePtr original, QWidget* parent = 0);
~CopyInstanceDialog();
void updateDialogState();
@@ -41,10 +40,12 @@ public:
QString iconKey() const;
const InstanceCopyPrefs& getChosenOptions() const;
-private
-slots:
+ public slots:
+ void help();
+
+ private slots:
void on_iconButton_clicked();
- void on_instNameTextBox_textChanged(const QString &arg1);
+ void on_instNameTextBox_textChanged(const QString& arg1);
// Checkboxes
void on_selectAllCheckbox_stateChanged(int state);
void on_copySavesCheckbox_stateChanged(int state);
@@ -55,13 +56,23 @@ slots:
void on_copyServersCheckbox_stateChanged(int state);
void on_copyModsCheckbox_stateChanged(int state);
void on_copyScreenshotsCheckbox_stateChanged(int state);
+ void on_symbolicLinksCheckbox_stateChanged(int state);
+ void on_hardLinksCheckbox_stateChanged(int state);
+ void on_recursiveLinkCheckbox_stateChanged(int state);
+ void on_dontLinkSavesCheckbox_stateChanged(int state);
+ void on_useCloneCheckbox_stateChanged(int state);
-private:
+ private:
void checkAllCheckboxes(const bool& b);
void updateSelectAllCheckbox();
+ void updateUseCloneCheckbox();
+ void updateLinkOptions();
+
/* data */
- Ui::CopyInstanceDialog *ui;
+ Ui::CopyInstanceDialog* ui;
QString InstIconKey;
InstancePtr m_original;
InstanceCopyPrefs m_selectedOptions;
+ bool m_cloneSupported = false;
+ bool m_linkSupported = false;
};
diff --git a/launcher/ui/dialogs/CopyInstanceDialog.ui b/launcher/ui/dialogs/CopyInstanceDialog.ui
index b7828fe3..5060debc 100644
--- a/launcher/ui/dialogs/CopyInstanceDialog.ui
+++ b/launcher/ui/dialogs/CopyInstanceDialog.ui
@@ -9,8 +9,8 @@
<rect>
<x>0</x>
<y>0</y>
- <width>341</width>
- <height>399</height>
+ <width>575</width>
+ <height>695</height>
</rect>
</property>
<property name="windowTitle">
@@ -113,93 +113,268 @@
</layout>
</item>
<item>
- <layout class="QHBoxLayout" name="selectAllButtonLayout">
- <item>
- <widget class="QCheckBox" name="selectAllCheckbox">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="layoutDirection">
- <enum>Qt::LeftToRight</enum>
- </property>
- <property name="text">
- <string>Select all</string>
- </property>
- <property name="checked">
- <bool>false</bool>
- </property>
- </widget>
- </item>
- </layout>
+ <widget class="QGroupBox" name="copyOptionsGroup">
+ <property name="title">
+ <string>Instance Copy Options</string>
+ </property>
+ <layout class="QGridLayout" name="copyOptionsLayout">
+ <item row="1" column="0">
+ <widget class="QCheckBox" name="keepPlaytimeCheckbox">
+ <property name="text">
+ <string>Keep play time</string>
+ </property>
+ </widget>
+ </item>
+ <item row="6" column="1">
+ <widget class="QCheckBox" name="copyModsCheckbox">
+ <property name="toolTip">
+ <string>Disabling this will still keep the mod loader (ex: Fabric, Quilt, etc.) but erase the mods folder and their configs.</string>
+ </property>
+ <property name="text">
+ <string>Copy mods</string>
+ </property>
+ </widget>
+ </item>
+ <item row="6" column="0">
+ <widget class="QCheckBox" name="copyResPacksCheckbox">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>Copy resource packs</string>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="0">
+ <widget class="QCheckBox" name="copyGameOptionsCheckbox">
+ <property name="toolTip">
+ <string>Copy the in-game options like FOV, max framerate, etc.</string>
+ </property>
+ <property name="text">
+ <string>Copy game options</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="1">
+ <widget class="QCheckBox" name="copyShaderPacksCheckbox">
+ <property name="text">
+ <string>Copy shader packs</string>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="1">
+ <widget class="QCheckBox" name="copyServersCheckbox">
+ <property name="text">
+ <string>Copy servers</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="0">
+ <widget class="QCheckBox" name="copySavesCheckbox">
+ <property name="text">
+ <string>Copy saves</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QCheckBox" name="copyScreenshotsCheckbox">
+ <property name="text">
+ <string>Copy screenshots</string>
+ </property>
+ </widget>
+ </item>
+ <item row="7" column="1">
+ <widget class="QCheckBox" name="selectAllCheckbox">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="layoutDirection">
+ <enum>Qt::LeftToRight</enum>
+ </property>
+ <property name="text">
+ <string>Select all</string>
+ </property>
+ <property name="checked">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
</item>
<item>
- <layout class="QGridLayout" name="copyOptionsLayout">
- <item row="6" column="1">
- <widget class="QCheckBox" name="copyModsCheckbox">
- <property name="toolTip">
- <string>Disabling this will still keep the mod loader (ex: Fabric, Quilt, etc.) but erase the mods folder and their configs.</string>
- </property>
- <property name="text">
- <string>Copy mods</string>
- </property>
- </widget>
- </item>
- <item row="5" column="0">
- <widget class="QCheckBox" name="copyGameOptionsCheckbox">
+ <widget class="Line" name="line_2">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="advancedOptionsLabel">
+ <property name="text">
+ <string>Advanced Copy Options</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QVBoxLayout" name="copyModeLayout">
+ <item>
+ <widget class="QGroupBox" name="linkFilesGroup">
<property name="toolTip">
- <string>Copy the in-game options like FOV, max framerate, etc.</string>
+ <string>Use symbolic or hard links instead of copying files.</string>
</property>
- <property name="text">
- <string>Copy game options</string>
- </property>
- </widget>
- </item>
- <item row="3" column="0">
- <widget class="QCheckBox" name="copySavesCheckbox">
- <property name="text">
- <string>Copy saves</string>
- </property>
- </widget>
- </item>
- <item row="3" column="1">
- <widget class="QCheckBox" name="copyShaderPacksCheckbox">
- <property name="text">
- <string>Copy shader packs</string>
- </property>
- </widget>
- </item>
- <item row="5" column="1">
- <widget class="QCheckBox" name="copyServersCheckbox">
- <property name="text">
- <string>Copy servers</string>
+ <property name="title">
+ <string>Symbolic and Hard Link Options</string>
</property>
- </widget>
- </item>
- <item row="6" column="0">
- <widget class="QCheckBox" name="copyResPacksCheckbox">
- <property name="enabled">
- <bool>true</bool>
+ <property name="flat">
+ <bool>false</bool>
</property>
- <property name="text">
- <string>Copy resource packs</string>
+ <property name="checkable">
+ <bool>false</bool>
</property>
- </widget>
- </item>
- <item row="1" column="0">
- <widget class="QCheckBox" name="keepPlaytimeCheckbox">
- <property name="text">
- <string>Keep play time</string>
+ <property name="checked">
+ <bool>false</bool>
</property>
+ <layout class="QVBoxLayout" name="linkOptionsLayout">
+ <item>
+ <widget class="QLabel" name="linkOptionsLabel">
+ <property name="text">
+ <string>Links are supported on most filesystems except FAT</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QGridLayout" name="linkOptionsGridLayout" rowstretch="0,0,0,0" columnstretch="0,0" rowminimumheight="0,0,0,0" columnminimumwidth="0,0">
+ <property name="leftMargin">
+ <number>6</number>
+ </property>
+ <property name="topMargin">
+ <number>6</number>
+ </property>
+ <property name="rightMargin">
+ <number>6</number>
+ </property>
+ <property name="bottomMargin">
+ <number>6</number>
+ </property>
+ <item row="2" column="1">
+ <widget class="QCheckBox" name="recursiveLinkCheckbox">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="toolTip">
+ <string>Link each resource individually instead of linking whole folders at once</string>
+ </property>
+ <property name="text">
+ <string>Link files recursively</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="1">
+ <widget class="QCheckBox" name="dontLinkSavesCheckbox">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="toolTip">
+ <string>If &quot;copy saves&quot; is selected world save data will be copied instead of linked and thus not shared between instances.</string>
+ </property>
+ <property name="text">
+ <string>Don't link saves</string>
+ </property>
+ <property name="checked">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="0">
+ <widget class="QCheckBox" name="hardLinksCheckbox">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="toolTip">
+ <string>Use hard links instead of copying files.</string>
+ </property>
+ <property name="text">
+ <string>Use hard links</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QCheckBox" name="symbolicLinksCheckbox">
+ <property name="toolTip">
+ <string>Use symbolic links instead of copying files.</string>
+ </property>
+ <property name="text">
+ <string>Use symbolic links</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
</widget>
</item>
- <item row="1" column="1">
- <widget class="QCheckBox" name="copyScreenshotsCheckbox">
- <property name="text">
- <string>Copy screenshots</string>
+ <item>
+ <widget class="QGroupBox" name="horizontalGroupBox">
+ <property name="title">
+ <string>CoW (Copy-on-Write) Options</string>
</property>
+ <layout class="QHBoxLayout" name="useCloneLayout">
+ <item>
+ <widget class="QCheckBox" name="useCloneCheckbox">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="toolTip">
+ <string>Files cloned with reflinks take up no extra space until they are modified.</string>
+ </property>
+ <property name="text">
+ <string>Clone instead of copying</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="CoWSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QLabel" name="cloneSupportedLabel">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>1</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Your filesystem and/or OS doesn't support reflinks</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ <property name="margin">
+ <number>4</number>
+ </property>
+ </widget>
+ </item>
+ </layout>
</widget>
</item>
</layout>
@@ -210,7 +385,7 @@
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
- <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Help|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
@@ -220,10 +395,21 @@
<tabstop>iconButton</tabstop>
<tabstop>instNameTextBox</tabstop>
<tabstop>groupBox</tabstop>
+ <tabstop>keepPlaytimeCheckbox</tabstop>
+ <tabstop>copyScreenshotsCheckbox</tabstop>
+ <tabstop>copySavesCheckbox</tabstop>
+ <tabstop>copyShaderPacksCheckbox</tabstop>
+ <tabstop>copyGameOptionsCheckbox</tabstop>
+ <tabstop>copyServersCheckbox</tabstop>
+ <tabstop>copyResPacksCheckbox</tabstop>
+ <tabstop>copyModsCheckbox</tabstop>
+ <tabstop>symbolicLinksCheckbox</tabstop>
+ <tabstop>recursiveLinkCheckbox</tabstop>
+ <tabstop>hardLinksCheckbox</tabstop>
+ <tabstop>dontLinkSavesCheckbox</tabstop>
+ <tabstop>useCloneCheckbox</tabstop>
</tabstops>
- <resources>
- <include location="../../graphics.qrc"/>
- </resources>
+ <resources/>
<connections>
<connection>
<sender>buttonBox</sender>
@@ -232,8 +418,8 @@
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
- <x>254</x>
- <y>316</y>
+ <x>269</x>
+ <y>692</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
@@ -248,8 +434,8 @@
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
- <x>322</x>
- <y>316</y>
+ <x>337</x>
+ <y>692</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
diff --git a/launcher/ui/dialogs/ExportInstanceDialog.cpp b/launcher/ui/dialogs/ExportInstanceDialog.cpp
index 57fe8119..603a723c 100644
--- a/launcher/ui/dialogs/ExportInstanceDialog.cpp
+++ b/launcher/ui/dialogs/ExportInstanceDialog.cpp
@@ -45,6 +45,8 @@
#include <QDebug>
#include <QSaveFile>
#include <QStack>
+#include <QFileInfo>
+#include "StringUtils.h"
#include "SeparatorPrefixTree.h"
#include "Application.h"
#include <icons/IconList.h>
@@ -138,7 +140,8 @@ bool ExportInstanceDialog::doExport()
QMessageBox::warning(this, tr("Error"), tr("Unable to export instance"));
return false;
}
- if (!MMCZip::compressDirFiles(output, m_instance->instanceRoot(), files))
+
+ if (!MMCZip::compressDirFiles(output, m_instance->instanceRoot(), files, true))
{
QMessageBox::warning(this, tr("Error"), tr("Unable to export instance"));
return false;
diff --git a/launcher/ui/dialogs/NewInstanceDialog.cpp b/launcher/ui/dialogs/NewInstanceDialog.cpp
index df182f09..64ed7673 100644
--- a/launcher/ui/dialogs/NewInstanceDialog.cpp
+++ b/launcher/ui/dialogs/NewInstanceDialog.cpp
@@ -56,7 +56,6 @@
#include "ui/widgets/PageContainer.h"
#include "ui/pages/modplatform/VanillaPage.h"
#include "ui/pages/modplatform/atlauncher/AtlPage.h"
-#include "ui/pages/modplatform/ftb/FtbPage.h"
#include "ui/pages/modplatform/legacy_ftb/Page.h"
#include "ui/pages/modplatform/flame/FlamePage.h"
#include "ui/pages/modplatform/ImportPage.h"
@@ -168,7 +167,6 @@ QList<BasePage *> NewInstanceDialog::getPages()
pages.append(new AtlPage(this));
if (APPLICATION->capabilities() & Application::SupportsFlame)
pages.append(new FlamePage(this));
- pages.append(new FtbPage(this));
pages.append(new LegacyFTB::Page(this));
pages.append(new ModrinthPage(this));
pages.append(new TechnicPage(this));
diff --git a/launcher/ui/dialogs/ProgressDialog.cpp b/launcher/ui/dialogs/ProgressDialog.cpp
index da73a449..246a0fd4 100644
--- a/launcher/ui/dialogs/ProgressDialog.cpp
+++ b/launcher/ui/dialogs/ProgressDialog.cpp
@@ -1,29 +1,69 @@
-/* Copyright 2013-2021 MultiMC Contributors
+/// SPDX-License-Identifier: GPL-3.0-only
+/*
+ * PrismLaucher - Minecraft Launcher
+ * Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com>
*
- * 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 "ProgressDialog.h"
#include "ui_ProgressDialog.h"
+#include <limits>
#include <QDebug>
#include <QKeyEvent>
#include "tasks/Task.h"
+#include "ui/widgets/SubTaskProgressBar.h"
+
+
+// map a value in a numeric range of an arbitrary type to between 0 and INT_MAX
+// for getting the best precision out of the qt progress bar
+template<typename T, std::enable_if_t<std::is_arithmetic_v<T>, bool> = true>
+std::tuple<int, int> map_int_zero_max(T current, T range_max, T range_min)
+{
+ int int_max = std::numeric_limits<int>::max();
+
+ auto type_range = range_max - range_min;
+ double percentage = static_cast<double>(current - range_min) / static_cast<double>(type_range);
+ int mapped_current = percentage * int_max;
+
+ return {mapped_current, int_max};
+}
+
+
ProgressDialog::ProgressDialog(QWidget* parent) : QDialog(parent), ui(new Ui::ProgressDialog)
{
ui->setupUi(this);
+ ui->taskProgressScrollArea->setHidden(true);
this->setWindowFlags(this->windowFlags() & ~Qt::WindowContextHelpButtonHint);
setAttribute(Qt::WidgetAttribute::WA_QuitOnClose, true);
setSkipButton(false);
@@ -54,10 +94,24 @@ ProgressDialog::~ProgressDialog()
}
void ProgressDialog::updateSize()
-{
+{
+ QSize lastSize = this->size();
QSize qSize = QSize(480, minimumSizeHint().height());
- resize(qSize);
- setFixedSize(qSize);
+
+ // if the current window is too small
+ if ((lastSize != qSize) && (lastSize.height() < qSize.height()))
+ {
+ resize(qSize);
+
+ // keep the dialog in the center after a resize
+ this->move(
+ this->parentWidget()->x() + (this->parentWidget()->width() - this->width()) / 2,
+ this->parentWidget()->y() + (this->parentWidget()->height() - this->height()) / 2
+ );
+ }
+
+ setMinimumSize(qSize);
+
}
int ProgressDialog::execWithTask(Task* task)
@@ -79,17 +133,15 @@ int ProgressDialog::execWithTask(Task* task)
connect(task, &Task::failed, this, &ProgressDialog::onTaskFailed);
connect(task, &Task::succeeded, this, &ProgressDialog::onTaskSucceeded);
connect(task, &Task::status, this, &ProgressDialog::changeStatus);
- connect(task, &Task::stepStatus, this, &ProgressDialog::changeStatus);
+ connect(task, &Task::details, this, &ProgressDialog::changeStatus);
+ connect(task, &Task::stepProgress, this, &ProgressDialog::changeStepProgress);
connect(task, &Task::progress, this, &ProgressDialog::changeProgress);
-
connect(task, &Task::aborted, this, &ProgressDialog::hide);
connect(task, &Task::abortStatusChanged, ui->skipButton, &QPushButton::setEnabled);
m_is_multi_step = task->isMultiStep();
- if (!m_is_multi_step) {
- ui->globalStatusLabel->setHidden(true);
- ui->globalProgressBar->setHidden(true);
- }
+ ui->taskProgressScrollArea->setHidden(!m_is_multi_step);
+ updateSize();
// It's a good idea to start the task after we entered the dialog's event loop :^)
if (!task->isRunning()) {
@@ -149,23 +201,53 @@ void ProgressDialog::onTaskSucceeded()
void ProgressDialog::changeStatus(const QString& status)
{
ui->globalStatusLabel->setText(task->getStatus());
- ui->statusLabel->setText(task->getStepStatus());
+ ui->globalStatusDetailsLabel->setText(task->getDetails());
updateSize();
}
+void ProgressDialog::addTaskProgress(TaskStepProgress const& progress)
+{
+ SubTaskProgressBar* task_bar = new SubTaskProgressBar(this);
+ taskProgress.insert(progress.uid, task_bar);
+ ui->taskProgressLayout->addWidget(task_bar);
+}
+
+void ProgressDialog::changeStepProgress(TaskStepProgress const& task_progress)
+{
+ m_is_multi_step = true;
+ if(ui->taskProgressScrollArea->isHidden()) {
+ ui->taskProgressScrollArea->setHidden(false);
+ updateSize();
+ }
+
+ if (!taskProgress.contains(task_progress.uid))
+ addTaskProgress(task_progress);
+ auto task_bar = taskProgress.value(task_progress.uid);
+
+
+ auto const [mapped_current, mapped_total] = map_int_zero_max<qint64>(task_progress.current, task_progress.total, 0);
+ if (task_progress.total <= 0) {
+ task_bar->setRange(0, 0);
+ } else {
+ task_bar->setRange(0, mapped_total);
+ }
+
+ task_bar->setValue(mapped_current);
+ task_bar->setStatus(task_progress.status);
+ task_bar->setDetails(task_progress.details);
+
+ if (task_progress.isDone()) {
+ task_bar->setVisible(false);
+ }
+
+}
+
void ProgressDialog::changeProgress(qint64 current, qint64 total)
{
ui->globalProgressBar->setMaximum(total);
ui->globalProgressBar->setValue(current);
- if (!m_is_multi_step) {
- ui->taskProgressBar->setMaximum(total);
- ui->taskProgressBar->setValue(current);
- } else {
- ui->taskProgressBar->setMaximum(task->getStepProgress());
- ui->taskProgressBar->setValue(task->getStepTotalProgress());
- }
}
void ProgressDialog::keyPressEvent(QKeyEvent* e)
diff --git a/launcher/ui/dialogs/ProgressDialog.h b/launcher/ui/dialogs/ProgressDialog.h
index 0b4b78a4..fc9a0fbc 100644
--- a/launcher/ui/dialogs/ProgressDialog.h
+++ b/launcher/ui/dialogs/ProgressDialog.h
@@ -1,22 +1,50 @@
-/* Copyright 2013-2021 MultiMC Contributors
+/// SPDX-License-Identifier: GPL-3.0-only
+/*
+ * PrismLaucher - Minecraft Launcher
+ * Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.com>
*
- * 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 <QDialog>
#include <memory>
+#include <QHash>
+#include <QUuid>
+
+#include "QObjectPtr.h"
+#include "tasks/Task.h"
+
+#include "ui/widgets/SubTaskProgressBar.h"
class Task;
class SequentialTask;
@@ -52,6 +80,7 @@ slots:
void changeStatus(const QString &status);
void changeProgress(qint64 current, qint64 total);
+ void changeStepProgress(TaskStepProgress const& task_progress);
private
@@ -64,6 +93,7 @@ protected:
private:
bool handleImmediateResult(QDialog::DialogCode &result);
+ void addTaskProgress(TaskStepProgress const& progress);
private:
Ui::ProgressDialog *ui;
@@ -71,4 +101,8 @@ private:
Task *task;
bool m_is_multi_step = false;
+ QHash<QUuid, SubTaskProgressBar*> taskProgress;
+
+
};
+
diff --git a/launcher/ui/dialogs/ProgressDialog.ui b/launcher/ui/dialogs/ProgressDialog.ui
index 34ab71e3..a4d08124 100644
--- a/launcher/ui/dialogs/ProgressDialog.ui
+++ b/launcher/ui/dialogs/ProgressDialog.ui
@@ -2,75 +2,135 @@
<ui version="4.0">
<class>ProgressDialog</class>
<widget class="QDialog" name="ProgressDialog">
- <property name="minimumSize">
- <size>
- <width>400</width>
- <height>0</height>
- </size>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>480</width>
+ <height>210</height>
+ </rect>
</property>
- <property name="maximumSize">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>1</horstretch>
+ <verstretch>1</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
<size>
- <width>600</width>
- <height>16777215</height>
+ <width>480</width>
+ <height>210</height>
</size>
</property>
<property name="windowTitle">
<string>Please wait...</string>
</property>
- <layout class="QGridLayout" name="gridLayout">
- <item row="4" column="0">
- <widget class="QPushButton" name="skipButton">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
+ <property name="sizeGripEnabled">
+ <bool>true</bool>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout" stretch="0,0,0,0">
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout" stretch="1,0">
+ <item>
+ <widget class="QLabel" name="globalStatusLabel">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>15</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>Global Task Status...</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="globalStatusDetailsLabel">
+ <property name="text">
+ <string>Global Status Details...</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QProgressBar" name="globalProgressBar">
+ <property name="enabled">
+ <bool>true</bool>
</property>
- <property name="text">
- <string>Skip</string>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>24</height>
+ </size>
</property>
- </widget>
- </item>
- <item row="0" column="0">
- <widget class="QLabel" name="globalStatusLabel">
- <property name="text">
- <string>Global Task Status...</string>
+ <property name="value">
+ <number>24</number>
</property>
</widget>
</item>
- <item row="2" column="0">
- <widget class="QLabel" name="statusLabel">
+ <item>
+ <widget class="QScrollArea" name="taskProgressScrollArea">
<property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding">
+ <sizepolicy hsizetype="Expanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
- <property name="text">
- <string>Task Status...</string>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>100</height>
+ </size>
</property>
- <property name="wordWrap">
- <bool>true</bool>
+ <property name="frameShape">
+ <enum>QFrame::StyledPanel</enum>
</property>
- </widget>
- </item>
- <item row="3" column="0">
- <widget class="QProgressBar" name="taskProgressBar">
- <property name="value">
- <number>24</number>
+ <property name="horizontalScrollBarPolicy">
+ <enum>Qt::ScrollBarAsNeeded</enum>
+ </property>
+ <property name="sizeAdjustPolicy">
+ <enum>QAbstractScrollArea::AdjustToContents</enum>
</property>
- <property name="textVisible">
- <bool>false</bool>
+ <property name="widgetResizable">
+ <bool>true</bool>
</property>
+ <widget class="QWidget" name="taskProgressContainer">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>464</width>
+ <height>96</height>
+ </rect>
+ </property>
+ <layout class="QVBoxLayout" name="taskProgressLayout">
+ <property name="spacing">
+ <number>2</number>
+ </property>
+ </layout>
+ </widget>
</widget>
</item>
- <item row="1" column="0">
- <widget class="QProgressBar" name="globalProgressBar">
- <property name="enabled">
- <bool>true</bool>
+ <item>
+ <widget class="QPushButton" name="skipButton">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
</property>
- <property name="value">
- <number>24</number>
+ <property name="text">
+ <string>Skip</string>
</property>
</widget>
</item>
diff --git a/launcher/ui/pages/global/LauncherPage.cpp b/launcher/ui/pages/global/LauncherPage.cpp
index 324eb461..816dde72 100644
--- a/launcher/ui/pages/global/LauncherPage.cpp
+++ b/launcher/ui/pages/global/LauncherPage.cpp
@@ -217,6 +217,7 @@ void LauncherPage::applySettings()
s->set("CentralModsDir", ui->modsDirTextBox->text());
s->set("IconsDir", ui->iconsDirTextBox->text());
s->set("DownloadsDir", ui->downloadsDirTextBox->text());
+ s->set("DownloadsDirWatchRecursive", ui->downloadsDirWatchRecursiveCheckBox->isChecked());
auto sortMode = (InstSortMode)ui->sortingModeGroup->checkedId();
switch (sortMode)
@@ -274,6 +275,7 @@ void LauncherPage::loadSettings()
ui->modsDirTextBox->setText(s->get("CentralModsDir").toString());
ui->iconsDirTextBox->setText(s->get("IconsDir").toString());
ui->downloadsDirTextBox->setText(s->get("DownloadsDir").toString());
+ ui->downloadsDirWatchRecursiveCheckBox->setChecked(s->get("DownloadsDirWatchRecursive").toBool());
QString sortMode = s->get("InstSortMode").toString();
diff --git a/launcher/ui/pages/global/LauncherPage.ui b/launcher/ui/pages/global/LauncherPage.ui
index 923b7f95..55bd3eea 100644
--- a/launcher/ui/pages/global/LauncherPage.ui
+++ b/launcher/ui/pages/global/LauncherPage.ui
@@ -67,19 +67,16 @@
<string>Folders</string>
</property>
<layout class="QGridLayout" name="foldersBoxLayout">
- <item row="0" column="2">
- <widget class="QToolButton" name="instDirBrowseBtn">
+ <item row="3" column="0">
+ <widget class="QLabel" name="labelDownloadsDir">
<property name="text">
- <string notr="true">...</string>
+ <string>&amp;Downloads:</string>
+ </property>
+ <property name="buddy">
+ <cstring>downloadsDirTextBox</cstring>
</property>
</widget>
</item>
- <item row="1" column="1">
- <widget class="QLineEdit" name="modsDirTextBox"/>
- </item>
- <item row="0" column="1">
- <widget class="QLineEdit" name="instDirTextBox"/>
- </item>
<item row="0" column="0">
<widget class="QLabel" name="labelInstDir">
<property name="text">
@@ -90,16 +87,25 @@
</property>
</widget>
</item>
+ <item row="0" column="1">
+ <widget class="QLineEdit" name="instDirTextBox"/>
+ </item>
+ <item row="3" column="1">
+ <widget class="QLineEdit" name="downloadsDirTextBox"/>
+ </item>
<item row="2" column="1">
<widget class="QLineEdit" name="iconsDirTextBox"/>
</item>
- <item row="2" column="2">
- <widget class="QToolButton" name="iconsDirBrowseBtn">
+ <item row="3" column="2">
+ <widget class="QToolButton" name="downloadsDirBrowseBtn">
<property name="text">
<string notr="true">...</string>
</property>
</widget>
</item>
+ <item row="1" column="1">
+ <widget class="QLineEdit" name="modsDirTextBox"/>
+ </item>
<item row="1" column="2">
<widget class="QToolButton" name="modsDirBrowseBtn">
<property name="text">
@@ -117,33 +123,37 @@
</property>
</widget>
</item>
- <item row="2" column="0">
- <widget class="QLabel" name="labelIconsDir">
+ <item row="0" column="2">
+ <widget class="QToolButton" name="instDirBrowseBtn">
<property name="text">
- <string>&amp;Icons:</string>
+ <string notr="true">...</string>
</property>
- <property name="buddy">
- <cstring>iconsDirTextBox</cstring>
+ </widget>
+ </item>
+ <item row="2" column="2">
+ <widget class="QToolButton" name="iconsDirBrowseBtn">
+ <property name="text">
+ <string notr="true">...</string>
</property>
</widget>
</item>
- <item row="3" column="0">
- <widget class="QLabel" name="labelDownloadsDir">
+ <item row="2" column="0">
+ <widget class="QLabel" name="labelIconsDir">
<property name="text">
- <string>&amp;Downloads:</string>
+ <string>&amp;Icons:</string>
</property>
<property name="buddy">
- <cstring>downloadsDirTextBox</cstring>
+ <cstring>iconsDirTextBox</cstring>
</property>
</widget>
</item>
- <item row="3" column="1">
- <widget class="QLineEdit" name="downloadsDirTextBox"/>
- </item>
- <item row="3" column="2">
- <widget class="QToolButton" name="downloadsDirBrowseBtn">
+ <item row="4" column="1" colspan="2">
+ <widget class="QCheckBox" name="downloadsDirWatchRecursiveCheckBox">
+ <property name="toolTip">
+ <string>When enabled, in addition to the downloads folder, its sub folders will also be searched when looking for resources (e.g. when looking for blocked mods on CurseForge).</string>
+ </property>
<property name="text">
- <string>...</string>
+ <string>Check downloads folder recursively</string>
</property>
</widget>
</item>
diff --git a/launcher/ui/pages/global/MinecraftPage.cpp b/launcher/ui/pages/global/MinecraftPage.cpp
index cc597fe0..eca3e865 100644
--- a/launcher/ui/pages/global/MinecraftPage.cpp
+++ b/launcher/ui/pages/global/MinecraftPage.cpp
@@ -46,7 +46,6 @@
MinecraftPage::MinecraftPage(QWidget *parent) : QWidget(parent), ui(new Ui::MinecraftPage)
{
ui->setupUi(this);
- ui->tabWidget->tabBar()->hide();
loadSettings();
updateCheckboxStuff();
}
diff --git a/launcher/ui/pages/global/MinecraftPage.ui b/launcher/ui/pages/global/MinecraftPage.ui
index 640f436d..103881b5 100644
--- a/launcher/ui/pages/global/MinecraftPage.ui
+++ b/launcher/ui/pages/global/MinecraftPage.ui
@@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>936</width>
- <height>1134</height>
+ <height>541</height>
</rect>
</property>
<property name="sizePolicy">
@@ -39,7 +39,7 @@
</property>
<widget class="QWidget" name="minecraftTab">
<attribute name="title">
- <string notr="true">Minecraft</string>
+ <string notr="true">General</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
@@ -112,22 +112,29 @@
</widget>
</item>
<item>
- <widget class="QGroupBox" name="nativeLibWorkaroundGroupBox">
+ <widget class="QGroupBox" name="gameTimeGroupBox">
<property name="title">
- <string>Native library workarounds</string>
+ <string>Game time</string>
</property>
- <layout class="QVBoxLayout" name="verticalLayout_5">
+ <layout class="QVBoxLayout" name="verticalLayout_6">
<item>
- <widget class="QCheckBox" name="useNativeGLFWCheck">
+ <widget class="QCheckBox" name="showGameTime">
<property name="text">
- <string>Use system installation of &amp;GLFW</string>
+ <string>Show time spent &amp;playing instances</string>
</property>
</widget>
</item>
<item>
- <widget class="QCheckBox" name="useNativeOpenALCheck">
+ <widget class="QCheckBox" name="showGlobalGameTime">
<property name="text">
- <string>Use system installation of &amp;OpenAL</string>
+ <string>Show time spent playing across &amp;all instances</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="recordGameTime">
+ <property name="text">
+ <string>&amp;Record time spent playing instances</string>
</property>
</widget>
</item>
@@ -135,38 +142,28 @@
</widget>
</item>
<item>
- <widget class="QGroupBox" name="perfomanceGroupBox">
+ <widget class="QGroupBox" name="groupBox">
<property name="title">
- <string>Performance</string>
+ <string>Miscellaneous</string>
</property>
- <layout class="QVBoxLayout" name="verticalLayout_2">
- <item>
- <widget class="QCheckBox" name="enableFeralGamemodeCheck">
- <property name="toolTip">
- <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Enable Feral Interactive's GameMode, to potentially improve gaming performance.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
- </property>
- <property name="text">
- <string>Enable Feral GameMode</string>
- </property>
- </widget>
- </item>
+ <layout class="QVBoxLayout" name="verticalLayout">
<item>
- <widget class="QCheckBox" name="enableMangoHud">
+ <widget class="QCheckBox" name="closeAfterLaunchCheck">
<property name="toolTip">
- <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Enable MangoHud's advanced performance overlay.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The launcher will automatically reopen when the game crashes or exits.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
- <string>Enable MangoHud</string>
+ <string>&amp;Close the launcher after game window opens</string>
</property>
</widget>
</item>
<item>
- <widget class="QCheckBox" name="useDiscreteGpuCheck">
+ <widget class="QCheckBox" name="quitAfterGameStopCheck">
<property name="toolTip">
- <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Use the discrete GPU instead of the primary GPU.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The launcher will automatically quit after the game exits or crashes.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
- <string>Use discrete GPU</string>
+ <string>&amp;Quit the launcher after game window closes</string>
</property>
</widget>
</item>
@@ -174,29 +171,42 @@
</widget>
</item>
<item>
- <widget class="QGroupBox" name="gameTimeGroupBox">
+ <spacer name="verticalSpacerMinecraft">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="tab">
+ <attribute name="title">
+ <string>Tweaks</string>
+ </attribute>
+ <layout class="QVBoxLayout" name="verticalLayout_12">
+ <item>
+ <widget class="QGroupBox" name="nativeLibWorkaroundGroupBox">
<property name="title">
- <string>Game time</string>
+ <string>Native library workarounds</string>
</property>
- <layout class="QVBoxLayout" name="verticalLayout_6">
+ <layout class="QVBoxLayout" name="verticalLayout_11">
<item>
- <widget class="QCheckBox" name="showGameTime">
- <property name="text">
- <string>Show time spent &amp;playing instances</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QCheckBox" name="showGlobalGameTime">
+ <widget class="QCheckBox" name="useNativeGLFWCheck">
<property name="text">
- <string>Show time spent playing across &amp;all instances</string>
+ <string>Use system installation of &amp;GLFW</string>
</property>
</widget>
</item>
<item>
- <widget class="QCheckBox" name="recordGameTime">
+ <widget class="QCheckBox" name="useNativeOpenALCheck">
<property name="text">
- <string>&amp;Record time spent playing instances</string>
+ <string>Use system installation of &amp;OpenAL</string>
</property>
</widget>
</item>
@@ -204,28 +214,38 @@
</widget>
</item>
<item>
- <widget class="QGroupBox" name="groupBox">
+ <widget class="QGroupBox" name="perfomanceGroupBox">
<property name="title">
- <string>Miscellaneous</string>
+ <string>Performance</string>
</property>
- <layout class="QVBoxLayout" name="verticalLayout">
+ <layout class="QVBoxLayout" name="verticalLayout_2">
<item>
- <widget class="QCheckBox" name="closeAfterLaunchCheck">
+ <widget class="QCheckBox" name="enableFeralGamemodeCheck">
<property name="toolTip">
- <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The launcher will automatically reopen when the game crashes or exits.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Enable Feral Interactive's GameMode, to potentially improve gaming performance.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
- <string>&amp;Close the launcher after game window opens</string>
+ <string>Enable Feral GameMode</string>
</property>
</widget>
</item>
<item>
- <widget class="QCheckBox" name="quitAfterGameStopCheck">
+ <widget class="QCheckBox" name="enableMangoHud">
<property name="toolTip">
- <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The launcher will automatically quit after the game exits or crashes.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Enable MangoHud's advanced performance overlay.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
- <string>&amp;Quit the launcher after game window closes</string>
+ <string>Enable MangoHud</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="useDiscreteGpuCheck">
+ <property name="toolTip">
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Use the discrete GPU instead of the primary GPU.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ <property name="text">
+ <string>Use discrete GPU</string>
</property>
</widget>
</item>
@@ -233,14 +253,14 @@
</widget>
</item>
<item>
- <spacer name="verticalSpacerMinecraft">
+ <spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
- <width>0</width>
- <height>0</height>
+ <width>20</width>
+ <height>40</height>
</size>
</property>
</spacer>
@@ -255,11 +275,6 @@
<tabstop>maximizedCheckBox</tabstop>
<tabstop>windowWidthSpinBox</tabstop>
<tabstop>windowHeightSpinBox</tabstop>
- <tabstop>useNativeGLFWCheck</tabstop>
- <tabstop>useNativeOpenALCheck</tabstop>
- <tabstop>enableFeralGamemodeCheck</tabstop>
- <tabstop>enableMangoHud</tabstop>
- <tabstop>useDiscreteGpuCheck</tabstop>
</tabstops>
<resources/>
<connections/>
diff --git a/launcher/ui/pages/instance/VersionPage.cpp b/launcher/ui/pages/instance/VersionPage.cpp
index 7fff3b93..fffb96f2 100644
--- a/launcher/ui/pages/instance/VersionPage.cpp
+++ b/launcher/ui/pages/instance/VersionPage.cpp
@@ -501,7 +501,7 @@ void VersionPage::on_actionDownload_All_triggered()
return;
}
ProgressDialog tDialog(this);
- connect(updateTask.get(), SIGNAL(failed(QString)), SLOT(onGameUpdateError(QString)));
+ connect(updateTask.get(), &Task::failed, this, &VersionPage::onGameUpdateError);
// FIXME: unused return value
tDialog.execWithTask(updateTask.get());
updateButtons();
diff --git a/launcher/ui/pages/instance/WorldListPage.cpp b/launcher/ui/pages/instance/WorldListPage.cpp
index d4a395d9..b6ad159e 100644
--- a/launcher/ui/pages/instance/WorldListPage.cpp
+++ b/launcher/ui/pages/instance/WorldListPage.cpp
@@ -107,6 +107,7 @@ WorldListPage::WorldListPage(BaseInstance *inst, std::shared_ptr<WorldList> worl
auto head = ui->worldTreeView->header();
head->setSectionResizeMode(0, QHeaderView::Stretch);
head->setSectionResizeMode(1, QHeaderView::ResizeToContents);
+ head->setSectionResizeMode(4, QHeaderView::ResizeToContents);
connect(ui->worldTreeView->selectionModel(), &QItemSelectionModel::currentChanged, this, &WorldListPage::worldChanged);
worldChanged(QModelIndex(), QModelIndex());
diff --git a/launcher/ui/pages/modplatform/ftb/FtbFilterModel.cpp b/launcher/ui/pages/modplatform/ftb/FtbFilterModel.cpp
deleted file mode 100644
index e2b548f2..00000000
--- a/launcher/ui/pages/modplatform/ftb/FtbFilterModel.cpp
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright 2020-2021 Jamie Mansfield <jmansfield@cadixdev.org>
- *
- * 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 "FtbFilterModel.h"
-
-#include <QDebug>
-
-#include "modplatform/modpacksch/FTBPackManifest.h"
-
-#include "StringUtils.h"
-
-namespace Ftb {
-
-FilterModel::FilterModel(QObject *parent) : QSortFilterProxyModel(parent)
-{
- currentSorting = Sorting::ByPlays;
- sortings.insert(tr("Sort by Plays"), Sorting::ByPlays);
- sortings.insert(tr("Sort by Installs"), Sorting::ByInstalls);
- sortings.insert(tr("Sort by Name"), Sorting::ByName);
-}
-
-const QMap<QString, FilterModel::Sorting> FilterModel::getAvailableSortings()
-{
- return sortings;
-}
-
-QString FilterModel::translateCurrentSorting()
-{
- return sortings.key(currentSorting);
-}
-
-void FilterModel::setSorting(Sorting sorting)
-{
- currentSorting = sorting;
- invalidate();
-}
-
-FilterModel::Sorting FilterModel::getCurrentSorting()
-{
- return currentSorting;
-}
-
-void FilterModel::setSearchTerm(const QString& term)
-{
- searchTerm = term.trimmed();
- invalidate();
-}
-
-bool FilterModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
-{
- if (searchTerm.isEmpty()) {
- return true;
- }
-
- auto index = sourceModel()->index(sourceRow, 0, sourceParent);
- auto pack = sourceModel()->data(index, Qt::UserRole).value<ModpacksCH::Modpack>();
- return pack.name.contains(searchTerm, Qt::CaseInsensitive);
-}
-
-bool FilterModel::lessThan(const QModelIndex &left, const QModelIndex &right) const
-{
- ModpacksCH::Modpack leftPack = sourceModel()->data(left, Qt::UserRole).value<ModpacksCH::Modpack>();
- ModpacksCH::Modpack rightPack = sourceModel()->data(right, Qt::UserRole).value<ModpacksCH::Modpack>();
-
- if (currentSorting == ByPlays) {
- return leftPack.plays < rightPack.plays;
- }
- else if (currentSorting == ByInstalls) {
- return leftPack.installs < rightPack.installs;
- }
- else if (currentSorting == ByName) {
- return StringUtils::naturalCompare(leftPack.name, rightPack.name, Qt::CaseSensitive) >= 0;
- }
-
- // Invalid sorting set, somehow...
- qWarning() << "Invalid sorting set!";
- return true;
-}
-
-}
diff --git a/launcher/ui/pages/modplatform/ftb/FtbFilterModel.h b/launcher/ui/pages/modplatform/ftb/FtbFilterModel.h
deleted file mode 100644
index 1be28e99..00000000
--- a/launcher/ui/pages/modplatform/ftb/FtbFilterModel.h
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright 2020-2021 Jamie Mansfield <jmansfield@cadixdev.org>
- *
- * 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 <QtCore/QSortFilterProxyModel>
-
-namespace Ftb {
-
-class FilterModel : public QSortFilterProxyModel
-{
- Q_OBJECT
-
-public:
- FilterModel(QObject* parent = Q_NULLPTR);
- enum Sorting {
- ByPlays,
- ByInstalls,
- ByName,
- };
- const QMap<QString, Sorting> getAvailableSortings();
- QString translateCurrentSorting();
- void setSorting(Sorting sorting);
- Sorting getCurrentSorting();
- void setSearchTerm(const QString& term);
-
-protected:
- bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
- bool lessThan(const QModelIndex &left, const QModelIndex &right) const override;
-
-private:
- QMap<QString, Sorting> sortings;
- Sorting currentSorting;
- QString searchTerm { "" };
-
-};
-
-}
diff --git a/launcher/ui/pages/modplatform/ftb/FtbListModel.cpp b/launcher/ui/pages/modplatform/ftb/FtbListModel.cpp
deleted file mode 100644
index e8065415..00000000
--- a/launcher/ui/pages/modplatform/ftb/FtbListModel.cpp
+++ /dev/null
@@ -1,304 +0,0 @@
-/*
- * Copyright 2020-2021 Jamie Mansfield <jmansfield@cadixdev.org>
- *
- * 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 "FtbListModel.h"
-
-#include "BuildConfig.h"
-#include "Application.h"
-#include "Json.h"
-
-#include <QPainter>
-
-namespace Ftb {
-
-ListModel::ListModel(QObject *parent) : QAbstractListModel(parent)
-{
-}
-
-ListModel::~ListModel()
-{
-}
-
-int ListModel::rowCount(const QModelIndex &parent) const
-{
- return parent.isValid() ? 0 : modpacks.size();
-}
-
-int ListModel::columnCount(const QModelIndex &parent) const
-{
- return parent.isValid() ? 0 : 1;
-}
-
-QVariant ListModel::data(const QModelIndex &index, int role) const
-{
- int pos = index.row();
- if(pos >= modpacks.size() || pos < 0 || !index.isValid())
- {
- return QString("INVALID INDEX %1").arg(pos);
- }
-
- ModpacksCH::Modpack pack = modpacks.at(pos);
- if(role == Qt::DisplayRole)
- {
- return pack.name;
- }
- else if (role == Qt::ToolTipRole)
- {
- return pack.synopsis;
- }
- else if(role == Qt::DecorationRole)
- {
- QIcon placeholder = APPLICATION->getThemedIcon("screenshot-placeholder");
-
- auto iter = m_logoMap.find(pack.name);
- if (iter != m_logoMap.end()) {
- auto & logo = *iter;
- if(!logo.result.isNull()) {
- return logo.result;
- }
- return placeholder;
- }
-
- for(auto art : pack.art) {
- if(art.type == "square") {
- ((ListModel *)this)->requestLogo(pack.name, art.url);
- }
- }
- return placeholder;
- }
- else if(role == Qt::UserRole)
- {
- QVariant v;
- v.setValue(pack);
- return v;
- }
-
- return QVariant();
-}
-
-void ListModel::getLogo(const QString &logo, const QString &logoUrl, LogoCallback callback)
-{
- if(m_logoMap.contains(logo))
- {
- callback(APPLICATION->metacache()->resolveEntry("ModpacksCHPacks", QString("logos/%1").arg(logo.section(".", 0, 0)))->getFullPath());
- }
- else
- {
- requestLogo(logo, logoUrl);
- }
-}
-
-void ListModel::request()
-{
- m_aborted = false;
-
- beginResetModel();
- modpacks.clear();
- endResetModel();
-
- auto netJob = makeShared<NetJob>("Ftb::Request", APPLICATION->network());
- auto url = QString(BuildConfig.MODPACKSCH_API_BASE_URL + "public/modpack/all");
- netJob->addNetAction(Net::Download::makeByteArray(QUrl(url), &response));
- jobPtr = netJob;
- jobPtr->start();
-
- QObject::connect(netJob.get(), &NetJob::succeeded, this, &ListModel::requestFinished);
- QObject::connect(netJob.get(), &NetJob::failed, this, &ListModel::requestFailed);
-}
-
-void ListModel::abortRequest()
-{
- m_aborted = jobPtr->abort();
- jobPtr.reset();
-}
-
-void ListModel::requestFinished()
-{
- jobPtr.reset();
- remainingPacks.clear();
-
- QJsonParseError parse_error {};
- QJsonDocument doc = QJsonDocument::fromJson(response, &parse_error);
- if(parse_error.error != QJsonParseError::NoError) {
- qWarning() << "Error while parsing JSON response from ModpacksCH at " << parse_error.offset << " reason: " << parse_error.errorString();
- qWarning() << response;
- return;
- }
-
- auto packs = doc.object().value("packs").toArray();
- for(auto pack : packs) {
- auto packId = pack.toInt();
- remainingPacks.append(packId);
- }
-
- if(!remainingPacks.isEmpty()) {
- currentPack = remainingPacks.at(0);
- requestPack();
- }
-}
-
-void ListModel::requestFailed(QString reason)
-{
- jobPtr.reset();
- remainingPacks.clear();
-}
-
-void ListModel::requestPack()
-{
- auto netJob = makeShared<NetJob>("Ftb::Search", APPLICATION->network());
- auto searchUrl = QString(BuildConfig.MODPACKSCH_API_BASE_URL + "public/modpack/%1").arg(currentPack);
- netJob->addNetAction(Net::Download::makeByteArray(QUrl(searchUrl), &response));
- jobPtr = netJob;
- jobPtr->start();
-
- QObject::connect(netJob.get(), &NetJob::succeeded, this, &ListModel::packRequestFinished);
- QObject::connect(netJob.get(), &NetJob::failed, this, &ListModel::packRequestFailed);
-}
-
-void ListModel::packRequestFinished()
-{
- if (!jobPtr || m_aborted)
- return;
-
- jobPtr.reset();
- remainingPacks.removeOne(currentPack);
-
- QJsonParseError parse_error;
- QJsonDocument doc = QJsonDocument::fromJson(response, &parse_error);
-
- if(parse_error.error != QJsonParseError::NoError) {
- qWarning() << "Error while parsing JSON response from ModpacksCH at " << parse_error.offset << " reason: " << parse_error.errorString();
- qWarning() << response;
- return;
- }
-
- auto obj = doc.object();
-
- ModpacksCH::Modpack pack;
- try
- {
- ModpacksCH::loadModpack(pack, obj);
- }
- catch (const JSONValidationError &e)
- {
- qDebug() << QString::fromUtf8(response);
- qWarning() << "Error while reading pack manifest from ModpacksCH: " << e.cause();
- return;
- }
-
- // Since there is no guarantee that packs have a version, this will just
- // ignore those "dud" packs.
- if (pack.versions.empty())
- {
- qWarning() << "ModpacksCH Pack " << pack.id << " ignored. reason: lacking any versions";
- }
- else
- {
- beginInsertRows(QModelIndex(), modpacks.size(), modpacks.size());
- modpacks.append(pack);
- endInsertRows();
- }
-
- if(!remainingPacks.isEmpty()) {
- currentPack = remainingPacks.at(0);
- requestPack();
- }
-}
-
-void ListModel::packRequestFailed(QString reason)
-{
- jobPtr.reset();
- remainingPacks.removeOne(currentPack);
-}
-
-void ListModel::logoLoaded(QString logo, bool stale)
-{
- auto & logoObj = m_logoMap[logo];
- logoObj.downloadJob.reset();
- QString smallPath = logoObj.fullpath + ".small";
-
- QFileInfo smallInfo(smallPath);
-
- if(stale || !smallInfo.exists()) {
- QImage image(logoObj.fullpath);
- if (image.isNull())
- {
- logoObj.failed = true;
- return;
- }
- QImage small;
- if (image.width() > image.height()) {
- small = image.scaledToWidth(512).scaledToWidth(256, Qt::SmoothTransformation);
- }
- else {
- small = image.scaledToHeight(512).scaledToHeight(256, Qt::SmoothTransformation);
- }
- QPoint offset((256 - small.width()) / 2, (256 - small.height()) / 2);
- QImage square(QSize(256, 256), QImage::Format_ARGB32);
- square.fill(Qt::transparent);
-
- QPainter painter(&square);
- painter.drawImage(offset, small);
- painter.end();
-
- square.save(logoObj.fullpath + ".small", "PNG");
- }
-
- logoObj.result = QIcon(logoObj.fullpath + ".small");
- for(int i = 0; i < modpacks.size(); i++) {
- if(modpacks[i].name == logo) {
- emit dataChanged(createIndex(i, 0), createIndex(i, 0), {Qt::DecorationRole});
- }
- }
-}
-
-void ListModel::logoFailed(QString logo)
-{
- m_logoMap[logo].failed = true;
- m_logoMap[logo].downloadJob.reset();
-}
-
-void ListModel::requestLogo(QString logo, QString url)
-{
- if(m_logoMap.contains(logo)) {
- return;
- }
-
- MetaEntryPtr entry = APPLICATION->metacache()->resolveEntry("ModpacksCHPacks", QString("logos/%1").arg(logo.section(".", 0, 0)));
-
- bool stale = entry->isStale();
-
- auto job = makeShared<NetJob>(QString("ModpacksCH Icon Download %1").arg(logo), APPLICATION->network());
- job->addNetAction(Net::Download::makeCached(QUrl(url), entry));
-
- auto fullPath = entry->getFullPath();
- QObject::connect(job.get(), &NetJob::finished, this, [this, logo, fullPath, stale]
- {
- logoLoaded(logo, stale);
- });
-
- QObject::connect(job.get(), &NetJob::failed, this, [this, logo]
- {
- logoFailed(logo);
- });
-
- auto &newLogoEntry = m_logoMap[logo];
- newLogoEntry.downloadJob = job;
- newLogoEntry.fullpath = fullPath;
- job->start();
-}
-
-}
diff --git a/launcher/ui/pages/modplatform/ftb/FtbListModel.h b/launcher/ui/pages/modplatform/ftb/FtbListModel.h
deleted file mode 100644
index d7a120f0..00000000
--- a/launcher/ui/pages/modplatform/ftb/FtbListModel.h
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright 2020-2021 Jamie Mansfield <jmansfield@cadixdev.org>
- *
- * 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 <QAbstractListModel>
-
-#include "modplatform/modpacksch/FTBPackManifest.h"
-#include "net/NetJob.h"
-#include <QIcon>
-
-namespace Ftb {
-
-struct Logo {
- QString fullpath;
- NetJob::Ptr downloadJob;
- QIcon result;
- bool failed = false;
-};
-
-typedef QMap<QString, Logo> LogoMap;
-typedef std::function<void(QString)> LogoCallback;
-
-class ListModel : public QAbstractListModel
-{
- Q_OBJECT
-
-public:
- ListModel(QObject *parent);
- virtual ~ListModel();
-
- int rowCount(const QModelIndex &parent) const override;
- int columnCount(const QModelIndex &parent) const override;
- QVariant data(const QModelIndex &index, int role) const override;
-
- void request();
- void abortRequest();
-
- void getLogo(const QString &logo, const QString &logoUrl, LogoCallback callback);
-
- [[nodiscard]] bool isMakingRequest() const { return jobPtr.get(); }
- [[nodiscard]] bool wasAborted() const { return m_aborted; }
-
-private slots:
- void requestFinished();
- void requestFailed(QString reason);
-
- void requestPack();
- void packRequestFinished();
- void packRequestFailed(QString reason);
-
- void logoFailed(QString logo);
- void logoLoaded(QString logo, bool stale);
-
-private:
- void requestLogo(QString file, QString url);
-
-private:
- bool m_aborted = false;
-
- QList<ModpacksCH::Modpack> modpacks;
- LogoMap m_logoMap;
-
- NetJob::Ptr jobPtr;
- int currentPack;
- QList<int> remainingPacks;
- QByteArray response;
-};
-
-}
diff --git a/launcher/ui/pages/modplatform/ftb/FtbPage.cpp b/launcher/ui/pages/modplatform/ftb/FtbPage.cpp
deleted file mode 100644
index 7d59a6ae..00000000
--- a/launcher/ui/pages/modplatform/ftb/FtbPage.cpp
+++ /dev/null
@@ -1,199 +0,0 @@
-// SPDX-License-Identifier: GPL-3.0-only
-/*
- * PolyMC - Minecraft Launcher
- * Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org>
- * Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
- *
- * 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 2020-2021 Jamie Mansfield <jmansfield@cadixdev.org>
- * Copyright 2021 Philip T <me@phit.link>
- *
- * 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 "FtbPage.h"
-#include "ui_FtbPage.h"
-
-#include <QKeyEvent>
-
-#include "ui/dialogs/NewInstanceDialog.h"
-#include "modplatform/modpacksch/FTBPackInstallTask.h"
-
-#include "Markdown.h"
-
-FtbPage::FtbPage(NewInstanceDialog* dialog, QWidget *parent)
- : QWidget(parent), ui(new Ui::FtbPage), dialog(dialog)
-{
- ui->setupUi(this);
-
- filterModel = new Ftb::FilterModel(this);
- listModel = new Ftb::ListModel(this);
- filterModel->setSourceModel(listModel);
- ui->packView->setModel(filterModel);
- ui->packView->setSortingEnabled(true);
- ui->packView->header()->hide();
- ui->packView->setIndentation(0);
-
- ui->searchEdit->installEventFilter(this);
-
- ui->versionSelectionBox->view()->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
- ui->versionSelectionBox->view()->parentWidget()->setMaximumHeight(300);
-
- for(int i = 0; i < filterModel->getAvailableSortings().size(); i++)
- {
- ui->sortByBox->addItem(filterModel->getAvailableSortings().keys().at(i));
- }
- ui->sortByBox->setCurrentText(filterModel->translateCurrentSorting());
-
- connect(ui->searchEdit, &QLineEdit::textChanged, this, &FtbPage::triggerSearch);
- connect(ui->sortByBox, &QComboBox::currentTextChanged, this, &FtbPage::onSortingSelectionChanged);
- connect(ui->packView->selectionModel(), &QItemSelectionModel::currentChanged, this, &FtbPage::onSelectionChanged);
- connect(ui->versionSelectionBox, &QComboBox::currentTextChanged, this, &FtbPage::onVersionSelectionChanged);
-
- ui->packDescription->setMetaEntry("FTBPacks");
-}
-
-FtbPage::~FtbPage()
-{
- delete ui;
-}
-
-bool FtbPage::eventFilter(QObject* watched, QEvent* event)
-{
- if (watched == ui->searchEdit && event->type() == QEvent::KeyPress) {
- QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
- if (keyEvent->key() == Qt::Key_Return) {
- triggerSearch();
- keyEvent->accept();
- return true;
- }
- }
- return QWidget::eventFilter(watched, event);
-}
-
-bool FtbPage::shouldDisplay() const
-{
- return true;
-}
-
-void FtbPage::retranslate()
-{
- ui->retranslateUi(this);
-}
-
-void FtbPage::openedImpl()
-{
- if(!initialised || listModel->wasAborted())
- {
- listModel->request();
- initialised = true;
- }
-
- suggestCurrent();
-}
-
-void FtbPage::closedImpl()
-{
- if (listModel->isMakingRequest())
- listModel->abortRequest();
-}
-
-void FtbPage::suggestCurrent()
-{
- if(!isOpened)
- {
- return;
- }
-
- if (selectedVersion.isEmpty())
- {
- dialog->setSuggestedPack();
- return;
- }
-
- dialog->setSuggestedPack(selected.name, selectedVersion, new ModpacksCH::PackInstallTask(selected, selectedVersion, this));
- for(auto art : selected.art) {
- if(art.type == "square") {
- QString editedLogoName;
- editedLogoName = selected.name;
-
- listModel->getLogo(selected.name, art.url, [this, editedLogoName](QString logo)
- {
- dialog->setSuggestedIconFromFile(logo + ".small", editedLogoName);
- });
- }
- }
-}
-
-void FtbPage::triggerSearch()
-{
- filterModel->setSearchTerm(ui->searchEdit->text());
-}
-
-void FtbPage::onSortingSelectionChanged(QString data)
-{
- auto toSet = filterModel->getAvailableSortings().value(data);
- filterModel->setSorting(toSet);
-}
-
-void FtbPage::onSelectionChanged(QModelIndex first, QModelIndex second)
-{
- ui->versionSelectionBox->clear();
-
- if(!first.isValid())
- {
- if(isOpened)
- {
- dialog->setSuggestedPack();
- }
- return;
- }
-
- selected = filterModel->data(first, Qt::UserRole).value<ModpacksCH::Modpack>();
-
- QString output = markdownToHTML(selected.description.toUtf8());
- ui->packDescription->setHtml(output);
-
- // reverse foreach, so that the newest versions are first
- for (auto i = selected.versions.size(); i--;) {
- ui->versionSelectionBox->addItem(selected.versions.at(i).name);
- }
-
- suggestCurrent();
-}
-
-void FtbPage::onVersionSelectionChanged(QString data)
-{
- if(data.isNull() || data.isEmpty())
- {
- selectedVersion = "";
- return;
- }
-
- selectedVersion = data;
- suggestCurrent();
-}
diff --git a/launcher/ui/pages/modplatform/ftb/FtbPage.h b/launcher/ui/pages/modplatform/ftb/FtbPage.h
deleted file mode 100644
index 631ae7f5..00000000
--- a/launcher/ui/pages/modplatform/ftb/FtbPage.h
+++ /dev/null
@@ -1,105 +0,0 @@
-// SPDX-License-Identifier: GPL-3.0-only
-/*
- * PolyMC - Minecraft Launcher
- * Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org>
- *
- * 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.
- */
-
-#pragma once
-
-#include "FtbFilterModel.h"
-#include "FtbListModel.h"
-
-#include <QWidget>
-
-#include "Application.h"
-#include "ui/pages/BasePage.h"
-#include "tasks/Task.h"
-
-namespace Ui
-{
- class FtbPage;
-}
-
-class NewInstanceDialog;
-
-class FtbPage : public QWidget, public BasePage
-{
-Q_OBJECT
-
-public:
- explicit FtbPage(NewInstanceDialog* dialog, QWidget *parent = 0);
- virtual ~FtbPage();
- virtual QString displayName() const override
- {
- return "FTB";
- }
- virtual QIcon icon() const override
- {
- return APPLICATION->getThemedIcon("ftb_logo");
- }
- virtual QString id() const override
- {
- return "ftb";
- }
- virtual QString helpPage() const override
- {
- return "FTB-platform";
- }
- virtual bool shouldDisplay() const override;
- void retranslate() override;
-
- void openedImpl() override;
- void closedImpl() override;
-
- bool eventFilter(QObject * watched, QEvent * event) override;
-
-private:
- void suggestCurrent();
-
-private slots:
- void triggerSearch();
-
- void onSortingSelectionChanged(QString data);
- void onSelectionChanged(QModelIndex first, QModelIndex second);
- void onVersionSelectionChanged(QString data);
-
-private:
- Ui::FtbPage *ui = nullptr;
- NewInstanceDialog* dialog = nullptr;
- Ftb::ListModel* listModel = nullptr;
- Ftb::FilterModel* filterModel = nullptr;
-
- ModpacksCH::Modpack selected;
- QString selectedVersion;
-
- bool initialised { false };
-};
diff --git a/launcher/ui/pages/modplatform/ftb/FtbPage.ui b/launcher/ui/pages/modplatform/ftb/FtbPage.ui
deleted file mode 100644
index 8de0f4e6..00000000
--- a/launcher/ui/pages/modplatform/ftb/FtbPage.ui
+++ /dev/null
@@ -1,86 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<ui version="4.0">
- <class>FtbPage</class>
- <widget class="QWidget" name="FtbPage">
- <property name="geometry">
- <rect>
- <x>0</x>
- <y>0</y>
- <width>875</width>
- <height>745</height>
- </rect>
- </property>
- <layout class="QGridLayout" name="gridLayout">
- <item row="2" column="0" colspan="2">
- <layout class="QGridLayout" name="gridLayout_4" columnstretch="0,0,0" rowminimumheight="0" columnminimumwidth="0,0,0">
- <item row="0" column="2">
- <widget class="QComboBox" name="versionSelectionBox"/>
- </item>
- <item row="0" column="1">
- <widget class="QLabel" name="label">
- <property name="text">
- <string>Version selected:</string>
- </property>
- <property name="alignment">
- <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
- </property>
- </widget>
- </item>
- <item row="0" column="0">
- <widget class="QComboBox" name="sortByBox"/>
- </item>
- </layout>
- </item>
- <item row="0" column="0">
- <widget class="QLineEdit" name="searchEdit">
- <property name="placeholderText">
- <string>Search and filter...</string>
- </property>
- <property name="clearButtonEnabled">
- <bool>true</bool>
- </property>
- </widget>
- </item>
- <item row="1" column="0" colspan="2">
- <layout class="QGridLayout" name="gridLayout_3">
- <item row="0" column="0">
- <widget class="QTreeView" name="packView">
- <property name="alternatingRowColors">
- <bool>true</bool>
- </property>
- <property name="iconSize">
- <size>
- <width>48</width>
- <height>48</height>
- </size>
- </property>
- </widget>
- </item>
- <item row="0" column="1">
- <widget class="ProjectDescriptionPage" name="packDescription">
- <property name="openExternalLinks">
- <bool>true</bool>
- </property>
- <property name="openLinks">
- <bool>true</bool>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- </layout>
- </widget>
- <customwidgets>
- <customwidget>
- <class>ProjectDescriptionPage</class>
- <extends>QTextBrowser</extends>
- <header>ui/widgets/ProjectDescriptionPage.h</header>
- </customwidget>
- </customwidgets>
- <tabstops>
- <tabstop>searchEdit</tabstop>
- <tabstop>versionSelectionBox</tabstop>
- </tabstops>
- <resources/>
- <connections/>
-</ui>
diff --git a/launcher/ui/themes/ITheme.cpp b/launcher/ui/themes/ITheme.cpp
index 22043e44..8f0757e1 100644
--- a/launcher/ui/themes/ITheme.cpp
+++ b/launcher/ui/themes/ITheme.cpp
@@ -38,7 +38,7 @@
#include <QDir>
#include "Application.h"
-void ITheme::apply()
+void ITheme::apply(bool)
{
APPLICATION->setStyleSheet(QString());
QApplication::setStyle(QStyleFactory::create(qtTheme()));
diff --git a/launcher/ui/themes/ITheme.h b/launcher/ui/themes/ITheme.h
index 2e5b7f25..a0a638bd 100644
--- a/launcher/ui/themes/ITheme.h
+++ b/launcher/ui/themes/ITheme.h
@@ -41,7 +41,7 @@ class QStyle;
class ITheme {
public:
virtual ~ITheme() {}
- virtual void apply();
+ virtual void apply(bool initial);
virtual QString id() = 0;
virtual QString name() = 0;
virtual bool hasStyleSheet() = 0;
diff --git a/launcher/ui/themes/SystemTheme.cpp b/launcher/ui/themes/SystemTheme.cpp
index 24875e33..a95bc875 100644
--- a/launcher/ui/themes/SystemTheme.cpp
+++ b/launcher/ui/themes/SystemTheme.cpp
@@ -60,9 +60,13 @@ SystemTheme::SystemTheme()
themeDebugLog() << "System theme not found, defaulted to Fusion";
}
-void SystemTheme::apply()
+void SystemTheme::apply(bool initial)
{
- ITheme::apply();
+ // See https://github.com/MultiMC/Launcher/issues/1790
+ // or https://github.com/PrismLauncher/PrismLauncher/issues/490
+ if (initial)
+ return;
+ ITheme::apply(initial);
}
QString SystemTheme::id()
diff --git a/launcher/ui/themes/SystemTheme.h b/launcher/ui/themes/SystemTheme.h
index b5c03def..05f31233 100644
--- a/launcher/ui/themes/SystemTheme.h
+++ b/launcher/ui/themes/SystemTheme.h
@@ -40,7 +40,7 @@ class SystemTheme : public ITheme {
public:
SystemTheme();
virtual ~SystemTheme() {}
- void apply() override;
+ void apply(bool initial) override;
QString id() override;
QString name() override;
diff --git a/launcher/ui/themes/ThemeManager.cpp b/launcher/ui/themes/ThemeManager.cpp
index 13406485..94ac8a24 100644
--- a/launcher/ui/themes/ThemeManager.cpp
+++ b/launcher/ui/themes/ThemeManager.cpp
@@ -116,22 +116,22 @@ void ThemeManager::setIconTheme(const QString& name)
QIcon::setThemeName(name);
}
-void ThemeManager::applyCurrentlySelectedTheme()
+void ThemeManager::applyCurrentlySelectedTheme(bool initial)
{
setIconTheme(APPLICATION->settings()->get("IconTheme").toString());
themeDebugLog() << "<> Icon theme set.";
- setApplicationTheme(APPLICATION->settings()->get("ApplicationTheme").toString());
+ setApplicationTheme(APPLICATION->settings()->get("ApplicationTheme").toString(), initial);
themeDebugLog() << "<> Application theme set.";
}
-void ThemeManager::setApplicationTheme(const QString& name)
+void ThemeManager::setApplicationTheme(const QString& name, bool initial)
{
auto systemPalette = qApp->palette();
auto themeIter = m_themes.find(name);
if (themeIter != m_themes.end()) {
auto& theme = themeIter->second;
themeDebugLog() << "applying theme" << theme->name();
- theme->apply();
+ theme->apply(initial);
} else {
themeWarningLog() << "Tried to set invalid theme:" << name;
}
diff --git a/launcher/ui/themes/ThemeManager.h b/launcher/ui/themes/ThemeManager.h
index 9af44b5a..87f36d9c 100644
--- a/launcher/ui/themes/ThemeManager.h
+++ b/launcher/ui/themes/ThemeManager.h
@@ -37,8 +37,8 @@ class ThemeManager {
QList<ITheme*> getValidApplicationThemes();
void setIconTheme(const QString& name);
- void applyCurrentlySelectedTheme();
- void setApplicationTheme(const QString& name);
+ void applyCurrentlySelectedTheme(bool initial = false);
+ void setApplicationTheme(const QString& name, bool initial = false);
/// <summary>
/// Returns the cat based on selected cat and with events (Birthday, XMas, etc.)
diff --git a/launcher/ui/widgets/ProgressWidget.cpp b/launcher/ui/widgets/ProgressWidget.cpp
index f736af08..9181de7f 100644
--- a/launcher/ui/widgets/ProgressWidget.cpp
+++ b/launcher/ui/widgets/ProgressWidget.cpp
@@ -51,6 +51,7 @@ void ProgressWidget::watch(const Task* task)
connect(m_task, &Task::finished, this, &ProgressWidget::handleTaskFinish);
connect(m_task, &Task::status, this, &ProgressWidget::handleTaskStatus);
+ // TODO: should we connect &Task::details
connect(m_task, &Task::progress, this, &ProgressWidget::handleTaskProgress);
connect(m_task, &Task::destroyed, this, &ProgressWidget::taskDestroyed);
diff --git a/launcher/ui/widgets/SubTaskProgressBar.cpp b/launcher/ui/widgets/SubTaskProgressBar.cpp
new file mode 100644
index 00000000..84ea5f20
--- /dev/null
+++ b/launcher/ui/widgets/SubTaskProgressBar.cpp
@@ -0,0 +1,58 @@
+// SPDX-License-Identifier: GPL-3.0-only
+/*
+ * PrismLaucher - Minecraft Launcher
+ * Copyright (C) 2022 Rachel Powers <508861+Ryex@users.noreply.github.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 "SubTaskProgressBar.h"
+#include "ui_SubTaskProgressBar.h"
+
+unique_qobject_ptr<SubTaskProgressBar> SubTaskProgressBar::create(QWidget* parent)
+{
+ auto progress_bar = new SubTaskProgressBar(parent);
+ return unique_qobject_ptr<SubTaskProgressBar>(progress_bar);
+}
+
+SubTaskProgressBar::SubTaskProgressBar(QWidget* parent)
+ : ui(new Ui::SubTaskProgressBar)
+{
+ ui->setupUi(this);
+}
+SubTaskProgressBar::~SubTaskProgressBar()
+{
+ delete ui;
+}
+
+void SubTaskProgressBar::setRange(int min, int max)
+{
+ ui->progressBar->setRange(min, max);
+}
+
+void SubTaskProgressBar::setValue(int value)
+{
+ ui->progressBar->setValue(value);
+}
+
+void SubTaskProgressBar::setStatus(QString status)
+{
+ ui->statusLabel->setText(status);
+}
+
+void SubTaskProgressBar::setDetails(QString details)
+{
+ ui->statusDetailsLabel->setText(details);
+}
+
diff --git a/launcher/ui/widgets/SubTaskProgressBar.h b/launcher/ui/widgets/SubTaskProgressBar.h
new file mode 100644
index 00000000..8f8aeea2
--- /dev/null
+++ b/launcher/ui/widgets/SubTaskProgressBar.h
@@ -0,0 +1,48 @@
+// SPDX-License-Identifier: GPL-3.0-only
+/*
+ * PrismLaucher - Minecraft Launcher
+ * Copyright (C) 2022 Rachel Powers <508861+Ryex@users.noreply.github.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 <QWidget>
+#include "QObjectPtr.h"
+
+namespace Ui {
+class SubTaskProgressBar;
+}
+
+class SubTaskProgressBar : public QWidget
+{
+ Q_OBJECT
+
+public:
+ static unique_qobject_ptr<SubTaskProgressBar> create(QWidget* parent = nullptr);
+
+ SubTaskProgressBar(QWidget* parent = nullptr);
+ ~SubTaskProgressBar();
+
+ void setRange(int min, int max);
+ void setValue(int value);
+ void setStatus(QString status);
+ void setDetails(QString details);
+
+
+
+private:
+ Ui::SubTaskProgressBar* ui;
+
+};
diff --git a/launcher/ui/widgets/SubTaskProgressBar.ui b/launcher/ui/widgets/SubTaskProgressBar.ui
new file mode 100644
index 00000000..5431eab6
--- /dev/null
+++ b/launcher/ui/widgets/SubTaskProgressBar.ui
@@ -0,0 +1,94 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>SubTaskProgressBar</class>
+ <widget class="QWidget" name="SubTaskProgressBar">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>312</width>
+ <height>86</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="windowTitle">
+ <string>Form</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout" stretch="0,0">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout" stretch="1,0">
+ <property name="spacing">
+ <number>8</number>
+ </property>
+ <item>
+ <widget class="QLabel" name="statusLabel">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="font">
+ <font>
+ <pointsize>8</pointsize>
+ </font>
+ </property>
+ <property name="text">
+ <string>Sub Task Status...</string>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="statusDetailsLabel">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="font">
+ <font>
+ <pointsize>8</pointsize>
+ </font>
+ </property>
+ <property name="text">
+ <string>Status Details</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QProgressBar" name="progressBar">
+ <property name="font">
+ <font>
+ <pointsize>8</pointsize>
+ </font>
+ </property>
+ <property name="value">
+ <number>24</number>
+ </property>
+ <property name="textVisible">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>