Merge pull request #31 from flowln/who_needs_webview
This commit is contained in:
commit
4e08f28246
@ -816,6 +816,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
|
||||
m_metacache->addBase("FlamePacks", QDir("cache/FlamePacks").absolutePath());
|
||||
m_metacache->addBase("FlameMods", QDir("cache/FlameMods").absolutePath());
|
||||
m_metacache->addBase("ModrinthPacks", QDir("cache/ModrinthPacks").absolutePath());
|
||||
m_metacache->addBase("ModrinthModpacks", QDir("cache/ModrinthModpacks").absolutePath());
|
||||
m_metacache->addBase("root", QDir::currentPath());
|
||||
m_metacache->addBase("translations", QDir("translations").absolutePath());
|
||||
m_metacache->addBase("icons", QDir("cache/icons").absolutePath());
|
||||
|
@ -854,6 +854,10 @@ SET(LAUNCHER_SOURCES
|
||||
ui/widgets/PageContainer.cpp
|
||||
ui/widgets/PageContainer.h
|
||||
ui/widgets/PageContainer_p.h
|
||||
ui/widgets/ProjectDescriptionPage.h
|
||||
ui/widgets/ProjectDescriptionPage.cpp
|
||||
ui/widgets/VariableSizedImageObject.h
|
||||
ui/widgets/VariableSizedImageObject.cpp
|
||||
ui/widgets/ProjectItem.h
|
||||
ui/widgets/ProjectItem.cpp
|
||||
ui/widgets/VersionListView.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()
|
||||
|
@ -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>
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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>
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
|
@ -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>
|
||||
|
@ -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
|
||||
|
@ -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));
|
||||
|
||||
|
@ -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()
|
||||
|
@ -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>
|
||||
|
23
launcher/ui/widgets/ProjectDescriptionPage.cpp
Normal file
23
launcher/ui/widgets/ProjectDescriptionPage.cpp
Normal file
@ -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();
|
||||
}
|
32
launcher/ui/widgets/ProjectDescriptionPage.h
Normal file
32
launcher/ui/widgets/ProjectDescriptionPage.h
Normal file
@ -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;
|
||||
};
|
127
launcher/ui/widgets/VariableSizedImageObject.cpp
Normal file
127
launcher/ui/widgets/VariableSizedImageObject.cpp
Normal file
@ -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();
|
||||
}
|
64
launcher/ui/widgets/VariableSizedImageObject.h
Normal file
64
launcher/ui/widgets/VariableSizedImageObject.h
Normal file
@ -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;
|
||||
};
|
Loading…
Reference in New Issue
Block a user