Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement "jumping" to other mods inside the mod browser #367

Merged
merged 14 commits into from Nov 10, 2022
19 changes: 16 additions & 3 deletions launcher/ui/dialogs/ModDownloadDialog.cpp
Expand Up @@ -2,6 +2,7 @@
/*
* PolyMC - Minecraft Launcher
TheKodeToad marked this conversation as resolved.
Show resolved Hide resolved
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
* Copyright (C) 2022 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
Expand Down Expand Up @@ -131,6 +132,8 @@ QList<BasePage*> ModDownloadDialog::getPages()
if (APPLICATION->capabilities() & Application::SupportsFlame)
pages.append(FlameModPage::create(this, m_instance));

m_selected_page = dynamic_cast<ModPage*>(pages[0]);

return pages;
}

Expand Down Expand Up @@ -178,12 +181,22 @@ void ModDownloadDialog::selectedPageChanged(BasePage* previous, BasePage* select
return;
}

auto* selected_page = dynamic_cast<ModPage*>(selected);
if (!selected_page) {
m_selected_page = dynamic_cast<ModPage*>(selected);
if (!m_selected_page) {
qCritical() << "Page '" << selected->displayName() << "' in ModDownloadDialog is not a ModPage!";
return;
}

// Same effect as having a global search bar
selected_page->setSearchTerm(prev_page->getSearchTerm());
m_selected_page->setSearchTerm(prev_page->getSearchTerm());
}

bool ModDownloadDialog::selectPage(QString pageId)
{
return m_container->selectPage(pageId);
}

ModPage* ModDownloadDialog::getSelectedPage()
{
return m_selected_page;
}
TheKodeToad marked this conversation as resolved.
Show resolved Hide resolved
7 changes: 7 additions & 0 deletions launcher/ui/dialogs/ModDownloadDialog.h
Expand Up @@ -2,6 +2,7 @@
/*
* PolyMC - Minecraft Launcher
TheKodeToad marked this conversation as resolved.
Show resolved Hide resolved
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
* Copyright (C) 2022 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
Expand All @@ -24,6 +25,7 @@
#include "ModDownloadTask.h"
#include "minecraft/mod/ModFolderModel.h"
#include "ui/pages/BasePageProvider.h"
#include "ui/pages/modplatform/ModPage.h"
TheKodeToad marked this conversation as resolved.
Show resolved Hide resolved

namespace Ui
{
Expand Down Expand Up @@ -53,6 +55,10 @@ class ModDownloadDialog final : public QDialog, public BasePageProvider
const QList<ModDownloadTask*> getTasks();
const std::shared_ptr<ModFolderModel> &mods;

bool selectPage(QString pageId);

ModPage* getSelectedPage();

public slots:
void confirm();
void accept() override;
Expand All @@ -66,6 +72,7 @@ private slots:
PageContainer * m_container = nullptr;
QDialogButtonBox * m_buttons = nullptr;
QVBoxLayout *m_verticalLayout = nullptr;
ModPage *m_selected_page = nullptr;
TheKodeToad marked this conversation as resolved.
Show resolved Hide resolved

QHash<QString, ModDownloadTask*> modTask;
BaseInstance *m_instance;
Expand Down
82 changes: 76 additions & 6 deletions launcher/ui/pages/modplatform/ModPage.cpp
Expand Up @@ -2,6 +2,7 @@
/*
* PolyMC - Minecraft Launcher
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
* Copyright (C) 2022 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
Expand Down Expand Up @@ -37,6 +38,7 @@
#include "Application.h"
#include "ui_ModPage.h"

#include <QDesktopServices>
#include <QKeyEvent>
#include <memory>

Expand Down Expand Up @@ -80,6 +82,8 @@ ModPage::ModPage(ModDownloadDialog* dialog, BaseInstance* instance, ModAPI* api)

ui->packView->setItemDelegate(new ProjectItemDelegate(this));
ui->packView->installEventFilter(this);

connect(ui->packDescription, &QTextBrowser::anchorClicked, this, &ModPage::openUrl);
}

ModPage::~ModPage()
Expand Down Expand Up @@ -158,8 +162,8 @@ void ModPage::triggerSearch()
{
auto changed = m_filter_widget->changed();
m_filter = m_filter_widget->getFilter();
if(changed){

if (changed) {
ui->packView->clearSelection();
ui->packDescription->clear();
ui->versionSelectionBox->clear();
Expand Down Expand Up @@ -241,6 +245,73 @@ void ModPage::onModSelected()
ui->packView->adjustSize();
}

void ModPage::openUrl(const QUrl& url)
{
// do not allow other url schemes for security reasons
if (!(url.scheme() == "http" || url.scheme() == "https")) {
qWarning() << "Unsupported scheme" << url.scheme();
return;
}

// detect mod URLs and search instead
int prefixLength;
const char* page;

if ((url.host() == "modrinth.com" || url.host() == "www.modrinth.com")
&& url.path().startsWith("/mod/")) {
prefixLength = 5;
page = "modrinth";
} else if (APPLICATION->capabilities() & Application::SupportsFlame
&& (url.host() == "curseforge.com" || url.host() == "www.curseforge.com")
&& url.path().toLower().startsWith("/minecraft/mc-mods/")) {
prefixLength = 19;
page = "curseforge";
}
else
prefixLength = 0;

if (prefixLength != 0) {
QString slug = url.path().mid(prefixLength);

// remove trailing slash(es)
while (slug.endsWith('/'))
slug.remove(slug.length() - 1, 1);

// ensure that the path doesn't contain any further slashes,
// and the user isn't opening the same mod; they probably
// intended to view in their web browser
if (!slug.isEmpty() && !slug.contains('/') && slug != current.slug) {
dialog->selectPage(page);

ModPage* newPage = dialog->getSelectedPage();
newPage->ui->searchEdit->setText(slug);
newPage->triggerSearch();

ModPlatform::ListModel* model = newPage->listModel;
QListView* view = newPage->ui->packView;

connect(model->activeJob(), &Task::finished, [url, slug, model, view] {
for (int row = 0; row < model->rowCount({}); row++) {
QModelIndex index = model->index(row);
ModPlatform::IndexedPack pack = model->data(index, Qt::UserRole).value<ModPlatform::IndexedPack>();
if (pack.slug == slug) {
view->setCurrentIndex(index);
return;
}
}

// The final fallback.
QDesktopServices::openUrl(url);
});

return;
}
}

// open in the user's web browser
QDesktopServices::openUrl(url);
}


/******** Make changes to the UI ********/

Expand Down Expand Up @@ -270,8 +341,8 @@ void ModPage::updateModVersions(int prev_count)
if ((valid || m_filter->versions.empty()) && !optedOut(version))
ui->versionSelectionBox->addItem(version.version, QVariant(i));
}
if (ui->versionSelectionBox->count() == 0 && prev_count != 0) {
ui->versionSelectionBox->addItem(tr("No valid version found!"), QVariant(-1));
if (ui->versionSelectionBox->count() == 0 && prev_count != 0) {
ui->versionSelectionBox->addItem(tr("No valid version found!"), QVariant(-1));
ui->modSelectionButton->setText(tr("Cannot select invalid version :("));
}

Expand Down Expand Up @@ -317,8 +388,7 @@ void ModPage::updateUi()
text += "<br>" + tr(" by ") + authorStrs.join(", ");
}


if(current.extraDataLoaded) {
if (current.extraDataLoaded) {
if (!current.extraData.donate.isEmpty()) {
text += "<br><br>" + tr("Donate information: ");
auto donateToStr = [](ModPlatform::DonationData& donate) -> QString {
Expand Down
1 change: 1 addition & 0 deletions launcher/ui/pages/modplatform/ModPage.h
Expand Up @@ -82,6 +82,7 @@ class ModPage : public QWidget, public BasePage {
void onSelectionChanged(QModelIndex first, QModelIndex second);
void onVersionSelectionChanged(QString data);
void onModSelected();
virtual void openUrl(const QUrl& url);

protected:
Ui::ModPage* ui = nullptr;
Expand Down
4 changes: 2 additions & 2 deletions launcher/ui/pages/modplatform/ModPage.ui
Expand Up @@ -16,10 +16,10 @@
<item row="1" column="2">
<widget class="ProjectDescriptionPage" name="packDescription">
<property name="openExternalLinks">
<bool>true</bool>
<bool>false</bool>
</property>
<property name="openLinks">
<bool>true</bool>
<bool>false</bool>
</property>
</widget>
</item>
Expand Down
20 changes: 18 additions & 2 deletions launcher/ui/pages/modplatform/flame/FlameModPage.cpp
Expand Up @@ -2,6 +2,7 @@
/*
* PolyMC - Minecraft Launcher
TheKodeToad marked this conversation as resolved.
Show resolved Hide resolved
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
* Copyright (C) 2022 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
Expand Down Expand Up @@ -39,7 +40,7 @@
#include "FlameModModel.h"
#include "ui/dialogs/ModDownloadDialog.h"

FlameModPage::FlameModPage(ModDownloadDialog* dialog, BaseInstance* instance)
FlameModPage::FlameModPage(ModDownloadDialog* dialog, BaseInstance* instance)
: ModPage(dialog, instance, new FlameAPI())
{
listModel = new FlameMod::ListModel(this);
Expand All @@ -53,7 +54,7 @@ FlameModPage::FlameModPage(ModDownloadDialog* dialog, BaseInstance* instance)
ui->sortByBox->addItem(tr("Sort by Author"));
ui->sortByBox->addItem(tr("Sort by Downloads"));

// sometimes Qt just ignores virtual slots and doesn't work as intended it seems,
// sometimes Qt just ignores virtual slots and doesn't work as intended it seems,
// so it's best not to connect them in the parent's contructor...
connect(ui->sortByBox, SIGNAL(currentIndexChanged(int)), this, SLOT(triggerSearch()));
connect(ui->packView->selectionModel(), &QItemSelectionModel::currentChanged, this, &FlameModPage::onSelectionChanged);
Expand All @@ -78,3 +79,18 @@ bool FlameModPage::optedOut(ModPlatform::IndexedVersion& ver) const
// other mod providers start loading before being selected, at least with
// my Qt, so we need to implement this in every derived class...
auto FlameModPage::shouldDisplay() const -> bool { return true; }

void FlameModPage::openUrl(const QUrl& url)
{
if (url.scheme().isEmpty()) {
QString query = url.query(QUrl::FullyDecoded);
if (query.startsWith("remoteUrl=")) {
// attempt to resolve url from warning page
query.remove(0, 10);
ModPage::openUrl({QUrl::fromPercentEncoding(query.toUtf8())}); // double decoding is necessary
return;
}
}

ModPage::openUrl(url);
}
TheKodeToad marked this conversation as resolved.
Show resolved Hide resolved
3 changes: 3 additions & 0 deletions launcher/ui/pages/modplatform/flame/FlameModPage.h
Expand Up @@ -2,6 +2,7 @@
/*
* PolyMC - Minecraft Launcher
TheKodeToad marked this conversation as resolved.
Show resolved Hide resolved
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
* Copyright (C) 2022 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
Expand Down Expand Up @@ -64,4 +65,6 @@ class FlameModPage : public ModPage {
bool optedOut(ModPlatform::IndexedVersion& ver) const override;

auto shouldDisplay() const -> bool override;

void openUrl(const QUrl& url) override;
};
2 changes: 1 addition & 1 deletion launcher/ui/pages/modplatform/modrinth/ModrinthModPage.cpp
Expand Up @@ -53,7 +53,7 @@ ModrinthModPage::ModrinthModPage(ModDownloadDialog* dialog, BaseInstance* instan
ui->sortByBox->addItem(tr("Sort by Last Updated"));
ui->sortByBox->addItem(tr("Sort by Newest"));

// sometimes Qt just ignores virtual slots and doesn't work as intended it seems,
// sometimes Qt just ignores virtual slots and doesn't work as intended it seems,
// so it's best not to connect them in the parent's constructor...
connect(ui->sortByBox, SIGNAL(currentIndexChanged(int)), this, SLOT(triggerSearch()));
connect(ui->packView->selectionModel(), &QItemSelectionModel::currentChanged, this, &ModrinthModPage::onSelectionChanged);
Expand Down