Added Cubemap API Hub. Closes #9

This commit is contained in:
Digital Artifex
2025-11-20 03:10:37 -05:00
parent 74a66ca4cc
commit ab6d4d7a75
91 changed files with 1013 additions and 161 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 656 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 694 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 703 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 667 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 672 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 683 KiB

View File

@@ -1,13 +0,0 @@
Author
======
This is the work of Emil Persson, aka Humus.
http://www.humus.name
License
=======
This work is licensed under a Creative Commons Attribution 3.0 Unported License.
http://creativecommons.org/licenses/by/3.0/

Binary file not shown.

Before

Width:  |  Height:  |  Size: 906 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 869 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1013 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 595 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1022 KiB

View File

@@ -1,13 +0,0 @@
Author
======
This is the work of Emil Persson, aka Humus.
http://www.humus.name
License
=======
This work is licensed under a Creative Commons Attribution 3.0 Unported License.
http://creativecommons.org/licenses/by/3.0/

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1015 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 899 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 469 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1002 KiB

View File

@@ -1,13 +0,0 @@
Author
======
This is the work of Emil Persson, aka Humus.
http://www.humus.name
License
=======
This work is licensed under a Creative Commons Attribution 3.0 Unported License.
http://creativecommons.org/licenses/by/3.0/

Binary file not shown.

Before

Width:  |  Height:  |  Size: 810 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 821 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 735 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 812 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 770 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 865 KiB

View File

@@ -1,13 +0,0 @@
Author
======
This is the work of Emil Persson, aka Humus.
http://www.humus.name
License
=======
This work is licensed under a Creative Commons Attribution 3.0 Unported License.
http://creativecommons.org/licenses/by/3.0/

Binary file not shown.

Before

Width:  |  Height:  |  Size: 730 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 992 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 729 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 736 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 491 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 730 KiB

View File

@@ -1,13 +0,0 @@
Author
======
This is the work of Emil Persson, aka Humus.
http://www.humus.name
License
=======
This work is licensed under a Creative Commons Attribution 3.0 Unported License.
http://creativecommons.org/licenses/by/3.0/

Binary file not shown.

Before

Width:  |  Height:  |  Size: 860 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 699 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 834 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 796 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 874 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 913 KiB

View File

@@ -1,13 +0,0 @@
Author
======
This is the work of Emil Persson, aka Humus.
http://www.humus.name
License
=======
This work is licensed under a Creative Commons Attribution 3.0 Unported License.
http://creativecommons.org/licenses/by/3.0/

Binary file not shown.

Before

Width:  |  Height:  |  Size: 708 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 853 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 942 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 705 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 368 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 443 KiB

View File

@@ -1,13 +0,0 @@
Author
======
This is the work of Emil Persson, aka Humus.
http://www.humus.name
License
=======
This work is licensed under a Creative Commons Attribution 3.0 Unported License.
http://creativecommons.org/licenses/by/3.0/

Binary file not shown.

Before

Width:  |  Height:  |  Size: 938 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 997 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1009 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 915 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 537 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 844 KiB

View File

@@ -1,13 +0,0 @@
Author
======
This is the work of Emil Persson, aka Humus.
http://www.humus.name
License
=======
This work is licensed under a Creative Commons Attribution 3.0 Unported License.
http://creativecommons.org/licenses/by/3.0/

Binary file not shown.

Before

Width:  |  Height:  |  Size: 893 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 714 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 902 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 879 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 453 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 812 KiB

View File

@@ -1,13 +0,0 @@
Author
======
This is the work of Emil Persson, aka Humus.
http://www.humus.name
License
=======
This work is licensed under a Creative Commons Attribution 3.0 Unported License.
http://creativecommons.org/licenses/by/3.0/

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1000 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 710 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 876 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 854 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 788 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 900 KiB

View File

@@ -1,13 +0,0 @@
Author
======
This is the work of Emil Persson, aka Humus.
http://www.humus.name
License
=======
This work is licensed under a Creative Commons Attribution 3.0 Unported License.
http://creativecommons.org/licenses/by/3.0/

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 982 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 823 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 684 KiB

View File

@@ -1,13 +0,0 @@
Author
======
This is the work of Emil Persson, aka Humus.
http://www.humus.name
License
=======
This work is licensed under a Creative Commons Attribution 3.0 Unported License.
http://creativecommons.org/licenses/by/3.0/

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1012 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 840 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 946 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 205 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 920 KiB

View File

@@ -1,13 +0,0 @@
Author
======
This is the work of Emil Persson, aka Humus.
http://www.humus.name
License
=======
This work is licensed under a Creative Commons Attribution 3.0 Unported License.
http://creativecommons.org/licenses/by/3.0/

View File

@@ -0,0 +1,390 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Dialogs
import QtQuick.Layouts
import com.github.digitalartifex.komplex as Komplex
Item
{
property alias selectedFile: searchModel.lastSavedFile
id: mainItem
Komplex.CubemapSearchModel
{
id: searchModel
}
ColumnLayout
{
anchors.fill: parent
RowLayout
{
Layout.fillHeight: false
Layout.fillWidth: true
Layout.margins: 6
TextField
{
Layout.preferredHeight: 32
Layout.fillWidth: true
id: searchField
placeholderText: "Search"
onEditingFinished: mainItem.updateSearch()
Keys.onPressed: (event) =>
{
if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter)
{
searchField.focus = false; // Unfocus the TextField
event.accepted = true; // Prevent further propagation of the key event
}
}
}
Button
{
Layout.preferredHeight: 32
Layout.preferredWidth: 32
icon.name: "search-symbolic"
onClicked: mainItem.updateSearch()
}
}
Component
{
id: highlight
Rectangle {
width: view.cellWidth; height: view.cellHeight
color: palette.highlight; radius: 5
x: view.currentItem.x
y: view.currentItem.y
Behavior on x { SpringAnimation { spring: 3; damping: 0.2 } }
Behavior on y { SpringAnimation { spring: 3; damping: 0.2 } }
}
}
Rectangle
{
Layout.fillHeight: true
Layout.fillWidth: true
color: palette.base
clip: true
RowLayout
{
anchors.fill: parent
GridView
{
// The standard size
property int idealCellHeight: 300
property int idealCellWidth: 300
cellWidth: width / Math.floor(width / idealCellWidth)
cellHeight: idealCellHeight
id: view
model: searchModel
highlight: highlight
highlightFollowsCurrentItem: false
Layout.fillHeight: true
Layout.fillWidth: true
Layout.margins: 6
delegate: Column
{
id: entry
leftPadding: Math.floor((width - thumbnailImage.width) / 2)
topPadding: 10
rightPadding: Math.floor((width - thumbnailImage.width) / 2)
bottomPadding: 10
width: view.cellWidth
required property string id
required property string name
required property string description
required property string thumbnail
required property int index
property int itemIndex: index
Image
{
width: 280
height: 200
id: thumbnailImage
source: parent.thumbnail//"https://api.artifex.services/v1/cubemaps/thumbnail/" + parent.id
anchors.horizontalCenter: parent.horizontalCenter
MouseArea
{
z: 9000
anchors.fill: parent
onClicked: (mouse) => {
view.currentIndex = entry.itemIndex
}
}
Rectangle
{
color: palette.base
anchors.fill: parent
visible: thumbnailImage.status === Image.Loading
RowLayout
{
anchors.fill: parent
BusyIndicator
{
Layout.alignment: Qt.AlignCenter
Layout.preferredHeight: 64
Layout.preferredWidth: 64
visible: running
}
}
}
Rectangle
{
color: palette.dark
anchors.fill: parent
visible: thumbnailImage.status === Image.Error
Text
{
color: palette.text
anchors.centerIn: parent
text: qsTr("Error Loading Image")
}
}
}
Text
{
elide: Text.ElideRight
topPadding: 4
bottomPadding: 2
text: "<h3>" + name + "</h3>"
anchors.horizontalCenter: parent.horizontalCenter
width: 280
color: palette.link
font.bold: true
}
Text
{
leftPadding: 8
rightPadding: 8
text: qsTr(description)
anchors.horizontalCenter: parent.horizontalCenter
elide: Text.ElideRight
width: 280
color: palette.text
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
maximumLineCount: 2
font.italic: true
}
RowLayout
{
visible: parent.itemIndex === view.currentIndex
width: 280
Button
{
Layout.topMargin: 4
Layout.alignment: Qt.AlignHCenter
Layout.preferredHeight: 32
Layout.fillWidth: true
text: "Download"
icon.source: "./icons/download.svg"
icon.name: "download-symbolic"
onClicked: {
progressDialog.id = entry.id
progressDialog.description = entry.description
progressDialog.name = entry.name
progressDialog.thumbnail = entry.thumbnail
progressDialog.open()
searchModel.download(entry.id)
}
}
}
}
populate: Transition
{
NumberAnimation { property: "opacity"; from: 0; to: 1; duration: 1000 }
}
}
}
}
RowLayout
{
Layout.margins: 6
Layout.fillWidth: true
Button
{
text: "Previous"
enabled: searchModel.previousPage !== ""
onClicked: searchModel.back()
}
RowLayout
{
Layout.margins: 6
Layout.fillWidth: true
Text
{
visible: searchModel.totalResults > 0
color: palette.text
text: (searchModel.currentOffset + 1) + "-" + (searchModel.resultsPerPage + searchModel.currentOffset) + " of " + searchModel.totalResults
}
Text
{
color: palette.text
Layout.fillWidth: true
text: "Page " + (searchModel.currentOffset / searchModel.resultsPerPage) + " of " + Math.ceil(searchModel.totalResults / searchModel.resultsPerPage)
}
}
Button
{
text: "Next"
enabled: searchModel.nextPage !== ""
onClicked: searchModel.next()
}
}
}
Rectangle
{
color: palette.base
width: mainItem.width
height: mainItem.height
visible: searchModel.status === Komplex.PexelsImageSearchModel.Searching
RowLayout
{
anchors.fill: parent
BusyIndicator
{
Layout.alignment: Qt.AlignCenter
Layout.preferredHeight: 128
Layout.preferredWidth: 128
visible: running
}
}
}
Dialog
{
property string name
property string description
property string id
property string thumbnail
modal: Qt.WindowModal
width: 600
height: 420
anchors.centerIn: parent
clip: true
id: progressDialog
ColumnLayout
{
anchors.fill: parent
Image
{
Layout.fillHeight: true
Layout.fillWidth: true
fillMode: Image.PreserveAspectCrop
source: progressDialog.thumbnail
}
Text
{
text: "Downloading Cubemap..."
color: palette.text
}
ProgressBar
{
value: searchModel.downloadProgress
Layout.fillWidth: true
Layout.preferredHeight: 6
}
}
Connections
{
target: searchModel
function onDownloadFinished()
{
progressDialog.close()
}
}
}
Dialog
{
width: 420
height: 105
id: warningDialog
ColumnLayout
{
Text
{
id: header
text: "Installation Error"
font.pointSize: 14
color: palette.text
}
Text
{
id: informative
text: searchModel.statusMessage
font.pointSize: 10
color: palette.text
}
DialogButtonBox
{
Layout.alignment: Qt.AlignRight
standardButtons: DialogButtonBox.Ok
onAccepted: warningDialog.close()
}
}
Connections
{
target: searchModel
function onStatusChanged()
{
if(searchModel.status === Komplex.CubemapSearchModel.Error)
{
warningDialog.open();
progressDialog.close();
}
}
}
}
function updateSearch()
{
console.log(searchField.text)
searchModel.query = searchField.text
}
}

View File

@@ -31,10 +31,11 @@ add_library(
ShaderToyMetadata.h ShaderToyMetadata.h
ShaderToySearchModel.h ShaderToySearchModel.h
ShaderToySearchModel.cpp ShaderToySearchModel.cpp
ShaderToyAPI.h
PexelsAPI.h
KomplexSearchModel.h KomplexSearchModel.h
KomplexSearchModel.cpp KomplexSearchModel.cpp
CubemapSearch.h
CubemapSearch.cpp
CubemapMetadata.h
) )
qt_add_qml_module( qt_add_qml_module(
@@ -66,10 +67,11 @@ qt_add_qml_module(
ShaderToyMetadata.h ShaderToyMetadata.h
ShaderToySearchModel.h ShaderToySearchModel.h
ShaderToySearchModel.cpp ShaderToySearchModel.cpp
ShaderToyAPI.h
PexelsAPI.h
KomplexSearchModel.h KomplexSearchModel.h
KomplexSearchModel.cpp KomplexSearchModel.cpp
CubemapSearch.h
CubemapSearch.cpp
CubemapMetadata.h
NO_GENERATE_PLUGIN_SOURCE NO_GENERATE_PLUGIN_SOURCE
) )

17
plugin/CubemapMetadata.h Normal file
View File

@@ -0,0 +1,17 @@
#ifndef CUBEMAPMETADATA_H
#define CUBEMAPMETADATA_H
#include <QObject>
#include <QUrl>
#include "Komplex_global.h"
struct KOMPLEX_EXPORT CubemapMetadata
{
QString description;
QString id;
QString name;
QUrl thumbnail;
};
#endif // CUBEMAPMETADATA_H

440
plugin/CubemapSearch.cpp Normal file
View File

@@ -0,0 +1,440 @@
#include "CubemapSearch.h"
CubemapSearchModel::CubemapSearchModel(QObject *parent) : QAbstractItemModel { parent }
{
m_networkManager.setAutoDeleteReplies(true);
}
CubemapSearchModel::~CubemapSearchModel()
{
}
int CubemapSearchModel::rowCount(const QModelIndex &parent) const
{
Q_UNUSED(parent)
return m_data.size();
}
QVariant CubemapSearchModel::data(const QModelIndex &index, int role) const
{
if(index.row() < 0 || index.row() >= m_data.count())
return QVariant();
QVariant data;
switch (static_cast<DataRoles>(role)) {
case Description:
data = QVariant::fromValue<QString>(m_data[index.row()].description);
break;
case Name:
data = QVariant::fromValue<QString>(m_data[index.row()].name);
break;
case Thumbnail:
data = QVariant::fromValue<QUrl>(m_data[index.row()].thumbnail);
break;
case Id:
data = QVariant::fromValue<QString>(m_data[index.row()].id);
break;
}
return data;
}
QHash<int, QByteArray> CubemapSearchModel::roleNames() const
{
return m_dataRoles;
}
void CubemapSearchModel::getSearchResults(QString url)
{
setStatus(Searching);
QNetworkRequest request;
request.setUrl(QUrl(url));
QNetworkReply *reply = m_networkManager.get(request);
QObject::connect
(
reply,
&QNetworkReply::finished,
this,
[this, reply]()
{
if(reply->error())
qWarning() << reply->errorString();
QByteArray data = reply->readAll();
QJsonParseError jsonError;
QJsonDocument document = QJsonDocument::fromJson(data, &jsonError);
if(jsonError.error != QJsonParseError::NoError)
{
qWarning() << jsonError.errorString();
return;
}
QJsonObject rootObject = document.object();
if(rootObject.contains(QStringLiteral("total_results")))
setTotalResults(rootObject[QStringLiteral("total_results")].toInt());
else
setTotalResults(0);
if(currentOffset() > 0)
{
setPreviousPage(
QStringLiteral("https://api.artifex.services/v1/cubemaps/search/%1/%2/%3").arg(
query(),
std::clamp(
currentOffset() - resultsPerPage(),
static_cast<quint64>(0),
totalResults() - resultsPerPage()
),
resultsPerPage()
)
);
}
else
setPreviousPage(QString());
if((currentOffset() + resultsPerPage()) < totalResults())
{
setNextPage(
QStringLiteral("https://api.artifex.services/v1/cubemaps/search/%1/%2/%3").arg(
query(),
std::clamp(
currentOffset() + resultsPerPage(),
static_cast<quint64>(0),
totalResults() - resultsPerPage()
),
resultsPerPage()
)
);
}
else
setNextPage(QString());
beginResetModel();
m_data.clear();
endResetModel();
if(rootObject.contains(QStringLiteral("results")) && rootObject[QStringLiteral("results")].isArray())
{
QJsonArray resultsArray = rootObject[QStringLiteral("results")].toArray();
beginInsertRows(QModelIndex(), 0, resultsArray.count() - 1);
for(const QJsonValue &resultRef : std::as_const(resultsArray))
{
if(!resultRef.isObject())
continue;
QJsonObject resultObject = resultRef.toObject();
CubemapMetadata cubemap;
cubemap.description = resultObject[QStringLiteral("description")].toString();
cubemap.id = resultObject[QStringLiteral("id")].toString();
cubemap.name = resultObject[QStringLiteral("name")].toString();
cubemap.thumbnail = QUrl(QStringLiteral("https://api.artifex.services/v1/cubemaps/thumbnail/%1").arg(resultObject[QStringLiteral("id")].toString()));
m_data.append(cubemap);
}
endInsertRows();
}
setStatus(Idle);
}
);
}
int CubemapSearchModel::status() const
{
return static_cast<int>(m_status);
}
void CubemapSearchModel::setStatus(const int &status, const QString &message)
{
setStatusMessage(message);
if (m_status == static_cast<Status>(status))
return;
m_status = static_cast<Status>(status);
Q_EMIT statusChanged();
}
QString CubemapSearchModel::statusMessage() const
{
return m_statusMessage;
}
void CubemapSearchModel::setStatusMessage(const QString &message)
{
if (m_statusMessage == message)
return;
m_statusMessage = message;
Q_EMIT statusMessageChanged();
}
QString CubemapSearchModel::lastSavedFile() const
{
return m_lastSavedFile;
}
void CubemapSearchModel::setLastSavedFile(const QString &lastSavedFile)
{
if (m_lastSavedFile == lastSavedFile)
return;
m_lastSavedFile = lastSavedFile;
Q_EMIT lastSavedFileChanged();
}
qreal CubemapSearchModel::downloadProgress() const
{
return m_downloadProgress;
}
void CubemapSearchModel::setDownloadProgress(qreal downloadProgress)
{
if (m_downloadProgress == downloadProgress)
return;
m_downloadProgress = downloadProgress;
Q_EMIT downloadProgressChanged();
}
quint64 CubemapSearchModel::currentOffset() const
{
return m_currentOffset;
}
void CubemapSearchModel::setCurrentOffset(quint64 currentPage)
{
if (m_currentOffset == currentPage)
return;
m_currentOffset = currentPage;
Q_EMIT currentOffsetChanged();
}
QModelIndex CubemapSearchModel::index(int row, int column, const QModelIndex &parent) const
{
Q_UNUSED(parent)
return createIndex(row, column, &m_data.at(row));
}
int CubemapSearchModel::columnCount(const QModelIndex &parent) const
{
Q_UNUSED(parent)
return 0;
}
QModelIndex CubemapSearchModel::parent(const QModelIndex &index) const
{
Q_UNUSED(index)
return QModelIndex();
}
void CubemapSearchModel::next()
{
if(m_nextPage.isEmpty())
return;
getSearchResults(m_nextPage);
}
void CubemapSearchModel::back()
{
if(m_previousPage.isEmpty())
return;
getSearchResults(m_previousPage);
}
void CubemapSearchModel::download(QString id)
{
QNetworkRequest request(QUrl(QStringLiteral("https://api.artifex.services/v1/cubemaps/item/%1").arg(id)));
QNetworkReply *reply = m_networkManager.get(request);
setStatus(Downloading);
QObject::connect
(
reply,
&QNetworkReply::finished,
this,
[this, reply, id]()
{
if(reply->error())
{
qWarning() << reply->errorString();
setStatus(Error, QStringLiteral("Could not download resource %1").arg(id));
return;
}
QByteArray data = reply->readAll();
QString zipFileLocation = QStringLiteral("%1/.local/share/komplex/cubemaps/%2.zip").arg(QStandardPaths::writableLocation(QStandardPaths::HomeLocation), id);
QString fileLocation = QStringLiteral("%1/.local/share/komplex/cubemaps/%2").arg(QStandardPaths::writableLocation(QStandardPaths::HomeLocation), id);
QFile zipFile(zipFileLocation);
if(zipFile.exists())
zipFile.remove();
if(!zipFile.open(QFile::ReadWrite))
{
qWarning() << "Could not open cubemap file:" << zipFileLocation;
setStatus(Error, QStringLiteral("Could not open cubemap file"));
return;
}
quint64 written = zipFile.write(data);
if(written != data.length())
{
zipFile.close();
qWarning() << "Could not write cubemap file:" << zipFileLocation;
setStatus(Error, QStringLiteral("Could not open write file"));
return;
}
zipFile.close();
QStringList params {
zipFileLocation,
QStringLiteral("-d"),
fileLocation
};
QProcess process;
process.setProcessChannelMode(QProcess::MergedChannels);
process.start(QStringLiteral("unzip"), params);
if(!process.waitForStarted() || !process.waitForFinished())
{
QString message;
switch(process.error())
{
case QProcess::Crashed:
message = QStringLiteral("Unzip process crashed");
break;
case QProcess::FailedToStart:
message = QStringLiteral("Unzip process failed to start");
break;
case QProcess::Timedout:
message = QStringLiteral("Unzip process timedout");
break;
case QProcess::WriteError:
message = QStringLiteral("Unzip process crashed due to a write error");
break;
case QProcess::ReadError:
message = QStringLiteral("Unzip process crashed due to a read error");
break;
case QProcess::UnknownError:
default:
message = QStringLiteral("Unzip process crashed due to an unknown error");
break;
}
setStatus(Error, message);
}
qWarning() << process.errorString() << process.readAllStandardOutput();// << command;
if(status() != Error)
{
setLastSavedFile(fileLocation);
Q_EMIT downloadFinished();
}
setStatus(Idle);
}
);
QObject::connect
(
reply,
&QNetworkReply::downloadProgress,
this,
[this](qint64 received, qint64 total)
{
setDownloadProgress(static_cast<qreal>(received) / static_cast<qreal>(total));
}
);
}
QString CubemapSearchModel::previousPage() const
{
return m_previousPage;
}
void CubemapSearchModel::setPreviousPage(const QString &previousPage)
{
if (m_previousPage == previousPage)
return;
m_previousPage = previousPage;
Q_EMIT previousPageChanged();
}
QString CubemapSearchModel::nextPage() const
{
return m_nextPage;
}
void CubemapSearchModel::setNextPage(const QString &nextPage)
{
if (m_nextPage == nextPage)
return;
m_nextPage = nextPage;
Q_EMIT nextPageChanged();
}
quint64 CubemapSearchModel::totalResults() const
{
return m_totalResults;
}
void CubemapSearchModel::setTotalResults(quint64 totalResults)
{
if (m_totalResults == totalResults)
return;
m_totalResults = totalResults;
Q_EMIT totalResultsChanged();
}
quint16 CubemapSearchModel::resultsPerPage() const
{
return m_resultsPerPage;
}
void CubemapSearchModel::setResultsPerPage(quint16 resultsPerPage)
{
if (m_resultsPerPage == resultsPerPage)
return;
m_resultsPerPage = resultsPerPage;
Q_EMIT resultsPerPageChanged();
}
QString CubemapSearchModel::query() const
{
return m_query;
}
void CubemapSearchModel::setQuery(const QString &query)
{
if (m_query == query)
return;
m_query = query;
Q_EMIT queryChanged();
getSearchResults(QStringLiteral("https://api.artifex.services/v1/cubemaps/search/%1/0/%2").arg(m_query).arg(m_resultsPerPage));
}

156
plugin/CubemapSearch.h Normal file
View File

@@ -0,0 +1,156 @@
#ifndef CUBEMAPSEARCH_H
#define CUBEMAPSEARCH_H
#include <QObject>
#include <QCache>
#include <QFile>
#include <QProcess>
#include <QEventLoop>
#include <QStandardPaths>
#include <QQuickImageProvider>
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QAbstractItemModel>
#include <QJsonDocument>
#include <QJsonParseError>
#include <QJsonObject>
#include <QJsonArray>
#include <QJsonValue>
#include "CubemapMetadata.h"
#include "Komplex_global.h"
class KOMPLEX_EXPORT CubemapSearchModel : public QAbstractItemModel
{
Q_OBJECT
public:
enum DataRoles
{
Description = Qt::UserRole + 1,
Id,
Name,
Thumbnail
};
Q_ENUM(DataRoles)
enum Status
{
Idle,
Searching,
Error,
Downloading
};
Q_ENUM(Status)
CubemapSearchModel(QObject *parent = nullptr);
~CubemapSearchModel();
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
QString query() const;
void setQuery(const QString &query);
quint16 resultsPerPage() const;
void setResultsPerPage(quint16 resultsPerPage);
quint64 totalResults() const;
void setTotalResults(quint64 totalResults);
QString nextPage() const;
void setNextPage(const QString &nextPage);
QString previousPage() const;
void setPreviousPage(const QString &previousPage);
quint64 currentOffset() const;
void setCurrentOffset(quint64 currentOffset);
QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
QModelIndex parent(const QModelIndex &index) const override;
Q_INVOKABLE void next();
Q_INVOKABLE void back();
Q_INVOKABLE void download(QString id);
qreal downloadProgress() const;
void setDownloadProgress(qreal downloadProgress);
QString lastSavedFile() const;
void setLastSavedFile(const QString &lastSavedFile);
int status() const;
void setStatus(const int &status, const QString &message = QString());
QString statusMessage() const;
void setStatusMessage(const QString &message);
Q_SIGNALS:
void queryChanged();
void resultsPerPageChanged();
void totalResultsChanged();
void nextPageChanged();
void previousPageChanged();
void currentOffsetChanged();
void downloadProgressChanged();
void downloadFinished();
void lastSavedFileChanged();
void statusChanged();
void statusMessageChanged();
protected:
QHash<int, QByteArray> roleNames() const override;
private:
void getSearchResults(QString url);
QNetworkAccessManager m_networkManager;
QString m_query;
quint16 m_resultsPerPage = 9;
quint64 m_totalResults = 0;
quint64 m_currentOffset = 0;
qreal m_downloadProgress = 0;
QString m_nextPage;
QString m_previousPage;
QString m_lastSavedFile;
QString m_statusMessage;
QList<CubemapMetadata> m_data;
Status m_status = Status::Idle;
static inline const QHash<int, QByteArray> m_dataRoles =
{
{
static_cast<int>(Description),
QByteArray("description")
},
{
static_cast<int>(Id),
QByteArray("id")
},
{
static_cast<int>(Name),
QByteArray("name")
},
{
static_cast<int>(Thumbnail),
QByteArray("thumbnail")
}
};
Q_PROPERTY(QString query READ query WRITE setQuery NOTIFY queryChanged FINAL)
Q_PROPERTY(quint16 resultsPerPage READ resultsPerPage WRITE setResultsPerPage NOTIFY resultsPerPageChanged FINAL)
Q_PROPERTY(quint64 totalResults READ totalResults WRITE setTotalResults NOTIFY totalResultsChanged FINAL)
Q_PROPERTY(QString nextPage READ nextPage WRITE setNextPage NOTIFY nextPageChanged FINAL)
Q_PROPERTY(QString previousPage READ previousPage WRITE setPreviousPage NOTIFY previousPageChanged FINAL)
Q_PROPERTY(quint64 currentOffset READ currentOffset WRITE setCurrentOffset NOTIFY currentOffsetChanged FINAL)
Q_PROPERTY(qreal downloadProgress READ downloadProgress WRITE setDownloadProgress NOTIFY downloadProgressChanged FINAL)
Q_PROPERTY(QString lastSavedFile READ lastSavedFile WRITE setLastSavedFile NOTIFY lastSavedFileChanged FINAL)
Q_PROPERTY(int status READ status WRITE setStatus NOTIFY statusChanged FINAL)
Q_PROPERTY(QString statusMessage READ statusMessage WRITE setStatusMessage NOTIFY statusMessageChanged FINAL)
};
Q_DECLARE_METATYPE(CubemapSearchModel)
#endif // CUBEMAPSEARCH_H

View File

@@ -7,6 +7,7 @@
#include "ShaderPackModel.h" #include "ShaderPackModel.h"
#include "PexelsVideoSearch.h" #include "PexelsVideoSearch.h"
#include "PexelsImageSearch.h" #include "PexelsImageSearch.h"
#include "CubemapSearch.h"
#include "ShaderToySearchModel.h" #include "ShaderToySearchModel.h"
#include "GeometryProvider.h" #include "GeometryProvider.h"
#include "KomplexSearchModel.h" #include "KomplexSearchModel.h"
@@ -49,6 +50,7 @@ public:
qmlRegisterType<PexelsVideoSearchModel>(uri, 1, 0, "PexelsVideoSearchModel"); qmlRegisterType<PexelsVideoSearchModel>(uri, 1, 0, "PexelsVideoSearchModel");
qmlRegisterType<PexelsImageSearchModel>(uri, 1, 0, "PexelsImageSearchModel"); qmlRegisterType<PexelsImageSearchModel>(uri, 1, 0, "PexelsImageSearchModel");
qmlRegisterType<KomplexSearchModel>(uri, 1, 0, "KomplexSearchModel"); qmlRegisterType<KomplexSearchModel>(uri, 1, 0, "KomplexSearchModel");
qmlRegisterType<CubemapSearchModel>(uri, 1, 0, "CubemapSearchModel");
} }
void unregisterTypes() override void unregisterTypes() override

View File

@@ -5,4 +5,5 @@ classname ShaderPackModel
classname PexelsImageSearchModel classname PexelsImageSearchModel
classname PexelsVideoSearchModel classname PexelsVideoSearchModel
classname ShaderToySearchModel classname ShaderToySearchModel
classname KomplexSearchModel classname KomplexSearchModel
classname CubemapSearchModel