aboutsummaryrefslogtreecommitdiff
path: root/launcher/ui
diff options
context:
space:
mode:
Diffstat (limited to 'launcher/ui')
-rw-r--r--launcher/ui/MainWindow.cpp142
-rw-r--r--launcher/ui/MainWindow.h14
-rw-r--r--launcher/ui/dialogs/AboutDialog.cpp24
-rw-r--r--launcher/ui/dialogs/NewInstanceDialog.cpp8
-rw-r--r--launcher/ui/dialogs/NewsDialog.cpp4
-rw-r--r--launcher/ui/dialogs/NewsDialog.ui9
-rw-r--r--launcher/ui/dialogs/ProgressDialog.cpp5
-rw-r--r--launcher/ui/dialogs/UpdateDialog.cpp8
-rw-r--r--launcher/ui/pages/instance/ExternalResourcesPage.cpp4
-rw-r--r--launcher/ui/pages/instance/ModFolderPage.cpp4
-rw-r--r--launcher/ui/pages/modplatform/ModPage.cpp1
-rw-r--r--launcher/ui/pages/modplatform/ModPage.ui9
-rw-r--r--launcher/ui/pages/modplatform/flame/FlameModPage.cpp2
-rw-r--r--launcher/ui/pages/modplatform/ftb/FtbListModel.cpp11
-rw-r--r--launcher/ui/pages/modplatform/ftb/FtbListModel.h6
-rw-r--r--launcher/ui/pages/modplatform/ftb/FtbPage.cpp10
-rw-r--r--launcher/ui/pages/modplatform/ftb/FtbPage.h1
-rw-r--r--launcher/ui/pages/modplatform/ftb/FtbPage.ui9
-rw-r--r--launcher/ui/pages/modplatform/modrinth/ModrinthModPage.cpp2
-rw-r--r--launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp4
-rw-r--r--launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp2
-rw-r--r--launcher/ui/pages/modplatform/modrinth/ModrinthPage.ui9
-rw-r--r--launcher/ui/widgets/ProjectDescriptionPage.cpp23
-rw-r--r--launcher/ui/widgets/ProjectDescriptionPage.h32
-rw-r--r--launcher/ui/widgets/VariableSizedImageObject.cpp127
-rw-r--r--launcher/ui/widgets/VariableSizedImageObject.h64
26 files changed, 378 insertions, 156 deletions
diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp
index 51de1c48..0fab0202 100644
--- a/launcher/ui/MainWindow.cpp
+++ b/launcher/ui/MainWindow.cpp
@@ -229,19 +229,13 @@ public:
TranslatedAction actionRenameInstance;
TranslatedAction actionChangeInstGroup;
TranslatedAction actionChangeInstIcon;
- TranslatedAction actionEditInstNotes;
TranslatedAction actionEditInstance;
- TranslatedAction actionWorlds;
- TranslatedAction actionMods;
TranslatedAction actionViewSelectedInstFolder;
- TranslatedAction actionViewSelectedMCFolder;
TranslatedAction actionDeleteInstance;
- TranslatedAction actionConfig_Folder;
TranslatedAction actionCAT;
TranslatedAction actionCopyInstance;
TranslatedAction actionLaunchInstanceOffline;
TranslatedAction actionLaunchInstanceDemo;
- TranslatedAction actionScreenshots;
TranslatedAction actionExportInstance;
QVector<TranslatedAction *> all_actions;
@@ -258,6 +252,7 @@ public:
QMenu * helpMenu = nullptr;
TranslatedToolButton helpMenuButton;
+ TranslatedAction actionClearMetadata;
TranslatedAction actionReportBug;
TranslatedAction actionDISCORD;
TranslatedAction actionMATRIX;
@@ -347,6 +342,13 @@ public:
actionUndoTrashInstance->setShortcut(QKeySequence("Ctrl+Z"));
all_actions.append(&actionUndoTrashInstance);
+ actionClearMetadata = TranslatedAction(MainWindow);
+ actionClearMetadata->setObjectName(QStringLiteral("actionClearMetadata"));
+ actionClearMetadata->setIcon(APPLICATION->getThemedIcon("refresh"));
+ actionClearMetadata.setTextId(QT_TRANSLATE_NOOP("MainWindow", "&Clear Metadata Cache"));
+ actionClearMetadata.setTooltipId(QT_TRANSLATE_NOOP("MainWindow", "Clear cached metadata"));
+ all_actions.append(&actionClearMetadata);
+
if (!BuildConfig.BUG_TRACKER_URL.isEmpty()) {
actionReportBug = TranslatedAction(MainWindow);
actionReportBug->setObjectName(QStringLiteral("actionReportBug"));
@@ -445,6 +447,8 @@ public:
helpMenu = new QMenu(MainWindow);
helpMenu->setToolTipsVisible(true);
+ helpMenu->addAction(actionClearMetadata);
+
if (!BuildConfig.BUG_TRACKER_URL.isEmpty()) {
helpMenu->addAction(actionReportBug);
}
@@ -505,16 +509,8 @@ public:
fileMenu->addAction(actionCloseWindow);
fileMenu->addSeparator();
fileMenu->addAction(actionEditInstance);
- fileMenu->addAction(actionEditInstNotes);
- fileMenu->addAction(actionMods);
- fileMenu->addAction(actionWorlds);
- fileMenu->addAction(actionScreenshots);
fileMenu->addAction(actionChangeInstGroup);
- fileMenu->addSeparator();
- fileMenu->addAction(actionViewSelectedMCFolder);
- fileMenu->addAction(actionConfig_Folder);
fileMenu->addAction(actionViewSelectedInstFolder);
- fileMenu->addSeparator();
fileMenu->addAction(actionExportInstance);
fileMenu->addAction(actionDeleteInstance);
fileMenu->addAction(actionCopyInstance);
@@ -537,6 +533,8 @@ public:
helpMenu = menuBar->addMenu(tr("&Help"));
helpMenu->setSeparatorsCollapsible(false);
+ helpMenu->addAction(actionClearMetadata);
+ helpMenu->addSeparator();
helpMenu->addAction(actionAbout);
helpMenu->addAction(actionOpenWiki);
helpMenu->addAction(actionNewsMenuBar);
@@ -586,13 +584,7 @@ public:
void setInstanceActionsEnabled(bool enabled)
{
actionEditInstance->setEnabled(enabled);
- actionEditInstNotes->setEnabled(enabled);
- actionMods->setEnabled(enabled);
- actionWorlds->setEnabled(enabled);
- actionScreenshots->setEnabled(enabled);
actionChangeInstGroup->setEnabled(enabled);
- actionViewSelectedMCFolder->setEnabled(enabled);
- actionConfig_Folder->setEnabled(enabled);
actionViewSelectedInstFolder->setEnabled(enabled);
actionExportInstance->setEnabled(enabled);
actionDeleteInstance->setEnabled(enabled);
@@ -687,35 +679,11 @@ public:
actionEditInstance = TranslatedAction(MainWindow);
actionEditInstance->setObjectName(QStringLiteral("actionEditInstance"));
- actionEditInstance.setTextId(QT_TRANSLATE_NOOP("MainWindow", "Edit Inst&ance..."));
+ actionEditInstance.setTextId(QT_TRANSLATE_NOOP("MainWindow", "&Edit..."));
actionEditInstance.setTooltipId(QT_TRANSLATE_NOOP("MainWindow", "Change the instance settings, mods and versions."));
actionEditInstance->setShortcut(QKeySequence(tr("Ctrl+I")));
all_actions.append(&actionEditInstance);
- actionEditInstNotes = TranslatedAction(MainWindow);
- actionEditInstNotes->setObjectName(QStringLiteral("actionEditInstNotes"));
- actionEditInstNotes.setTextId(QT_TRANSLATE_NOOP("MainWindow", "E&dit Notes..."));
- actionEditInstNotes.setTooltipId(QT_TRANSLATE_NOOP("MainWindow", "Edit the notes for the selected instance."));
- all_actions.append(&actionEditInstNotes);
-
- actionMods = TranslatedAction(MainWindow);
- actionMods->setObjectName(QStringLiteral("actionMods"));
- actionMods.setTextId(QT_TRANSLATE_NOOP("MainWindow", "View &Mods"));
- actionMods.setTooltipId(QT_TRANSLATE_NOOP("MainWindow", "View the mods of this instance."));
- all_actions.append(&actionMods);
-
- actionWorlds = TranslatedAction(MainWindow);
- actionWorlds->setObjectName(QStringLiteral("actionWorlds"));
- actionWorlds.setTextId(QT_TRANSLATE_NOOP("MainWindow", "&View Worlds"));
- actionWorlds.setTooltipId(QT_TRANSLATE_NOOP("MainWindow", "View the worlds of this instance."));
- all_actions.append(&actionWorlds);
-
- actionScreenshots = TranslatedAction(MainWindow);
- actionScreenshots->setObjectName(QStringLiteral("actionScreenshots"));
- actionScreenshots.setTextId(QT_TRANSLATE_NOOP("MainWindow", "Manage &Screenshots"));
- actionScreenshots.setTooltipId(QT_TRANSLATE_NOOP("MainWindow", "View and upload screenshots for this instance."));
- all_actions.append(&actionScreenshots);
-
actionChangeInstGroup = TranslatedAction(MainWindow);
actionChangeInstGroup->setObjectName(QStringLiteral("actionChangeInstGroup"));
actionChangeInstGroup.setTextId(QT_TRANSLATE_NOOP("MainWindow", "&Change Group..."));
@@ -723,38 +691,22 @@ public:
actionChangeInstGroup->setShortcut(QKeySequence(tr("Ctrl+G")));
all_actions.append(&actionChangeInstGroup);
- actionViewSelectedMCFolder = TranslatedAction(MainWindow);
- actionViewSelectedMCFolder->setObjectName(QStringLiteral("actionViewSelectedMCFolder"));
- actionViewSelectedMCFolder.setTextId(QT_TRANSLATE_NOOP("MainWindow", "Minec&raft Folder"));
- actionViewSelectedMCFolder.setTooltipId(QT_TRANSLATE_NOOP("MainWindow", "Open the selected instance's Minecraft folder in a file browser."));
- actionViewSelectedMCFolder->setShortcut(QKeySequence(tr("Ctrl+M")));
- all_actions.append(&actionViewSelectedMCFolder);
-
- actionConfig_Folder = TranslatedAction(MainWindow);
- actionConfig_Folder->setObjectName(QStringLiteral("actionConfig_Folder"));
- actionConfig_Folder.setTextId(QT_TRANSLATE_NOOP("MainWindow", "Confi&g Folder"));
- actionConfig_Folder.setTooltipId(QT_TRANSLATE_NOOP("MainWindow", "Open the instance's config folder."));
- // Qt on macOS is "smart" and will eat up this action when added to the menu bar because it starts with the word "config"...
- // Docs: https://doc.qt.io/qt-5/qmenubar.html#qmenubar-as-a-global-menu-bar
- actionConfig_Folder->setMenuRole(QAction::NoRole);
- all_actions.append(&actionConfig_Folder);
-
actionViewSelectedInstFolder = TranslatedAction(MainWindow);
actionViewSelectedInstFolder->setObjectName(QStringLiteral("actionViewSelectedInstFolder"));
- actionViewSelectedInstFolder.setTextId(QT_TRANSLATE_NOOP("MainWindow", "&Instance Folder"));
+ actionViewSelectedInstFolder.setTextId(QT_TRANSLATE_NOOP("MainWindow", "&Folder"));
actionViewSelectedInstFolder.setTooltipId(QT_TRANSLATE_NOOP("MainWindow", "Open the selected instance's root folder in a file browser."));
all_actions.append(&actionViewSelectedInstFolder);
actionExportInstance = TranslatedAction(MainWindow);
actionExportInstance->setObjectName(QStringLiteral("actionExportInstance"));
- actionExportInstance.setTextId(QT_TRANSLATE_NOOP("MainWindow", "E&xport Instance..."));
+ actionExportInstance.setTextId(QT_TRANSLATE_NOOP("MainWindow", "E&xport..."));
actionExportInstance.setTooltipId(QT_TRANSLATE_NOOP("MainWindow", "Export the selected instance as a zip file."));
actionExportInstance->setShortcut(QKeySequence(tr("Ctrl+E")));
all_actions.append(&actionExportInstance);
actionDeleteInstance = TranslatedAction(MainWindow);
actionDeleteInstance->setObjectName(QStringLiteral("actionDeleteInstance"));
- actionDeleteInstance.setTextId(QT_TRANSLATE_NOOP("MainWindow", "Dele&te Instance"));
+ actionDeleteInstance.setTextId(QT_TRANSLATE_NOOP("MainWindow", "Dele&te"));
actionDeleteInstance.setTooltipId(QT_TRANSLATE_NOOP("MainWindow", "Delete the selected instance."));
actionDeleteInstance->setShortcuts({QKeySequence(tr("Backspace")), QKeySequence::Delete});
actionDeleteInstance->setAutoRepeat(false);
@@ -763,7 +715,7 @@ public:
actionCopyInstance = TranslatedAction(MainWindow);
actionCopyInstance->setObjectName(QStringLiteral("actionCopyInstance"));
actionCopyInstance->setIcon(APPLICATION->getThemedIcon("copy"));
- actionCopyInstance.setTextId(QT_TRANSLATE_NOOP("MainWindow", "Cop&y Instance..."));
+ actionCopyInstance.setTextId(QT_TRANSLATE_NOOP("MainWindow", "Cop&y..."));
actionCopyInstance.setTooltipId(QT_TRANSLATE_NOOP("MainWindow", "Copy the selected instance."));
actionCopyInstance->setShortcut(QKeySequence(tr("Ctrl+D")));
all_actions.append(&actionCopyInstance);
@@ -797,20 +749,10 @@ public:
instanceToolBar->addSeparator();
instanceToolBar->addAction(actionEditInstance);
- instanceToolBar->addAction(actionEditInstNotes);
- instanceToolBar->addAction(actionMods);
- instanceToolBar->addAction(actionWorlds);
- instanceToolBar->addAction(actionScreenshots);
instanceToolBar->addAction(actionChangeInstGroup);
- instanceToolBar->addSeparator();
-
- instanceToolBar->addAction(actionViewSelectedMCFolder);
- instanceToolBar->addAction(actionConfig_Folder);
instanceToolBar->addAction(actionViewSelectedInstFolder);
- instanceToolBar->addSeparator();
-
instanceToolBar->addAction(actionExportInstance);
instanceToolBar->addAction(actionDeleteInstance);
instanceToolBar->addAction(actionCopyInstance);
@@ -1890,15 +1832,6 @@ void MainWindow::on_actionViewCentralModsFolder_triggered()
DesktopServices::openDirectory(APPLICATION->settings()->get("CentralModsDir").toString(), true);
}
-void MainWindow::on_actionConfig_Folder_triggered()
-{
- if (m_selectedInstance)
- {
- QString str = m_selectedInstance->instanceConfigFolder();
- DesktopServices::openDirectory(QDir(str).absolutePath());
- }
-}
-
void MainWindow::checkForUpdates()
{
if(BuildConfig.UPDATER_ENABLED)
@@ -1927,36 +1860,16 @@ void MainWindow::globalSettingsClosed()
updateToolsMenu();
updateStatusCenter();
// This needs to be done to prevent UI elements disappearing in the event the config is changed
- // but PolyMC exits abnormally, causing the window state to never be saved:
+ // but Prism Launcher exits abnormally, causing the window state to never be saved:
APPLICATION->settings()->set("MainWindowState", saveState().toBase64());
update();
}
-void MainWindow::on_actionEditInstNotes_triggered()
-{
- APPLICATION->showInstanceWindow(m_selectedInstance, "notes");
-}
-
-void MainWindow::on_actionWorlds_triggered()
-{
- APPLICATION->showInstanceWindow(m_selectedInstance, "worlds");
-}
-
-void MainWindow::on_actionMods_triggered()
-{
- APPLICATION->showInstanceWindow(m_selectedInstance, "mods");
-}
-
void MainWindow::on_actionEditInstance_triggered()
{
APPLICATION->showInstanceWindow(m_selectedInstance);
}
-void MainWindow::on_actionScreenshots_triggered()
-{
- APPLICATION->showInstanceWindow(m_selectedInstance, "screenshots");
-}
-
void MainWindow::on_actionManageAccounts_triggered()
{
APPLICATION->ShowGlobalSettings(this, "accounts");
@@ -1967,6 +1880,11 @@ void MainWindow::on_actionReportBug_triggered()
DesktopServices::openUrl(QUrl(BuildConfig.BUG_TRACKER_URL));
}
+void MainWindow::on_actionClearMetadata_triggered()
+{
+ APPLICATION->metacache()->evictAll();
+}
+
void MainWindow::on_actionOpenWiki_triggered()
{
DesktopServices::openUrl(QUrl(BuildConfig.HELP_URL.arg("")));
@@ -2045,20 +1963,6 @@ void MainWindow::on_actionViewSelectedInstFolder_triggered()
}
}
-void MainWindow::on_actionViewSelectedMCFolder_triggered()
-{
- if (m_selectedInstance)
- {
- QString str = m_selectedInstance->gameRoot();
- if (!FS::ensureFilePathExists(str))
- {
- // TODO: report error
- return;
- }
- DesktopServices::openDirectory(QDir(str).absolutePath());
- }
-}
-
void MainWindow::closeEvent(QCloseEvent *event)
{
// Save the window state and geometry.
diff --git a/launcher/ui/MainWindow.h b/launcher/ui/MainWindow.h
index 8b41bf94..cb8cb4aa 100644
--- a/launcher/ui/MainWindow.h
+++ b/launcher/ui/MainWindow.h
@@ -112,12 +112,8 @@ private slots:
void on_actionViewInstanceFolder_triggered();
- void on_actionConfig_Folder_triggered();
-
void on_actionViewSelectedInstFolder_triggered();
- void on_actionViewSelectedMCFolder_triggered();
-
void refreshInstances();
void on_actionViewCentralModsFolder_triggered();
@@ -130,6 +126,8 @@ private slots:
void on_actionReportBug_triggered();
+ void on_actionClearMetadata_triggered();
+
void on_actionOpenWiki_triggered();
void on_actionMoreNews_triggered();
@@ -159,14 +157,6 @@ private slots:
void on_actionEditInstance_triggered();
- void on_actionEditInstNotes_triggered();
-
- void on_actionMods_triggered();
-
- void on_actionWorlds_triggered();
-
- void on_actionScreenshots_triggered();
-
void taskEnd();
/**
diff --git a/launcher/ui/dialogs/AboutDialog.cpp b/launcher/ui/dialogs/AboutDialog.cpp
index 47f204b3..cecda1df 100644
--- a/launcher/ui/dialogs/AboutDialog.cpp
+++ b/launcher/ui/dialogs/AboutDialog.cpp
@@ -69,7 +69,7 @@ QString getCreditsHtml()
#endif
stream << "<center>\n";
- //: %1 is the name of the launcher, determined at build time, e.g. "PolyMC Developers"
+ //: %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"));
@@ -79,15 +79,15 @@ QString getCreditsHtml()
stream << QString("<p>cozyGalvinism %1</p>\n") .arg(getGitHub("cozyGalvinism"));
stream << "<br />\n";
- //: %1 is the name of the launcher, determined at build time, e.g. "PolyMC Contributors"
+ //: %1 is the name of the launcher, determined at build time, e.g. "Prism Launcher Contributors"
stream << "<h3>" << QObject::tr("%1 Contributors", "About Credits").arg(BuildConfig.LAUNCHER_DISPLAYNAME) << "</h3>\n";
- stream << QString("<p>DioEgizio %1</p>\n") .arg(getGitHub("DioEgizio"));
- stream << QString("<p>flowln %1</p>\n") .arg(getGitHub("flowln"));
- stream << QString("<p>swirl %1</p>\n") .arg(getWebsite("https://swurl.xyz/"));
+ stream << QString("<p>DioEgizio %1</p>\n") .arg(getGitHub("DioEgizio"));
+ stream << QString("<p>flowln %1</p>\n") .arg(getGitHub("flowln"));
+ stream << QString("<p>swirl %1</p>\n") .arg(getWebsite("https://swurl.xyz/"));
stream << "<br />\n";
// TODO: possibly retrieve from git history at build time?
- //: %1 is the name of the launcher, determined at build time, e.g. "PolyMC Developers"
+ //: %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("MultiMC") << "</h3>\n";
stream << "<p>Andrew Okin &lt;<a href='mailto:forkk@forkk.net'>forkk@forkk.net</a>&gt;</p>\n";
stream << QString("<p>Petr Mrázek &lt;<a href='mailto:peterix@gmail.com'>peterix@gmail.com</a>&gt;</p>\n");
@@ -97,12 +97,20 @@ QString getCreditsHtml()
stream << "<br />\n";
stream << "<h3>" << QObject::tr("With thanks to", "About Credits") << "</h3>\n";
+ stream << QString("<p>Boba %1</p>\n") .arg(getWebsite("https://cmdplusv.neocities.org/"));
+ stream << QString("<p>Davi Rafael %1</p>\n") .arg(getWebsite("https://auti.one/"));
+ stream << QString("<p>Fulmine %1</p>\n") .arg(getWebsite("https://www.fulmine.xyz/"));
+ stream << QString("<p>ely %1</p>\n") .arg(getGitHub("elyrodso"));
+ stream << QString("<p>gon sawa %1</p>\n") .arg(getGitHub("gonsawa"));
+ stream << QString("<p>Pankakes</p>\n");
+ stream << QString("<p>tobimori %1</p>\n") .arg(getGitHub("tobimori"));
stream << "<p>Orochimarufan &lt;<a href='mailto:orochimarufan.x3@gmail.com'>orochimarufan.x3@gmail.com</a>&gt;</p>\n";
stream << "<p>TakSuyu &lt;<a href='mailto:taksuyu@gmail.com'>taksuyu@gmail.com</a>&gt;</p>\n";
stream << "<p>Kilobyte &lt;<a href='mailto:stiepen22@gmx.de'>stiepen22@gmx.de</a>&gt;</p>\n";
stream << "<p>Rootbear75 &lt;<a href='https://twitter.com/rootbear75'>@rootbear75</a>&gt;</p>\n";
stream << "<p>Zeker Zhayard &lt;<a href='https://twitter.com/zeker_zhayard'>@Zeker_Zhayard</a>&gt;</p>\n";
- stream << "<p>Everyone else who <a href='https://github.com/PolyMC/PolyMC/graphs/contributors'>contributed</a>!</p>\n";
+ stream << "<p>Everyone who helped establish our branding!</p>\n";
+ stream << "<p>And everyone else who <a href='https://github.com/PrismLauncher/PrismLauncher/graphs/contributors'>contributed</a>!</p>\n";
stream << "<br />\n";
stream << "</center>\n";
@@ -164,7 +172,7 @@ AboutDialog::AboutDialog(QWidget *parent) : QDialog(parent), ui(new Ui::AboutDia
QString urlText("<html><head/><body><p><a href=\"%1\">%1</a></p></body></html>");
ui->urlLabel->setText(urlText.arg(BuildConfig.LAUNCHER_GIT));
- QString copyText("© 2021-2022 %1");
+ QString copyText("© 2022 %1");
ui->copyLabel->setText(copyText.arg(BuildConfig.LAUNCHER_COPYRIGHT));
connect(ui->closeButton, SIGNAL(clicked()), SLOT(close()));
diff --git a/launcher/ui/dialogs/NewInstanceDialog.cpp b/launcher/ui/dialogs/NewInstanceDialog.cpp
index d203795a..df182f09 100644
--- a/launcher/ui/dialogs/NewInstanceDialog.cpp
+++ b/launcher/ui/dialogs/NewInstanceDialog.cpp
@@ -139,6 +139,10 @@ NewInstanceDialog::NewInstanceDialog(const QString & initialGroup, const QString
void NewInstanceDialog::reject()
{
APPLICATION->settings()->set("NewInstanceGeometry", saveGeometry().toBase64());
+
+ // This is just so that the pages get the close() call and can react to it, if needed.
+ m_container->prepareToClose();
+
QDialog::reject();
}
@@ -146,6 +150,10 @@ void NewInstanceDialog::accept()
{
APPLICATION->settings()->set("NewInstanceGeometry", saveGeometry().toBase64());
importIconNow();
+
+ // This is just so that the pages get the close() call and can react to it, if needed.
+ m_container->prepareToClose();
+
QDialog::accept();
}
diff --git a/launcher/ui/dialogs/NewsDialog.cpp b/launcher/ui/dialogs/NewsDialog.cpp
index d3b21627..e1b5dd74 100644
--- a/launcher/ui/dialogs/NewsDialog.cpp
+++ b/launcher/ui/dialogs/NewsDialog.cpp
@@ -20,7 +20,9 @@ NewsDialog::NewsDialog(QList<NewsEntryPtr> entries, QWidget* parent) : QDialog(p
auto article_entry = m_entries.constFind(first_item->text()).value();
ui->articleTitleLabel->setText(QString("<a href='%1'>%2</a>").arg(article_entry->link, first_item->text()));
+
ui->currentArticleContentBrowser->setText(article_entry->content);
+ ui->currentArticleContentBrowser->flush();
}
NewsDialog::~NewsDialog()
@@ -33,7 +35,9 @@ void NewsDialog::selectedArticleChanged(const QString& new_title)
auto const& article_entry = m_entries.constFind(new_title).value();
ui->articleTitleLabel->setText(QString("<a href='%1'>%2</a>").arg(article_entry->link, new_title));
+
ui->currentArticleContentBrowser->setText(article_entry->content);
+ ui->currentArticleContentBrowser->flush();
}
void NewsDialog::toggleArticleList()
diff --git a/launcher/ui/dialogs/NewsDialog.ui b/launcher/ui/dialogs/NewsDialog.ui
index 2aaa08f1..08f35a0b 100644
--- a/launcher/ui/dialogs/NewsDialog.ui
+++ b/launcher/ui/dialogs/NewsDialog.ui
@@ -49,7 +49,7 @@
</widget>
</item>
<item>
- <widget class="QTextBrowser" name="currentArticleContentBrowser">
+ <widget class="ProjectDescriptionPage" name="currentArticleContentBrowser">
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
@@ -91,6 +91,13 @@
</item>
</layout>
</widget>
+ <customwidgets>
+ <customwidget>
+ <class>ProjectDescriptionPage</class>
+ <extends>QTextBrowser</extends>
+ <header>ui/widgets/ProjectDescriptionPage.h</header>
+ </customwidget>
+ </customwidgets>
<resources/>
<connections>
<connection>
diff --git a/launcher/ui/dialogs/ProgressDialog.cpp b/launcher/ui/dialogs/ProgressDialog.cpp
index 258a32e4..68dd4d17 100644
--- a/launcher/ui/dialogs/ProgressDialog.cpp
+++ b/launcher/ui/dialogs/ProgressDialog.cpp
@@ -25,6 +25,7 @@ ProgressDialog::ProgressDialog(QWidget* parent) : QDialog(parent), ui(new Ui::Pr
{
ui->setupUi(this);
this->setWindowFlags(this->windowFlags() & ~Qt::WindowContextHelpButtonHint);
+ setAttribute(Qt::WidgetAttribute::WA_QuitOnClose, true);
setSkipButton(false);
changeProgress(0, 100);
}
@@ -67,7 +68,7 @@ int ProgressDialog::execWithTask(Task* task)
return QDialog::DialogCode::Accepted;
}
- QDialog::DialogCode result;
+ QDialog::DialogCode result {};
if (handleImmediateResult(result)) {
return result;
}
@@ -80,7 +81,7 @@ int ProgressDialog::execWithTask(Task* task)
connect(task, &Task::stepStatus, this, &ProgressDialog::changeStatus);
connect(task, &Task::progress, this, &ProgressDialog::changeProgress);
- connect(task, &Task::aborted, [this] { QDialog::reject(); });
+ connect(task, &Task::aborted, this, &ProgressDialog::hide);
connect(task, &Task::abortStatusChanged, ui->skipButton, &QPushButton::setEnabled);
m_is_multi_step = task->isMultiStep();
diff --git a/launcher/ui/dialogs/UpdateDialog.cpp b/launcher/ui/dialogs/UpdateDialog.cpp
index e0c5a495..9e82531a 100644
--- a/launcher/ui/dialogs/UpdateDialog.cpp
+++ b/launcher/ui/dialogs/UpdateDialog.cpp
@@ -73,12 +73,12 @@ void UpdateDialog::loadChangelog()
QString url;
if(channel == "stable")
{
- url = QString("https://raw.githubusercontent.com/PolyMC/PolyMC/%1/changelog.md").arg(channel);
+ url = QString("https://raw.githubusercontent.com/PrismLauncher/PrismLauncher/%1/changelog.md").arg(channel);
m_changelogType = CHANGELOG_MARKDOWN;
}
else
{
- url = QString("https://api.github.com/repos/PolyMC/PolyMC/compare/%1...%2").arg(BuildConfig.GIT_COMMIT, channel);
+ url = QString("https://api.github.com/repos/PrismLauncher/PrismLauncher/compare/%1...%2").arg(BuildConfig.GIT_COMMIT, channel);
m_changelogType = CHANGELOG_COMMITS;
}
dljob->addNetAction(Net::Download::makeByteArray(QUrl(url), &changelogData));
@@ -93,7 +93,7 @@ QString reprocessMarkdown(QByteArray markdown)
QString output = hoedown.process(markdown);
// HACK: easier than customizing hoedown
- output.replace(QRegularExpression("GH-([0-9]+)"), "<a href=\"https://github.com/PolyMC/PolyMC/issues/\\1\">GH-\\1</a>");
+ output.replace(QRegularExpression("GH-([0-9]+)"), "<a href=\"https://github.com/PrismLauncher/PrismLauncher/issues/\\1\">GH-\\1</a>");
qDebug() << output;
return output;
}
@@ -135,7 +135,7 @@ QString reprocessCommits(QByteArray json)
result += "<tr><td>";
if(issuenr.length())
{
- result += QString("<a href=\"https://github.com/PolyMC/PolyMC/issues/%1\">GH-%2</a>").arg(issuenr, issuenr);
+ result += QString("<a href=\"https://github.com/PrismLauncher/PrismLauncher/issues/%1\">GH-%2</a>").arg(issuenr, issuenr);
}
else if(prefix.length())
{
diff --git a/launcher/ui/pages/instance/ExternalResourcesPage.cpp b/launcher/ui/pages/instance/ExternalResourcesPage.cpp
index f31e8325..b6c873cc 100644
--- a/launcher/ui/pages/instance/ExternalResourcesPage.cpp
+++ b/launcher/ui/pages/instance/ExternalResourcesPage.cpp
@@ -103,10 +103,6 @@ void ExternalResourcesPage::runningStateChanged(bool running)
return;
m_controlsEnabled = !running;
- ui->actionAddItem->setEnabled(m_controlsEnabled);
- ui->actionDisableItem->setEnabled(m_controlsEnabled);
- ui->actionEnableItem->setEnabled(m_controlsEnabled);
- ui->actionRemoveItem->setEnabled(m_controlsEnabled);
}
bool ExternalResourcesPage::shouldDisplay() const
diff --git a/launcher/ui/pages/instance/ModFolderPage.cpp b/launcher/ui/pages/instance/ModFolderPage.cpp
index 28a874c2..f0106066 100644
--- a/launcher/ui/pages/instance/ModFolderPage.cpp
+++ b/launcher/ui/pages/instance/ModFolderPage.cpp
@@ -117,6 +117,10 @@ void ModFolderPage::runningStateChanged(bool running)
ExternalResourcesPage::runningStateChanged(running);
ui->actionDownloadItem->setEnabled(!running);
ui->actionUpdateItem->setEnabled(!running);
+ ui->actionAddItem->setEnabled(!running);
+ ui->actionEnableItem->setEnabled(!running);
+ ui->actionDisableItem->setEnabled(!running);
+ ui->actionRemoveItem->setEnabled(!running);
}
bool ModFolderPage::shouldDisplay() const
diff --git a/launcher/ui/pages/modplatform/ModPage.cpp b/launcher/ui/pages/modplatform/ModPage.cpp
index 2af9a10a..f2c1746f 100644
--- a/launcher/ui/pages/modplatform/ModPage.cpp
+++ b/launcher/ui/pages/modplatform/ModPage.cpp
@@ -352,4 +352,5 @@ void ModPage::updateUi()
HoeDown h;
ui->packDescription->setHtml(text + (current.extraData.body.isEmpty() ? current.description : h.process(current.extraData.body.toUtf8())));
+ ui->packDescription->flush();
}
diff --git a/launcher/ui/pages/modplatform/ModPage.ui b/launcher/ui/pages/modplatform/ModPage.ui
index afcd9bb7..943f02aa 100644
--- a/launcher/ui/pages/modplatform/ModPage.ui
+++ b/launcher/ui/pages/modplatform/ModPage.ui
@@ -14,7 +14,7 @@
<item row="1" column="0" colspan="4">
<layout class="QGridLayout" name="gridLayout_3">
<item row="1" column="2">
- <widget class="QTextBrowser" name="packDescription">
+ <widget class="ProjectDescriptionPage" name="packDescription">
<property name="openExternalLinks">
<bool>true</bool>
</property>
@@ -98,6 +98,13 @@
</item>
</layout>
</widget>
+ <customwidgets>
+ <customwidget>
+ <class>ProjectDescriptionPage</class>
+ <extends>QTextBrowser</extends>
+ <header>ui/widgets/ProjectDescriptionPage.h</header>
+ </customwidget>
+ </customwidgets>
<tabstops>
<tabstop>searchEdit</tabstop>
<tabstop>searchButton</tabstop>
diff --git a/launcher/ui/pages/modplatform/flame/FlameModPage.cpp b/launcher/ui/pages/modplatform/flame/FlameModPage.cpp
index 54a7be04..fd6e32ff 100644
--- a/launcher/ui/pages/modplatform/flame/FlameModPage.cpp
+++ b/launcher/ui/pages/modplatform/flame/FlameModPage.cpp
@@ -59,6 +59,8 @@ FlameModPage::FlameModPage(ModDownloadDialog* dialog, BaseInstance* instance)
connect(ui->packView->selectionModel(), &QItemSelectionModel::currentChanged, this, &FlameModPage::onSelectionChanged);
connect(ui->versionSelectionBox, &QComboBox::currentTextChanged, this, &FlameModPage::onVersionSelectionChanged);
connect(ui->modSelectionButton, &QPushButton::clicked, this, &FlameModPage::onModSelected);
+
+ ui->packDescription->setMetaEntry(metaEntryBase());
}
auto FlameModPage::validateVersion(ModPlatform::IndexedVersion& ver, QString mineVer, ModAPI::ModLoaderTypes loaders) const -> bool
diff --git a/launcher/ui/pages/modplatform/ftb/FtbListModel.cpp b/launcher/ui/pages/modplatform/ftb/FtbListModel.cpp
index ad15b6e6..3a149944 100644
--- a/launcher/ui/pages/modplatform/ftb/FtbListModel.cpp
+++ b/launcher/ui/pages/modplatform/ftb/FtbListModel.cpp
@@ -103,6 +103,8 @@ void ListModel::getLogo(const QString &logo, const QString &logoUrl, LogoCallbac
void ListModel::request()
{
+ m_aborted = false;
+
beginResetModel();
modpacks.clear();
endResetModel();
@@ -117,6 +119,12 @@ void ListModel::request()
QObject::connect(netJob, &NetJob::failed, this, &ListModel::requestFailed);
}
+void ListModel::abortRequest()
+{
+ m_aborted = jobPtr->abort();
+ jobPtr.reset();
+}
+
void ListModel::requestFinished()
{
jobPtr.reset();
@@ -162,6 +170,9 @@ void ListModel::requestPack()
void ListModel::packRequestFinished()
{
+ if (!jobPtr || m_aborted)
+ return;
+
jobPtr.reset();
remainingPacks.removeOne(currentPack);
diff --git a/launcher/ui/pages/modplatform/ftb/FtbListModel.h b/launcher/ui/pages/modplatform/ftb/FtbListModel.h
index 314cb789..d7a120f0 100644
--- a/launcher/ui/pages/modplatform/ftb/FtbListModel.h
+++ b/launcher/ui/pages/modplatform/ftb/FtbListModel.h
@@ -47,9 +47,13 @@ public:
QVariant data(const QModelIndex &index, int role) const override;
void request();
+ void abortRequest();
void getLogo(const QString &logo, const QString &logoUrl, LogoCallback callback);
+ [[nodiscard]] bool isMakingRequest() const { return jobPtr.get(); }
+ [[nodiscard]] bool wasAborted() const { return m_aborted; }
+
private slots:
void requestFinished();
void requestFailed(QString reason);
@@ -65,6 +69,8 @@ private:
void requestLogo(QString file, QString url);
private:
+ bool m_aborted = false;
+
QList<ModpacksCH::Modpack> modpacks;
LogoMap m_logoMap;
diff --git a/launcher/ui/pages/modplatform/ftb/FtbPage.cpp b/launcher/ui/pages/modplatform/ftb/FtbPage.cpp
index 8975d74e..b08f3bc4 100644
--- a/launcher/ui/pages/modplatform/ftb/FtbPage.cpp
+++ b/launcher/ui/pages/modplatform/ftb/FtbPage.cpp
@@ -73,6 +73,8 @@ FtbPage::FtbPage(NewInstanceDialog* dialog, QWidget *parent)
connect(ui->sortByBox, &QComboBox::currentTextChanged, this, &FtbPage::onSortingSelectionChanged);
connect(ui->packView->selectionModel(), &QItemSelectionModel::currentChanged, this, &FtbPage::onSelectionChanged);
connect(ui->versionSelectionBox, &QComboBox::currentTextChanged, this, &FtbPage::onVersionSelectionChanged);
+
+ ui->packDescription->setMetaEntry("FTBPacks");
}
FtbPage::~FtbPage()
@@ -105,7 +107,7 @@ void FtbPage::retranslate()
void FtbPage::openedImpl()
{
- if(!initialised)
+ if(!initialised || listModel->wasAborted())
{
listModel->request();
initialised = true;
@@ -114,6 +116,12 @@ void FtbPage::openedImpl()
suggestCurrent();
}
+void FtbPage::closedImpl()
+{
+ if (listModel->isMakingRequest())
+ listModel->abortRequest();
+}
+
void FtbPage::suggestCurrent()
{
if(!isOpened)
diff --git a/launcher/ui/pages/modplatform/ftb/FtbPage.h b/launcher/ui/pages/modplatform/ftb/FtbPage.h
index 90c8e7fd..631ae7f5 100644
--- a/launcher/ui/pages/modplatform/ftb/FtbPage.h
+++ b/launcher/ui/pages/modplatform/ftb/FtbPage.h
@@ -78,6 +78,7 @@ public:
void retranslate() override;
void openedImpl() override;
+ void closedImpl() override;
bool eventFilter(QObject * watched, QEvent * event) override;
diff --git a/launcher/ui/pages/modplatform/ftb/FtbPage.ui b/launcher/ui/pages/modplatform/ftb/FtbPage.ui
index 850bf091..8de0f4e6 100644
--- a/launcher/ui/pages/modplatform/ftb/FtbPage.ui
+++ b/launcher/ui/pages/modplatform/ftb/FtbPage.ui
@@ -57,7 +57,7 @@
</widget>
</item>
<item row="0" column="1">
- <widget class="QTextBrowser" name="packDescription">
+ <widget class="ProjectDescriptionPage" name="packDescription">
<property name="openExternalLinks">
<bool>true</bool>
</property>
@@ -70,6 +70,13 @@
</item>
</layout>
</widget>
+ <customwidgets>
+ <customwidget>
+ <class>ProjectDescriptionPage</class>
+ <extends>QTextBrowser</extends>
+ <header>ui/widgets/ProjectDescriptionPage.h</header>
+ </customwidget>
+ </customwidgets>
<tabstops>
<tabstop>searchEdit</tabstop>
<tabstop>versionSelectionBox</tabstop>
diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthModPage.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthModPage.cpp
index 5fa00b9b..62e417c8 100644
--- a/launcher/ui/pages/modplatform/modrinth/ModrinthModPage.cpp
+++ b/launcher/ui/pages/modplatform/modrinth/ModrinthModPage.cpp
@@ -59,6 +59,8 @@ ModrinthModPage::ModrinthModPage(ModDownloadDialog* dialog, BaseInstance* instan
connect(ui->packView->selectionModel(), &QItemSelectionModel::currentChanged, this, &ModrinthModPage::onSelectionChanged);
connect(ui->versionSelectionBox, &QComboBox::currentTextChanged, this, &ModrinthModPage::onVersionSelectionChanged);
connect(ui->modSelectionButton, &QPushButton::clicked, this, &ModrinthModPage::onModSelected);
+
+ ui->packDescription->setMetaEntry(metaEntryBase());
}
auto ModrinthModPage::validateVersion(ModPlatform::IndexedVersion& ver, QString mineVer, ModAPI::ModLoaderTypes loaders) const -> bool
diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp
index 43fda9de..e6704eef 100644
--- a/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp
+++ b/launcher/ui/pages/modplatform/modrinth/ModrinthModel.cpp
@@ -218,7 +218,7 @@ void ModpackListModel::getLogo(const QString& logo, const QString& logoUrl, Logo
{
if (m_logoMap.contains(logo)) {
callback(APPLICATION->metacache()
- ->resolveEntry("ModrinthPacks", QString("logos/%1").arg(logo.section(".", 0, 0)))
+ ->resolveEntry(m_parent->metaEntryBase(), QString("logos/%1").arg(logo.section(".", 0, 0)))
->getFullPath());
} else {
requestLogo(logo, logoUrl);
@@ -232,7 +232,7 @@ void ModpackListModel::requestLogo(QString logo, QString url)
}
MetaEntryPtr entry =
- APPLICATION->metacache()->resolveEntry("ModrinthPacks", QString("logos/%1").arg(logo.section(".", 0, 0)));
+ APPLICATION->metacache()->resolveEntry(m_parent->metaEntryBase(), QString("logos/%1").arg(logo.section(".", 0, 0)));
auto job = new NetJob(QString("%1 Icon Download %2").arg(m_parent->debugName()).arg(logo), APPLICATION->network());
job->addNetAction(Net::Download::makeCached(QUrl(url), entry));
diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp
index cea6cdee..4482774c 100644
--- a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp
+++ b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp
@@ -74,6 +74,7 @@ ModrinthPage::ModrinthPage(NewInstanceDialog* dialog, QWidget* parent) : QWidget
connect(ui->versionSelectionBox, &QComboBox::currentTextChanged, this, &ModrinthPage::onVersionSelectionChanged);
ui->packView->setItemDelegate(new ProjectItemDelegate(this));
+ ui->packDescription->setMetaEntry(metaEntryBase());
}
ModrinthPage::~ModrinthPage()
@@ -283,6 +284,7 @@ void ModrinthPage::updateUI()
text += h.process(current.extra.body.toUtf8());
ui->packDescription->setHtml(text + current.description);
+ ui->packDescription->flush();
}
void ModrinthPage::suggestCurrent()
diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.ui b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.ui
index 6a34701d..6d8b2b67 100644
--- a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.ui
+++ b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.ui
@@ -66,7 +66,7 @@
</widget>
</item>
<item>
- <widget class="QTextBrowser" name="packDescription">
+ <widget class="ProjectDescriptionPage" name="packDescription">
<property name="openExternalLinks">
<bool>true</bool>
</property>
@@ -99,6 +99,13 @@
</item>
</layout>
</widget>
+ <customwidgets>
+ <customwidget>
+ <class>ProjectDescriptionPage</class>
+ <extends>QTextBrowser</extends>
+ <header>ui/widgets/ProjectDescriptionPage.h</header>
+ </customwidget>
+ </customwidgets>
<tabstops>
<tabstop>searchEdit</tabstop>
<tabstop>searchButton</tabstop>
diff --git a/launcher/ui/widgets/ProjectDescriptionPage.cpp b/launcher/ui/widgets/ProjectDescriptionPage.cpp
new file mode 100644
index 00000000..c7e79a17
--- /dev/null
+++ b/launcher/ui/widgets/ProjectDescriptionPage.cpp
@@ -0,0 +1,23 @@
+#include "ProjectDescriptionPage.h"
+
+#include "VariableSizedImageObject.h"
+
+#include <QDebug>
+
+ProjectDescriptionPage::ProjectDescriptionPage(QWidget* parent) : QTextBrowser(parent), m_image_text_object(new VariableSizedImageObject)
+{
+ m_image_text_object->setParent(this);
+ document()->documentLayout()->registerHandler(QTextFormat::ImageObject, m_image_text_object.get());
+}
+
+void ProjectDescriptionPage::setMetaEntry(QString entry)
+{
+ if (m_image_text_object)
+ m_image_text_object->setMetaEntry(entry);
+}
+
+void ProjectDescriptionPage::flush()
+{
+ if (m_image_text_object)
+ m_image_text_object->flush();
+}
diff --git a/launcher/ui/widgets/ProjectDescriptionPage.h b/launcher/ui/widgets/ProjectDescriptionPage.h
new file mode 100644
index 00000000..3dd85302
--- /dev/null
+++ b/launcher/ui/widgets/ProjectDescriptionPage.h
@@ -0,0 +1,32 @@
+#pragma once
+
+#include <QTextBrowser>
+
+#include "QObjectPtr.h"
+
+QT_BEGIN_NAMESPACE
+class VariableSizedImageObject;
+QT_END_NAMESPACE
+
+/** This subclasses QTextBrowser to provide additional capabilities
+ * to it, like allowing for images to be shown.
+ */
+class ProjectDescriptionPage final : public QTextBrowser {
+ Q_OBJECT
+
+ public:
+ ProjectDescriptionPage(QWidget* parent = nullptr);
+
+ void setMetaEntry(QString entry);
+
+ public slots:
+ /** Flushes the current processing happening in the page.
+ *
+ * Should be called when changing the page's content entirely, to
+ * prevent old tasks from changing the new content.
+ */
+ void flush();
+
+ private:
+ shared_qobject_ptr<VariableSizedImageObject> m_image_text_object;
+};
diff --git a/launcher/ui/widgets/VariableSizedImageObject.cpp b/launcher/ui/widgets/VariableSizedImageObject.cpp
new file mode 100644
index 00000000..e57f7e95
--- /dev/null
+++ b/launcher/ui/widgets/VariableSizedImageObject.cpp
@@ -0,0 +1,127 @@
+// SPDX-License-Identifier: GPL-3.0-only
+/*
+ * PolyMC - Minecraft Launcher
+ * Copyright (c) 2022 flowln <flowlnlnln@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/>.
+ */
+
+#include "VariableSizedImageObject.h"
+
+#include <QAbstractTextDocumentLayout>
+#include <QDebug>
+#include <QPainter>
+#include <QTextObject>
+
+#include "Application.h"
+
+#include "net/NetJob.h"
+
+enum FormatProperties { ImageData = QTextFormat::UserProperty + 1 };
+
+QSizeF VariableSizedImageObject::intrinsicSize(QTextDocument* doc, int posInDocument, const QTextFormat& format)
+{
+ Q_UNUSED(posInDocument);
+
+ auto image = qvariant_cast<QImage>(format.property(ImageData));
+ auto size = image.size();
+
+ // Get the width of the text content to make the image similar sized.
+ // doc->textWidth() includes the margin, so we need to remove it.
+ auto doc_width = doc->textWidth() - 2 * doc->documentMargin();
+
+ if (size.width() > doc_width)
+ size *= doc_width / (double)size.width();
+
+ return { size };
+}
+void VariableSizedImageObject::drawObject(QPainter* painter,
+ const QRectF& rect,
+ QTextDocument* doc,
+ int posInDocument,
+ const QTextFormat& format)
+{
+ if (!format.hasProperty(ImageData)) {
+ QUrl image_url{ qvariant_cast<QString>(format.property(QTextFormat::ImageName)) };
+ if (m_fetching_images.contains(image_url))
+ return;
+
+ loadImage(doc, image_url, posInDocument);
+ return;
+ }
+
+ auto image = qvariant_cast<QImage>(format.property(ImageData));
+
+ painter->setRenderHint(QPainter::RenderHint::SmoothPixmapTransform);
+ painter->drawImage(rect, image);
+}
+
+void VariableSizedImageObject::flush()
+{
+ m_fetching_images.clear();
+}
+
+void VariableSizedImageObject::parseImage(QTextDocument* doc, QImage image, int posInDocument)
+{
+ QTextCursor cursor(doc);
+ cursor.setPosition(posInDocument);
+ cursor.setKeepPositionOnInsert(true);
+
+ auto image_char_format = cursor.charFormat();
+
+ image_char_format.setObjectType(QTextFormat::ImageObject);
+ image_char_format.setProperty(ImageData, image);
+
+ // Qt doesn't allow us to modify the properties of an existing object in the document.
+ // So we remove the old one and add the new one with the ImageData property set.
+ cursor.deleteChar();
+ cursor.insertText(QString(QChar::ObjectReplacementCharacter), image_char_format);
+}
+
+void VariableSizedImageObject::loadImage(QTextDocument* doc, const QUrl& source, int posInDocument)
+{
+ m_fetching_images.insert(source);
+
+ MetaEntryPtr entry = APPLICATION->metacache()->resolveEntry(
+ m_meta_entry,
+ QString("images/%1").arg(QString(QCryptographicHash::hash(source.toEncoded(), QCryptographicHash::Algorithm::Sha1).toHex())));
+
+ auto job = new NetJob(QString("Load Image: %1").arg(source.fileName()), APPLICATION->network());
+ job->addNetAction(Net::Download::makeCached(source, entry));
+
+ auto full_entry_path = entry->getFullPath();
+ auto source_url = source;
+ connect(job, &NetJob::succeeded, [this, doc, full_entry_path, source_url, posInDocument] {
+ qDebug() << "Loaded resource at" << full_entry_path;
+
+ // If we flushed, don't proceed.
+ if (!m_fetching_images.contains(source_url))
+ return;
+
+ QImage image(full_entry_path);
+ doc->addResource(QTextDocument::ImageResource, source_url, image);
+
+ parseImage(doc, image, posInDocument);
+
+ // This size hack is needed to prevent the content from being laid out in an area smaller
+ // than the total width available (weird).
+ auto size = doc->pageSize();
+ doc->adjustSize();
+ doc->setPageSize(size);
+
+ m_fetching_images.remove(source_url);
+ });
+ connect(job, &NetJob::finished, job, &NetJob::deleteLater);
+
+ job->start();
+}
diff --git a/launcher/ui/widgets/VariableSizedImageObject.h b/launcher/ui/widgets/VariableSizedImageObject.h
new file mode 100644
index 00000000..137487ee
--- /dev/null
+++ b/launcher/ui/widgets/VariableSizedImageObject.h
@@ -0,0 +1,64 @@
+// SPDX-License-Identifier: GPL-3.0-only
+/*
+ * PolyMC - Minecraft Launcher
+ * Copyright (c) 2022 flowln <flowlnlnln@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/>.
+ */
+
+#pragma once
+
+#include <QObject>
+#include <QString>
+#include <QTextObjectInterface>
+#include <QUrl>
+
+/** Custom image text object to be used instead of the normal one in ProjectDescriptionPage.
+ *
+ * Why? Because we want to re-scale images dynamically based on the document's size, in order to
+ * not have images being weirdly cropped out in different resolutions.
+ */
+class VariableSizedImageObject final : public QObject, public QTextObjectInterface {
+ Q_OBJECT
+ Q_INTERFACES(QTextObjectInterface)
+
+ public:
+ QSizeF intrinsicSize(QTextDocument* doc, int posInDocument, const QTextFormat& format) override;
+ void drawObject(QPainter* painter, const QRectF& rect, QTextDocument* doc, int posInDocument, const QTextFormat& format) override;
+
+ void setMetaEntry(QString meta_entry) { m_meta_entry = meta_entry; }
+
+ public slots:
+ /** Stops all currently loading images from modifying the document.
+ *
+ * This does not stop the ongoing network tasks, it only prevents their result
+ * from impacting the document any further.
+ */
+ void flush();
+
+ private:
+ /** Adds the image to the document, in the given position.
+ */
+ void parseImage(QTextDocument* doc, QImage image, int posInDocument);
+
+ /** Loads an image from an external source, and adds it to the document.
+ *
+ * This uses m_meta_entry to cache the image.
+ */
+ void loadImage(QTextDocument* doc, const QUrl& source, int posInDocument);
+
+ private:
+ QString m_meta_entry;
+
+ QSet<QUrl> m_fetching_images;
+};