Merge branch 'develop' into fix-implicit-fallthrough
Signed-off-by: PandaNinjas <admin@malwarefight.wip.la>
This commit is contained in:
@ -187,7 +187,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi
|
||||
|
||||
}
|
||||
|
||||
// set the menu for the folders help, and accounts tool buttons
|
||||
// set the menu for the folders help, accounts, and export tool buttons
|
||||
{
|
||||
auto foldersMenuButton = dynamic_cast<QToolButton*>(ui->mainToolBar->widgetForAction(ui->actionFoldersButton));
|
||||
ui->actionFoldersButton->setMenu(ui->foldersMenu);
|
||||
@ -201,6 +201,11 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi
|
||||
|
||||
auto accountMenuButton = dynamic_cast<QToolButton*>(ui->mainToolBar->widgetForAction(ui->actionAccountsButton));
|
||||
accountMenuButton->setPopupMode(QToolButton::InstantPopup);
|
||||
|
||||
auto exportInstanceMenu = new QMenu(this);
|
||||
exportInstanceMenu->addAction(ui->actionExportInstanceZip);
|
||||
exportInstanceMenu->addAction(ui->actionExportInstanceMrPack);
|
||||
ui->actionExportInstance->setMenu(exportInstanceMenu);
|
||||
}
|
||||
|
||||
// hide, disable and show stuff
|
||||
@ -219,6 +224,13 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi
|
||||
// disabled until we have an instance selected
|
||||
ui->instanceToolBar->setEnabled(false);
|
||||
setInstanceActionsEnabled(false);
|
||||
|
||||
// add a close button at the end of the main toolbar when running on gamescope / steam deck
|
||||
// FIXME: detect if we don't have server side decorations instead
|
||||
if (qgetenv("XDG_CURRENT_DESKTOP") == "gamescope") {
|
||||
ui->mainToolBar->addAction(ui->actionCloseWindow);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// add the toolbar toggles to the view menu
|
||||
@ -397,8 +409,6 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi
|
||||
// removing this looks stupid
|
||||
view->setFocus();
|
||||
|
||||
ui->actionExportInstance->setMenu(ui->exportInstanceMenu);
|
||||
|
||||
retranslateUi();
|
||||
}
|
||||
|
||||
@ -470,7 +480,23 @@ void MainWindow::lockToolbars(bool state)
|
||||
|
||||
void MainWindow::konamiTriggered()
|
||||
{
|
||||
qDebug() << "Super Secret Mode ACTIVATED!";
|
||||
QString gradient = " stop:0 rgba(125, 0, 0, 255), stop:0.166 rgba(125, 125, 0, 255), stop:0.333 rgba(0, 125, 0, 255), stop:0.5 rgba(0, 125, 125, 255), stop:0.666 rgba(0, 0, 125, 255), stop:0.833 rgba(125, 0, 125, 255), stop:1 rgba(125, 0, 0, 255));";
|
||||
QString stylesheet = "background-color: qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:0," + gradient;
|
||||
if (ui->mainToolBar->styleSheet() == stylesheet) {
|
||||
ui->mainToolBar->setStyleSheet("");
|
||||
ui->instanceToolBar->setStyleSheet("");
|
||||
ui->centralWidget->setStyleSheet("");
|
||||
ui->newsToolBar->setStyleSheet("");
|
||||
ui->statusBar->setStyleSheet("");
|
||||
qDebug() << "Super Secret Mode DEACTIVATED!";
|
||||
} else {
|
||||
ui->mainToolBar->setStyleSheet(stylesheet);
|
||||
ui->instanceToolBar->setStyleSheet("background-color: qlineargradient(spread:pad, x1:0, y1:0, x2:0, y2:1," + gradient);
|
||||
ui->centralWidget->setStyleSheet("background-color: qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:1," + gradient);
|
||||
ui->newsToolBar->setStyleSheet(stylesheet);
|
||||
ui->statusBar->setStyleSheet(stylesheet);
|
||||
qDebug() << "Super Secret Mode ACTIVATED!";
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::showInstanceContextMenu(const QPoint &pos)
|
||||
@ -901,21 +927,8 @@ void MainWindow::onCatToggled(bool state)
|
||||
|
||||
void MainWindow::setCatBackground(bool enabled)
|
||||
{
|
||||
if (enabled) {
|
||||
view->setStyleSheet(QString(R"(
|
||||
InstanceView
|
||||
{
|
||||
background-image: url(:/backgrounds/%1);
|
||||
background-attachment: fixed;
|
||||
background-clip: padding;
|
||||
background-position: bottom right;
|
||||
background-repeat: none;
|
||||
background-color:palette(base);
|
||||
})")
|
||||
.arg(ThemeManager::getCatImage()));
|
||||
} else {
|
||||
view->setStyleSheet(QString());
|
||||
}
|
||||
view->setPaintCat(enabled);
|
||||
view->viewport()->repaint();
|
||||
}
|
||||
|
||||
void MainWindow::runModalTask(Task *task)
|
||||
@ -1215,6 +1228,12 @@ void MainWindow::on_actionViewInstanceFolder_triggered()
|
||||
DesktopServices::openDirectory(str);
|
||||
}
|
||||
|
||||
void MainWindow::on_actionViewLauncherRootFolder_triggered()
|
||||
{
|
||||
const QString dataPath = QDir::currentPath();
|
||||
DesktopServices::openDirectory(dataPath);
|
||||
}
|
||||
|
||||
void MainWindow::refreshInstances()
|
||||
{
|
||||
APPLICATION->instances()->loadList();
|
||||
@ -1478,140 +1497,113 @@ void MainWindow::on_actionKillInstance_triggered()
|
||||
|
||||
void MainWindow::on_actionCreateInstanceShortcut_triggered()
|
||||
{
|
||||
if (m_selectedInstance)
|
||||
{
|
||||
auto desktopPath = FS::getDesktopDir();
|
||||
if (desktopPath.isEmpty()) {
|
||||
// TODO come up with an alternative solution (open "save file" dialog)
|
||||
QMessageBox::critical(this, tr("Create instance shortcut"), tr("Couldn't find desktop?!"));
|
||||
return;
|
||||
}
|
||||
if (!m_selectedInstance)
|
||||
return;
|
||||
auto desktopPath = FS::getDesktopDir();
|
||||
if (desktopPath.isEmpty()) {
|
||||
// TODO come up with an alternative solution (open "save file" dialog)
|
||||
QMessageBox::critical(this, tr("Create instance shortcut"), tr("Couldn't find desktop?!"));
|
||||
return;
|
||||
}
|
||||
|
||||
QString desktopFilePath;
|
||||
QString appPath = QApplication::applicationFilePath();
|
||||
QString iconPath;
|
||||
QStringList args;
|
||||
#if defined(Q_OS_MACOS)
|
||||
QString appPath = QApplication::applicationFilePath();
|
||||
if (appPath.startsWith("/private/var/")) {
|
||||
QMessageBox::critical(this, tr("Create instance shortcut"), tr("The launcher is in the folder it was extracted from, therefore it cannot create shortcuts."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (FS::createShortcut(FS::PathCombine(desktopPath, m_selectedInstance->name()),
|
||||
appPath, { "--launch", m_selectedInstance->id() },
|
||||
m_selectedInstance->name(), "")) {
|
||||
QMessageBox::information(this, tr("Create instance shortcut"), tr("Created a shortcut to this instance on your desktop!"));
|
||||
}
|
||||
else
|
||||
{
|
||||
QMessageBox::critical(this, tr("Create instance shortcut"), tr("Failed to create instance shortcut!"));
|
||||
}
|
||||
if (appPath.startsWith("/private/var/")) {
|
||||
QMessageBox::critical(this, tr("Create instance shortcut"),
|
||||
tr("The launcher is in the folder it was extracted from, therefore it cannot create shortcuts."));
|
||||
return;
|
||||
}
|
||||
#elif defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD)
|
||||
QString appPath = QApplication::applicationFilePath();
|
||||
if (appPath.startsWith("/tmp/.mount_")) {
|
||||
// AppImage!
|
||||
appPath = QProcessEnvironment::systemEnvironment().value(QStringLiteral("APPIMAGE"));
|
||||
if (appPath.isEmpty())
|
||||
{
|
||||
QMessageBox::critical(this, tr("Create instance shortcut"), tr("Launcher is running as misconfigured AppImage? ($APPIMAGE environment variable is missing)"));
|
||||
}
|
||||
else if (appPath.endsWith("/"))
|
||||
{
|
||||
appPath.chop(1);
|
||||
}
|
||||
if (appPath.startsWith("/tmp/.mount_")) {
|
||||
// AppImage!
|
||||
appPath = QProcessEnvironment::systemEnvironment().value(QStringLiteral("APPIMAGE"));
|
||||
if (appPath.isEmpty()) {
|
||||
QMessageBox::critical(this, tr("Create instance shortcut"),
|
||||
tr("Launcher is running as misconfigured AppImage? ($APPIMAGE environment variable is missing)"));
|
||||
} else if (appPath.endsWith("/")) {
|
||||
appPath.chop(1);
|
||||
}
|
||||
}
|
||||
|
||||
auto icon = APPLICATION->icons()->icon(m_selectedInstance->iconKey());
|
||||
if (icon == nullptr)
|
||||
{
|
||||
icon = APPLICATION->icons()->icon("grass");
|
||||
}
|
||||
auto icon = APPLICATION->icons()->icon(m_selectedInstance->iconKey());
|
||||
if (icon == nullptr) {
|
||||
icon = APPLICATION->icons()->icon("grass");
|
||||
}
|
||||
|
||||
QString iconPath = FS::PathCombine(m_selectedInstance->instanceRoot(), "icon.png");
|
||||
iconPath = FS::PathCombine(m_selectedInstance->instanceRoot(), "icon.png");
|
||||
|
||||
QFile iconFile(iconPath);
|
||||
if (!iconFile.open(QFile::WriteOnly))
|
||||
{
|
||||
QMessageBox::critical(this, tr("Create instance shortcut"), tr("Failed to create icon for shortcut."));
|
||||
return;
|
||||
}
|
||||
bool success = icon->icon().pixmap(64, 64).save(&iconFile, "PNG");
|
||||
iconFile.close();
|
||||
QFile iconFile(iconPath);
|
||||
if (!iconFile.open(QFile::WriteOnly)) {
|
||||
QMessageBox::critical(this, tr("Create instance shortcut"), tr("Failed to create icon for shortcut."));
|
||||
return;
|
||||
}
|
||||
bool success = icon->icon().pixmap(64, 64).save(&iconFile, "PNG");
|
||||
iconFile.close();
|
||||
|
||||
if (!success)
|
||||
{
|
||||
iconFile.remove();
|
||||
QMessageBox::critical(this, tr("Create instance shortcut"), tr("Failed to create icon for shortcut."));
|
||||
return;
|
||||
}
|
||||
if (!success) {
|
||||
iconFile.remove();
|
||||
QMessageBox::critical(this, tr("Create instance shortcut"), tr("Failed to create icon for shortcut."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (DesktopServices::isFlatpak()) {
|
||||
desktopFilePath = FS::PathCombine(desktopPath, FS::RemoveInvalidFilenameChars(m_selectedInstance->name()) + ".desktop");
|
||||
QFileDialog fileDialog;
|
||||
// workaround to make sure the portal file dialog opens in the desktop directory
|
||||
fileDialog.setDirectoryUrl(desktopPath);
|
||||
desktopFilePath = fileDialog.getSaveFileName(this, tr("Create Shortcut"), desktopFilePath, tr("Desktop Entries (*.desktop)"));
|
||||
if (desktopFilePath.isEmpty())
|
||||
return; // file dialog canceled by user
|
||||
appPath = "flatpak";
|
||||
QString flatpakAppId = BuildConfig.LAUNCHER_DESKTOPFILENAME;
|
||||
flatpakAppId.remove(".desktop");
|
||||
args.append({ "run", flatpakAppId });
|
||||
}
|
||||
|
||||
QString desktopFilePath = FS::PathCombine(desktopPath, m_selectedInstance->name() + ".desktop");
|
||||
QStringList args;
|
||||
if (DesktopServices::isFlatpak()) {
|
||||
QFileDialog fileDialog;
|
||||
// workaround to make sure the portal file dialog opens in the desktop directory
|
||||
fileDialog.setDirectoryUrl(desktopPath);
|
||||
desktopFilePath = fileDialog.getSaveFileName(
|
||||
this, tr("Create Shortcut"), desktopFilePath,
|
||||
tr("Desktop Entries (*.desktop)"));
|
||||
if (desktopFilePath.isEmpty())
|
||||
return; // file dialog canceled by user
|
||||
appPath = "flatpak";
|
||||
QString flatpakAppId = BuildConfig.LAUNCHER_DESKTOPFILENAME;
|
||||
flatpakAppId.remove(".desktop");
|
||||
args.append({ "run", flatpakAppId });
|
||||
}
|
||||
args.append({ "--launch", m_selectedInstance->id() });
|
||||
if (FS::createShortcut(desktopFilePath, appPath, args, m_selectedInstance->name(), iconPath)) {
|
||||
QMessageBox::information(this, tr("Create instance shortcut"), tr("Created a shortcut to this instance on your desktop!"));
|
||||
}
|
||||
else
|
||||
{
|
||||
iconFile.remove();
|
||||
QMessageBox::critical(this, tr("Create instance shortcut"), tr("Failed to create instance shortcut!"));
|
||||
}
|
||||
#elif defined(Q_OS_WIN)
|
||||
auto icon = APPLICATION->icons()->icon(m_selectedInstance->iconKey());
|
||||
if (icon == nullptr)
|
||||
{
|
||||
icon = APPLICATION->icons()->icon("grass");
|
||||
}
|
||||
auto icon = APPLICATION->icons()->icon(m_selectedInstance->iconKey());
|
||||
if (icon == nullptr) {
|
||||
icon = APPLICATION->icons()->icon("grass");
|
||||
}
|
||||
|
||||
QString iconPath = FS::PathCombine(m_selectedInstance->instanceRoot(), "icon.ico");
|
||||
iconPath = FS::PathCombine(m_selectedInstance->instanceRoot(), "icon.ico");
|
||||
|
||||
// part of fix for weird bug involving the window icon being replaced
|
||||
// dunno why it happens, but this 2-line fix seems to be enough, so w/e
|
||||
auto appIcon = APPLICATION->getThemedIcon("logo");
|
||||
// part of fix for weird bug involving the window icon being replaced
|
||||
// dunno why it happens, but this 2-line fix seems to be enough, so w/e
|
||||
auto appIcon = APPLICATION->getThemedIcon("logo");
|
||||
|
||||
QFile iconFile(iconPath);
|
||||
if (!iconFile.open(QFile::WriteOnly))
|
||||
{
|
||||
QMessageBox::critical(this, tr("Create instance shortcut"), tr("Failed to create icon for shortcut."));
|
||||
return;
|
||||
}
|
||||
bool success = icon->icon().pixmap(64, 64).save(&iconFile, "ICO");
|
||||
iconFile.close();
|
||||
QFile iconFile(iconPath);
|
||||
if (!iconFile.open(QFile::WriteOnly)) {
|
||||
QMessageBox::critical(this, tr("Create instance shortcut"), tr("Failed to create icon for shortcut."));
|
||||
return;
|
||||
}
|
||||
bool success = icon->icon().pixmap(64, 64).save(&iconFile, "ICO");
|
||||
iconFile.close();
|
||||
|
||||
// restore original window icon
|
||||
QGuiApplication::setWindowIcon(appIcon);
|
||||
// restore original window icon
|
||||
QGuiApplication::setWindowIcon(appIcon);
|
||||
|
||||
if (!success)
|
||||
{
|
||||
iconFile.remove();
|
||||
QMessageBox::critical(this, tr("Create instance shortcut"), tr("Failed to create icon for shortcut."));
|
||||
return;
|
||||
}
|
||||
if (!success) {
|
||||
iconFile.remove();
|
||||
QMessageBox::critical(this, tr("Create instance shortcut"), tr("Failed to create icon for shortcut."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (FS::createShortcut(FS::PathCombine(desktopPath, m_selectedInstance->name()),
|
||||
QApplication::applicationFilePath(), { "--launch", m_selectedInstance->id() },
|
||||
m_selectedInstance->name(), iconPath)) {
|
||||
QMessageBox::information(this, tr("Create instance shortcut"), tr("Created a shortcut to this instance on your desktop!"));
|
||||
}
|
||||
else
|
||||
{
|
||||
iconFile.remove();
|
||||
QMessageBox::critical(this, tr("Create instance shortcut"), tr("Failed to create instance shortcut!"));
|
||||
}
|
||||
#else
|
||||
QMessageBox::critical(this, tr("Create instance shortcut"), tr("Not supported on your platform!"));
|
||||
QMessageBox::critical(this, tr("Create instance shortcut"), tr("Not supported on your platform!"));
|
||||
return;
|
||||
#endif
|
||||
args.append({ "--launch", m_selectedInstance->id() });
|
||||
if (FS::createShortcut(desktopFilePath, appPath, args, m_selectedInstance->name(), iconPath)) {
|
||||
QMessageBox::information(this, tr("Create instance shortcut"), tr("Created a shortcut to this instance on your desktop!"));
|
||||
} else {
|
||||
#if not defined(Q_OS_MACOS)
|
||||
iconFile.remove();
|
||||
#endif
|
||||
QMessageBox::critical(this, tr("Create instance shortcut"), tr("Failed to create instance shortcut!"));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -113,6 +113,8 @@ private slots:
|
||||
|
||||
void on_actionViewInstanceFolder_triggered();
|
||||
|
||||
void on_actionViewLauncherRootFolder_triggered();
|
||||
|
||||
void on_actionViewSelectedInstFolder_triggered();
|
||||
|
||||
void refreshInstances();
|
||||
|
@ -150,10 +150,6 @@
|
||||
<addaction name="actionChangeInstGroup"/>
|
||||
<addaction name="actionViewSelectedInstFolder"/>
|
||||
<addaction name="actionExportInstance"/>
|
||||
<widget class="QMenu" name="exportInstanceMenu">
|
||||
<addaction name="actionExportInstanceZip"/>
|
||||
<addaction name="actionExportInstanceMrPack"/>
|
||||
</widget>
|
||||
<addaction name="actionCopyInstance"/>
|
||||
<addaction name="actionDeleteInstance"/>
|
||||
<addaction name="actionCreateInstanceShortcut"/>
|
||||
@ -191,6 +187,7 @@
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<addaction name="actionViewInstanceFolder"/>
|
||||
<addaction name="actionViewLauncherRootFolder"/>
|
||||
<addaction name="actionViewCentralModsFolder"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="accountsMenu">
|
||||
@ -467,11 +464,17 @@
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionExportInstanceZip">
|
||||
<property name="icon">
|
||||
<iconset theme="launcher"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Prism Launcher (zip)</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionExportInstanceMrPack">
|
||||
<property name="icon">
|
||||
<iconset theme="modrinth"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Modrinth (mrpack)</string>
|
||||
</property>
|
||||
@ -539,6 +542,18 @@
|
||||
<string>Open the instance folder in a file browser.</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionViewLauncherRootFolder">
|
||||
<property name="icon">
|
||||
<iconset theme="viewfolder">
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&View Launcher Root Folder</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Open the launcher's root folder in a file browser.</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionViewCentralModsFolder">
|
||||
<property name="icon">
|
||||
<iconset theme="centralmods">
|
||||
@ -562,7 +577,7 @@
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Report a &Bug...</string>
|
||||
<string>Report a Bug or Suggest a Feature</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Open the bug tracker to report a bug with %1.</string>
|
||||
|
@ -71,13 +71,18 @@ QString getCreditsHtml()
|
||||
//: %1 is the name of the launcher, determined at build time, e.g. "Prism Launcher Developers"
|
||||
stream << "<h3>" << QObject::tr("%1 Developers", "About Credits").arg(BuildConfig.LAUNCHER_DISPLAYNAME) << "</h3>\n";
|
||||
stream << QString("<p>Sefa Eyeoglu (Scrumplex) %1</p>\n") .arg(getWebsite("https://scrumplex.net"));
|
||||
stream << QString("<p>dada513 %1</p>\n") .arg(getGitHub("dada513"));
|
||||
stream << QString("<p>d-513 %1</p>\n") .arg(getGitHub("d-513"));
|
||||
stream << QString("<p>txtsd %1</p>\n") .arg(getWebsite("https://ihavea.quest"));
|
||||
stream << QString("<p>timoreo %1</p>\n") .arg(getGitHub("timoreo22"));
|
||||
stream << QString("<p>Ezekiel Smith (ZekeSmith) %1</p>\n") .arg(getGitHub("ZekeSmith"));
|
||||
stream << QString("<p>cozyGalvinism %1</p>\n") .arg(getGitHub("cozyGalvinism"));
|
||||
stream << QString("<p>DioEgizio %1</p>\n") .arg(getGitHub("DioEgizio"));
|
||||
stream << QString("<p>flowln %1</p>\n") .arg(getGitHub("flowln"));
|
||||
stream << QString("<p>DioEgizio %1</p>\n") .arg(getGitHub("DioEgizio"));
|
||||
stream << QString("<p>flowln %1</p>\n") .arg(getGitHub("flowln"));
|
||||
stream << QString("<p>ViRb3 %1</p>\n") .arg(getGitHub("ViRb3"));
|
||||
stream << QString("<p>Rachel Powers (Ryex) %1</p>\n") .arg(getGitHub("Ryex"));
|
||||
stream << QString("<p>TayouVR %1</p>\n") .arg(getGitHub("TayouVR"));
|
||||
stream << QString("<p>TheKodeToad %1</p>\n") .arg(getGitHub("TheKodeToad"));
|
||||
stream << QString("<p>getchoo %1</p>\n") .arg(getGitHub("getchoo"));
|
||||
stream << "<br />\n";
|
||||
|
||||
// TODO: possibly retrieve from git history at build time?
|
||||
|
@ -35,24 +35,26 @@
|
||||
*/
|
||||
|
||||
#include "ExportInstanceDialog.h"
|
||||
#include "ui_ExportInstanceDialog.h"
|
||||
#include <BaseInstance.h>
|
||||
#include <MMCZip.h>
|
||||
#include <QFileDialog>
|
||||
#include <QMessageBox>
|
||||
#include <QFileSystemModel>
|
||||
#include <QMessageBox>
|
||||
#include "FileIgnoreProxy.h"
|
||||
#include "ui_ExportInstanceDialog.h"
|
||||
|
||||
#include <QSortFilterProxyModel>
|
||||
#include <QDebug>
|
||||
#include <QSaveFile>
|
||||
#include <QStack>
|
||||
#include <QFileInfo>
|
||||
#include "SeparatorPrefixTree.h"
|
||||
#include "Application.h"
|
||||
#include <icons/IconList.h>
|
||||
#include <FileSystem.h>
|
||||
#include <icons/IconList.h>
|
||||
#include <QDebug>
|
||||
#include <QFileInfo>
|
||||
#include <QSaveFile>
|
||||
#include <QSortFilterProxyModel>
|
||||
#include <QStack>
|
||||
#include <functional>
|
||||
#include "Application.h"
|
||||
#include "SeparatorPrefixTree.h"
|
||||
|
||||
ExportInstanceDialog::ExportInstanceDialog(InstancePtr instance, QWidget *parent)
|
||||
ExportInstanceDialog::ExportInstanceDialog(InstancePtr instance, QWidget* parent)
|
||||
: QDialog(parent), ui(new Ui::ExportInstanceDialog), m_instance(instance)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
@ -60,8 +62,12 @@ ExportInstanceDialog::ExportInstanceDialog(InstancePtr instance, QWidget *parent
|
||||
model->setIconProvider(&icons);
|
||||
auto root = instance->instanceRoot();
|
||||
proxyModel = new FileIgnoreProxy(root, this);
|
||||
loadPackIgnore();
|
||||
proxyModel->setSourceModel(model);
|
||||
auto prefix = QDir(instance->instanceRoot()).relativeFilePath(instance->gameRoot());
|
||||
proxyModel->ignoreFilesWithPath().insert({ FS::PathCombine(prefix, "logs"), FS::PathCombine(prefix, "crash-reports") });
|
||||
proxyModel->ignoreFilesWithName().append({ ".DS_Store", "thumbs.db", "Thumbs.db" });
|
||||
loadPackIgnore();
|
||||
|
||||
ui->treeView->setModel(proxyModel);
|
||||
ui->treeView->setRootIndex(proxyModel->mapFromSource(model->index(root)));
|
||||
ui->treeView->sortByColumn(0, Qt::AscendingOrder);
|
||||
@ -133,11 +139,9 @@ bool ExportInstanceDialog::doExport()
|
||||
|
||||
SaveIcon(m_instance);
|
||||
|
||||
auto & blocked = proxyModel->blockedPaths();
|
||||
using std::placeholders::_1;
|
||||
auto files = QFileInfoList();
|
||||
if (!MMCZip::collectFileListRecursively(m_instance->instanceRoot(), nullptr, &files,
|
||||
std::bind(&SeparatorPrefixTree<'/'>::covers, blocked, _1))) {
|
||||
std::bind(&FileIgnoreProxy::filterFile, proxyModel, std::placeholders::_1))) {
|
||||
QMessageBox::warning(this, tr("Error"), tr("Unable to export instance"));
|
||||
return false;
|
||||
}
|
||||
|
@ -52,6 +52,8 @@ ExportMrPackDialog::ExportMrPackDialog(InstancePtr instance, QWidget* parent)
|
||||
// use the game root - everything outside cannot be exported
|
||||
const QDir root(instance->gameRoot());
|
||||
proxy = new FileIgnoreProxy(instance->gameRoot(), this);
|
||||
proxy->ignoreFilesWithPath().insert({ "logs", "crash-reports" });
|
||||
proxy->ignoreFilesWithName().append({ ".DS_Store", "thumbs.db", "Thumbs.db" });
|
||||
proxy->setSourceModel(model);
|
||||
|
||||
const QDir::Filters filter(QDir::AllEntries | QDir::NoDotAndDotDot | QDir::AllDirs | QDir::Hidden);
|
||||
@ -98,7 +100,7 @@ void ExportMrPackDialog::done(int result)
|
||||
return;
|
||||
|
||||
ModrinthPackExportTask task(ui->name->text(), ui->version->text(), ui->summary->text(), instance, output,
|
||||
[this](const QString& path) { return proxy->blockedPaths().covers(path); });
|
||||
std::bind(&FileIgnoreProxy::filterFile, proxy, std::placeholders::_1));
|
||||
|
||||
connect(&task, &Task::failed,
|
||||
[this](const QString reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->show(); });
|
||||
|
@ -54,7 +54,7 @@
|
||||
#include <utility>
|
||||
|
||||
#include "ui/widgets/PageContainer.h"
|
||||
#include "ui/pages/modplatform/VanillaPage.h"
|
||||
#include "ui/pages/modplatform/CustomPage.h"
|
||||
#include "ui/pages/modplatform/atlauncher/AtlPage.h"
|
||||
#include "ui/pages/modplatform/legacy_ftb/Page.h"
|
||||
#include "ui/pages/modplatform/flame/FlamePage.h"
|
||||
@ -162,7 +162,7 @@ QList<BasePage *> NewInstanceDialog::getPages()
|
||||
|
||||
importPage = new ImportPage(this);
|
||||
|
||||
pages.append(new VanillaPage(this));
|
||||
pages.append(new CustomPage(this));
|
||||
pages.append(importPage);
|
||||
pages.append(new AtlPage(this));
|
||||
if (APPLICATION->capabilities() & Application::SupportsFlame)
|
||||
|
@ -32,7 +32,7 @@ NewsDialog::~NewsDialog()
|
||||
|
||||
void NewsDialog::selectedArticleChanged(const QString& new_title)
|
||||
{
|
||||
auto const& article_entry = m_entries.constFind(new_title).value();
|
||||
auto article_entry = m_entries.constFind(new_title).value();
|
||||
|
||||
ui->articleTitleLabel->setText(QString("<a href='%1'>%2</a>").arg(article_entry->link, new_title));
|
||||
|
||||
|
@ -18,6 +18,8 @@
|
||||
*/
|
||||
|
||||
#include "ResourceDownloadDialog.h"
|
||||
#include <QEventLoop>
|
||||
#include <QList>
|
||||
|
||||
#include <QPushButton>
|
||||
#include <algorithm>
|
||||
@ -30,6 +32,10 @@
|
||||
#include "minecraft/mod/ShaderPackFolderModel.h"
|
||||
#include "minecraft/mod/TexturePackFolderModel.h"
|
||||
|
||||
#include "minecraft/mod/tasks/GetModDependenciesTask.h"
|
||||
#include "modplatform/ModIndex.h"
|
||||
#include "ui/dialogs/CustomMessageBox.h"
|
||||
#include "ui/dialogs/ProgressDialog.h"
|
||||
#include "ui/dialogs/ReviewMessageBox.h"
|
||||
|
||||
#include "ui/pages/modplatform/ResourcePage.h"
|
||||
@ -117,18 +123,71 @@ void ResourceDownloadDialog::connectButtons()
|
||||
connect(HelpButton, &QPushButton::clicked, m_container, &PageContainer::help);
|
||||
}
|
||||
|
||||
static ModPlatform::ProviderCapabilities ProviderCaps;
|
||||
|
||||
QStringList getRequiredBy(QList<ResourceDownloadDialog::DownloadTaskPtr> tasks, ResourceDownloadDialog::DownloadTaskPtr pack)
|
||||
{
|
||||
auto addonId = pack->getPack()->addonId;
|
||||
auto provider = pack->getPack()->provider;
|
||||
auto version = pack->getVersionID();
|
||||
auto req = QStringList();
|
||||
for (auto& task : tasks) {
|
||||
if (provider != task->getPack()->provider)
|
||||
continue;
|
||||
auto deps = task->getVersion().dependencies;
|
||||
if (auto dep = std::find_if(deps.begin(), deps.end(),
|
||||
[addonId, provider, version](const ModPlatform::Dependency& d) {
|
||||
return d.type == ModPlatform::DependencyType::REQUIRED &&
|
||||
(provider == ModPlatform::ResourceProvider::MODRINTH && d.addonId.toString().isEmpty()
|
||||
? version == d.version
|
||||
: d.addonId == addonId);
|
||||
});
|
||||
dep != deps.end()) {
|
||||
req.append(task->getName());
|
||||
}
|
||||
}
|
||||
return req;
|
||||
}
|
||||
|
||||
void ResourceDownloadDialog::confirm()
|
||||
{
|
||||
auto confirm_dialog = ReviewMessageBox::create(this, tr("Confirm %1 to download").arg(resourcesString()));
|
||||
confirm_dialog->retranslateUi(resourcesString());
|
||||
|
||||
if (auto task = getModDependenciesTask(); task) {
|
||||
connect(task.get(), &Task::failed, this,
|
||||
[&](QString reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->exec(); });
|
||||
|
||||
connect(task.get(), &Task::succeeded, this, [&]() {
|
||||
QStringList warnings = task->warnings();
|
||||
if (warnings.count()) {
|
||||
CustomMessageBox::selectable(this, tr("Warnings"), warnings.join('\n'), QMessageBox::Warning)->exec();
|
||||
}
|
||||
});
|
||||
|
||||
// Check for updates
|
||||
ProgressDialog progress_dialog(this);
|
||||
progress_dialog.setSkipButton(true, tr("Abort"));
|
||||
progress_dialog.setWindowTitle(tr("Checking for dependencies..."));
|
||||
auto ret = progress_dialog.execWithTask(task.get());
|
||||
|
||||
// If the dialog was skipped / some download error happened
|
||||
if (ret == QDialog::DialogCode::Rejected) {
|
||||
QMetaObject::invokeMethod(this, "reject", Qt::QueuedConnection);
|
||||
return;
|
||||
} else {
|
||||
for (auto dep : task->getDependecies())
|
||||
addResource(dep->pack, dep->version);
|
||||
}
|
||||
}
|
||||
|
||||
auto selected = getTasks();
|
||||
std::sort(selected.begin(), selected.end(), [](const DownloadTaskPtr& a, const DownloadTaskPtr& b) {
|
||||
return QString::compare(a->getName(), b->getName(), Qt::CaseInsensitive) < 0;
|
||||
});
|
||||
|
||||
auto confirm_dialog = ReviewMessageBox::create(this, tr("Confirm %1 to download").arg(resourcesString()));
|
||||
confirm_dialog->retranslateUi(resourcesString());
|
||||
|
||||
for (auto& task : selected) {
|
||||
confirm_dialog->appendResource({ task->getName(), task->getFilename(), task->getCustomPath() });
|
||||
confirm_dialog->appendResource({ task->getName(), task->getFilename(), task->getCustomPath(),
|
||||
ProviderCaps.name(task->getProvider()), getRequiredBy(selected, task) });
|
||||
}
|
||||
|
||||
if (confirm_dialog->exec()) {
|
||||
@ -231,6 +290,19 @@ QList<BasePage*> ModDownloadDialog::getPages()
|
||||
return pages;
|
||||
}
|
||||
|
||||
GetModDependenciesTask::Ptr ModDownloadDialog::getModDependenciesTask()
|
||||
{
|
||||
if (auto model = dynamic_cast<ModFolderModel*>(getBaseModel().get()); model) {
|
||||
QList<std::shared_ptr<GetModDependenciesTask::PackDependency>> selectedVers;
|
||||
for (auto& selected : getTasks()) {
|
||||
selectedVers.append(std::make_shared<GetModDependenciesTask::PackDependency>(selected->getPack(), selected->getVersion()));
|
||||
}
|
||||
|
||||
return makeShared<GetModDependenciesTask>(this, m_instance, model, selectedVers);
|
||||
}
|
||||
return nullptr;
|
||||
};
|
||||
|
||||
ResourcePackDownloadDialog::ResourcePackDownloadDialog(QWidget* parent,
|
||||
const std::shared_ptr<ResourcePackFolderModel>& resource_packs,
|
||||
BaseInstance* instance)
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include <QLayout>
|
||||
|
||||
#include "QObjectPtr.h"
|
||||
#include "minecraft/mod/tasks/GetModDependenciesTask.h"
|
||||
#include "modplatform/ModIndex.h"
|
||||
#include "ui/pages/BasePageProvider.h"
|
||||
|
||||
@ -81,6 +82,8 @@ class ResourceDownloadDialog : public QDialog, public BasePageProvider {
|
||||
[[nodiscard]] virtual QString geometrySaveKey() const { return ""; }
|
||||
void setButtonStatus();
|
||||
|
||||
[[nodiscard]] virtual GetModDependenciesTask::Ptr getModDependenciesTask() { return nullptr; }
|
||||
|
||||
protected:
|
||||
const std::shared_ptr<ResourceFolderModel> m_base_model;
|
||||
|
||||
@ -103,6 +106,7 @@ class ModDownloadDialog final : public ResourceDownloadDialog {
|
||||
[[nodiscard]] QString geometrySaveKey() const override { return "ModDownloadGeometry"; }
|
||||
|
||||
QList<BasePage*> getPages() override;
|
||||
GetModDependenciesTask::Ptr getModDependenciesTask() override;
|
||||
|
||||
private:
|
||||
BaseInstance* m_instance;
|
||||
|
@ -40,7 +40,8 @@ void ReviewMessageBox::appendResource(ResourceInformation&& info)
|
||||
auto filenameItem = new QTreeWidgetItem(itemTop);
|
||||
filenameItem->setText(0, tr("Filename: %1").arg(info.filename));
|
||||
|
||||
itemTop->insertChildren(0, { filenameItem });
|
||||
auto childIndx = 0;
|
||||
itemTop->insertChildren(childIndx++, { filenameItem });
|
||||
|
||||
if (!info.custom_file_path.isEmpty()) {
|
||||
auto customPathItem = new QTreeWidgetItem(itemTop);
|
||||
@ -49,7 +50,31 @@ void ReviewMessageBox::appendResource(ResourceInformation&& info)
|
||||
itemTop->insertChildren(1, { customPathItem });
|
||||
|
||||
itemTop->setIcon(1, QIcon(APPLICATION->getThemedIcon("status-yellow")));
|
||||
itemTop->setToolTip(1, tr("This file will be downloaded to a folder location different from the default, possibly due to its loader requiring it."));
|
||||
itemTop->setToolTip(
|
||||
childIndx++,
|
||||
tr("This file will be downloaded to a folder location different from the default, possibly due to its loader requiring it."));
|
||||
}
|
||||
|
||||
auto providerItem = new QTreeWidgetItem(itemTop);
|
||||
providerItem->setText(0, tr("Provider: %1").arg(info.provider));
|
||||
|
||||
itemTop->insertChildren(childIndx++, { providerItem });
|
||||
|
||||
if (!info.required_by.isEmpty()) {
|
||||
auto requiredByItem = new QTreeWidgetItem(itemTop);
|
||||
if (info.required_by.length() == 1) {
|
||||
requiredByItem->setText(0, tr("Required by: %1").arg(info.required_by.back()));
|
||||
} else {
|
||||
requiredByItem->setText(0, tr("Required by:"));
|
||||
auto i = 0;
|
||||
for (auto req : info.required_by) {
|
||||
auto reqItem = new QTreeWidgetItem(requiredByItem);
|
||||
reqItem->setText(0, req);
|
||||
reqItem->insertChildren(i++, { reqItem });
|
||||
}
|
||||
}
|
||||
|
||||
itemTop->insertChildren(childIndx++, { requiredByItem });
|
||||
}
|
||||
|
||||
ui->modTreeWidget->addTopLevelItem(itemTop);
|
||||
|
@ -13,9 +13,11 @@ class ReviewMessageBox : public QDialog {
|
||||
static auto create(QWidget* parent, QString&& title, QString&& icon = "") -> ReviewMessageBox*;
|
||||
|
||||
using ResourceInformation = struct res_info {
|
||||
QString name;
|
||||
QString filename;
|
||||
QString custom_file_path {};
|
||||
QString name;
|
||||
QString filename;
|
||||
QString custom_file_path{};
|
||||
QString provider;
|
||||
QStringList required_by;
|
||||
};
|
||||
|
||||
void appendResource(ResourceInformation&& info);
|
||||
|
@ -1,16 +1,36 @@
|
||||
/* Copyright 2013-2021 MultiMC Contributors
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* This file incorporates work covered by the following copyright and
|
||||
* permission notice:
|
||||
*
|
||||
* Copyright 2013-2021 MultiMC Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "VersionSelectDialog.h"
|
||||
@ -22,15 +42,10 @@
|
||||
#include <QtWidgets/QVBoxLayout>
|
||||
#include <QDebug>
|
||||
|
||||
#include "ui/dialogs/ProgressDialog.h"
|
||||
#include "ui/widgets/VersionSelectWidget.h"
|
||||
#include "ui/dialogs/CustomMessageBox.h"
|
||||
|
||||
#include "BaseVersion.h"
|
||||
#include "BaseVersionList.h"
|
||||
#include "tasks/Task.h"
|
||||
#include "Application.h"
|
||||
#include "VersionProxyModel.h"
|
||||
|
||||
VersionSelectDialog::VersionSelectDialog(BaseVersionList *vlist, QString title, QWidget *parent, bool cancelable)
|
||||
: QDialog(parent)
|
||||
@ -40,7 +55,7 @@ VersionSelectDialog::VersionSelectDialog(BaseVersionList *vlist, QString title,
|
||||
m_verticalLayout = new QVBoxLayout(this);
|
||||
m_verticalLayout->setObjectName(QStringLiteral("verticalLayout"));
|
||||
|
||||
m_versionWidget = new VersionSelectWidget(parent);
|
||||
m_versionWidget = new VersionSelectWidget(true, parent);
|
||||
m_verticalLayout->addWidget(m_versionWidget);
|
||||
|
||||
m_horizontalLayout = new QHBoxLayout();
|
||||
|
@ -48,6 +48,7 @@
|
||||
#include <QAccessible>
|
||||
|
||||
#include "VisualGroup.h"
|
||||
#include "ui/themes/ThemeManager.h"
|
||||
#include <QDebug>
|
||||
|
||||
#include <Application.h>
|
||||
@ -73,6 +74,7 @@ InstanceView::InstanceView(QWidget *parent)
|
||||
setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
|
||||
setAcceptDrops(true);
|
||||
setAutoScroll(true);
|
||||
setPaintCat(APPLICATION->settings()->get("TheCat").toBool());
|
||||
}
|
||||
|
||||
InstanceView::~InstanceView()
|
||||
@ -498,12 +500,34 @@ void InstanceView::mouseDoubleClickEvent(QMouseEvent *event)
|
||||
}
|
||||
}
|
||||
|
||||
void InstanceView::paintEvent(QPaintEvent *event)
|
||||
void InstanceView::setPaintCat(bool visible)
|
||||
{
|
||||
m_catVisible = visible;
|
||||
if (visible)
|
||||
m_catPixmap.load(QString(":/backgrounds/%1").arg(ThemeManager::getCatImage()));
|
||||
else
|
||||
m_catPixmap = QPixmap();
|
||||
}
|
||||
|
||||
void InstanceView::paintEvent(QPaintEvent* event)
|
||||
{
|
||||
executeDelayedItemsLayout();
|
||||
|
||||
QPainter painter(this->viewport());
|
||||
|
||||
if (m_catVisible) {
|
||||
int widWidth = this->viewport()->width();
|
||||
int widHeight = this->viewport()->height();
|
||||
if (m_catPixmap.width() < widWidth)
|
||||
widWidth = m_catPixmap.width();
|
||||
if (m_catPixmap.height() < widHeight)
|
||||
widHeight = m_catPixmap.height();
|
||||
auto pixmap = m_catPixmap.scaled(widWidth, widHeight, Qt::KeepAspectRatio);
|
||||
QRect rectOfPixmap = pixmap.rect();
|
||||
rectOfPixmap.moveBottomRight(this->viewport()->rect().bottomRight());
|
||||
painter.drawPixmap(rectOfPixmap.topLeft(), pixmap);
|
||||
}
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||
QStyleOptionViewItem option;
|
||||
initViewItemOption(&option);
|
||||
|
@ -85,10 +85,8 @@ public:
|
||||
|
||||
virtual QRegion visualRegionForSelection(const QItemSelection &selection) const override;
|
||||
|
||||
int spacing() const
|
||||
{
|
||||
return m_spacing;
|
||||
};
|
||||
int spacing() const { return m_spacing; };
|
||||
void setPaintCat(bool visible);
|
||||
|
||||
public slots:
|
||||
virtual void updateGeometries() override;
|
||||
@ -139,6 +137,8 @@ private:
|
||||
int m_currentItemsPerRow = -1;
|
||||
int m_currentCursorColumn= -1;
|
||||
mutable QCache<int, QRect> geometryCache;
|
||||
bool m_catVisible = false;
|
||||
QPixmap m_catPixmap;
|
||||
|
||||
// point where the currently active mouse action started in geometry coordinates
|
||||
QPoint m_pressedPosition;
|
||||
|
@ -35,15 +35,16 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QString>
|
||||
#include <QIcon>
|
||||
#include <QString>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
#include "BasePageContainer.h"
|
||||
|
||||
class BasePage
|
||||
{
|
||||
public:
|
||||
class BasePage {
|
||||
public:
|
||||
using updateExtraInfoFunc = std::function<void(QString, QString)>;
|
||||
virtual ~BasePage() {}
|
||||
virtual QString id() const = 0;
|
||||
virtual QString displayName() const = 0;
|
||||
@ -63,17 +64,16 @@ public:
|
||||
}
|
||||
virtual void openedImpl() {}
|
||||
virtual void closedImpl() {}
|
||||
virtual void setParentContainer(BasePageContainer * container)
|
||||
{
|
||||
m_container = container;
|
||||
};
|
||||
virtual void retranslate() { }
|
||||
virtual void setParentContainer(BasePageContainer* container) { m_container = container; };
|
||||
virtual void retranslate() {}
|
||||
|
||||
public:
|
||||
public:
|
||||
int stackIndex = -1;
|
||||
int listIndex = -1;
|
||||
protected:
|
||||
BasePageContainer * m_container = nullptr;
|
||||
updateExtraInfoFunc updateExtraInfo;
|
||||
|
||||
protected:
|
||||
BasePageContainer* m_container = nullptr;
|
||||
bool isOpened = false;
|
||||
};
|
||||
|
||||
|
@ -172,7 +172,7 @@
|
||||
<string>Disable using metadata provided by mod providers (like Modrinth or Curseforge) for mods.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Disable using metadata for mods?</string>
|
||||
<string>Disable using metadata for mods</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@ -307,21 +307,21 @@
|
||||
<item>
|
||||
<widget class="QCheckBox" name="showConsoleCheck">
|
||||
<property name="text">
|
||||
<string>Show console while the game is &running?</string>
|
||||
<string>Show console while the game is &running</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="autoCloseConsoleCheck">
|
||||
<property name="text">
|
||||
<string>&Automatically close console when the game quits?</string>
|
||||
<string>&Automatically close console when the game quits</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="showConsoleErrorCheck">
|
||||
<property name="text">
|
||||
<string>Show console when the game &crashes?</string>
|
||||
<string>Show console when the game &crashes</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
@ -51,7 +51,7 @@
|
||||
<item>
|
||||
<widget class="QCheckBox" name="maximizedCheckBox">
|
||||
<property name="text">
|
||||
<string>Start Minecraft &maximized?</string>
|
||||
<string>Start Minecraft &maximized</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
@ -1,3 +1,38 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* This file incorporates work covered by the following copyright and
|
||||
* permission notice:
|
||||
*
|
||||
* Copyright 2013-2021 MultiMC Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "ExternalResourcesPage.h"
|
||||
#include "ui/dialogs/CustomMessageBox.h"
|
||||
#include "ui_ExternalResourcesPage.h"
|
||||
@ -9,6 +44,7 @@
|
||||
|
||||
#include <QKeyEvent>
|
||||
#include <QMenu>
|
||||
#include <algorithm>
|
||||
|
||||
ExternalResourcesPage::ExternalResourcesPage(BaseInstance* instance, std::shared_ptr<ResourceFolderModel> model, QWidget* parent)
|
||||
: QMainWindow(parent), m_instance(instance), ui(new Ui::ExternalResourcesPage), m_model(model)
|
||||
@ -24,6 +60,8 @@ ExternalResourcesPage::ExternalResourcesPage(BaseInstance* instance, std::shared
|
||||
m_filterModel->setSourceModel(m_model.get());
|
||||
m_filterModel->setFilterKeyColumn(-1);
|
||||
ui->treeView->setModel(m_filterModel);
|
||||
// must come after setModel
|
||||
ui->treeView->setResizeModes(m_model->columnResizeModes());
|
||||
|
||||
ui->treeView->installEventFilter(this);
|
||||
ui->treeView->sortByColumn(1, Qt::AscendingOrder);
|
||||
@ -43,7 +81,21 @@ ExternalResourcesPage::ExternalResourcesPage(BaseInstance* instance, std::shared
|
||||
|
||||
auto selection_model = ui->treeView->selectionModel();
|
||||
connect(selection_model, &QItemSelectionModel::currentChanged, this, &ExternalResourcesPage::current);
|
||||
auto updateExtra = [this]() {
|
||||
if (updateExtraInfo)
|
||||
updateExtraInfo(id(), extraHeaderInfoString());
|
||||
};
|
||||
connect(selection_model, &QItemSelectionModel::selectionChanged, this, updateExtra);
|
||||
connect(model.get(), &ResourceFolderModel::updateFinished, this, updateExtra);
|
||||
|
||||
connect(ui->filterEdit, &QLineEdit::textChanged, this, &ExternalResourcesPage::filterTextChanged);
|
||||
|
||||
auto viewHeader = ui->treeView->header();
|
||||
viewHeader->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
|
||||
connect(viewHeader, &QHeaderView::customContextMenuRequested, this, &ExternalResourcesPage::ShowHeaderContextMenu);
|
||||
|
||||
m_model->loadHiddenColumns(ui->treeView);
|
||||
}
|
||||
|
||||
ExternalResourcesPage::~ExternalResourcesPage()
|
||||
@ -65,6 +117,13 @@ void ExternalResourcesPage::ShowContextMenu(const QPoint& pos)
|
||||
delete menu;
|
||||
}
|
||||
|
||||
void ExternalResourcesPage::ShowHeaderContextMenu(const QPoint& pos)
|
||||
{
|
||||
auto menu = m_model->createHeaderContextMenu(ui->treeView);
|
||||
menu->exec(ui->treeView->mapToGlobal(pos));
|
||||
menu->deleteLater();
|
||||
}
|
||||
|
||||
void ExternalResourcesPage::openedImpl()
|
||||
{
|
||||
m_model->startWatching();
|
||||
@ -96,7 +155,6 @@ void ExternalResourcesPage::itemActivated(const QModelIndex&)
|
||||
return;
|
||||
|
||||
auto selection = m_filterModel->mapSelectionToSource(ui->treeView->selectionModel()->selection());
|
||||
m_model->setResourceEnabled(selection.indexes(), EnableAction::TOGGLE);
|
||||
}
|
||||
|
||||
void ExternalResourcesPage::filterTextChanged(const QString& newContents)
|
||||
@ -248,6 +306,15 @@ bool ExternalResourcesPage::onSelectionChanged(const QModelIndex& current, const
|
||||
int row = sourceCurrent.row();
|
||||
Resource const& resource = m_model->at(row);
|
||||
ui->frame->updateWithResource(resource);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
QString ExternalResourcesPage::extraHeaderInfoString()
|
||||
{
|
||||
if (ui && ui->treeView && ui->treeView->selectionModel()) {
|
||||
auto selection = m_filterModel->mapSelectionToSource(ui->treeView->selectionModel()->selection()).indexes();
|
||||
if (auto count = std::count_if(selection.cbegin(), selection.cend(), [](auto v) { return v.column() == 0; }); count != 0)
|
||||
return tr(" (%1 installed, %2 selected)").arg(m_model->size()).arg(count);
|
||||
}
|
||||
return tr(" (%1 installed)").arg(m_model->size());
|
||||
}
|
||||
|
@ -29,6 +29,7 @@ class ExternalResourcesPage : public QMainWindow, public BasePage {
|
||||
virtual QString helpPage() const override = 0;
|
||||
|
||||
virtual bool shouldDisplay() const override = 0;
|
||||
QString extraHeaderInfoString();
|
||||
|
||||
void openedImpl() override;
|
||||
void closedImpl() override;
|
||||
@ -60,6 +61,7 @@ class ExternalResourcesPage : public QMainWindow, public BasePage {
|
||||
virtual void viewConfigs();
|
||||
|
||||
void ShowContextMenu(const QPoint& pos);
|
||||
void ShowHeaderContextMenu(const QPoint& pos);
|
||||
|
||||
protected:
|
||||
BaseInstance* m_instance = nullptr;
|
||||
|
@ -62,6 +62,9 @@
|
||||
<property name="dragDropMode">
|
||||
<enum>QAbstractItemView::DropOnly</enum>
|
||||
</property>
|
||||
<property name="uniformRowHeights">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
|
@ -60,6 +60,10 @@ InstanceSettingsPage::InstanceSettingsPage(BaseInstance *inst, QWidget *parent)
|
||||
m_settings = inst->settings();
|
||||
ui->setupUi(this);
|
||||
|
||||
// As the signal will (probably) not be triggered once we click edit, let's update it manually instead.
|
||||
updateRunningStatus(m_instance->isRunning());
|
||||
|
||||
connect(m_instance, &BaseInstance::runningStatusChanged, this, &InstanceSettingsPage::updateRunningStatus);
|
||||
connect(ui->openGlobalJavaSettingsButton, &QCommandLinkButton::clicked, this, &InstanceSettingsPage::globalSettingsButtonClicked);
|
||||
connect(APPLICATION, &Application::globalSettingsAboutToOpen, this, &InstanceSettingsPage::applySettings);
|
||||
connect(APPLICATION, &Application::globalSettingsClosed, this, &InstanceSettingsPage::loadSettings);
|
||||
@ -70,11 +74,6 @@ InstanceSettingsPage::InstanceSettingsPage(BaseInstance *inst, QWidget *parent)
|
||||
updateThresholds();
|
||||
}
|
||||
|
||||
bool InstanceSettingsPage::shouldDisplay() const
|
||||
{
|
||||
return !m_instance->isRunning();
|
||||
}
|
||||
|
||||
InstanceSettingsPage::~InstanceSettingsPage()
|
||||
{
|
||||
delete ui;
|
||||
@ -86,12 +85,12 @@ void InstanceSettingsPage::globalSettingsButtonClicked(bool)
|
||||
case 0:
|
||||
APPLICATION->ShowGlobalSettings(this, "java-settings");
|
||||
return;
|
||||
case 1:
|
||||
APPLICATION->ShowGlobalSettings(this, "minecraft-settings");
|
||||
return;
|
||||
case 2:
|
||||
APPLICATION->ShowGlobalSettings(this, "custom-commands");
|
||||
return;
|
||||
default:
|
||||
APPLICATION->ShowGlobalSettings(this, "minecraft-settings");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@ -524,3 +523,8 @@ void InstanceSettingsPage::updateThresholds()
|
||||
ui->labelMaxMemIcon->setPixmap(pix);
|
||||
}
|
||||
}
|
||||
|
||||
void InstanceSettingsPage::updateRunningStatus(bool running)
|
||||
{
|
||||
setEnabled(!running);
|
||||
}
|
||||
|
@ -75,12 +75,12 @@ public:
|
||||
{
|
||||
return "Instance-settings";
|
||||
}
|
||||
virtual bool shouldDisplay() const override;
|
||||
void retranslate() override;
|
||||
|
||||
void updateThresholds();
|
||||
|
||||
private slots:
|
||||
void updateRunningStatus(bool running);
|
||||
void on_javaDetectBtn_clicked();
|
||||
void on_javaTestBtn_clicked();
|
||||
void on_javaBrowseBtn_clicked();
|
||||
|
@ -269,7 +269,7 @@
|
||||
<item>
|
||||
<widget class="QCheckBox" name="maximizedCheckBox">
|
||||
<property name="text">
|
||||
<string>Start Minecraft maximized?</string>
|
||||
<string>Start Minecraft maximized</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@ -341,21 +341,21 @@
|
||||
<item>
|
||||
<widget class="QCheckBox" name="showConsoleCheck">
|
||||
<property name="text">
|
||||
<string>Show console while the game is running?</string>
|
||||
<string>Show console while the game is running</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="autoCloseConsoleCheck">
|
||||
<property name="text">
|
||||
<string>Automatically close console when the game quits?</string>
|
||||
<string>Automatically close console when the game quits</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="showConsoleErrorCheck">
|
||||
<property name="text">
|
||||
<string>Show console when the game crashes?</string>
|
||||
<string>Show console when the game crashes</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
@ -205,7 +205,7 @@ ModrinthManagedPackPage::ModrinthManagedPackPage(BaseInstance* inst, InstanceWin
|
||||
{
|
||||
Q_ASSERT(inst->isManagedPack());
|
||||
connect(ui->versionsComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(suggestVersion()));
|
||||
connect(ui->updateButton, &QPushButton::pressed, this, &ModrinthManagedPackPage::update);
|
||||
connect(ui->updateButton, &QPushButton::clicked, this, &ModrinthManagedPackPage::update);
|
||||
}
|
||||
|
||||
// MODRINTH
|
||||
@ -226,7 +226,7 @@ void ModrinthManagedPackPage::parseManagedPack()
|
||||
|
||||
QString id = m_inst->getManagedPackID();
|
||||
|
||||
m_fetch_job->addNetAction(Net::Download::makeByteArray(QString("%1/project/%2/version").arg(BuildConfig.MODRINTH_PROD_URL, id), response.get()));
|
||||
m_fetch_job->addNetAction(Net::Download::makeByteArray(QString("%1/project/%2/version").arg(BuildConfig.MODRINTH_PROD_URL, id), response));
|
||||
|
||||
QObject::connect(m_fetch_job.get(), &NetJob::succeeded, this, [this, response, id] {
|
||||
QJsonParseError parse_error{};
|
||||
@ -332,7 +332,7 @@ FlameManagedPackPage::FlameManagedPackPage(BaseInstance* inst, InstanceWindow* i
|
||||
{
|
||||
Q_ASSERT(inst->isManagedPack());
|
||||
connect(ui->versionsComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(suggestVersion()));
|
||||
connect(ui->updateButton, &QPushButton::pressed, this, &FlameManagedPackPage::update);
|
||||
connect(ui->updateButton, &QPushButton::clicked, this, &FlameManagedPackPage::update);
|
||||
}
|
||||
|
||||
void FlameManagedPackPage::parseManagedPack()
|
||||
@ -369,7 +369,7 @@ void FlameManagedPackPage::parseManagedPack()
|
||||
|
||||
QString id = m_inst->getManagedPackID();
|
||||
|
||||
m_fetch_job->addNetAction(Net::Download::makeByteArray(QString("%1/mods/%2/files").arg(BuildConfig.FLAME_BASE_URL, id), response.get()));
|
||||
m_fetch_job->addNetAction(Net::Download::makeByteArray(QString("%1/mods/%2/files").arg(BuildConfig.FLAME_BASE_URL, id), response));
|
||||
|
||||
QObject::connect(m_fetch_job.get(), &NetJob::succeeded, this, [this, response, id] {
|
||||
QJsonParseError parse_error{};
|
||||
|
@ -4,6 +4,7 @@
|
||||
* Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org>
|
||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||
* Copyright (C) 2022 TheKodeToad <TheKodeToad@proton.me>
|
||||
* Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -86,28 +87,20 @@ ModFolderPage::ModFolderPage(BaseInstance* inst, std::shared_ptr<ModFolderModel>
|
||||
connect(ui->actionUpdateItem, &QAction::triggered, this, &ModFolderPage::updateMods);
|
||||
|
||||
auto check_allow_update = [this] {
|
||||
return (!m_instance || !m_instance->isRunning()) &&
|
||||
(ui->treeView->selectionModel()->hasSelection() || !m_model->empty());
|
||||
return (!m_instance || !m_instance->isRunning()) && (ui->treeView->selectionModel()->hasSelection() || !m_model->empty());
|
||||
};
|
||||
|
||||
connect(ui->treeView->selectionModel(), &QItemSelectionModel::selectionChanged, this, [this, check_allow_update] {
|
||||
ui->actionUpdateItem->setEnabled(check_allow_update());
|
||||
});
|
||||
connect(ui->treeView->selectionModel(), &QItemSelectionModel::selectionChanged, this,
|
||||
[this, check_allow_update] { ui->actionUpdateItem->setEnabled(check_allow_update()); });
|
||||
|
||||
connect(mods.get(), &ModFolderModel::rowsInserted, this, [this, check_allow_update] {
|
||||
ui->actionUpdateItem->setEnabled(check_allow_update());
|
||||
});
|
||||
connect(mods.get(), &ModFolderModel::rowsInserted, this,
|
||||
[this, check_allow_update] { ui->actionUpdateItem->setEnabled(check_allow_update()); });
|
||||
|
||||
connect(mods.get(), &ModFolderModel::rowsRemoved, this, [this, check_allow_update] {
|
||||
ui->actionUpdateItem->setEnabled(check_allow_update());
|
||||
});
|
||||
connect(mods.get(), &ModFolderModel::rowsRemoved, this,
|
||||
[this, check_allow_update] { ui->actionUpdateItem->setEnabled(check_allow_update()); });
|
||||
|
||||
connect(mods.get(), &ModFolderModel::updateFinished, this, [this, check_allow_update, mods] {
|
||||
ui->actionUpdateItem->setEnabled(check_allow_update());
|
||||
|
||||
// Prevent a weird crash when trying to open the mods page twice in a session o.O
|
||||
disconnect(mods.get(), &ModFolderModel::updateFinished, this, 0);
|
||||
});
|
||||
connect(mods.get(), &ModFolderModel::updateFinished, this,
|
||||
[this, check_allow_update] { ui->actionUpdateItem->setEnabled(check_allow_update()); });
|
||||
|
||||
connect(m_instance, &BaseInstance::runningStatusChanged, this, &ModFolderPage::runningStateChanged);
|
||||
ModFolderPage::runningStateChanged(m_instance && m_instance->isRunning());
|
||||
|
@ -36,6 +36,7 @@
|
||||
*/
|
||||
|
||||
#include "ScreenshotsPage.h"
|
||||
#include "BuildConfig.h"
|
||||
#include "ui_ScreenshotsPage.h"
|
||||
|
||||
#include <QModelIndex>
|
||||
@ -96,37 +97,30 @@ public:
|
||||
return;
|
||||
if ((info.suffix().compare("png", Qt::CaseInsensitive) != 0))
|
||||
return;
|
||||
int tries = 5;
|
||||
while (tries)
|
||||
{
|
||||
if (!m_cache->stale(m_path))
|
||||
return;
|
||||
QImage image(m_path);
|
||||
if (image.isNull())
|
||||
{
|
||||
QThread::msleep(500);
|
||||
tries--;
|
||||
continue;
|
||||
}
|
||||
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();
|
||||
|
||||
QIcon icon(QPixmap::fromImage(square));
|
||||
m_cache->add(m_path, icon);
|
||||
m_resultEmitter.emitResultsReady(m_path);
|
||||
if (!m_cache->stale(m_path))
|
||||
return;
|
||||
QImage image(m_path);
|
||||
if (image.isNull()) {
|
||||
m_resultEmitter.emitResultsFailed(m_path);
|
||||
qDebug() << "Error loading screenshot: " + m_path + ". Perhaps too large?";
|
||||
return;
|
||||
}
|
||||
m_resultEmitter.emitResultsFailed(m_path);
|
||||
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();
|
||||
|
||||
QIcon icon(QPixmap::fromImage(square));
|
||||
m_cache->add(m_path, icon);
|
||||
m_resultEmitter.emitResultsReady(m_path);
|
||||
}
|
||||
QString m_path;
|
||||
SharedIconCachePtr m_cache;
|
||||
@ -145,9 +139,12 @@ public:
|
||||
m_thumbnailCache = std::make_shared<SharedIconCache>();
|
||||
m_thumbnailCache->add("placeholder", APPLICATION->getThemedIcon("screenshot-placeholder"));
|
||||
connect(&watcher, SIGNAL(fileChanged(QString)), SLOT(fileChanged(QString)));
|
||||
// FIXME: the watched file set is not updated when files are removed
|
||||
}
|
||||
virtual ~FilterModel() { m_thumbnailingPool.waitForDone(500); }
|
||||
virtual ~FilterModel() {
|
||||
m_thumbnailingPool.clear();
|
||||
if (!m_thumbnailingPool.waitForDone(500))
|
||||
qDebug() << "Thumbnail pool took longer than 500ms to finish";
|
||||
}
|
||||
virtual QVariant data(const QModelIndex &proxyIndex, int role = Qt::DisplayRole) const
|
||||
{
|
||||
auto model = sourceModel();
|
||||
@ -214,10 +211,12 @@ private slots:
|
||||
void fileChanged(QString filepath)
|
||||
{
|
||||
m_thumbnailCache->setStale(filepath);
|
||||
thumbnailImage(filepath);
|
||||
// reinsert the path...
|
||||
watcher.removePath(filepath);
|
||||
watcher.addPath(filepath);
|
||||
if (QFile::exists(filepath)) {
|
||||
watcher.addPath(filepath);
|
||||
thumbnailImage(filepath);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
@ -380,16 +379,18 @@ void ScreenshotsPage::on_actionUpload_triggered()
|
||||
if (selection.isEmpty())
|
||||
return;
|
||||
|
||||
|
||||
QString text;
|
||||
QUrl baseUrl(BuildConfig.IMGUR_BASE_URL);
|
||||
if (selection.size() > 1)
|
||||
text = tr("You are about to upload %1 screenshots.\n\n"
|
||||
text = tr("You are about to upload %1 screenshots to %2.\n"
|
||||
"You should double-check for personal information.\n\n"
|
||||
"Are you sure?")
|
||||
.arg(selection.size());
|
||||
.arg(QString::number(selection.size()), baseUrl.host());
|
||||
else
|
||||
text =
|
||||
tr("You are about to upload the selected screenshot.\n\n"
|
||||
"Are you sure?");
|
||||
text = tr("You are about to upload the selected screenshot to %1.\n"
|
||||
"You should double-check for personal information.\n\n"
|
||||
"Are you sure?")
|
||||
.arg(baseUrl.host());
|
||||
|
||||
auto response = CustomMessageBox::selectable(this, "Confirm Upload", text, QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No,
|
||||
QMessageBox::No)
|
||||
|
@ -287,7 +287,6 @@ void VersionPage::updateButtons(int row)
|
||||
ui->actionAdd_Empty->setEnabled(controlsEnabled);
|
||||
ui->actionImport_Components->setEnabled(controlsEnabled);
|
||||
ui->actionReload->setEnabled(controlsEnabled);
|
||||
ui->actionInstall_mods->setEnabled(controlsEnabled);
|
||||
ui->actionReplace_Minecraft_jar->setEnabled(controlsEnabled);
|
||||
ui->actionAdd_to_Minecraft_jar->setEnabled(controlsEnabled);
|
||||
ui->actionAdd_Agents->setEnabled(controlsEnabled);
|
||||
|
@ -102,7 +102,6 @@
|
||||
<addaction name="actionInstall_Fabric"/>
|
||||
<addaction name="actionInstall_Quilt"/>
|
||||
<addaction name="actionInstall_LiteLoader"/>
|
||||
<addaction name="actionInstall_mods"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionAdd_to_Minecraft_jar"/>
|
||||
<addaction name="actionReplace_Minecraft_jar"/>
|
||||
@ -112,7 +111,6 @@
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionMinecraftFolder"/>
|
||||
<addaction name="actionLibrariesFolder"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionReload"/>
|
||||
<addaction name="actionDownload_All"/>
|
||||
</widget>
|
||||
@ -204,14 +202,6 @@
|
||||
<string>Install the LiteLoader package.</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionInstall_mods">
|
||||
<property name="text">
|
||||
<string>Install mods</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Install normal mods.</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionAdd_to_Minecraft_jar">
|
||||
<property name="text">
|
||||
<string>Add to Minecraft.jar</string>
|
||||
|
@ -33,8 +33,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "VanillaPage.h"
|
||||
#include "ui_VanillaPage.h"
|
||||
#include "CustomPage.h"
|
||||
#include "ui_CustomPage.h"
|
||||
|
||||
#include <QTabBar>
|
||||
|
||||
@ -46,32 +46,32 @@
|
||||
#include "minecraft/VanillaInstanceCreationTask.h"
|
||||
#include "ui/dialogs/NewInstanceDialog.h"
|
||||
|
||||
VanillaPage::VanillaPage(NewInstanceDialog *dialog, QWidget *parent)
|
||||
: QWidget(parent), dialog(dialog), ui(new Ui::VanillaPage)
|
||||
CustomPage::CustomPage(NewInstanceDialog *dialog, QWidget *parent)
|
||||
: QWidget(parent), dialog(dialog), ui(new Ui::CustomPage)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
ui->tabWidget->tabBar()->hide();
|
||||
connect(ui->versionList, &VersionSelectWidget::selectedVersionChanged, this, &VanillaPage::setSelectedVersion);
|
||||
connect(ui->versionList, &VersionSelectWidget::selectedVersionChanged, this, &CustomPage::setSelectedVersion);
|
||||
filterChanged();
|
||||
connect(ui->alphaFilter, &QCheckBox::stateChanged, this, &VanillaPage::filterChanged);
|
||||
connect(ui->betaFilter, &QCheckBox::stateChanged, this, &VanillaPage::filterChanged);
|
||||
connect(ui->snapshotFilter, &QCheckBox::stateChanged, this, &VanillaPage::filterChanged);
|
||||
connect(ui->oldSnapshotFilter, &QCheckBox::stateChanged, this, &VanillaPage::filterChanged);
|
||||
connect(ui->releaseFilter, &QCheckBox::stateChanged, this, &VanillaPage::filterChanged);
|
||||
connect(ui->experimentsFilter, &QCheckBox::stateChanged, this, &VanillaPage::filterChanged);
|
||||
connect(ui->refreshBtn, &QPushButton::clicked, this, &VanillaPage::refresh);
|
||||
connect(ui->alphaFilter, &QCheckBox::stateChanged, this, &CustomPage::filterChanged);
|
||||
connect(ui->betaFilter, &QCheckBox::stateChanged, this, &CustomPage::filterChanged);
|
||||
connect(ui->snapshotFilter, &QCheckBox::stateChanged, this, &CustomPage::filterChanged);
|
||||
connect(ui->oldSnapshotFilter, &QCheckBox::stateChanged, this, &CustomPage::filterChanged);
|
||||
connect(ui->releaseFilter, &QCheckBox::stateChanged, this, &CustomPage::filterChanged);
|
||||
connect(ui->experimentsFilter, &QCheckBox::stateChanged, this, &CustomPage::filterChanged);
|
||||
connect(ui->refreshBtn, &QPushButton::clicked, this, &CustomPage::refresh);
|
||||
|
||||
connect(ui->loaderVersionList, &VersionSelectWidget::selectedVersionChanged, this, &VanillaPage::setSelectedLoaderVersion);
|
||||
connect(ui->noneFilter, &QRadioButton::toggled, this, &VanillaPage::loaderFilterChanged);
|
||||
connect(ui->forgeFilter, &QRadioButton::toggled, this, &VanillaPage::loaderFilterChanged);
|
||||
connect(ui->fabricFilter, &QRadioButton::toggled, this, &VanillaPage::loaderFilterChanged);
|
||||
connect(ui->quiltFilter, &QRadioButton::toggled, this, &VanillaPage::loaderFilterChanged);
|
||||
connect(ui->liteLoaderFilter, &QRadioButton::toggled, this, &VanillaPage::loaderFilterChanged);
|
||||
connect(ui->loaderRefreshBtn, &QPushButton::clicked, this, &VanillaPage::loaderRefresh);
|
||||
connect(ui->loaderVersionList, &VersionSelectWidget::selectedVersionChanged, this, &CustomPage::setSelectedLoaderVersion);
|
||||
connect(ui->noneFilter, &QRadioButton::toggled, this, &CustomPage::loaderFilterChanged);
|
||||
connect(ui->forgeFilter, &QRadioButton::toggled, this, &CustomPage::loaderFilterChanged);
|
||||
connect(ui->fabricFilter, &QRadioButton::toggled, this, &CustomPage::loaderFilterChanged);
|
||||
connect(ui->quiltFilter, &QRadioButton::toggled, this, &CustomPage::loaderFilterChanged);
|
||||
connect(ui->liteLoaderFilter, &QRadioButton::toggled, this, &CustomPage::loaderFilterChanged);
|
||||
connect(ui->loaderRefreshBtn, &QPushButton::clicked, this, &CustomPage::loaderRefresh);
|
||||
|
||||
}
|
||||
|
||||
void VanillaPage::openedImpl()
|
||||
void CustomPage::openedImpl()
|
||||
{
|
||||
if(!initialized)
|
||||
{
|
||||
@ -85,19 +85,19 @@ void VanillaPage::openedImpl()
|
||||
}
|
||||
}
|
||||
|
||||
void VanillaPage::refresh()
|
||||
void CustomPage::refresh()
|
||||
{
|
||||
ui->versionList->loadList();
|
||||
}
|
||||
|
||||
void VanillaPage::loaderRefresh()
|
||||
void CustomPage::loaderRefresh()
|
||||
{
|
||||
if(ui->noneFilter->isChecked())
|
||||
return;
|
||||
ui->loaderVersionList->loadList();
|
||||
}
|
||||
|
||||
void VanillaPage::filterChanged()
|
||||
void CustomPage::filterChanged()
|
||||
{
|
||||
QStringList out;
|
||||
if(ui->alphaFilter->isChecked())
|
||||
@ -116,7 +116,7 @@ void VanillaPage::filterChanged()
|
||||
ui->versionList->setFilter(BaseVersionList::TypeRole, new RegexpFilter(regexp, false));
|
||||
}
|
||||
|
||||
void VanillaPage::loaderFilterChanged()
|
||||
void CustomPage::loaderFilterChanged()
|
||||
{
|
||||
QString minecraftVersion;
|
||||
if (m_selectedVersion)
|
||||
@ -172,37 +172,37 @@ void VanillaPage::loaderFilterChanged()
|
||||
ui->loaderVersionList->setEmptyString(tr("No versions are currently available for Minecraft %1").arg(minecraftVersion));
|
||||
}
|
||||
|
||||
VanillaPage::~VanillaPage()
|
||||
CustomPage::~CustomPage()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
bool VanillaPage::shouldDisplay() const
|
||||
bool CustomPage::shouldDisplay() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void VanillaPage::retranslate()
|
||||
void CustomPage::retranslate()
|
||||
{
|
||||
ui->retranslateUi(this);
|
||||
}
|
||||
|
||||
BaseVersion::Ptr VanillaPage::selectedVersion() const
|
||||
BaseVersion::Ptr CustomPage::selectedVersion() const
|
||||
{
|
||||
return m_selectedVersion;
|
||||
}
|
||||
|
||||
BaseVersion::Ptr VanillaPage::selectedLoaderVersion() const
|
||||
BaseVersion::Ptr CustomPage::selectedLoaderVersion() const
|
||||
{
|
||||
return m_selectedLoaderVersion;
|
||||
}
|
||||
|
||||
QString VanillaPage::selectedLoader() const
|
||||
QString CustomPage::selectedLoader() const
|
||||
{
|
||||
return m_selectedLoader;
|
||||
}
|
||||
|
||||
void VanillaPage::suggestCurrent()
|
||||
void CustomPage::suggestCurrent()
|
||||
{
|
||||
if (!isOpened)
|
||||
{
|
||||
@ -227,14 +227,14 @@ void VanillaPage::suggestCurrent()
|
||||
dialog->setSuggestedIcon("default");
|
||||
}
|
||||
|
||||
void VanillaPage::setSelectedVersion(BaseVersion::Ptr version)
|
||||
void CustomPage::setSelectedVersion(BaseVersion::Ptr version)
|
||||
{
|
||||
m_selectedVersion = version;
|
||||
suggestCurrent();
|
||||
loaderFilterChanged();
|
||||
}
|
||||
|
||||
void VanillaPage::setSelectedLoaderVersion(BaseVersion::Ptr version)
|
||||
void CustomPage::setSelectedLoaderVersion(BaseVersion::Ptr version)
|
||||
{
|
||||
m_selectedLoaderVersion = version;
|
||||
suggestCurrent();
|
@ -43,21 +43,21 @@
|
||||
|
||||
namespace Ui
|
||||
{
|
||||
class VanillaPage;
|
||||
class CustomPage;
|
||||
}
|
||||
|
||||
class NewInstanceDialog;
|
||||
|
||||
class VanillaPage : public QWidget, public BasePage
|
||||
class CustomPage : public QWidget, public BasePage
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit VanillaPage(NewInstanceDialog *dialog, QWidget *parent = 0);
|
||||
virtual ~VanillaPage();
|
||||
explicit CustomPage(NewInstanceDialog *dialog, QWidget *parent = 0);
|
||||
virtual ~CustomPage();
|
||||
virtual QString displayName() const override
|
||||
{
|
||||
return tr("Vanilla");
|
||||
return tr("Custom");
|
||||
}
|
||||
virtual QIcon icon() const override
|
||||
{
|
||||
@ -96,7 +96,7 @@ private:
|
||||
private:
|
||||
bool initialized = false;
|
||||
NewInstanceDialog *dialog = nullptr;
|
||||
Ui::VanillaPage *ui = nullptr;
|
||||
Ui::CustomPage *ui = nullptr;
|
||||
bool m_versionSetByUser = false;
|
||||
BaseVersion::Ptr m_selectedVersion;
|
||||
BaseVersion::Ptr m_selectedLoaderVersion;
|
@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>VanillaPage</class>
|
||||
<widget class="QWidget" name="VanillaPage">
|
||||
<class>CustomPage</class>
|
||||
<widget class="QWidget" name="CustomPage">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
@ -57,7 +57,7 @@ public:
|
||||
virtual ~ImportPage();
|
||||
virtual QString displayName() const override
|
||||
{
|
||||
return tr("Import from zip");
|
||||
return tr("Import");
|
||||
}
|
||||
virtual QIcon icon() const override
|
||||
{
|
||||
|
@ -6,12 +6,14 @@
|
||||
|
||||
#include "minecraft/MinecraftInstance.h"
|
||||
#include "minecraft/PackProfile.h"
|
||||
#include "minecraft/mod/ModFolderModel.h"
|
||||
|
||||
#include <QMessageBox>
|
||||
#include <algorithm>
|
||||
|
||||
namespace ResourceDownload {
|
||||
|
||||
ModModel::ModModel(BaseInstance const& base_inst, ResourceAPI* api) : ResourceModel(api), m_base_instance(base_inst) {}
|
||||
ModModel::ModModel(BaseInstance& base_inst, ResourceAPI* api) : ResourceModel(api), m_base_instance(base_inst) {}
|
||||
|
||||
/******** Make data requests ********/
|
||||
|
||||
@ -24,7 +26,7 @@ ResourceAPI::SearchArgs ModModel::createSearchArguments()
|
||||
|
||||
std::optional<std::list<Version>> versions{};
|
||||
|
||||
{ // Version filter
|
||||
{ // Version filter
|
||||
if (!m_filter->versions.empty())
|
||||
versions = m_filter->versions;
|
||||
}
|
||||
@ -67,4 +69,14 @@ void ModModel::searchWithTerm(const QString& term, unsigned int sort, bool filte
|
||||
refresh();
|
||||
}
|
||||
|
||||
bool ModModel::isPackInstalled(ModPlatform::IndexedPack::Ptr pack) const
|
||||
{
|
||||
auto allMods = static_cast<MinecraftInstance&>(m_base_instance).loaderModList()->allMods();
|
||||
return std::any_of(allMods.cbegin(), allMods.cend(), [pack](Mod* mod) {
|
||||
if (auto meta = mod->metadata(); meta)
|
||||
return meta->provider == pack->provider && meta->project_id == pack->addonId;
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace ResourceDownload
|
||||
|
@ -24,7 +24,7 @@ class ModModel : public ResourceModel {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ModModel(const BaseInstance&, ResourceAPI* api);
|
||||
ModModel(BaseInstance&, ResourceAPI* api);
|
||||
|
||||
/* Ask the API for more information */
|
||||
void searchWithTerm(const QString& term, unsigned int sort, bool filter_changed);
|
||||
@ -32,6 +32,7 @@ class ModModel : public ResourceModel {
|
||||
void loadIndexedPack(ModPlatform::IndexedPack& m, QJsonObject& obj) override = 0;
|
||||
void loadExtraPackInfo(ModPlatform::IndexedPack& m, QJsonObject& obj) override = 0;
|
||||
void loadIndexedPackVersions(ModPlatform::IndexedPack& m, QJsonArray& arr) override = 0;
|
||||
virtual ModPlatform::IndexedVersion loadDependencyVersions(const ModPlatform::Dependency& m, QJsonArray& arr) = 0;
|
||||
|
||||
void setFilter(std::shared_ptr<ModFilterWidget::Filter> filter) { m_filter = filter; }
|
||||
|
||||
@ -42,9 +43,10 @@ class ModModel : public ResourceModel {
|
||||
|
||||
protected:
|
||||
auto documentToArray(QJsonDocument& obj) const -> QJsonArray override = 0;
|
||||
virtual bool isPackInstalled(ModPlatform::IndexedPack::Ptr) const override;
|
||||
|
||||
protected:
|
||||
const BaseInstance& m_base_instance;
|
||||
BaseInstance& m_base_instance;
|
||||
|
||||
std::shared_ptr<ModFilterWidget::Filter> m_filter = nullptr;
|
||||
};
|
||||
|
@ -89,17 +89,13 @@ void ModPage::filterMods()
|
||||
|
||||
void ModPage::triggerSearch()
|
||||
{
|
||||
auto changed = m_filter_widget->changed();
|
||||
m_filter = m_filter_widget->getFilter();
|
||||
m_ui->packView->clearSelection();
|
||||
m_ui->packDescription->clear();
|
||||
m_ui->versionSelectionBox->clear();
|
||||
updateSelectionButton();
|
||||
|
||||
if (changed) {
|
||||
m_ui->packView->clearSelection();
|
||||
m_ui->packDescription->clear();
|
||||
m_ui->versionSelectionBox->clear();
|
||||
updateSelectionButton();
|
||||
}
|
||||
|
||||
static_cast<ModModel*>(m_model)->searchWithTerm(getSearchTerm(), m_ui->sortByBox->currentData().toUInt(), changed);
|
||||
static_cast<ModModel*>(m_model)->searchWithTerm(getSearchTerm(), m_ui->sortByBox->currentData().toUInt(), m_filter_widget->changed());
|
||||
m_fetch_progress.watch(m_model->activeSearchJob().get());
|
||||
}
|
||||
|
||||
@ -122,6 +118,8 @@ void ModPage::updateVersionList()
|
||||
QString mcVersion = packProfile->getComponentVersion("net.minecraft");
|
||||
|
||||
auto current_pack = getCurrentPack();
|
||||
if (!current_pack)
|
||||
return;
|
||||
for (int i = 0; i < current_pack->versions.size(); i++) {
|
||||
auto version = current_pack->versions[i];
|
||||
bool valid = false;
|
||||
|
@ -77,6 +77,8 @@ auto ResourceModel::data(const QModelIndex& index, int role) const -> QVariant
|
||||
return pack->description;
|
||||
case UserDataTypes::SELECTED:
|
||||
return pack->isAnyVersionSelected();
|
||||
case UserDataTypes::INSTALLED:
|
||||
return this->isPackInstalled(pack);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -95,6 +97,7 @@ QHash<int, QByteArray> ResourceModel::roleNames() const
|
||||
roles[UserDataTypes::TITLE] = "title";
|
||||
roles[UserDataTypes::DESCRIPTION] = "description";
|
||||
roles[UserDataTypes::SELECTED] = "selected";
|
||||
roles[UserDataTypes::INSTALLED] = "installed";
|
||||
|
||||
return roles;
|
||||
}
|
||||
|
@ -116,6 +116,8 @@ class ResourceModel : public QAbstractListModel {
|
||||
virtual void loadExtraPackInfo(ModPlatform::IndexedPack&, QJsonObject&);
|
||||
virtual void loadIndexedPackVersions(ModPlatform::IndexedPack&, QJsonArray&);
|
||||
|
||||
virtual bool isPackInstalled(ModPlatform::IndexedPack::Ptr) const { return false; }
|
||||
|
||||
protected:
|
||||
/* Basic search parameters */
|
||||
enum class SearchState { None, CanFetchMore, ResetRequested, Finished } m_search_state = SearchState::None;
|
||||
|
@ -174,7 +174,11 @@ ModPlatform::IndexedPack::Ptr ResourcePage::getCurrentPack() const
|
||||
void ResourcePage::updateUi()
|
||||
{
|
||||
auto current_pack = getCurrentPack();
|
||||
|
||||
if (!current_pack) {
|
||||
m_ui->packDescription->setHtml({});
|
||||
m_ui->packDescription->flush();
|
||||
return;
|
||||
}
|
||||
QString text = "";
|
||||
QString name = current_pack->name;
|
||||
|
||||
@ -240,10 +244,13 @@ void ResourcePage::updateSelectionButton()
|
||||
}
|
||||
|
||||
m_ui->resourceSelectionButton->setEnabled(true);
|
||||
if (!getCurrentPack()->isVersionSelected(m_selected_version_index)) {
|
||||
m_ui->resourceSelectionButton->setText(tr("Select %1 for download").arg(resourceString()));
|
||||
if (auto current_pack = getCurrentPack(); current_pack) {
|
||||
if (!current_pack->isVersionSelected(m_selected_version_index))
|
||||
m_ui->resourceSelectionButton->setText(tr("Select %1 for download").arg(resourceString()));
|
||||
else
|
||||
m_ui->resourceSelectionButton->setText(tr("Deselect %1 for download").arg(resourceString()));
|
||||
} else {
|
||||
m_ui->resourceSelectionButton->setText(tr("Deselect %1 for download").arg(resourceString()));
|
||||
qWarning() << "Tried to update the selected button but there is not a pack selected";
|
||||
}
|
||||
}
|
||||
|
||||
@ -255,13 +262,14 @@ void ResourcePage::updateVersionList()
|
||||
m_ui->versionSelectionBox->clear();
|
||||
m_ui->versionSelectionBox->blockSignals(false);
|
||||
|
||||
for (int i = 0; i < current_pack->versions.size(); i++) {
|
||||
auto& version = current_pack->versions[i];
|
||||
if (optedOut(version))
|
||||
continue;
|
||||
if (current_pack)
|
||||
for (int i = 0; i < current_pack->versions.size(); i++) {
|
||||
auto& version = current_pack->versions[i];
|
||||
if (optedOut(version))
|
||||
continue;
|
||||
|
||||
m_ui->versionSelectionBox->addItem(current_pack->versions[i].version, QVariant(i));
|
||||
}
|
||||
m_ui->versionSelectionBox->addItem(current_pack->versions[i].version, QVariant(i));
|
||||
}
|
||||
|
||||
if (m_ui->versionSelectionBox->count() == 0) {
|
||||
m_ui->versionSelectionBox->addItem(tr("No valid version found."), QVariant(-1));
|
||||
@ -280,7 +288,7 @@ void ResourcePage::onSelectionChanged(QModelIndex curr, QModelIndex prev)
|
||||
auto current_pack = getCurrentPack();
|
||||
|
||||
bool request_load = false;
|
||||
if (!current_pack->versionsLoaded) {
|
||||
if (!current_pack || !current_pack->versionsLoaded) {
|
||||
m_ui->resourceSelectionButton->setText(tr("Loading versions..."));
|
||||
m_ui->resourceSelectionButton->setEnabled(false);
|
||||
|
||||
@ -289,7 +297,7 @@ void ResourcePage::onSelectionChanged(QModelIndex curr, QModelIndex prev)
|
||||
updateVersionList();
|
||||
}
|
||||
|
||||
if (!current_pack->extraDataLoaded)
|
||||
if (current_pack && !current_pack->extraDataLoaded)
|
||||
request_load = true;
|
||||
|
||||
if (request_load)
|
||||
@ -337,7 +345,7 @@ void ResourcePage::onResourceSelected()
|
||||
return;
|
||||
|
||||
auto current_pack = getCurrentPack();
|
||||
if (!current_pack->versionsLoaded)
|
||||
if (!current_pack || !current_pack->versionsLoaded)
|
||||
return;
|
||||
|
||||
auto& version = current_pack->versions[m_selected_version_index];
|
||||
@ -383,7 +391,7 @@ void ResourcePage::openUrl(const QUrl& url)
|
||||
const QString slug = match.captured(1);
|
||||
|
||||
// ensure the user isn't opening the same mod
|
||||
if (slug != getCurrentPack()->slug) {
|
||||
if (auto current_pack = getCurrentPack(); current_pack && slug != current_pack->slug) {
|
||||
m_parent_dialog->selectPage(page);
|
||||
|
||||
auto newPage = m_parent_dialog->getSelectedPage();
|
||||
|
@ -16,62 +16,49 @@
|
||||
|
||||
#include "AtlListModel.h"
|
||||
|
||||
#include <BuildConfig.h>
|
||||
#include <Application.h>
|
||||
#include <BuildConfig.h>
|
||||
#include <Json.h>
|
||||
|
||||
namespace Atl {
|
||||
|
||||
ListModel::ListModel(QObject *parent) : QAbstractListModel(parent)
|
||||
{
|
||||
}
|
||||
ListModel::ListModel(QObject* parent) : QAbstractListModel(parent) {}
|
||||
|
||||
ListModel::~ListModel()
|
||||
{
|
||||
}
|
||||
ListModel::~ListModel() {}
|
||||
|
||||
int ListModel::rowCount(const QModelIndex &parent) const
|
||||
int ListModel::rowCount(const QModelIndex& parent) const
|
||||
{
|
||||
return parent.isValid() ? 0 : modpacks.size();
|
||||
}
|
||||
|
||||
int ListModel::columnCount(const QModelIndex &parent) const
|
||||
int ListModel::columnCount(const QModelIndex& parent) const
|
||||
{
|
||||
return parent.isValid() ? 0 : 1;
|
||||
}
|
||||
|
||||
QVariant ListModel::data(const QModelIndex &index, int role) const
|
||||
QVariant ListModel::data(const QModelIndex& index, int role) const
|
||||
{
|
||||
int pos = index.row();
|
||||
if(pos >= modpacks.size() || pos < 0 || !index.isValid())
|
||||
{
|
||||
if (pos >= modpacks.size() || pos < 0 || !index.isValid()) {
|
||||
return QString("INVALID INDEX %1").arg(pos);
|
||||
}
|
||||
|
||||
ATLauncher::IndexedPack pack = modpacks.at(pos);
|
||||
if(role == Qt::DisplayRole)
|
||||
{
|
||||
if (role == Qt::DisplayRole) {
|
||||
return pack.name;
|
||||
}
|
||||
else if (role == Qt::ToolTipRole)
|
||||
{
|
||||
} else if (role == Qt::ToolTipRole) {
|
||||
return pack.name;
|
||||
}
|
||||
else if(role == Qt::DecorationRole)
|
||||
{
|
||||
if(m_logoMap.contains(pack.safeName))
|
||||
{
|
||||
} else if (role == Qt::DecorationRole) {
|
||||
if (m_logoMap.contains(pack.safeName)) {
|
||||
return (m_logoMap.value(pack.safeName));
|
||||
}
|
||||
auto icon = APPLICATION->getThemedIcon("atlauncher-placeholder");
|
||||
|
||||
auto url = QString(BuildConfig.ATL_DOWNLOAD_SERVER_URL + "launcher/images/%1.png").arg(pack.safeName.toLower());
|
||||
((ListModel *)this)->requestLogo(pack.safeName, url);
|
||||
((ListModel*)this)->requestLogo(pack.safeName, url);
|
||||
|
||||
return icon;
|
||||
}
|
||||
else if(role == Qt::UserRole)
|
||||
{
|
||||
} else if (role == Qt::UserRole) {
|
||||
QVariant v;
|
||||
v.setValue(pack);
|
||||
return v;
|
||||
@ -88,7 +75,7 @@ void ListModel::request()
|
||||
|
||||
auto netJob = makeShared<NetJob>("Atl::Request", APPLICATION->network());
|
||||
auto url = QString(BuildConfig.ATL_DOWNLOAD_SERVER_URL + "launcher/json/packsnew.json");
|
||||
netJob->addNetAction(Net::Download::makeByteArray(QUrl(url), &response));
|
||||
netJob->addNetAction(Net::Download::makeByteArray(QUrl(url), response));
|
||||
jobPtr = netJob;
|
||||
jobPtr->start();
|
||||
|
||||
@ -101,36 +88,38 @@ void ListModel::requestFinished()
|
||||
jobPtr.reset();
|
||||
|
||||
QJsonParseError parse_error;
|
||||
QJsonDocument doc = QJsonDocument::fromJson(response, &parse_error);
|
||||
if(parse_error.error != QJsonParseError::NoError) {
|
||||
QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error);
|
||||
if (parse_error.error != QJsonParseError::NoError) {
|
||||
qWarning() << "Error while parsing JSON response from ATL at " << parse_error.offset << " reason: " << parse_error.errorString();
|
||||
qWarning() << response;
|
||||
qWarning() << *response;
|
||||
return;
|
||||
}
|
||||
|
||||
QList<ATLauncher::IndexedPack> newList;
|
||||
|
||||
auto packs = doc.array();
|
||||
for(auto packRaw : packs) {
|
||||
for (auto packRaw : packs) {
|
||||
auto packObj = packRaw.toObject();
|
||||
|
||||
ATLauncher::IndexedPack pack;
|
||||
|
||||
try {
|
||||
ATLauncher::loadIndexedPack(pack, packObj);
|
||||
}
|
||||
catch (const JSONValidationError &e) {
|
||||
qDebug() << QString::fromUtf8(response);
|
||||
} catch (const JSONValidationError& e) {
|
||||
qDebug() << QString::fromUtf8(*response);
|
||||
qWarning() << "Error while reading pack manifest from ATLauncher: " << e.cause();
|
||||
return;
|
||||
}
|
||||
|
||||
// ignore packs without a published version
|
||||
if(pack.versions.length() == 0) continue;
|
||||
if (pack.versions.length() == 0)
|
||||
continue;
|
||||
// only display public packs (for now)
|
||||
if(pack.type != ATLauncher::PackType::Public) continue;
|
||||
if (pack.type != ATLauncher::PackType::Public)
|
||||
continue;
|
||||
// ignore "system" packs (Vanilla, Vanilla with Forge, etc)
|
||||
if(pack.system) continue;
|
||||
if (pack.system)
|
||||
continue;
|
||||
|
||||
newList.append(pack);
|
||||
}
|
||||
@ -145,14 +134,12 @@ void ListModel::requestFailed(QString reason)
|
||||
jobPtr.reset();
|
||||
}
|
||||
|
||||
void ListModel::getLogo(const QString &logo, const QString &logoUrl, LogoCallback callback)
|
||||
void ListModel::getLogo(const QString& logo, const QString& logoUrl, LogoCallback callback)
|
||||
{
|
||||
if(m_logoMap.contains(logo))
|
||||
{
|
||||
callback(APPLICATION->metacache()->resolveEntry("ATLauncherPacks", QString("logos/%1").arg(logo.section(".", 0, 0)))->getFullPath());
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_logoMap.contains(logo)) {
|
||||
callback(
|
||||
APPLICATION->metacache()->resolveEntry("ATLauncherPacks", QString("logos/%1").arg(logo.section(".", 0, 0)))->getFullPath());
|
||||
} else {
|
||||
requestLogo(logo, logoUrl);
|
||||
}
|
||||
}
|
||||
@ -168,36 +155,34 @@ void ListModel::logoLoaded(QString logo, QIcon out)
|
||||
m_loadingLogos.removeAll(logo);
|
||||
m_logoMap.insert(logo, out);
|
||||
|
||||
for(int i = 0; i < modpacks.size(); i++) {
|
||||
if(modpacks[i].safeName == logo) {
|
||||
emit dataChanged(createIndex(i, 0), createIndex(i, 0), {Qt::DecorationRole});
|
||||
for (int i = 0; i < modpacks.size(); i++) {
|
||||
if (modpacks[i].safeName == logo) {
|
||||
emit dataChanged(createIndex(i, 0), createIndex(i, 0), { Qt::DecorationRole });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ListModel::requestLogo(QString file, QString url)
|
||||
{
|
||||
if(m_loadingLogos.contains(file) || m_failedLogos.contains(file))
|
||||
{
|
||||
if (m_loadingLogos.contains(file) || m_failedLogos.contains(file)) {
|
||||
return;
|
||||
}
|
||||
|
||||
MetaEntryPtr entry = APPLICATION->metacache()->resolveEntry("ATLauncherPacks", QString("logos/%1").arg(file.section(".", 0, 0)));
|
||||
NetJob *job = new NetJob(QString("ATLauncher Icon Download %1").arg(file), APPLICATION->network());
|
||||
auto job = new NetJob(QString("ATLauncher Icon Download %1").arg(file), APPLICATION->network());
|
||||
job->addNetAction(Net::Download::makeCached(QUrl(url), entry));
|
||||
|
||||
auto fullPath = entry->getFullPath();
|
||||
QObject::connect(job, &NetJob::succeeded, this, [this, file, fullPath]
|
||||
{
|
||||
QObject::connect(job, &NetJob::succeeded, this, [this, file, fullPath, job] {
|
||||
job->deleteLater();
|
||||
emit logoLoaded(file, QIcon(fullPath));
|
||||
if(waitingCallbacks.contains(file))
|
||||
{
|
||||
if (waitingCallbacks.contains(file)) {
|
||||
waitingCallbacks.value(file)(fullPath);
|
||||
}
|
||||
});
|
||||
|
||||
QObject::connect(job, &NetJob::failed, this, [this, file]
|
||||
{
|
||||
QObject::connect(job, &NetJob::failed, this, [this, file, job] {
|
||||
job->deleteLater();
|
||||
emit logoFailed(file);
|
||||
});
|
||||
|
||||
@ -206,4 +191,4 @@ void ListModel::requestLogo(QString file, QString url)
|
||||
m_loadingLogos.append(file);
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace Atl
|
||||
|
@ -18,42 +18,41 @@
|
||||
|
||||
#include <QAbstractListModel>
|
||||
|
||||
#include "net/NetJob.h"
|
||||
#include <QIcon>
|
||||
#include <modplatform/atlauncher/ATLPackIndex.h>
|
||||
#include <QIcon>
|
||||
#include "net/NetJob.h"
|
||||
|
||||
namespace Atl {
|
||||
|
||||
typedef QMap<QString, QIcon> LogoMap;
|
||||
typedef std::function<void(QString)> LogoCallback;
|
||||
|
||||
class ListModel : public QAbstractListModel
|
||||
{
|
||||
class ListModel : public QAbstractListModel {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ListModel(QObject *parent);
|
||||
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;
|
||||
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 getLogo(const QString &logo, const QString &logoUrl, LogoCallback callback);
|
||||
void getLogo(const QString& logo, const QString& logoUrl, LogoCallback callback);
|
||||
|
||||
private slots:
|
||||
private slots:
|
||||
void requestFinished();
|
||||
void requestFailed(QString reason);
|
||||
|
||||
void logoFailed(QString logo);
|
||||
void logoLoaded(QString logo, QIcon out);
|
||||
|
||||
private:
|
||||
private:
|
||||
void requestLogo(QString file, QString url);
|
||||
|
||||
private:
|
||||
private:
|
||||
QList<ATLauncher::IndexedPack> modpacks;
|
||||
|
||||
QStringList m_failedLogos;
|
||||
@ -62,7 +61,7 @@ private:
|
||||
QMap<QString, LogoCallback> waitingCallbacks;
|
||||
|
||||
NetJob::Ptr jobPtr;
|
||||
QByteArray response;
|
||||
std::shared_ptr<QByteArray> response = std::make_shared<QByteArray>();
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace Atl
|
||||
|
@ -152,7 +152,7 @@ Qt::ItemFlags AtlOptionalModListModel::flags(const QModelIndex &index) const {
|
||||
void AtlOptionalModListModel::useShareCode(const QString& code) {
|
||||
m_jobPtr.reset(new NetJob("Atl::Request", APPLICATION->network()));
|
||||
auto url = QString(BuildConfig.ATL_API_BASE_URL + "share-codes/" + code);
|
||||
m_jobPtr->addNetAction(Net::Download::makeByteArray(QUrl(url), &m_response));
|
||||
m_jobPtr->addNetAction(Net::Download::makeByteArray(QUrl(url), m_response));
|
||||
|
||||
connect(m_jobPtr.get(), &NetJob::succeeded,
|
||||
this, &AtlOptionalModListModel::shareCodeSuccess);
|
||||
@ -166,10 +166,10 @@ void AtlOptionalModListModel::shareCodeSuccess() {
|
||||
m_jobPtr.reset();
|
||||
|
||||
QJsonParseError parse_error {};
|
||||
auto doc = QJsonDocument::fromJson(m_response, &parse_error);
|
||||
auto doc = QJsonDocument::fromJson(*m_response, &parse_error);
|
||||
if (parse_error.error != QJsonParseError::NoError) {
|
||||
qWarning() << "Error while parsing JSON response from ATL at " << parse_error.offset << " reason: " << parse_error.errorString();
|
||||
qWarning() << m_response;
|
||||
qWarning() << *m_response;
|
||||
return;
|
||||
}
|
||||
auto obj = doc.object();
|
||||
@ -179,7 +179,7 @@ void AtlOptionalModListModel::shareCodeSuccess() {
|
||||
ATLauncher::loadShareCodeResponse(response, obj);
|
||||
}
|
||||
catch (const JSONValidationError& e) {
|
||||
qDebug() << QString::fromUtf8(m_response);
|
||||
qDebug() << QString::fromUtf8(*m_response);
|
||||
qWarning() << "Error while reading response from ATLauncher: " << e.cause();
|
||||
return;
|
||||
}
|
||||
|
@ -82,9 +82,9 @@ private:
|
||||
void toggleMod(ATLauncher::VersionMod mod, int index);
|
||||
void setMod(ATLauncher::VersionMod mod, int index, bool enable, bool shouldEmit = true);
|
||||
|
||||
private:
|
||||
private:
|
||||
NetJob::Ptr m_jobPtr;
|
||||
QByteArray m_response;
|
||||
std::shared_ptr<QByteArray> m_response = std::make_shared<QByteArray>();
|
||||
|
||||
ATLauncher::PackVersion m_version;
|
||||
QVector<ATLauncher::VersionMod> m_mods;
|
||||
|
@ -42,15 +42,15 @@
|
||||
class AtlUserInteractionSupportImpl : public QObject, public ATLauncher::UserInteractionSupport {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
public:
|
||||
AtlUserInteractionSupportImpl(QWidget* parent);
|
||||
virtual ~AtlUserInteractionSupportImpl() = default;
|
||||
|
||||
private:
|
||||
private:
|
||||
QString chooseVersion(Meta::VersionList::Ptr vlist, QString minecraftVersion) override;
|
||||
std::optional<QVector<QString>> chooseOptionalMods(ATLauncher::PackVersion version, QVector<ATLauncher::VersionMod> mods) override;
|
||||
void displayMessage(QString message) override;
|
||||
|
||||
private:
|
||||
private:
|
||||
QWidget* m_parent;
|
||||
|
||||
};
|
||||
|
@ -60,6 +60,8 @@ QVariant ListModel::data(const QModelIndex& index, int role) const
|
||||
return pack.description;
|
||||
case UserDataTypes::SELECTED:
|
||||
return false;
|
||||
case UserDataTypes::INSTALLED:
|
||||
return false;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -169,7 +171,7 @@ void ListModel::performPaginatedSearch()
|
||||
.arg(currentSearchTerm)
|
||||
.arg(currentSort + 1);
|
||||
|
||||
netJob->addNetAction(Net::Download::makeByteArray(QUrl(searchUrl), &response));
|
||||
netJob->addNetAction(Net::Download::makeByteArray(QUrl(searchUrl), response));
|
||||
jobPtr = netJob;
|
||||
jobPtr->start();
|
||||
QObject::connect(netJob.get(), &NetJob::succeeded, this, &ListModel::searchRequestFinished);
|
||||
@ -202,11 +204,11 @@ void Flame::ListModel::searchRequestFinished()
|
||||
jobPtr.reset();
|
||||
|
||||
QJsonParseError parse_error;
|
||||
QJsonDocument doc = QJsonDocument::fromJson(response, &parse_error);
|
||||
QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error);
|
||||
if (parse_error.error != QJsonParseError::NoError) {
|
||||
qWarning() << "Error while parsing JSON response from CurseForge at " << parse_error.offset
|
||||
<< " reason: " << parse_error.errorString();
|
||||
qWarning() << response;
|
||||
qWarning() << *response;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -3,46 +3,44 @@
|
||||
#include <RWStorage.h>
|
||||
|
||||
#include <QAbstractListModel>
|
||||
#include <QSortFilterProxyModel>
|
||||
#include <QThreadPool>
|
||||
#include <QIcon>
|
||||
#include <QStyledItemDelegate>
|
||||
#include <QList>
|
||||
#include <QMetaType>
|
||||
#include <QSortFilterProxyModel>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
#include <QMetaType>
|
||||
#include <QStyledItemDelegate>
|
||||
#include <QThreadPool>
|
||||
|
||||
#include <functional>
|
||||
#include <net/NetJob.h>
|
||||
#include <functional>
|
||||
|
||||
#include <modplatform/flame/FlamePackIndex.h>
|
||||
|
||||
namespace Flame {
|
||||
|
||||
|
||||
typedef QMap<QString, QIcon> LogoMap;
|
||||
typedef std::function<void(QString)> LogoCallback;
|
||||
|
||||
class ListModel : public QAbstractListModel
|
||||
{
|
||||
class ListModel : public QAbstractListModel {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ListModel(QObject *parent);
|
||||
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;
|
||||
bool setData(const QModelIndex &index, const QVariant &value, int role) override;
|
||||
Qt::ItemFlags flags(const QModelIndex &index) const override;
|
||||
bool canFetchMore(const QModelIndex & parent) const override;
|
||||
void fetchMore(const QModelIndex & parent) override;
|
||||
int rowCount(const QModelIndex& parent) const override;
|
||||
int columnCount(const QModelIndex& parent) const override;
|
||||
QVariant data(const QModelIndex& index, int role) const override;
|
||||
bool setData(const QModelIndex& index, const QVariant& value, int role) override;
|
||||
Qt::ItemFlags flags(const QModelIndex& index) const override;
|
||||
bool canFetchMore(const QModelIndex& parent) const override;
|
||||
void fetchMore(const QModelIndex& parent) override;
|
||||
|
||||
void getLogo(const QString &logo, const QString &logoUrl, LogoCallback callback);
|
||||
void searchWithTerm(const QString & term, const int sort);
|
||||
void getLogo(const QString& logo, const QString& logoUrl, LogoCallback callback);
|
||||
void searchWithTerm(const QString& term, const int sort);
|
||||
|
||||
private slots:
|
||||
private slots:
|
||||
void performPaginatedSearch();
|
||||
|
||||
void logoFailed(QString logo);
|
||||
@ -51,10 +49,10 @@ private slots:
|
||||
void searchRequestFinished();
|
||||
void searchRequestFailed(QString reason);
|
||||
|
||||
private:
|
||||
private:
|
||||
void requestLogo(QString file, QString url);
|
||||
|
||||
private:
|
||||
private:
|
||||
QList<IndexedPack> modpacks;
|
||||
QStringList m_failedLogos;
|
||||
QStringList m_loadingLogos;
|
||||
@ -64,14 +62,9 @@ private:
|
||||
QString currentSearchTerm;
|
||||
int currentSort = 0;
|
||||
int nextSearchOffset = 0;
|
||||
enum SearchState {
|
||||
None,
|
||||
CanPossiblyFetchMore,
|
||||
ResetRequested,
|
||||
Finished
|
||||
} searchState = None;
|
||||
enum SearchState { None, CanPossiblyFetchMore, ResetRequested, Finished } searchState = None;
|
||||
NetJob::Ptr jobPtr;
|
||||
QByteArray response;
|
||||
std::shared_ptr<QByteArray> response = std::make_shared<QByteArray>();
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace Flame
|
||||
|
@ -130,7 +130,7 @@ void FlamePage::onSelectionChanged(QModelIndex curr, QModelIndex prev)
|
||||
if (current.versionsLoaded == false) {
|
||||
qDebug() << "Loading flame modpack versions";
|
||||
auto netJob = new NetJob(QString("Flame::PackVersions(%1)").arg(current.name), APPLICATION->network());
|
||||
auto response = new QByteArray();
|
||||
auto response = std::make_shared<QByteArray>();
|
||||
int addonId = current.addonId;
|
||||
netJob->addNetAction(Net::Download::makeByteArray(QString("https://api.curseforge.com/v1/mods/%1/files").arg(addonId), response));
|
||||
|
||||
@ -170,10 +170,7 @@ void FlamePage::onSelectionChanged(QModelIndex curr, QModelIndex prev)
|
||||
}
|
||||
suggestCurrent();
|
||||
});
|
||||
QObject::connect(netJob, &NetJob::finished, this, [response, netJob] {
|
||||
netJob->deleteLater();
|
||||
delete response;
|
||||
});
|
||||
QObject::connect(netJob, &NetJob::finished, this, [response, netJob] { netJob->deleteLater(); });
|
||||
netJob->start();
|
||||
} else {
|
||||
for (auto version : current.versions) {
|
||||
|
@ -11,7 +11,7 @@
|
||||
|
||||
namespace ResourceDownload {
|
||||
|
||||
FlameModModel::FlameModModel(BaseInstance const& base) : ModModel(base, new FlameAPI) {}
|
||||
FlameModModel::FlameModModel(BaseInstance& base) : ModModel(base, new FlameAPI) {}
|
||||
|
||||
void FlameModModel::loadIndexedPack(ModPlatform::IndexedPack& m, QJsonObject& obj)
|
||||
{
|
||||
@ -29,6 +29,11 @@ void FlameModModel::loadIndexedPackVersions(ModPlatform::IndexedPack& m, QJsonAr
|
||||
FlameMod::loadIndexedPackVersions(m, arr, APPLICATION->network(), &m_base_instance);
|
||||
}
|
||||
|
||||
auto FlameModModel::loadDependencyVersions(const ModPlatform::Dependency& m, QJsonArray& arr) -> ModPlatform::IndexedVersion
|
||||
{
|
||||
return FlameMod::loadDependencyVersions(m, arr);
|
||||
};
|
||||
|
||||
auto FlameModModel::documentToArray(QJsonDocument& obj) const -> QJsonArray
|
||||
{
|
||||
return Json::ensureArray(obj.object(), "data");
|
||||
@ -81,7 +86,7 @@ void FlameTexturePackModel::loadIndexedPackVersions(ModPlatform::IndexedPack& m,
|
||||
auto const& mc_versions = version.mcVersion;
|
||||
|
||||
if (std::any_of(mc_versions.constBegin(), mc_versions.constEnd(),
|
||||
[this](auto const& mc_version){ return Version(mc_version) <= maximumTexturePackVersion(); }))
|
||||
[this](auto const& mc_version) { return Version(mc_version) <= maximumTexturePackVersion(); }))
|
||||
filtered_versions.push_back(version);
|
||||
}
|
||||
|
||||
|
@ -14,7 +14,7 @@ class FlameModModel : public ModModel {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
FlameModModel(const BaseInstance&);
|
||||
FlameModModel(BaseInstance&);
|
||||
~FlameModModel() override = default;
|
||||
|
||||
private:
|
||||
@ -24,6 +24,7 @@ class FlameModModel : public ModModel {
|
||||
void loadIndexedPack(ModPlatform::IndexedPack& m, QJsonObject& obj) override;
|
||||
void loadExtraPackInfo(ModPlatform::IndexedPack& m, QJsonObject& obj) override;
|
||||
void loadIndexedPackVersions(ModPlatform::IndexedPack& m, QJsonArray& arr) override;
|
||||
auto loadDependencyVersions(const ModPlatform::Dependency& m, QJsonArray& arr) -> ModPlatform::IndexedVersion override;
|
||||
|
||||
auto documentToArray(QJsonDocument& obj) const -> QJsonArray override;
|
||||
};
|
||||
|
@ -38,11 +38,11 @@
|
||||
#include "net/HttpMetaCache.h"
|
||||
#include "net/NetJob.h"
|
||||
|
||||
#include "StringUtils.h"
|
||||
#include <Version.h>
|
||||
#include "StringUtils.h"
|
||||
|
||||
#include <QtMath>
|
||||
#include <QLabel>
|
||||
#include <QtMath>
|
||||
|
||||
#include <RWStorage.h>
|
||||
|
||||
@ -50,33 +50,33 @@
|
||||
|
||||
namespace LegacyFTB {
|
||||
|
||||
FilterModel::FilterModel(QObject *parent) : QSortFilterProxyModel(parent)
|
||||
FilterModel::FilterModel(QObject* parent) : QSortFilterProxyModel(parent)
|
||||
{
|
||||
currentSorting = Sorting::ByGameVersion;
|
||||
sortings.insert(tr("Sort by Name"), Sorting::ByName);
|
||||
sortings.insert(tr("Sort by Game Version"), Sorting::ByGameVersion);
|
||||
}
|
||||
|
||||
bool FilterModel::lessThan(const QModelIndex &left, const QModelIndex &right) const
|
||||
bool FilterModel::lessThan(const QModelIndex& left, const QModelIndex& right) const
|
||||
{
|
||||
Modpack leftPack = sourceModel()->data(left, Qt::UserRole).value<Modpack>();
|
||||
Modpack rightPack = sourceModel()->data(right, Qt::UserRole).value<Modpack>();
|
||||
|
||||
if(currentSorting == Sorting::ByGameVersion) {
|
||||
if (currentSorting == Sorting::ByGameVersion) {
|
||||
Version lv(leftPack.mcVersion);
|
||||
Version rv(rightPack.mcVersion);
|
||||
return lv < rv;
|
||||
|
||||
} else if(currentSorting == Sorting::ByName) {
|
||||
} else if (currentSorting == Sorting::ByName) {
|
||||
return StringUtils::naturalCompare(leftPack.name, rightPack.name, Qt::CaseSensitive) >= 0;
|
||||
}
|
||||
|
||||
//UHM, some inavlid value set?!
|
||||
// UHM, some inavlid value set?!
|
||||
qWarning() << "Invalid sorting set!";
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FilterModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
|
||||
bool FilterModel::filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@ -102,18 +102,13 @@ FilterModel::Sorting FilterModel::getCurrentSorting()
|
||||
return currentSorting;
|
||||
}
|
||||
|
||||
ListModel::ListModel(QObject *parent) : QAbstractListModel(parent)
|
||||
{
|
||||
}
|
||||
ListModel::ListModel(QObject* parent) : QAbstractListModel(parent) {}
|
||||
|
||||
ListModel::~ListModel()
|
||||
{
|
||||
}
|
||||
ListModel::~ListModel() {}
|
||||
|
||||
QString ListModel::translatePackType(PackType type) const
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
switch (type) {
|
||||
case PackType::Public:
|
||||
return tr("Public Modpack");
|
||||
case PackType::ThirdParty:
|
||||
@ -125,67 +120,51 @@ QString ListModel::translatePackType(PackType type) const
|
||||
return QString();
|
||||
}
|
||||
|
||||
int ListModel::rowCount(const QModelIndex &parent) const
|
||||
int ListModel::rowCount(const QModelIndex& parent) const
|
||||
{
|
||||
return parent.isValid() ? 0 : modpacks.size();
|
||||
}
|
||||
|
||||
int ListModel::columnCount(const QModelIndex &parent) const
|
||||
int ListModel::columnCount(const QModelIndex& parent) const
|
||||
{
|
||||
return parent.isValid() ? 0 : 1;
|
||||
}
|
||||
|
||||
QVariant ListModel::data(const QModelIndex &index, int role) const
|
||||
QVariant ListModel::data(const QModelIndex& index, int role) const
|
||||
{
|
||||
int pos = index.row();
|
||||
if(pos >= modpacks.size() || pos < 0 || !index.isValid())
|
||||
{
|
||||
if (pos >= modpacks.size() || pos < 0 || !index.isValid()) {
|
||||
return QString("INVALID INDEX %1").arg(pos);
|
||||
}
|
||||
|
||||
Modpack pack = modpacks.at(pos);
|
||||
if(role == Qt::DisplayRole)
|
||||
{
|
||||
if (role == Qt::DisplayRole) {
|
||||
return pack.name + "\n" + translatePackType(pack.type);
|
||||
}
|
||||
else if (role == Qt::ToolTipRole)
|
||||
{
|
||||
if(pack.description.length() > 100)
|
||||
{
|
||||
//some magic to prevent to long tooltips and replace html linebreaks
|
||||
} else if (role == Qt::ToolTipRole) {
|
||||
if (pack.description.length() > 100) {
|
||||
// some magic to prevent to long tooltips and replace html linebreaks
|
||||
QString edit = pack.description.left(97);
|
||||
edit = edit.left(edit.lastIndexOf("<br>")).left(edit.lastIndexOf(" ")).append("...");
|
||||
return edit;
|
||||
|
||||
}
|
||||
return pack.description;
|
||||
}
|
||||
else if(role == Qt::DecorationRole)
|
||||
{
|
||||
if(m_logoMap.contains(pack.logo))
|
||||
{
|
||||
} else if (role == Qt::DecorationRole) {
|
||||
if (m_logoMap.contains(pack.logo)) {
|
||||
return (m_logoMap.value(pack.logo));
|
||||
}
|
||||
QIcon icon = APPLICATION->getThemedIcon("screenshot-placeholder");
|
||||
((ListModel *)this)->requestLogo(pack.logo);
|
||||
((ListModel*)this)->requestLogo(pack.logo);
|
||||
return icon;
|
||||
}
|
||||
else if(role == Qt::ForegroundRole)
|
||||
{
|
||||
if(pack.broken)
|
||||
{
|
||||
//FIXME: Hardcoded color
|
||||
} else if (role == Qt::ForegroundRole) {
|
||||
if (pack.broken) {
|
||||
// FIXME: Hardcoded color
|
||||
return QColor(255, 0, 50);
|
||||
}
|
||||
else if(pack.bugged)
|
||||
{
|
||||
//FIXME: Hardcoded color
|
||||
//bugged pack, currently only indicates bugged xml
|
||||
} else if (pack.bugged) {
|
||||
// FIXME: Hardcoded color
|
||||
// bugged pack, currently only indicates bugged xml
|
||||
return QColor(244, 229, 66);
|
||||
}
|
||||
}
|
||||
else if(role == Qt::UserRole)
|
||||
{
|
||||
} else if (role == Qt::UserRole) {
|
||||
QVariant v;
|
||||
v.setValue(pack);
|
||||
return v;
|
||||
@ -222,8 +201,7 @@ Modpack ListModel::at(int row)
|
||||
|
||||
void ListModel::remove(int row)
|
||||
{
|
||||
if(row < 0 || row >= modpacks.size())
|
||||
{
|
||||
if (row < 0 || row >= modpacks.size()) {
|
||||
qWarning() << "Attempt to remove FTB modpacks with invalid row" << row;
|
||||
return;
|
||||
}
|
||||
@ -247,27 +225,25 @@ void ListModel::logoFailed(QString logo)
|
||||
|
||||
void ListModel::requestLogo(QString file)
|
||||
{
|
||||
if(m_loadingLogos.contains(file) || m_failedLogos.contains(file))
|
||||
{
|
||||
if (m_loadingLogos.contains(file) || m_failedLogos.contains(file)) {
|
||||
return;
|
||||
}
|
||||
|
||||
MetaEntryPtr entry = APPLICATION->metacache()->resolveEntry("FTBPacks", QString("logos/%1").arg(file.section(".", 0, 0)));
|
||||
NetJob *job = new NetJob(QString("FTB Icon Download for %1").arg(file), APPLICATION->network());
|
||||
NetJob* job = new NetJob(QString("FTB Icon Download for %1").arg(file), APPLICATION->network());
|
||||
job->addNetAction(Net::Download::makeCached(QUrl(QString(BuildConfig.LEGACY_FTB_CDN_BASE_URL + "static/%1").arg(file)), entry));
|
||||
|
||||
auto fullPath = entry->getFullPath();
|
||||
QObject::connect(job, &NetJob::finished, this, [this, file, fullPath]
|
||||
{
|
||||
QObject::connect(job, &NetJob::finished, this, [this, file, fullPath, job] {
|
||||
job->deleteLater();
|
||||
emit logoLoaded(file, QIcon(fullPath));
|
||||
if(waitingCallbacks.contains(file))
|
||||
{
|
||||
if (waitingCallbacks.contains(file)) {
|
||||
waitingCallbacks.value(file)(fullPath);
|
||||
}
|
||||
});
|
||||
|
||||
QObject::connect(job, &NetJob::failed, this, [this, file]
|
||||
{
|
||||
QObject::connect(job, &NetJob::failed, this, [this, file, job] {
|
||||
job->deleteLater();
|
||||
emit logoFailed(file);
|
||||
});
|
||||
|
||||
@ -276,21 +252,18 @@ void ListModel::requestLogo(QString file)
|
||||
m_loadingLogos.append(file);
|
||||
}
|
||||
|
||||
void ListModel::getLogo(const QString &logo, LogoCallback callback)
|
||||
void ListModel::getLogo(const QString& logo, LogoCallback callback)
|
||||
{
|
||||
if(m_logoMap.contains(logo))
|
||||
{
|
||||
if (m_logoMap.contains(logo)) {
|
||||
callback(APPLICATION->metacache()->resolveEntry("FTBPacks", QString("logos/%1").arg(logo.section(".", 0, 0)))->getFullPath());
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
requestLogo(logo);
|
||||
}
|
||||
}
|
||||
|
||||
Qt::ItemFlags ListModel::flags(const QModelIndex &index) const
|
||||
Qt::ItemFlags ListModel::flags(const QModelIndex& index) const
|
||||
{
|
||||
return QAbstractListModel::flags(index);
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace LegacyFTB
|
||||
|
@ -116,8 +116,8 @@ Page::Page(NewInstanceDialog* dialog, QWidget *parent)
|
||||
connect(ui->thirdPartyPackList->selectionModel(), &QItemSelectionModel::currentChanged, this, &Page::onThirdPartyPackSelectionChanged);
|
||||
connect(ui->privatePackList->selectionModel(), &QItemSelectionModel::currentChanged, this, &Page::onPrivatePackSelectionChanged);
|
||||
|
||||
connect(ui->addPackBtn, &QPushButton::pressed, this, &Page::onAddPackClicked);
|
||||
connect(ui->removePackBtn, &QPushButton::pressed, this, &Page::onRemovePackClicked);
|
||||
connect(ui->addPackBtn, &QPushButton::clicked, this, &Page::onAddPackClicked);
|
||||
connect(ui->removePackBtn, &QPushButton::clicked, this, &Page::onRemovePackClicked);
|
||||
|
||||
connect(ui->tabWidget, &QTabWidget::currentChanged, this, &Page::onTabChanged);
|
||||
|
||||
|
@ -106,6 +106,8 @@ auto ModpackListModel::data(const QModelIndex& index, int role) const -> QVarian
|
||||
return pack.description;
|
||||
case UserDataTypes::SELECTED:
|
||||
return false;
|
||||
case UserDataTypes::INSTALLED:
|
||||
return false;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -129,27 +131,27 @@ void ModpackListModel::performPaginatedSearch()
|
||||
// TODO: Move to standalone API
|
||||
auto netJob = makeShared<NetJob>("Modrinth::SearchModpack", APPLICATION->network());
|
||||
auto searchAllUrl = QString(BuildConfig.MODRINTH_PROD_URL +
|
||||
"/search?"
|
||||
"offset=%1&"
|
||||
"limit=%2&"
|
||||
"query=%3&"
|
||||
"index=%4&"
|
||||
"facets=[[\"project_type:modpack\"]]")
|
||||
"/search?"
|
||||
"offset=%1&"
|
||||
"limit=%2&"
|
||||
"query=%3&"
|
||||
"index=%4&"
|
||||
"facets=[[\"project_type:modpack\"]]")
|
||||
.arg(nextSearchOffset)
|
||||
.arg(m_modpacks_per_page)
|
||||
.arg(currentSearchTerm)
|
||||
.arg(currentSort);
|
||||
|
||||
netJob->addNetAction(Net::Download::makeByteArray(QUrl(searchAllUrl), &m_all_response));
|
||||
netJob->addNetAction(Net::Download::makeByteArray(QUrl(searchAllUrl), m_all_response));
|
||||
|
||||
QObject::connect(netJob.get(), &NetJob::succeeded, this, [this] {
|
||||
QJsonParseError parse_error_all{};
|
||||
|
||||
QJsonDocument doc_all = QJsonDocument::fromJson(m_all_response, &parse_error_all);
|
||||
QJsonDocument doc_all = QJsonDocument::fromJson(*m_all_response, &parse_error_all);
|
||||
if (parse_error_all.error != QJsonParseError::NoError) {
|
||||
qWarning() << "Error while parsing JSON response from " << debugName() << " at " << parse_error_all.offset
|
||||
<< " reason: " << parse_error_all.errorString();
|
||||
qWarning() << m_all_response;
|
||||
qWarning() << *m_all_response;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -110,9 +110,9 @@ class ModpackListModel : public QAbstractListModel {
|
||||
|
||||
NetJob::Ptr jobPtr;
|
||||
|
||||
QByteArray m_all_response;
|
||||
std::shared_ptr<QByteArray> m_all_response = std::make_shared<QByteArray>();
|
||||
QByteArray m_specific_response;
|
||||
|
||||
int m_modpacks_per_page = 20;
|
||||
};
|
||||
} // namespace ModPlatform
|
||||
} // namespace Modrinth
|
||||
|
@ -123,7 +123,7 @@ void ModrinthPage::onSelectionChanged(QModelIndex curr, QModelIndex prev)
|
||||
qDebug() << "Loading modrinth modpack information";
|
||||
|
||||
auto netJob = new NetJob(QString("Modrinth::PackInformation(%1)").arg(current.name), APPLICATION->network());
|
||||
auto response = new QByteArray();
|
||||
auto response = std::make_shared<QByteArray>();
|
||||
|
||||
QString id = current.id;
|
||||
|
||||
@ -162,10 +162,7 @@ void ModrinthPage::onSelectionChanged(QModelIndex curr, QModelIndex prev)
|
||||
|
||||
suggestCurrent();
|
||||
});
|
||||
QObject::connect(netJob, &NetJob::finished, this, [response, netJob] {
|
||||
netJob->deleteLater();
|
||||
delete response;
|
||||
});
|
||||
QObject::connect(netJob, &NetJob::finished, this, [response, netJob] { netJob->deleteLater(); });
|
||||
netJob->start();
|
||||
} else
|
||||
updateUI();
|
||||
@ -174,7 +171,7 @@ void ModrinthPage::onSelectionChanged(QModelIndex curr, QModelIndex prev)
|
||||
qDebug() << "Loading modrinth modpack versions";
|
||||
|
||||
auto netJob = new NetJob(QString("Modrinth::PackVersions(%1)").arg(current.name), APPLICATION->network());
|
||||
auto response = new QByteArray();
|
||||
auto response = std::make_shared<QByteArray>();
|
||||
|
||||
QString id = current.id;
|
||||
|
||||
@ -217,10 +214,7 @@ void ModrinthPage::onSelectionChanged(QModelIndex curr, QModelIndex prev)
|
||||
|
||||
suggestCurrent();
|
||||
});
|
||||
QObject::connect(netJob, &NetJob::finished, this, [response, netJob] {
|
||||
netJob->deleteLater();
|
||||
delete response;
|
||||
});
|
||||
QObject::connect(netJob, &NetJob::finished, this, [response, netJob] { netJob->deleteLater(); });
|
||||
netJob->start();
|
||||
|
||||
} else {
|
||||
@ -260,10 +254,8 @@ void ModrinthPage::updateUI()
|
||||
text += donates.join(", ");
|
||||
}
|
||||
|
||||
if (!current.extra.issuesUrl.isEmpty()
|
||||
|| !current.extra.sourceUrl.isEmpty()
|
||||
|| !current.extra.wikiUrl.isEmpty()
|
||||
|| !current.extra.discordUrl.isEmpty()) {
|
||||
if (!current.extra.issuesUrl.isEmpty() || !current.extra.sourceUrl.isEmpty() || !current.extra.wikiUrl.isEmpty() ||
|
||||
!current.extra.discordUrl.isEmpty()) {
|
||||
text += "<br><br>" + tr("External links:") + "<br>";
|
||||
}
|
||||
|
||||
|
@ -25,7 +25,7 @@
|
||||
|
||||
namespace ResourceDownload {
|
||||
|
||||
ModrinthModModel::ModrinthModModel(BaseInstance const& base) : ModModel(base, new ModrinthAPI) {}
|
||||
ModrinthModModel::ModrinthModModel(BaseInstance& base) : ModModel(base, new ModrinthAPI) {}
|
||||
|
||||
void ModrinthModModel::loadIndexedPack(ModPlatform::IndexedPack& m, QJsonObject& obj)
|
||||
{
|
||||
@ -42,12 +42,17 @@ void ModrinthModModel::loadIndexedPackVersions(ModPlatform::IndexedPack& m, QJso
|
||||
::Modrinth::loadIndexedPackVersions(m, arr, APPLICATION->network(), &m_base_instance);
|
||||
}
|
||||
|
||||
auto ModrinthModModel::loadDependencyVersions(const ModPlatform::Dependency& m, QJsonArray& arr) -> ModPlatform::IndexedVersion
|
||||
{
|
||||
return ::Modrinth::loadDependencyVersions(m, arr);
|
||||
};
|
||||
|
||||
auto ModrinthModModel::documentToArray(QJsonDocument& obj) const -> QJsonArray
|
||||
{
|
||||
return obj.object().value("hits").toArray();
|
||||
}
|
||||
|
||||
ModrinthResourcePackModel::ModrinthResourcePackModel(const BaseInstance& base) : ResourcePackResourceModel(base, new ModrinthAPI){}
|
||||
ModrinthResourcePackModel::ModrinthResourcePackModel(const BaseInstance& base) : ResourcePackResourceModel(base, new ModrinthAPI) {}
|
||||
|
||||
void ModrinthResourcePackModel::loadIndexedPack(ModPlatform::IndexedPack& m, QJsonObject& obj)
|
||||
{
|
||||
@ -69,7 +74,7 @@ auto ModrinthResourcePackModel::documentToArray(QJsonDocument& obj) const -> QJs
|
||||
return obj.object().value("hits").toArray();
|
||||
}
|
||||
|
||||
ModrinthTexturePackModel::ModrinthTexturePackModel(const BaseInstance& base) : TexturePackResourceModel(base, new ModrinthAPI){}
|
||||
ModrinthTexturePackModel::ModrinthTexturePackModel(const BaseInstance& base) : TexturePackResourceModel(base, new ModrinthAPI) {}
|
||||
|
||||
void ModrinthTexturePackModel::loadIndexedPack(ModPlatform::IndexedPack& m, QJsonObject& obj)
|
||||
{
|
||||
@ -91,7 +96,7 @@ auto ModrinthTexturePackModel::documentToArray(QJsonDocument& obj) const -> QJso
|
||||
return obj.object().value("hits").toArray();
|
||||
}
|
||||
|
||||
ModrinthShaderPackModel::ModrinthShaderPackModel(const BaseInstance& base) : ShaderPackResourceModel(base, new ModrinthAPI){}
|
||||
ModrinthShaderPackModel::ModrinthShaderPackModel(const BaseInstance& base) : ShaderPackResourceModel(base, new ModrinthAPI) {}
|
||||
|
||||
void ModrinthShaderPackModel::loadIndexedPack(ModPlatform::IndexedPack& m, QJsonObject& obj)
|
||||
{
|
||||
|
@ -30,7 +30,7 @@ class ModrinthModModel : public ModModel {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ModrinthModModel(const BaseInstance&);
|
||||
ModrinthModModel(BaseInstance&);
|
||||
~ModrinthModModel() override = default;
|
||||
|
||||
private:
|
||||
@ -40,6 +40,7 @@ class ModrinthModModel : public ModModel {
|
||||
void loadIndexedPack(ModPlatform::IndexedPack& m, QJsonObject& obj) override;
|
||||
void loadExtraPackInfo(ModPlatform::IndexedPack& m, QJsonObject& obj) override;
|
||||
void loadIndexedPackVersions(ModPlatform::IndexedPack& m, QJsonArray& arr) override;
|
||||
auto loadDependencyVersions(const ModPlatform::Dependency& m, QJsonArray& arr) -> ModPlatform::IndexedVersion override;
|
||||
|
||||
auto documentToArray(QJsonDocument& obj) const -> QJsonArray override;
|
||||
};
|
||||
|
@ -40,39 +40,28 @@
|
||||
|
||||
#include <QIcon>
|
||||
|
||||
Technic::ListModel::ListModel(QObject *parent) : QAbstractListModel(parent)
|
||||
{
|
||||
}
|
||||
Technic::ListModel::ListModel(QObject* parent) : QAbstractListModel(parent) {}
|
||||
|
||||
Technic::ListModel::~ListModel()
|
||||
{
|
||||
}
|
||||
Technic::ListModel::~ListModel() {}
|
||||
|
||||
QVariant Technic::ListModel::data(const QModelIndex& index, int role) const
|
||||
{
|
||||
int pos = index.row();
|
||||
if(pos >= modpacks.size() || pos < 0 || !index.isValid())
|
||||
{
|
||||
if (pos >= modpacks.size() || pos < 0 || !index.isValid()) {
|
||||
return QString("INVALID INDEX %1").arg(pos);
|
||||
}
|
||||
|
||||
Modpack pack = modpacks.at(pos);
|
||||
if(role == Qt::DisplayRole)
|
||||
{
|
||||
if (role == Qt::DisplayRole) {
|
||||
return pack.name;
|
||||
}
|
||||
else if(role == Qt::DecorationRole)
|
||||
{
|
||||
if(m_logoMap.contains(pack.logoName))
|
||||
{
|
||||
} else if (role == Qt::DecorationRole) {
|
||||
if (m_logoMap.contains(pack.logoName)) {
|
||||
return (m_logoMap.value(pack.logoName));
|
||||
}
|
||||
QIcon icon = APPLICATION->getThemedIcon("screenshot-placeholder");
|
||||
((ListModel *)this)->requestLogo(pack.logoName, pack.logoUrl);
|
||||
((ListModel*)this)->requestLogo(pack.logoName, pack.logoUrl);
|
||||
return icon;
|
||||
}
|
||||
else if(role == Qt::UserRole)
|
||||
{
|
||||
} else if (role == Qt::UserRole) {
|
||||
QVariant v;
|
||||
v.setValue(pack);
|
||||
return v;
|
||||
@ -92,16 +81,15 @@ int Technic::ListModel::rowCount(const QModelIndex& parent) const
|
||||
|
||||
void Technic::ListModel::searchWithTerm(const QString& term)
|
||||
{
|
||||
if(currentSearchTerm == term && currentSearchTerm.isNull() == term.isNull()) {
|
||||
if (currentSearchTerm == term && currentSearchTerm.isNull() == term.isNull()) {
|
||||
return;
|
||||
}
|
||||
currentSearchTerm = term;
|
||||
if(jobPtr) {
|
||||
if (jobPtr) {
|
||||
jobPtr->abort();
|
||||
searchState = ResetRequested;
|
||||
return;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
beginResetModel();
|
||||
modpacks.clear();
|
||||
endResetModel();
|
||||
@ -115,26 +103,20 @@ void Technic::ListModel::performSearch()
|
||||
auto netJob = makeShared<NetJob>("Technic::Search", APPLICATION->network());
|
||||
QString searchUrl = "";
|
||||
if (currentSearchTerm.isEmpty()) {
|
||||
searchUrl = QString("%1trending?build=%2")
|
||||
.arg(BuildConfig.TECHNIC_API_BASE_URL, BuildConfig.TECHNIC_API_BUILD);
|
||||
searchUrl = QString("%1trending?build=%2").arg(BuildConfig.TECHNIC_API_BASE_URL, BuildConfig.TECHNIC_API_BUILD);
|
||||
searchMode = List;
|
||||
}
|
||||
else if (currentSearchTerm.startsWith("http://api.technicpack.net/modpack/")) {
|
||||
searchUrl = QString("https://%1?build=%2")
|
||||
.arg(currentSearchTerm.mid(7), BuildConfig.TECHNIC_API_BUILD);
|
||||
} else if (currentSearchTerm.startsWith("http://api.technicpack.net/modpack/")) {
|
||||
searchUrl = QString("https://%1?build=%2").arg(currentSearchTerm.mid(7), BuildConfig.TECHNIC_API_BUILD);
|
||||
searchMode = Single;
|
||||
}
|
||||
else if (currentSearchTerm.startsWith("https://api.technicpack.net/modpack/")) {
|
||||
} else if (currentSearchTerm.startsWith("https://api.technicpack.net/modpack/")) {
|
||||
searchUrl = QString("%1?build=%2").arg(currentSearchTerm, BuildConfig.TECHNIC_API_BUILD);
|
||||
searchMode = Single;
|
||||
}
|
||||
else {
|
||||
searchUrl = QString(
|
||||
"%1search?build=%2&q=%3"
|
||||
).arg(BuildConfig.TECHNIC_API_BASE_URL, BuildConfig.TECHNIC_API_BUILD, currentSearchTerm);
|
||||
} else {
|
||||
searchUrl =
|
||||
QString("%1search?build=%2&q=%3").arg(BuildConfig.TECHNIC_API_BASE_URL, BuildConfig.TECHNIC_API_BUILD, currentSearchTerm);
|
||||
searchMode = List;
|
||||
}
|
||||
netJob->addNetAction(Net::Download::makeByteArray(QUrl(searchUrl), &response));
|
||||
netJob->addNetAction(Net::Download::makeByteArray(QUrl(searchUrl), response));
|
||||
jobPtr = netJob;
|
||||
jobPtr->start();
|
||||
QObject::connect(netJob.get(), &NetJob::succeeded, this, &ListModel::searchRequestFinished);
|
||||
@ -146,11 +128,11 @@ void Technic::ListModel::searchRequestFinished()
|
||||
jobPtr.reset();
|
||||
|
||||
QJsonParseError parse_error;
|
||||
QJsonDocument doc = QJsonDocument::fromJson(response, &parse_error);
|
||||
if(parse_error.error != QJsonParseError::NoError)
|
||||
{
|
||||
qWarning() << "Error while parsing JSON response from Technic at " << parse_error.offset << " reason: " << parse_error.errorString();
|
||||
qWarning() << response;
|
||||
QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error);
|
||||
if (parse_error.error != QJsonParseError::NoError) {
|
||||
qWarning() << "Error while parsing JSON response from Technic at " << parse_error.offset
|
||||
<< " reason: " << parse_error.errorString();
|
||||
qWarning() << *response;
|
||||
return;
|
||||
}
|
||||
|
||||
@ -161,7 +143,7 @@ void Technic::ListModel::searchRequestFinished()
|
||||
switch (searchMode) {
|
||||
case List: {
|
||||
auto objs = Json::requireArray(root, "modpacks");
|
||||
for (auto technicPack: objs) {
|
||||
for (auto technicPack : objs) {
|
||||
Modpack pack;
|
||||
auto technicPackObject = Json::requireObject(technicPack);
|
||||
pack.name = Json::requireString(technicPackObject, "name");
|
||||
@ -170,11 +152,10 @@ void Technic::ListModel::searchRequestFinished()
|
||||
continue;
|
||||
|
||||
auto rawURL = Json::ensureString(technicPackObject, "iconUrl", "null");
|
||||
if(rawURL == "null") {
|
||||
if (rawURL == "null") {
|
||||
pack.logoUrl = "null";
|
||||
pack.logoName = "null";
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
pack.logoUrl = rawURL;
|
||||
pack.logoName = rawURL.section(QLatin1Char('/'), -1).section(QLatin1Char('.'), 0, 0);
|
||||
}
|
||||
@ -199,8 +180,7 @@ void Technic::ListModel::searchRequestFinished()
|
||||
|
||||
pack.logoUrl = iconUrl;
|
||||
pack.logoName = iconUrl.section(QLatin1Char('/'), -1).section(QLatin1Char('.'), 0, 0);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
pack.logoUrl = "null";
|
||||
pack.logoName = "null";
|
||||
}
|
||||
@ -210,10 +190,8 @@ void Technic::ListModel::searchRequestFinished()
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (const JSONValidationError &err)
|
||||
{
|
||||
qCritical() << "Couldn't parse technic search results:" << err.cause() ;
|
||||
} catch (const JSONValidationError& err) {
|
||||
qCritical() << "Couldn't parse technic search results:" << err.cause();
|
||||
return;
|
||||
}
|
||||
searchState = Finished;
|
||||
@ -229,12 +207,9 @@ void Technic::ListModel::searchRequestFinished()
|
||||
|
||||
void Technic::ListModel::getLogo(const QString& logo, const QString& logoUrl, Technic::LogoCallback callback)
|
||||
{
|
||||
if(m_logoMap.contains(logo))
|
||||
{
|
||||
if (m_logoMap.contains(logo)) {
|
||||
callback(APPLICATION->metacache()->resolveEntry("TechnicPacks", QString("logos/%1").arg(logo))->getFullPath());
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
requestLogo(logo, logoUrl);
|
||||
}
|
||||
}
|
||||
@ -243,30 +218,24 @@ void Technic::ListModel::searchRequestFailed()
|
||||
{
|
||||
jobPtr.reset();
|
||||
|
||||
if(searchState == ResetRequested)
|
||||
{
|
||||
if (searchState == ResetRequested) {
|
||||
beginResetModel();
|
||||
modpacks.clear();
|
||||
endResetModel();
|
||||
|
||||
performSearch();
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
searchState = Finished;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Technic::ListModel::logoLoaded(QString logo, QString out)
|
||||
{
|
||||
m_loadingLogos.removeAll(logo);
|
||||
m_logoMap.insert(logo, QIcon(out));
|
||||
for(int i = 0; i < modpacks.size(); i++)
|
||||
{
|
||||
if(modpacks[i].logoName == logo)
|
||||
{
|
||||
emit dataChanged(createIndex(i, 0), createIndex(i, 0), {Qt::DecorationRole});
|
||||
for (int i = 0; i < modpacks.size(); i++) {
|
||||
if (modpacks[i].logoName == logo) {
|
||||
emit dataChanged(createIndex(i, 0), createIndex(i, 0), { Qt::DecorationRole });
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -279,24 +248,23 @@ void Technic::ListModel::logoFailed(QString logo)
|
||||
|
||||
void Technic::ListModel::requestLogo(QString logo, QString url)
|
||||
{
|
||||
if(m_loadingLogos.contains(logo) || m_failedLogos.contains(logo) || logo == "null")
|
||||
{
|
||||
if (m_loadingLogos.contains(logo) || m_failedLogos.contains(logo) || logo == "null") {
|
||||
return;
|
||||
}
|
||||
|
||||
MetaEntryPtr entry = APPLICATION->metacache()->resolveEntry("TechnicPacks", QString("logos/%1").arg(logo));
|
||||
NetJob *job = new NetJob(QString("Technic Icon Download %1").arg(logo), APPLICATION->network());
|
||||
auto job = new NetJob(QString("Technic Icon Download %1").arg(logo), APPLICATION->network());
|
||||
job->addNetAction(Net::Download::makeCached(QUrl(url), entry));
|
||||
|
||||
auto fullPath = entry->getFullPath();
|
||||
|
||||
QObject::connect(job, &NetJob::succeeded, this, [this, logo, fullPath]
|
||||
{
|
||||
QObject::connect(job, &NetJob::succeeded, this, [this, logo, fullPath, job] {
|
||||
job->deleteLater();
|
||||
logoLoaded(logo, fullPath);
|
||||
});
|
||||
|
||||
QObject::connect(job, &NetJob::failed, this, [this, logo]
|
||||
{
|
||||
QObject::connect(job, &NetJob::failed, this, [this, logo, job] {
|
||||
job->deleteLater();
|
||||
logoFailed(logo);
|
||||
});
|
||||
|
||||
|
@ -44,33 +44,32 @@ namespace Technic {
|
||||
|
||||
typedef std::function<void(QString)> LogoCallback;
|
||||
|
||||
class ListModel : public QAbstractListModel
|
||||
{
|
||||
class ListModel : public QAbstractListModel {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ListModel(QObject *parent);
|
||||
public:
|
||||
ListModel(QObject* parent);
|
||||
virtual ~ListModel();
|
||||
|
||||
virtual QVariant data(const QModelIndex& index, int role) const;
|
||||
virtual int columnCount(const QModelIndex& parent) const;
|
||||
virtual int rowCount(const QModelIndex& parent) const;
|
||||
|
||||
void getLogo(const QString &logo, const QString &logoUrl, LogoCallback callback);
|
||||
void searchWithTerm(const QString & term);
|
||||
void getLogo(const QString& logo, const QString& logoUrl, LogoCallback callback);
|
||||
void searchWithTerm(const QString& term);
|
||||
|
||||
private slots:
|
||||
private slots:
|
||||
void searchRequestFinished();
|
||||
void searchRequestFailed();
|
||||
|
||||
void logoFailed(QString logo);
|
||||
void logoLoaded(QString logo, QString out);
|
||||
|
||||
private:
|
||||
private:
|
||||
void performSearch();
|
||||
void requestLogo(QString logo, QString url);
|
||||
|
||||
private:
|
||||
private:
|
||||
QList<Modpack> modpacks;
|
||||
QStringList m_failedLogos;
|
||||
QStringList m_loadingLogos;
|
||||
@ -78,17 +77,13 @@ private:
|
||||
QMap<QString, LogoCallback> waitingCallbacks;
|
||||
|
||||
QString currentSearchTerm;
|
||||
enum SearchState {
|
||||
None,
|
||||
ResetRequested,
|
||||
Finished
|
||||
} searchState = None;
|
||||
enum SearchState { None, ResetRequested, Finished } searchState = None;
|
||||
enum SearchMode {
|
||||
List,
|
||||
Single,
|
||||
} searchMode = List;
|
||||
NetJob::Ptr jobPtr;
|
||||
QByteArray response;
|
||||
std::shared_ptr<QByteArray> response = std::make_shared<QByteArray>();
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace Technic
|
||||
|
@ -143,7 +143,7 @@ void TechnicPage::suggestCurrent()
|
||||
|
||||
auto netJob = makeShared<NetJob>(QString("Technic::PackMeta(%1)").arg(current.name), APPLICATION->network());
|
||||
QString slug = current.slug;
|
||||
netJob->addNetAction(Net::Download::makeByteArray(QString("%1modpack/%2?build=%3").arg(BuildConfig.TECHNIC_API_BASE_URL, slug, BuildConfig.TECHNIC_API_BUILD), &response));
|
||||
netJob->addNetAction(Net::Download::makeByteArray(QString("%1modpack/%2?build=%3").arg(BuildConfig.TECHNIC_API_BASE_URL, slug, BuildConfig.TECHNIC_API_BUILD), response));
|
||||
QObject::connect(netJob.get(), &NetJob::succeeded, this, [this, slug]
|
||||
{
|
||||
jobPtr.reset();
|
||||
@ -154,7 +154,7 @@ void TechnicPage::suggestCurrent()
|
||||
}
|
||||
|
||||
QJsonParseError parse_error {};
|
||||
QJsonDocument doc = QJsonDocument::fromJson(response, &parse_error);
|
||||
QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error);
|
||||
QJsonObject obj = doc.object();
|
||||
if(parse_error.error != QJsonParseError::NoError)
|
||||
{
|
||||
@ -249,7 +249,7 @@ void TechnicPage::metadataLoaded()
|
||||
|
||||
auto netJob = makeShared<NetJob>(QString("Technic::SolderMeta(%1)").arg(current.name), APPLICATION->network());
|
||||
auto url = QString("%1/modpack/%2").arg(current.url, current.slug);
|
||||
netJob->addNetAction(Net::Download::makeByteArray(QUrl(url), &response));
|
||||
netJob->addNetAction(Net::Download::makeByteArray(QUrl(url), response));
|
||||
|
||||
QObject::connect(netJob.get(), &NetJob::succeeded, this, &TechnicPage::onSolderLoaded);
|
||||
|
||||
@ -291,11 +291,11 @@ void TechnicPage::onSolderLoaded() {
|
||||
|
||||
current.versions.clear();
|
||||
|
||||
QJsonParseError parse_error {};
|
||||
auto doc = QJsonDocument::fromJson(response, &parse_error);
|
||||
QJsonParseError parse_error{};
|
||||
auto doc = QJsonDocument::fromJson(*response, &parse_error);
|
||||
if (parse_error.error != QJsonParseError::NoError) {
|
||||
qWarning() << "Error while parsing JSON response from Solder at " << parse_error.offset << " reason: " << parse_error.errorString();
|
||||
qWarning() << response;
|
||||
qWarning() << *response;
|
||||
fallback();
|
||||
return;
|
||||
}
|
||||
@ -304,8 +304,7 @@ void TechnicPage::onSolderLoaded() {
|
||||
TechnicSolder::Pack pack;
|
||||
try {
|
||||
TechnicSolder::loadPack(pack, obj);
|
||||
}
|
||||
catch (const JSONValidationError& err) {
|
||||
} catch (const JSONValidationError& err) {
|
||||
qCritical() << "Couldn't parse Solder pack metadata:" << err.cause();
|
||||
fallback();
|
||||
return;
|
||||
|
@ -104,5 +104,5 @@ private:
|
||||
QString selectedVersion;
|
||||
|
||||
NetJob::Ptr jobPtr;
|
||||
QByteArray response;
|
||||
std::shared_ptr<QByteArray> response = std::make_shared<QByteArray>();
|
||||
};
|
||||
|
@ -59,7 +59,7 @@ void SetupWizard::pageChanged(int id)
|
||||
{
|
||||
setButtonLayout({QWizard::CustomButton1, QWizard::Stretch, QWizard::BackButton, QWizard::NextButton, QWizard::FinishButton});
|
||||
auto customButton = button(QWizard::CustomButton1);
|
||||
connect(customButton, &QAbstractButton::pressed, [&](){
|
||||
connect(customButton, &QAbstractButton::clicked, [&](){
|
||||
auto basePagePtr = getCurrentBasePage();
|
||||
if(basePagePtr)
|
||||
{
|
||||
|
@ -47,6 +47,8 @@ InfoFrame::InfoFrame(QWidget *parent) :
|
||||
ui->setupUi(this);
|
||||
ui->descriptionLabel->setHidden(true);
|
||||
ui->nameLabel->setHidden(true);
|
||||
ui->licenseLabel->setHidden(true);
|
||||
ui->issueTrackerLabel->setHidden(true);
|
||||
updateHiddenState();
|
||||
}
|
||||
|
||||
@ -88,7 +90,41 @@ void InfoFrame::updateWithMod(Mod const& m)
|
||||
setDescription(m.description());
|
||||
}
|
||||
|
||||
setImage();
|
||||
setImage(m.icon({64,64}));
|
||||
|
||||
auto licenses = m.licenses();
|
||||
QString licenseText = "";
|
||||
if (!licenses.empty()) {
|
||||
for (auto l : licenses) {
|
||||
if (!licenseText.isEmpty()) {
|
||||
licenseText += "\n"; // add newline between licenses
|
||||
}
|
||||
if (!l.name.isEmpty()) {
|
||||
if (l.url.isEmpty()) {
|
||||
licenseText += l.name;
|
||||
} else {
|
||||
licenseText += "<a href=\"" + l.url + "\">" + l.name + "</a>";
|
||||
}
|
||||
} else if (!l.url.isEmpty()) {
|
||||
licenseText += "<a href=\"" + l.url + "\">" + l.url + "</a>";
|
||||
}
|
||||
if (!l.description.isEmpty() && l.description != l.name) {
|
||||
licenseText += " " + l.description;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!licenseText.isEmpty()) {
|
||||
setLicense(tr("License: %1").arg(licenseText));
|
||||
} else {
|
||||
setLicense();
|
||||
}
|
||||
|
||||
QString issueTracker = "";
|
||||
if (!m.issueTracker().isEmpty()) {
|
||||
issueTracker += tr("Report issues to: ");
|
||||
issueTracker += "<a href=\"" + m.issueTracker() + "\">" + m.issueTracker() + "</a>";
|
||||
}
|
||||
setIssueTracker(issueTracker);
|
||||
}
|
||||
|
||||
void InfoFrame::updateWithResource(const Resource& resource)
|
||||
@ -177,16 +213,16 @@ void InfoFrame::clear()
|
||||
setName();
|
||||
setDescription();
|
||||
setImage();
|
||||
setLicense();
|
||||
setIssueTracker();
|
||||
}
|
||||
|
||||
void InfoFrame::updateHiddenState()
|
||||
{
|
||||
if(ui->descriptionLabel->isHidden() && ui->nameLabel->isHidden())
|
||||
{
|
||||
if (ui->descriptionLabel->isHidden() && ui->nameLabel->isHidden() && ui->licenseLabel->isHidden() &&
|
||||
ui->issueTrackerLabel->isHidden()) {
|
||||
setHidden(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
setHidden(false);
|
||||
}
|
||||
}
|
||||
@ -251,6 +287,66 @@ void InfoFrame::setDescription(QString text)
|
||||
ui->descriptionLabel->setText(labeltext);
|
||||
}
|
||||
|
||||
void InfoFrame::setLicense(QString text)
|
||||
{
|
||||
if(text.isEmpty())
|
||||
{
|
||||
ui->licenseLabel->setHidden(true);
|
||||
updateHiddenState();
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
ui->licenseLabel->setHidden(false);
|
||||
updateHiddenState();
|
||||
}
|
||||
ui->licenseLabel->setToolTip("");
|
||||
QString intermediatetext = text.trimmed();
|
||||
bool prev(false);
|
||||
QChar rem('\n');
|
||||
QString finaltext;
|
||||
finaltext.reserve(intermediatetext.size());
|
||||
foreach(const QChar& c, intermediatetext)
|
||||
{
|
||||
if(c == rem && prev){
|
||||
continue;
|
||||
}
|
||||
prev = c == rem;
|
||||
finaltext += c;
|
||||
}
|
||||
QString labeltext;
|
||||
labeltext.reserve(300);
|
||||
if(finaltext.length() > 290)
|
||||
{
|
||||
ui->licenseLabel->setOpenExternalLinks(false);
|
||||
ui->licenseLabel->setTextFormat(Qt::TextFormat::RichText);
|
||||
m_description = text;
|
||||
// This allows injecting HTML here.
|
||||
labeltext.append("<html><body>" + finaltext.left(287) + "<a href=\"#mod_desc\">...</a></body></html>");
|
||||
QObject::connect(ui->licenseLabel, &QLabel::linkActivated, this, &InfoFrame::licenseEllipsisHandler);
|
||||
}
|
||||
else
|
||||
{
|
||||
ui->licenseLabel->setTextFormat(Qt::TextFormat::AutoText);
|
||||
labeltext.append(finaltext);
|
||||
}
|
||||
ui->licenseLabel->setText(labeltext);
|
||||
}
|
||||
|
||||
void InfoFrame::setIssueTracker(QString text)
|
||||
{
|
||||
if(text.isEmpty())
|
||||
{
|
||||
ui->issueTrackerLabel->setHidden(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
ui->issueTrackerLabel->setText(text);
|
||||
ui->issueTrackerLabel->setHidden(false);
|
||||
}
|
||||
updateHiddenState();
|
||||
}
|
||||
|
||||
void InfoFrame::setImage(QPixmap img)
|
||||
{
|
||||
if (img.isNull()) {
|
||||
@ -275,6 +371,20 @@ void InfoFrame::descriptionEllipsisHandler(QString link)
|
||||
}
|
||||
}
|
||||
|
||||
void InfoFrame::licenseEllipsisHandler(QString link)
|
||||
{
|
||||
if(!m_current_box)
|
||||
{
|
||||
m_current_box = CustomMessageBox::selectable(this, "", m_license);
|
||||
connect(m_current_box, &QMessageBox::finished, this, &InfoFrame::boxClosed);
|
||||
m_current_box->show();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_current_box->setText(m_license);
|
||||
}
|
||||
}
|
||||
|
||||
void InfoFrame::boxClosed(int result)
|
||||
{
|
||||
m_current_box = nullptr;
|
||||
|
@ -36,6 +36,8 @@ class InfoFrame : public QFrame {
|
||||
void setName(QString text = {});
|
||||
void setDescription(QString text = {});
|
||||
void setImage(QPixmap img = {});
|
||||
void setLicense(QString text = {});
|
||||
void setIssueTracker(QString text = {});
|
||||
|
||||
void clear();
|
||||
|
||||
@ -48,6 +50,7 @@ class InfoFrame : public QFrame {
|
||||
|
||||
public slots:
|
||||
void descriptionEllipsisHandler(QString link);
|
||||
void licenseEllipsisHandler(QString link);
|
||||
void boxClosed(int result);
|
||||
|
||||
private:
|
||||
@ -56,5 +59,6 @@ class InfoFrame : public QFrame {
|
||||
private:
|
||||
Ui::InfoFrame* ui;
|
||||
QString m_description;
|
||||
QString m_license;
|
||||
class QMessageBox* m_current_box = nullptr;
|
||||
};
|
||||
|
@ -35,25 +35,28 @@
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLabel" name="nameLabel">
|
||||
<item row="0" column="0" rowspan="2">
|
||||
<widget class="QLabel" name="iconLabel">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>64</width>
|
||||
<height>64</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
<property name="textFormat">
|
||||
<enum>Qt::RichText</enum>
|
||||
<property name="scaledContents">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="openExternalLinks">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
|
||||
<property name="margin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@ -82,28 +85,69 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0" rowspan="2">
|
||||
<widget class="QLabel" name="iconLabel">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>64</width>
|
||||
<height>64</height>
|
||||
</size>
|
||||
</property>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLabel" name="nameLabel">
|
||||
<property name="text">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
<property name="scaledContents">
|
||||
<bool>false</bool>
|
||||
<property name="textFormat">
|
||||
<enum>Qt::RichText</enum>
|
||||
</property>
|
||||
<property name="margin">
|
||||
<number>0</number>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="openExternalLinks">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QLabel" name="licenseLabel">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="textFormat">
|
||||
<enum>Qt::RichText</enum>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="openExternalLinks">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QLabel" name="issueTrackerLabel">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="textFormat">
|
||||
<enum>Qt::RichText</enum>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="openExternalLinks">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
@ -46,7 +46,7 @@ void JavaSettingsWidget::setupUi()
|
||||
m_verticalLayout = new QVBoxLayout(this);
|
||||
m_verticalLayout->setObjectName(QStringLiteral("verticalLayout"));
|
||||
|
||||
m_versionWidget = new VersionSelectWidget(this);
|
||||
m_versionWidget = new VersionSelectWidget(true, this);
|
||||
m_verticalLayout->addWidget(m_versionWidget);
|
||||
|
||||
m_horizontalLayout = new QHBoxLayout();
|
||||
|
@ -14,9 +14,6 @@
|
||||
*/
|
||||
|
||||
#include "ModListView.h"
|
||||
|
||||
#include "minecraft/mod/ModFolderModel.h"
|
||||
|
||||
#include <QHeaderView>
|
||||
#include <QMouseEvent>
|
||||
#include <QPainter>
|
||||
@ -65,17 +62,13 @@ void ModListView::setModel ( QAbstractItemModel* model )
|
||||
for(int i = 1; i < head->count(); i++)
|
||||
head->setSectionResizeMode(i, QHeaderView::ResizeToContents);
|
||||
}
|
||||
}
|
||||
|
||||
auto real_model = model;
|
||||
if (auto proxy_model = dynamic_cast<QSortFilterProxyModel*>(model); proxy_model)
|
||||
real_model = proxy_model->sourceModel();
|
||||
|
||||
if (auto mod_model = dynamic_cast<ModFolderModel*>(real_model); mod_model) {
|
||||
connect(mod_model, &ModFolderModel::updateFinished, this, [this, mod_model]{
|
||||
auto mods = mod_model->allMods();
|
||||
// Hide the 'Provider' column if no mod has a defined provider!
|
||||
setColumnHidden(ModFolderModel::Columns::ProviderColumn,
|
||||
std::none_of(mods.constBegin(), mods.constEnd(), [](auto const mod){ return mod->provider().has_value(); }));
|
||||
});
|
||||
void ModListView::setResizeModes(const QList<QHeaderView::ResizeMode> &modes)
|
||||
{
|
||||
auto head = header();
|
||||
for(int i = 0; i < modes.count(); i++) {
|
||||
head->setSectionResizeMode(i, modes[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -14,6 +14,7 @@
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <QHeaderView>
|
||||
#include <QTreeView>
|
||||
|
||||
class ModListView: public QTreeView
|
||||
@ -22,4 +23,5 @@ class ModListView: public QTreeView
|
||||
public:
|
||||
explicit ModListView ( QWidget* parent = 0 );
|
||||
virtual void setModel ( QAbstractItemModel* model );
|
||||
virtual void setResizeModes (const QList<QHeaderView::ResizeMode>& modes);
|
||||
};
|
||||
|
@ -93,6 +93,10 @@ PageContainer::PageContainer(BasePageProvider *pageProvider, QString defaultId,
|
||||
page->listIndex = counter;
|
||||
page->setParentContainer(this);
|
||||
counter++;
|
||||
page->updateExtraInfo = [this](QString id, QString info) {
|
||||
if (m_currentPage && id == m_currentPage->id())
|
||||
m_header->setText(m_currentPage->displayName() + info);
|
||||
};
|
||||
}
|
||||
m_model->setPages(pages);
|
||||
|
||||
|
@ -64,6 +64,17 @@ void ProjectItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem& o
|
||||
font.setBold(true);
|
||||
font.setUnderline(true);
|
||||
}
|
||||
if (index.data(UserDataTypes::INSTALLED).toBool()) {
|
||||
auto hRect = opt.rect;
|
||||
hRect.setX(hRect.x() + 1);
|
||||
hRect.setY(hRect.y() + 1);
|
||||
hRect.setHeight(hRect.height() - 2);
|
||||
hRect.setWidth(hRect.width() - 2);
|
||||
// Set nice font
|
||||
font.setItalic(true);
|
||||
font.setOverline(true);
|
||||
painter->drawRect(hRect);
|
||||
}
|
||||
|
||||
font.setPointSize(font.pointSize() + 2);
|
||||
painter->setFont(font);
|
||||
|
@ -6,7 +6,8 @@
|
||||
enum UserDataTypes {
|
||||
TITLE = 257, // QString
|
||||
DESCRIPTION = 258, // QString
|
||||
SELECTED = 259 // bool
|
||||
SELECTED = 259, // bool
|
||||
INSTALLED = 260 // bool
|
||||
};
|
||||
|
||||
/** This is an item delegate composed of:
|
||||
|
@ -1,7 +1,8 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* PolyMC - Minecraft Launcher
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||
* Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -125,14 +126,9 @@ void VersionListView::paintEvent(QPaintEvent *event)
|
||||
|
||||
QString VersionListView::currentEmptyString() const
|
||||
{
|
||||
if(m_itemCount) {
|
||||
return QString();
|
||||
}
|
||||
switch(m_emptyMode)
|
||||
{
|
||||
default:
|
||||
case VersionListView::Empty:
|
||||
return QString();
|
||||
case VersionListView::String:
|
||||
return m_emptyString;
|
||||
case VersionListView::ErrorString:
|
||||
|
@ -1,15 +1,20 @@
|
||||
#include "VersionSelectWidget.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QEvent>
|
||||
#include <QHeaderView>
|
||||
#include <QKeyEvent>
|
||||
#include <QProgressBar>
|
||||
#include <QVBoxLayout>
|
||||
#include <QHeaderView>
|
||||
|
||||
#include "VersionProxyModel.h"
|
||||
|
||||
#include "ui/dialogs/CustomMessageBox.h"
|
||||
|
||||
VersionSelectWidget::VersionSelectWidget(QWidget* parent)
|
||||
: QWidget(parent)
|
||||
VersionSelectWidget::VersionSelectWidget(QWidget* parent) : VersionSelectWidget(false, parent) {}
|
||||
|
||||
VersionSelectWidget::VersionSelectWidget(bool focusSearch, QWidget* parent)
|
||||
: QWidget(parent), focusSearch(focusSearch)
|
||||
{
|
||||
setObjectName(QStringLiteral("VersionSelectWidget"));
|
||||
verticalLayout = new QVBoxLayout(this);
|
||||
@ -30,6 +35,21 @@ VersionSelectWidget::VersionSelectWidget(QWidget* parent)
|
||||
listView->setModel(m_proxyModel);
|
||||
verticalLayout->addWidget(listView);
|
||||
|
||||
search = new QLineEdit(this);
|
||||
search->setPlaceholderText(tr("Search"));
|
||||
search->setClearButtonEnabled(true);
|
||||
verticalLayout->addWidget(search);
|
||||
connect(search, &QLineEdit::textEdited, [this](const QString& value) {
|
||||
m_proxyModel->setSearch(value);
|
||||
if (!value.isEmpty() || !listView->selectionModel()->hasSelection()) {
|
||||
const QModelIndex first = listView->model()->index(0, 0);
|
||||
listView->selectionModel()->setCurrentIndex(first, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows);
|
||||
listView->scrollToTop();
|
||||
} else
|
||||
listView->scrollTo(listView->selectionModel()->currentIndex(), QAbstractItemView::PositionAtCenter);
|
||||
});
|
||||
search->installEventFilter(this);
|
||||
|
||||
sneakyProgressBar = new QProgressBar(this);
|
||||
sneakyProgressBar->setObjectName(QStringLiteral("sneakyProgressBar"));
|
||||
sneakyProgressBar->setFormat(QStringLiteral("%p%"));
|
||||
@ -72,6 +92,23 @@ void VersionSelectWidget::setResizeOn(int column)
|
||||
listView->header()->setSectionResizeMode(resizeOnColumn, QHeaderView::Stretch);
|
||||
}
|
||||
|
||||
bool VersionSelectWidget::eventFilter(QObject *watched, QEvent *event) {
|
||||
if (watched == search && event->type() == QEvent::KeyPress) {
|
||||
const QKeyEvent* keyEvent = (QKeyEvent*)event;
|
||||
const bool up = keyEvent->key() == Qt::Key_Up;
|
||||
const bool down = keyEvent->key() == Qt::Key_Down;
|
||||
if (up || down) {
|
||||
const QModelIndex index = listView->model()->index(listView->currentIndex().row() + (up ? -1 : 1), 0);
|
||||
if (index.row() >= 0 && index.row() < listView->model()->rowCount()) {
|
||||
listView->selectionModel()->setCurrentIndex(index, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return QObject::eventFilter(watched, event);
|
||||
}
|
||||
|
||||
void VersionSelectWidget::initialize(BaseVersionList *vlist)
|
||||
{
|
||||
m_vlist = vlist;
|
||||
@ -79,6 +116,9 @@ void VersionSelectWidget::initialize(BaseVersionList *vlist)
|
||||
listView->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
|
||||
listView->header()->setSectionResizeMode(resizeOnColumn, QHeaderView::Stretch);
|
||||
|
||||
if (focusSearch)
|
||||
search->setFocus();
|
||||
|
||||
if (!m_vlist->isLoaded())
|
||||
{
|
||||
loadList();
|
||||
|
@ -1,22 +1,43 @@
|
||||
/* Copyright 2013-2021 MultiMC Contributors
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* This file incorporates work covered by the following copyright and
|
||||
* permission notice:
|
||||
*
|
||||
* Copyright 2013-2021 MultiMC Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QWidget>
|
||||
#include <QSortFilterProxyModel>
|
||||
#include <QLineEdit>
|
||||
#include "BaseVersionList.h"
|
||||
#include "VersionListView.h"
|
||||
|
||||
@ -30,7 +51,8 @@ class VersionSelectWidget: public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit VersionSelectWidget(QWidget *parent = 0);
|
||||
explicit VersionSelectWidget(QWidget *parent);
|
||||
explicit VersionSelectWidget(bool focusSearch = false, QWidget *parent = 0);
|
||||
~VersionSelectWidget();
|
||||
|
||||
//! loads the list if needed.
|
||||
@ -52,6 +74,7 @@ public:
|
||||
void setEmptyErrorString(QString emptyErrorString);
|
||||
void setEmptyMode(VersionListView::EmptyMode mode);
|
||||
void setResizeOn(int column);
|
||||
bool eventFilter(QObject* watched, QEvent* event) override;
|
||||
|
||||
signals:
|
||||
void selectedVersionChanged(BaseVersion::Ptr version);
|
||||
@ -75,9 +98,10 @@ private:
|
||||
int resizeOnColumn = 0;
|
||||
Task * loadTask;
|
||||
bool preselectedAlready = false;
|
||||
bool focusSearch;
|
||||
|
||||
private:
|
||||
QVBoxLayout *verticalLayout = nullptr;
|
||||
VersionListView *listView = nullptr;
|
||||
QLineEdit *search;
|
||||
QProgressBar *sneakyProgressBar = nullptr;
|
||||
};
|
||||
|
Reference in New Issue
Block a user