feat: add widget for a text browser with image support

Signed-off-by: flow <flowlnlnln@gmail.com>
This commit is contained in:
flow 2022-08-02 15:17:54 -03:00
parent 787234a53a
commit ea3be17220
No known key found for this signature in database
GPG Key ID: 8D0F221F0A59F469
5 changed files with 218 additions and 0 deletions

View File

@ -855,6 +855,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

View File

@ -0,0 +1,17 @@
#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);
}

View File

@ -0,0 +1,24 @@
#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);
private:
shared_qobject_ptr<VariableSizedImageObject> m_image_text_object;
};

View File

@ -0,0 +1,118 @@
// 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::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.append(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;
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.removeOne(source_url);
});
connect(job, &NetJob::finished, job, &NetJob::deleteLater);
job->start();
}

View File

@ -0,0 +1,55 @@
// 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 : 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; }
protected:
/** 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);
QString m_meta_entry;
QList<QUrl> m_fetching_images;
};