forked from qt-creator/qt-creator
Make Content Library Materials downloadable
Task-number: QDS-9267 Change-Id: Ib4da1871cd1d9f0bf52323793b7d8d1b028ae170 Reviewed-by: Miikka Heikkinen <miikka.heikkinen@qt.io>
This commit is contained in:
@@ -10,27 +10,36 @@ import QtQuick.Controls
|
|||||||
import StudioTheme 1.0 as StudioTheme
|
import StudioTheme 1.0 as StudioTheme
|
||||||
import ContentLibraryBackend
|
import ContentLibraryBackend
|
||||||
|
|
||||||
|
import WebFetcher 1.0
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
signal showContextMenu()
|
signal showContextMenu()
|
||||||
|
|
||||||
|
// Download states: "" (ie default, not downloaded), "unavailable", "downloading", "downloaded",
|
||||||
|
// "failed"
|
||||||
|
property string downloadState: modelData.isDownloaded() ? "downloaded" : ""
|
||||||
|
|
||||||
visible: modelData.bundleMaterialVisible
|
visible: modelData.bundleMaterialVisible
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: mouseArea
|
id: mouseArea
|
||||||
|
|
||||||
|
enabled: root.downloadState !== "downloading"
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||||
|
|
||||||
onPressed: (mouse) => {
|
onPressed: (mouse) => {
|
||||||
if (mouse.button === Qt.LeftButton && !materialsModel.importerRunning)
|
if (mouse.button === Qt.LeftButton && !materialsModel.importerRunning) {
|
||||||
|
if (root.downloadState === "downloaded")
|
||||||
ContentLibraryBackend.rootView.startDragMaterial(modelData, mapToGlobal(mouse.x, mouse.y))
|
ContentLibraryBackend.rootView.startDragMaterial(modelData, mapToGlobal(mouse.x, mouse.y))
|
||||||
else if (mouse.button === Qt.RightButton)
|
} else if (mouse.button === Qt.RightButton && root.downloadState === "downloaded") {
|
||||||
root.showContextMenu()
|
root.showContextMenu()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
@@ -38,6 +47,15 @@ Item {
|
|||||||
|
|
||||||
Item { width: 1; height: 5 } // spacer
|
Item { width: 1; height: 5 } // spacer
|
||||||
|
|
||||||
|
DownloadPane {
|
||||||
|
id: downloadPane
|
||||||
|
width: root.width - 10
|
||||||
|
height: img.width
|
||||||
|
visible: root.downloadState === "downloading"
|
||||||
|
|
||||||
|
onRequestCancel: downloader.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
id: img
|
id: img
|
||||||
|
|
||||||
@@ -46,6 +64,7 @@ Item {
|
|||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
source: modelData.bundleMaterialIcon
|
source: modelData.bundleMaterialIcon
|
||||||
cache: false
|
cache: false
|
||||||
|
visible: root.downloadState != "downloading"
|
||||||
|
|
||||||
Rectangle { // circular indicator for imported bundle materials
|
Rectangle { // circular indicator for imported bundle materials
|
||||||
width: 10
|
width: 10
|
||||||
@@ -83,13 +102,52 @@ Item {
|
|||||||
anchors.right: img.right
|
anchors.right: img.right
|
||||||
anchors.bottom: img.bottom
|
anchors.bottom: img.bottom
|
||||||
enabled: !ContentLibraryBackend.materialsModel.importerRunning
|
enabled: !ContentLibraryBackend.materialsModel.importerRunning
|
||||||
visible: containsMouse || mouseArea.containsMouse
|
visible: root.downloadState === "downloaded"
|
||||||
|
&& (containsMouse || mouseArea.containsMouse)
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
ContentLibraryBackend.materialsModel.addToProject(modelData)
|
ContentLibraryBackend.materialsModel.addToProject(modelData)
|
||||||
}
|
}
|
||||||
|
} // IconButton
|
||||||
|
|
||||||
|
Text { // download icon
|
||||||
|
color: root.downloadState === "unavailable" || root.downloadState === "failed"
|
||||||
|
? StudioTheme.Values.themeRedLight
|
||||||
|
: StudioTheme.Values.themeTextColor
|
||||||
|
|
||||||
|
font.family: StudioTheme.Constants.iconFont.family
|
||||||
|
text: root.downloadState === "unavailable"
|
||||||
|
? StudioTheme.Constants.downloadUnavailable
|
||||||
|
: StudioTheme.Constants.download
|
||||||
|
|
||||||
|
font.pixelSize: 22
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
anchors.bottomMargin: 0
|
||||||
|
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
style: Text.Outline
|
||||||
|
styleColor: "black"
|
||||||
|
|
||||||
|
visible: root.downloadState !== "downloaded"
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
acceptedButtons: Qt.LeftButton
|
||||||
|
|
||||||
|
onClicked: (mouse) => {
|
||||||
|
if (root.downloadState !== "" && root.downloadState !== "failed")
|
||||||
|
return
|
||||||
|
|
||||||
|
downloadPane.beginDownload(Qt.binding(function() { return downloader.progress }))
|
||||||
|
|
||||||
|
root.downloadState = ""
|
||||||
|
downloader.start()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
} // Image
|
||||||
|
|
||||||
TextInput {
|
TextInput {
|
||||||
id: matName
|
id: matName
|
||||||
@@ -110,5 +168,44 @@ Item {
|
|||||||
selectionColor: StudioTheme.Values.themeTextSelectionColor
|
selectionColor: StudioTheme.Values.themeTextSelectionColor
|
||||||
selectedTextColor: StudioTheme.Values.themeTextSelectedTextColor
|
selectedTextColor: StudioTheme.Values.themeTextSelectedTextColor
|
||||||
}
|
}
|
||||||
|
} // Column
|
||||||
|
|
||||||
|
MultiFileDownloader {
|
||||||
|
id: downloader
|
||||||
|
|
||||||
|
baseUrl: modelData.bundleMaterialBaseWebUrl
|
||||||
|
files: modelData.bundleMaterialFiles
|
||||||
|
|
||||||
|
targetDirPath: modelData.bundleMaterialParentPath
|
||||||
|
|
||||||
|
onDownloadStarting: {
|
||||||
|
root.downloadState = "downloading"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onFinishedChanged: {
|
||||||
|
downloadPane.endDownload()
|
||||||
|
|
||||||
|
root.downloadState = "downloaded"
|
||||||
|
}
|
||||||
|
|
||||||
|
onDownloadCanceled: {
|
||||||
|
downloadPane.endDownload()
|
||||||
|
|
||||||
|
root.downloadState = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
onDownloadFailed: {
|
||||||
|
downloadPane.endDownload()
|
||||||
|
|
||||||
|
root.downloadState = "failed"
|
||||||
|
}
|
||||||
|
|
||||||
|
downloader: FileDownloader {
|
||||||
|
id: fileDownloader
|
||||||
|
url: downloader.nextUrl
|
||||||
|
probeUrl: false
|
||||||
|
downloadEnabled: true
|
||||||
|
targetFilePath: downloader.nextTargetPath
|
||||||
|
} // FileDownloader
|
||||||
|
} // MultiFileDownloader
|
||||||
}
|
}
|
||||||
|
@@ -232,7 +232,7 @@ Item {
|
|||||||
FileExtractor {
|
FileExtractor {
|
||||||
id: extractor
|
id: extractor
|
||||||
archiveName: downloader.completeBaseName
|
archiveName: downloader.completeBaseName
|
||||||
sourceFile: downloader.tempFile
|
sourceFile: downloader.outputFile
|
||||||
targetPath: modelData.textureParentPath
|
targetPath: modelData.textureParentPath
|
||||||
alwaysCreateDir: false
|
alwaysCreateDir: false
|
||||||
clearTargetPathContents: false
|
clearTargetPathContents: false
|
||||||
|
@@ -0,0 +1,81 @@
|
|||||||
|
// Copyright (C) 2023 The Qt Company Ltd.
|
||||||
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
|
||||||
|
import StudioTheme 1.0 as StudioTheme
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
color: StudioTheme.Values.themeThumbnailBackground
|
||||||
|
border.color: "#00000000"
|
||||||
|
|
||||||
|
signal requestCancel
|
||||||
|
|
||||||
|
property alias allowCancel: progressBar.closeButtonVisible
|
||||||
|
property alias progressValue: progressBar.value
|
||||||
|
property alias progressLabel: progressLabel.text
|
||||||
|
|
||||||
|
function beginDownload(progressFunction)
|
||||||
|
{
|
||||||
|
progressBar.visible = true
|
||||||
|
root.progressLabel = qsTr("Downloading...")
|
||||||
|
root.allowCancel = true
|
||||||
|
root.progressValue = progressFunction
|
||||||
|
}
|
||||||
|
|
||||||
|
function endDownload()
|
||||||
|
{
|
||||||
|
root.allowCancel = false
|
||||||
|
root.progressLabel = ""
|
||||||
|
root.progressValue = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
TextureProgressBar {
|
||||||
|
id: progressBar
|
||||||
|
anchors.rightMargin: 10
|
||||||
|
anchors.leftMargin: 10
|
||||||
|
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
|
||||||
|
visible: false
|
||||||
|
|
||||||
|
onCancelRequested: {
|
||||||
|
root.requestCancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: progressLabel
|
||||||
|
color: StudioTheme.Values.themeTextColor
|
||||||
|
text: qsTr("Progress:")
|
||||||
|
anchors.bottom: parent.top
|
||||||
|
anchors.bottomMargin: 5
|
||||||
|
anchors.left: parent.left
|
||||||
|
font.pixelSize: 12
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
anchors.top: parent.bottom
|
||||||
|
anchors.topMargin: 5
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: progressAmount
|
||||||
|
color: StudioTheme.Values.themeTextColor
|
||||||
|
text: progressBar.value.toFixed(1)
|
||||||
|
|
||||||
|
font.pixelSize: 12
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: percentSign
|
||||||
|
color: StudioTheme.Values.themeTextColor
|
||||||
|
text: qsTr("%")
|
||||||
|
font.pixelSize: 12
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // TextureProgressBar
|
||||||
|
} // Rectangle
|
@@ -22,6 +22,7 @@ add_qtc_library(QmlDesignerUtils STATIC
|
|||||||
SOURCES
|
SOURCES
|
||||||
asset.cpp asset.h
|
asset.cpp asset.h
|
||||||
filedownloader.cpp filedownloader.h
|
filedownloader.cpp filedownloader.h
|
||||||
|
multifiledownloader.cpp multifiledownloader.h
|
||||||
fileextractor.cpp fileextractor.h
|
fileextractor.cpp fileextractor.h
|
||||||
hdrimage.cpp hdrimage.h
|
hdrimage.cpp hdrimage.h
|
||||||
ktximage.cpp ktximage.h
|
ktximage.cpp ktximage.h
|
||||||
|
@@ -3,6 +3,8 @@
|
|||||||
|
|
||||||
#include "contentlibrarymaterial.h"
|
#include "contentlibrarymaterial.h"
|
||||||
|
|
||||||
|
#include <QFileInfo>
|
||||||
|
|
||||||
namespace QmlDesigner {
|
namespace QmlDesigner {
|
||||||
|
|
||||||
ContentLibraryMaterial::ContentLibraryMaterial(QObject *parent,
|
ContentLibraryMaterial::ContentLibraryMaterial(QObject *parent,
|
||||||
@@ -10,8 +12,15 @@ ContentLibraryMaterial::ContentLibraryMaterial(QObject *parent,
|
|||||||
const QString &qml,
|
const QString &qml,
|
||||||
const TypeName &type,
|
const TypeName &type,
|
||||||
const QUrl &icon,
|
const QUrl &icon,
|
||||||
const QStringList &files)
|
const QStringList &files,
|
||||||
: QObject(parent), m_name(name), m_qml(qml), m_type(type), m_icon(icon), m_files(files) {}
|
const QString &downloadPath,
|
||||||
|
const QString &baseWebUrl)
|
||||||
|
: QObject(parent), m_name(name), m_qml(qml), m_type(type), m_icon(icon), m_files(files)
|
||||||
|
, m_downloadPath(downloadPath), m_baseWebUrl(baseWebUrl)
|
||||||
|
{
|
||||||
|
m_allFiles = m_files;
|
||||||
|
m_allFiles.push_back(m_qml);
|
||||||
|
}
|
||||||
|
|
||||||
bool ContentLibraryMaterial::filter(const QString &searchText)
|
bool ContentLibraryMaterial::filter(const QString &searchText)
|
||||||
{
|
{
|
||||||
@@ -64,4 +73,25 @@ bool ContentLibraryMaterial::imported() const
|
|||||||
return m_imported;
|
return m_imported;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ContentLibraryMaterial::isDownloaded() const
|
||||||
|
{
|
||||||
|
QString fullPath = qmlFilePath();
|
||||||
|
return QFileInfo(fullPath).isFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ContentLibraryMaterial::qmlFilePath() const
|
||||||
|
{
|
||||||
|
return m_downloadPath + "/" + m_qml;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ContentLibraryMaterial::parentDirPath() const
|
||||||
|
{
|
||||||
|
return m_downloadPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList ContentLibraryMaterial::allFiles() const
|
||||||
|
{
|
||||||
|
return m_allFiles;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace QmlDesigner
|
} // namespace QmlDesigner
|
||||||
|
@@ -19,6 +19,9 @@ class ContentLibraryMaterial : public QObject
|
|||||||
Q_PROPERTY(QUrl bundleMaterialIcon MEMBER m_icon CONSTANT)
|
Q_PROPERTY(QUrl bundleMaterialIcon MEMBER m_icon CONSTANT)
|
||||||
Q_PROPERTY(bool bundleMaterialVisible MEMBER m_visible NOTIFY materialVisibleChanged)
|
Q_PROPERTY(bool bundleMaterialVisible MEMBER m_visible NOTIFY materialVisibleChanged)
|
||||||
Q_PROPERTY(bool bundleMaterialImported READ imported WRITE setImported NOTIFY materialImportedChanged)
|
Q_PROPERTY(bool bundleMaterialImported READ imported WRITE setImported NOTIFY materialImportedChanged)
|
||||||
|
Q_PROPERTY(QString bundleMaterialBaseWebUrl MEMBER m_baseWebUrl CONSTANT)
|
||||||
|
Q_PROPERTY(QString bundleMaterialParentPath READ parentDirPath CONSTANT)
|
||||||
|
Q_PROPERTY(QStringList bundleMaterialFiles READ allFiles CONSTANT)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ContentLibraryMaterial(QObject *parent,
|
ContentLibraryMaterial(QObject *parent,
|
||||||
@@ -26,18 +29,25 @@ public:
|
|||||||
const QString &qml,
|
const QString &qml,
|
||||||
const TypeName &type,
|
const TypeName &type,
|
||||||
const QUrl &icon,
|
const QUrl &icon,
|
||||||
const QStringList &files);
|
const QStringList &files,
|
||||||
|
const QString &downloadPath,
|
||||||
|
const QString &baseWebUrl);
|
||||||
|
|
||||||
bool filter(const QString &searchText);
|
bool filter(const QString &searchText);
|
||||||
|
|
||||||
|
Q_INVOKABLE bool isDownloaded() const;
|
||||||
|
|
||||||
QUrl icon() const;
|
QUrl icon() const;
|
||||||
QString qml() const;
|
QString qml() const;
|
||||||
TypeName type() const;
|
TypeName type() const;
|
||||||
QStringList files() const;
|
QStringList files() const;
|
||||||
bool visible() const;
|
bool visible() const;
|
||||||
|
QString qmlFilePath() const;
|
||||||
|
|
||||||
bool setImported(bool imported);
|
bool setImported(bool imported);
|
||||||
bool imported() const;
|
bool imported() const;
|
||||||
|
QString parentDirPath() const;
|
||||||
|
QStringList allFiles() const;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void materialVisibleChanged();
|
void materialVisibleChanged();
|
||||||
@@ -52,6 +62,10 @@ private:
|
|||||||
|
|
||||||
bool m_visible = true;
|
bool m_visible = true;
|
||||||
bool m_imported = false;
|
bool m_imported = false;
|
||||||
|
|
||||||
|
QString m_downloadPath;
|
||||||
|
QString m_baseWebUrl;
|
||||||
|
QStringList m_allFiles;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace QmlDesigner
|
} // namespace QmlDesigner
|
||||||
|
@@ -7,7 +7,11 @@
|
|||||||
#include "contentlibrarymaterial.h"
|
#include "contentlibrarymaterial.h"
|
||||||
#include "contentlibrarymaterialscategory.h"
|
#include "contentlibrarymaterialscategory.h"
|
||||||
#include "contentlibrarywidget.h"
|
#include "contentlibrarywidget.h"
|
||||||
|
#include "filedownloader.h"
|
||||||
|
#include "fileextractor.h"
|
||||||
|
#include "multifiledownloader.h"
|
||||||
#include "qmldesignerconstants.h"
|
#include "qmldesignerconstants.h"
|
||||||
|
#include "qmldesignerplugin.h"
|
||||||
|
|
||||||
#include <utils/algorithm.h>
|
#include <utils/algorithm.h>
|
||||||
#include <utils/hostosinfo.h>
|
#include <utils/hostosinfo.h>
|
||||||
@@ -16,6 +20,8 @@
|
|||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
#include <QJsonArray>
|
#include <QJsonArray>
|
||||||
#include <QJsonDocument>
|
#include <QJsonDocument>
|
||||||
|
#include <QQmlEngine>
|
||||||
|
#include <QStandardPaths>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
|
|
||||||
namespace QmlDesigner {
|
namespace QmlDesigner {
|
||||||
@@ -24,7 +30,19 @@ ContentLibraryMaterialsModel::ContentLibraryMaterialsModel(ContentLibraryWidget
|
|||||||
: QAbstractListModel(parent)
|
: QAbstractListModel(parent)
|
||||||
, m_widget(parent)
|
, m_widget(parent)
|
||||||
{
|
{
|
||||||
loadMaterialBundle();
|
m_downloadPath = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation)
|
||||||
|
+ "/QtDesignStudio/bundles/Materials";
|
||||||
|
|
||||||
|
m_baseUrl = QmlDesignerPlugin::settings()
|
||||||
|
.value(DesignerSettingsKey::DOWNLOADABLE_BUNDLES_URL)
|
||||||
|
.toString() + "/materials/v1";
|
||||||
|
|
||||||
|
qmlRegisterType<QmlDesigner::FileDownloader>("WebFetcher", 1, 0, "FileDownloader");
|
||||||
|
qmlRegisterType<QmlDesigner::MultiFileDownloader>("WebFetcher", 1, 0, "MultiFileDownloader");
|
||||||
|
|
||||||
|
QDir bundleDir{m_downloadPath};
|
||||||
|
if (fetchBundleMetadata(bundleDir) && fetchBundleIcons(bundleDir))
|
||||||
|
loadMaterialBundle(bundleDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
int ContentLibraryMaterialsModel::rowCount(const QModelIndex &) const
|
int ContentLibraryMaterialsModel::rowCount(const QModelIndex &) const
|
||||||
@@ -91,30 +109,127 @@ QHash<int, QByteArray> ContentLibraryMaterialsModel::roleNames() const
|
|||||||
return roles;
|
return roles;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ContentLibraryMaterialsModel::loadMaterialBundle()
|
bool ContentLibraryMaterialsModel::fetchBundleIcons(const QDir &bundleDir)
|
||||||
{
|
{
|
||||||
if (m_matBundleExists || m_probeMatBundleDir)
|
QString iconsPath = bundleDir.filePath("icons");
|
||||||
|
|
||||||
|
QDir iconsDir(iconsPath);
|
||||||
|
if (iconsDir.exists() && iconsDir.entryList().length() > 0)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
QString zipFileUrl = m_baseUrl + "/icons.zip";
|
||||||
|
|
||||||
|
FileDownloader *downloader = new FileDownloader(this);
|
||||||
|
downloader->setUrl(zipFileUrl);
|
||||||
|
downloader->setProbeUrl(false);
|
||||||
|
downloader->setDownloadEnabled(true);
|
||||||
|
|
||||||
|
QObject::connect(downloader, &FileDownloader::finishedChanged, this, [=]() {
|
||||||
|
FileExtractor *extractor = new FileExtractor(this);
|
||||||
|
extractor->setArchiveName(downloader->completeBaseName());
|
||||||
|
extractor->setSourceFile(downloader->outputFile());
|
||||||
|
extractor->setTargetPath(bundleDir.absolutePath());
|
||||||
|
extractor->setAlwaysCreateDir(false);
|
||||||
|
extractor->setClearTargetPathContents(false);
|
||||||
|
|
||||||
|
QObject::connect(extractor, &FileExtractor::finishedChanged, this, [=]() {
|
||||||
|
downloader->deleteLater();
|
||||||
|
extractor->deleteLater();
|
||||||
|
|
||||||
|
loadMaterialBundle(bundleDir);
|
||||||
|
});
|
||||||
|
|
||||||
|
extractor->extract();
|
||||||
|
});
|
||||||
|
|
||||||
|
downloader->start();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ContentLibraryMaterialsModel::fetchBundleMetadata(const QDir &bundleDir)
|
||||||
|
{
|
||||||
|
QString matBundlePath = bundleDir.filePath("material_bundle.json");
|
||||||
|
|
||||||
|
QFileInfo fi(matBundlePath);
|
||||||
|
if (fi.exists() && fi.size() > 0)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
QString metaFileUrl = m_baseUrl + "/material_bundle.json";
|
||||||
|
FileDownloader *downloader = new FileDownloader(this);
|
||||||
|
downloader->setUrl(metaFileUrl);
|
||||||
|
downloader->setProbeUrl(false);
|
||||||
|
downloader->setDownloadEnabled(true);
|
||||||
|
downloader->setTargetFilePath(matBundlePath);
|
||||||
|
|
||||||
|
QObject::connect(downloader, &FileDownloader::finishedChanged, this, [=]() {
|
||||||
|
if (fetchBundleIcons(bundleDir))
|
||||||
|
loadMaterialBundle(bundleDir);
|
||||||
|
|
||||||
|
downloader->deleteLater();
|
||||||
|
});
|
||||||
|
|
||||||
|
downloader->start();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ContentLibraryMaterialsModel::downloadSharedFiles(const QDir &targetDir, const QStringList &files)
|
||||||
|
{
|
||||||
|
QString metaFileUrl = m_baseUrl + "/shared_files.zip";
|
||||||
|
FileDownloader *downloader = new FileDownloader(this);
|
||||||
|
downloader->setUrl(metaFileUrl);
|
||||||
|
downloader->setProbeUrl(false);
|
||||||
|
downloader->setDownloadEnabled(true);
|
||||||
|
|
||||||
|
QObject::connect(downloader, &FileDownloader::finishedChanged, this, [=]() {
|
||||||
|
FileExtractor *extractor = new FileExtractor(this);
|
||||||
|
extractor->setArchiveName(downloader->completeBaseName());
|
||||||
|
extractor->setSourceFile(downloader->outputFile());
|
||||||
|
extractor->setTargetPath(targetDir.absolutePath());
|
||||||
|
extractor->setAlwaysCreateDir(false);
|
||||||
|
extractor->setClearTargetPathContents(false);
|
||||||
|
|
||||||
|
QObject::connect(extractor, &FileExtractor::finishedChanged, this, [this, downloader, extractor]() {
|
||||||
|
downloader->deleteLater();
|
||||||
|
extractor->deleteLater();
|
||||||
|
|
||||||
|
createImporter(m_importerBundlePath, m_importerBundleId, m_importerSharedFiles);
|
||||||
|
});
|
||||||
|
|
||||||
|
extractor->extract();
|
||||||
|
});
|
||||||
|
|
||||||
|
downloader->start();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ContentLibraryMaterialsModel::createImporter(const QString &bundlePath, const QString &bundleId,
|
||||||
|
const QStringList &sharedFiles)
|
||||||
|
{
|
||||||
|
m_importer = new Internal::ContentLibraryBundleImporter(bundlePath, bundleId, sharedFiles);
|
||||||
|
connect(m_importer, &Internal::ContentLibraryBundleImporter::importFinished, this,
|
||||||
|
[&](const QmlDesigner::NodeMetaInfo &metaInfo) {
|
||||||
|
m_importerRunning = false;
|
||||||
|
emit importerRunningChanged();
|
||||||
|
if (metaInfo.isValid())
|
||||||
|
emit bundleMaterialImported(metaInfo);
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(m_importer, &Internal::ContentLibraryBundleImporter::unimportFinished, this,
|
||||||
|
[&](const QmlDesigner::NodeMetaInfo &metaInfo) {
|
||||||
|
Q_UNUSED(metaInfo)
|
||||||
|
m_importerRunning = false;
|
||||||
|
emit importerRunningChanged();
|
||||||
|
emit bundleMaterialUnimported(metaInfo);
|
||||||
|
});
|
||||||
|
|
||||||
|
resetModel();
|
||||||
|
updateIsEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ContentLibraryMaterialsModel::loadMaterialBundle(const QDir &matBundleDir)
|
||||||
|
{
|
||||||
|
if (m_matBundleExists)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
QDir matBundleDir;
|
|
||||||
|
|
||||||
if (!qEnvironmentVariable("MATERIAL_BUNDLE_PATH").isEmpty())
|
|
||||||
matBundleDir.setPath(qEnvironmentVariable("MATERIAL_BUNDLE_PATH"));
|
|
||||||
else if (Utils::HostOsInfo::isMacHost())
|
|
||||||
matBundleDir.setPath(QCoreApplication::applicationDirPath() + "/../Resources/material_bundle");
|
|
||||||
|
|
||||||
// search for matBundleDir from exec dir and up
|
|
||||||
if (matBundleDir.dirName() == ".") {
|
|
||||||
m_probeMatBundleDir = true; // probe only once
|
|
||||||
|
|
||||||
matBundleDir.setPath(QCoreApplication::applicationDirPath());
|
|
||||||
while (!matBundleDir.cd("material_bundle") && matBundleDir.cdUp())
|
|
||||||
; // do nothing
|
|
||||||
|
|
||||||
if (matBundleDir.dirName() != "material_bundle") // bundlePathDir not found
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString matBundlePath = matBundleDir.filePath("material_bundle.json");
|
QString matBundlePath = matBundleDir.filePath("material_bundle.json");
|
||||||
|
|
||||||
if (m_matBundleObj.isEmpty()) {
|
if (m_matBundleObj.isEmpty()) {
|
||||||
@@ -160,7 +275,8 @@ void ContentLibraryMaterialsModel::loadMaterialBundle()
|
|||||||
bundleId,
|
bundleId,
|
||||||
qml.chopped(4)).toLatin1(); // chopped(4): remove .qml
|
qml.chopped(4)).toLatin1(); // chopped(4): remove .qml
|
||||||
|
|
||||||
auto bundleMat = new ContentLibraryMaterial(category, mat, qml, type, icon, files);
|
auto bundleMat = new ContentLibraryMaterial(category, mat, qml, type, icon, files,
|
||||||
|
m_downloadPath, m_baseUrl);
|
||||||
|
|
||||||
category->addBundleMaterial(bundleMat);
|
category->addBundleMaterial(bundleMat);
|
||||||
}
|
}
|
||||||
@@ -172,24 +288,22 @@ void ContentLibraryMaterialsModel::loadMaterialBundle()
|
|||||||
for (const auto /*QJson{Const,}ValueRef*/ &file : sharedFilesArr)
|
for (const auto /*QJson{Const,}ValueRef*/ &file : sharedFilesArr)
|
||||||
sharedFiles.append(file.toString());
|
sharedFiles.append(file.toString());
|
||||||
|
|
||||||
m_importer = new Internal::ContentLibraryBundleImporter(matBundleDir.path(), bundleId, sharedFiles);
|
QStringList missingSharedFiles;
|
||||||
connect(m_importer, &Internal::ContentLibraryBundleImporter::importFinished, this,
|
for (const QString &s : std::as_const(sharedFiles)) {
|
||||||
[&](const QmlDesigner::NodeMetaInfo &metaInfo) {
|
const QString fullSharedFilePath = matBundleDir.filePath(s);
|
||||||
m_importerRunning = false;
|
|
||||||
emit importerRunningChanged();
|
|
||||||
if (metaInfo.isValid())
|
|
||||||
emit bundleMaterialImported(metaInfo);
|
|
||||||
});
|
|
||||||
|
|
||||||
connect(m_importer, &Internal::ContentLibraryBundleImporter::unimportFinished, this,
|
if (!QFile::exists(fullSharedFilePath))
|
||||||
[&](const QmlDesigner::NodeMetaInfo &metaInfo) {
|
missingSharedFiles.push_back(s);
|
||||||
Q_UNUSED(metaInfo)
|
}
|
||||||
m_importerRunning = false;
|
|
||||||
emit importerRunningChanged();
|
|
||||||
emit bundleMaterialUnimported(metaInfo);
|
|
||||||
});
|
|
||||||
|
|
||||||
updateIsEmpty();
|
if (missingSharedFiles.length() > 0) {
|
||||||
|
m_importerBundlePath = matBundleDir.path();
|
||||||
|
m_importerBundleId = bundleId;
|
||||||
|
m_importerSharedFiles = sharedFiles;
|
||||||
|
downloadSharedFiles(matBundleDir, missingSharedFiles);
|
||||||
|
} else {
|
||||||
|
createImporter(matBundleDir.path(), bundleId, sharedFiles);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ContentLibraryMaterialsModel::hasRequiredQuick3DImport() const
|
bool ContentLibraryMaterialsModel::hasRequiredQuick3DImport() const
|
||||||
|
@@ -6,6 +6,7 @@
|
|||||||
#include "nodemetainfo.h"
|
#include "nodemetainfo.h"
|
||||||
|
|
||||||
#include <QAbstractListModel>
|
#include <QAbstractListModel>
|
||||||
|
#include <QDir>
|
||||||
#include <QJsonObject>
|
#include <QJsonObject>
|
||||||
|
|
||||||
namespace QmlDesigner {
|
namespace QmlDesigner {
|
||||||
@@ -70,8 +71,13 @@ signals:
|
|||||||
void matBundleExistsChanged();
|
void matBundleExistsChanged();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void loadMaterialBundle();
|
void loadMaterialBundle(const QDir &matBundleDir);
|
||||||
|
bool fetchBundleIcons(const QDir &bundleDir);
|
||||||
|
bool fetchBundleMetadata(const QDir &bundleDir);
|
||||||
bool isValidIndex(int idx) const;
|
bool isValidIndex(int idx) const;
|
||||||
|
void downloadSharedFiles(const QDir &targetDir, const QStringList &files);
|
||||||
|
void createImporter(const QString &bundlePath, const QString &bundleId,
|
||||||
|
const QStringList &sharedFiles);
|
||||||
|
|
||||||
ContentLibraryWidget *m_widget = nullptr;
|
ContentLibraryWidget *m_widget = nullptr;
|
||||||
QString m_searchText;
|
QString m_searchText;
|
||||||
@@ -82,11 +88,17 @@ private:
|
|||||||
bool m_isEmpty = true;
|
bool m_isEmpty = true;
|
||||||
bool m_matBundleExists = false;
|
bool m_matBundleExists = false;
|
||||||
bool m_hasModelSelection = false;
|
bool m_hasModelSelection = false;
|
||||||
bool m_probeMatBundleDir = false;
|
|
||||||
bool m_importerRunning = false;
|
bool m_importerRunning = false;
|
||||||
|
|
||||||
int m_quick3dMajorVersion = -1;
|
int m_quick3dMajorVersion = -1;
|
||||||
int m_quick3dMinorVersion = -1;
|
int m_quick3dMinorVersion = -1;
|
||||||
|
|
||||||
|
QString m_downloadPath;
|
||||||
|
QString m_baseUrl;
|
||||||
|
|
||||||
|
QString m_importerBundlePath;
|
||||||
|
QString m_importerBundleId;
|
||||||
|
QStringList m_importerSharedFiles;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace QmlDesigner
|
} // namespace QmlDesigner
|
||||||
|
@@ -116,7 +116,7 @@ void ContentLibraryTexturesModel::loadTextureBundle(const QString &bundlePath, c
|
|||||||
|
|
||||||
QString remoteBaseUrl = QmlDesignerPlugin::settings()
|
QString remoteBaseUrl = QmlDesignerPlugin::settings()
|
||||||
.value(DesignerSettingsKey::DOWNLOADABLE_BUNDLES_URL).toString()
|
.value(DesignerSettingsKey::DOWNLOADABLE_BUNDLES_URL).toString()
|
||||||
+ '/' + m_category;
|
+ "/textures/" + m_category;
|
||||||
|
|
||||||
const QFileInfoList dirs = bundleDir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot);
|
const QFileInfoList dirs = bundleDir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot);
|
||||||
for (const QFileInfo &dir : dirs) {
|
for (const QFileInfo &dir : dirs) {
|
||||||
|
@@ -54,7 +54,8 @@ bool ContentLibraryWidget::eventFilter(QObject *obj, QEvent *event)
|
|||||||
|
|
||||||
if (m_materialToDrag) {
|
if (m_materialToDrag) {
|
||||||
QMouseEvent *me = static_cast<QMouseEvent *>(event);
|
QMouseEvent *me = static_cast<QMouseEvent *>(event);
|
||||||
if ((me->globalPos() - m_dragStartPoint).manhattanLength() > 20) {
|
if ((me->globalPos() - m_dragStartPoint).manhattanLength() > 20
|
||||||
|
&& m_materialToDrag->isDownloaded()) {
|
||||||
QByteArray data;
|
QByteArray data;
|
||||||
QMimeData *mimeData = new QMimeData;
|
QMimeData *mimeData = new QMimeData;
|
||||||
QDataStream stream(&data, QIODevice::WriteOnly);
|
QDataStream stream(&data, QIODevice::WriteOnly);
|
||||||
|
@@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
#include <private/qqmldata_p.h>
|
#include <private/qqmldata_p.h>
|
||||||
#include <utils/networkaccessmanager.h>
|
#include <utils/networkaccessmanager.h>
|
||||||
|
#include <utils/filepath.h>
|
||||||
|
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QQmlEngine>
|
#include <QQmlEngine>
|
||||||
@@ -12,33 +13,52 @@ namespace QmlDesigner {
|
|||||||
|
|
||||||
FileDownloader::FileDownloader(QObject *parent)
|
FileDownloader::FileDownloader(QObject *parent)
|
||||||
: QObject(parent)
|
: QObject(parent)
|
||||||
{}
|
{
|
||||||
|
QObject::connect(this, &FileDownloader::downloadFailed, this, [this]() {
|
||||||
|
if (m_outputFile.exists())
|
||||||
|
m_outputFile.remove();
|
||||||
|
});
|
||||||
|
|
||||||
|
QObject::connect(this, &FileDownloader::downloadCanceled, this, [this]() {
|
||||||
|
if (m_outputFile.exists())
|
||||||
|
m_outputFile.remove();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
FileDownloader::~FileDownloader()
|
FileDownloader::~FileDownloader()
|
||||||
{
|
{
|
||||||
if (m_tempFile.exists())
|
// Delete the temp file only if a target Path was set (i.e. file will be moved)
|
||||||
m_tempFile.remove();
|
if (deleteFileAtTheEnd() && m_outputFile.exists())
|
||||||
|
m_outputFile.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FileDownloader::deleteFileAtTheEnd() const
|
||||||
|
{
|
||||||
|
return m_targetFilePath.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
void FileDownloader::start()
|
void FileDownloader::start()
|
||||||
{
|
{
|
||||||
emit downloadStarting();
|
emit downloadStarting();
|
||||||
|
|
||||||
m_tempFile.setFileName(QDir::tempPath() + "/" + name() + ".XXXXXX" + ".zip");
|
QString tempFileName = QDir::tempPath() + "/.qds_download_" + url().fileName();
|
||||||
m_tempFile.open(QIODevice::WriteOnly);
|
|
||||||
|
m_outputFile.setFileName(tempFileName);
|
||||||
|
m_outputFile.open(QIODevice::WriteOnly);
|
||||||
|
|
||||||
auto request = QNetworkRequest(m_url);
|
auto request = QNetworkRequest(m_url);
|
||||||
request.setAttribute(QNetworkRequest::RedirectPolicyAttribute,
|
request.setAttribute(QNetworkRequest::RedirectPolicyAttribute,
|
||||||
QNetworkRequest::UserVerifiedRedirectPolicy);
|
QNetworkRequest::UserVerifiedRedirectPolicy);
|
||||||
m_reply = Utils::NetworkAccessManager::instance()->get(request);
|
QNetworkReply *reply = Utils::NetworkAccessManager::instance()->get(request);
|
||||||
|
m_reply = reply;
|
||||||
|
|
||||||
QNetworkReply::connect(m_reply, &QNetworkReply::readyRead, this, [this]() {
|
QNetworkReply::connect(reply, &QNetworkReply::readyRead, this, [this, reply]() {
|
||||||
bool isDownloadingFile = false;
|
bool isDownloadingFile = false;
|
||||||
QString contentType;
|
QString contentType;
|
||||||
if (!m_reply->hasRawHeader("Content-Type")) {
|
if (!reply->hasRawHeader("Content-Type")) {
|
||||||
isDownloadingFile = true;
|
isDownloadingFile = true;
|
||||||
} else {
|
} else {
|
||||||
contentType = QString::fromUtf8(m_reply->rawHeader("Content-Type"));
|
contentType = QString::fromUtf8(reply->rawHeader("Content-Type"));
|
||||||
|
|
||||||
if (contentType.startsWith("application/")
|
if (contentType.startsWith("application/")
|
||||||
|| contentType.startsWith("image/")
|
|| contentType.startsWith("image/")
|
||||||
@@ -50,12 +70,12 @@ void FileDownloader::start()
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (isDownloadingFile)
|
if (isDownloadingFile)
|
||||||
m_tempFile.write(m_reply->readAll());
|
m_outputFile.write(reply->readAll());
|
||||||
else
|
else
|
||||||
m_reply->close();
|
reply->close();
|
||||||
});
|
});
|
||||||
|
|
||||||
QNetworkReply::connect(m_reply,
|
QNetworkReply::connect(reply,
|
||||||
&QNetworkReply::downloadProgress,
|
&QNetworkReply::downloadProgress,
|
||||||
this,
|
this,
|
||||||
[this](qint64 current, qint64 max) {
|
[this](qint64 current, qint64 max) {
|
||||||
@@ -66,33 +86,44 @@ void FileDownloader::start()
|
|||||||
}
|
}
|
||||||
|
|
||||||
m_progress = current * 100 / max;
|
m_progress = current * 100 / max;
|
||||||
|
|
||||||
emit progressChanged();
|
emit progressChanged();
|
||||||
});
|
});
|
||||||
|
|
||||||
QNetworkReply::connect(m_reply, &QNetworkReply::redirected, [this](const QUrl &) {
|
QNetworkReply::connect(reply, &QNetworkReply::redirected, [reply](const QUrl &) {
|
||||||
emit m_reply->redirectAllowed();
|
emit reply->redirectAllowed();
|
||||||
});
|
});
|
||||||
|
|
||||||
QNetworkReply::connect(m_reply, &QNetworkReply::finished, this, [this]() {
|
QNetworkReply::connect(reply, &QNetworkReply::finished, this, [this, reply]() {
|
||||||
if (m_reply->error()) {
|
if (reply->error()) {
|
||||||
if (m_tempFile.exists())
|
if (reply->error() != QNetworkReply::OperationCanceledError) {
|
||||||
m_tempFile.remove();
|
qWarning() << Q_FUNC_INFO << m_url << reply->errorString();
|
||||||
|
|
||||||
if (m_reply->error() != QNetworkReply::OperationCanceledError) {
|
|
||||||
qWarning() << Q_FUNC_INFO << m_url << m_reply->errorString();
|
|
||||||
emit downloadFailed();
|
emit downloadFailed();
|
||||||
} else {
|
} else {
|
||||||
emit downloadCanceled();
|
emit downloadCanceled();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
m_tempFile.flush();
|
m_outputFile.flush();
|
||||||
m_tempFile.close();
|
m_outputFile.close();
|
||||||
|
|
||||||
|
QString dirPath = QFileInfo(m_targetFilePath).dir().absolutePath();
|
||||||
|
if (!deleteFileAtTheEnd()) {
|
||||||
|
if (!QDir{}.mkpath(dirPath)) {
|
||||||
|
emit downloadFailed();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!QFileInfo().exists(m_targetFilePath) && !m_outputFile.rename(m_targetFilePath)) {
|
||||||
|
emit downloadFailed();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
m_finished = true;
|
m_finished = true;
|
||||||
emit tempFileChanged();
|
emit outputFileChanged();
|
||||||
emit finishedChanged();
|
emit finishedChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
reply->deleteLater();
|
||||||
m_reply = nullptr;
|
m_reply = nullptr;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -176,9 +207,9 @@ int FileDownloader::progress() const
|
|||||||
return m_progress;
|
return m_progress;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString FileDownloader::tempFile() const
|
QString FileDownloader::outputFile() const
|
||||||
{
|
{
|
||||||
return QFileInfo(m_tempFile).canonicalFilePath();
|
return QFileInfo(m_outputFile).canonicalFilePath();
|
||||||
}
|
}
|
||||||
|
|
||||||
QDateTime FileDownloader::lastModified() const
|
QDateTime FileDownloader::lastModified() const
|
||||||
@@ -242,4 +273,14 @@ void FileDownloader::doProbeUrl()
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FileDownloader::setTargetFilePath(const QString &path)
|
||||||
|
{
|
||||||
|
m_targetFilePath = path;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString FileDownloader::targetFilePath() const
|
||||||
|
{
|
||||||
|
return m_targetFilePath;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace QmlDesigner
|
} // namespace QmlDesigner
|
||||||
|
@@ -15,13 +15,14 @@ class FileDownloader : public QObject
|
|||||||
|
|
||||||
Q_PROPERTY(bool downloadEnabled WRITE setDownloadEnabled READ downloadEnabled NOTIFY downloadEnabledChanged)
|
Q_PROPERTY(bool downloadEnabled WRITE setDownloadEnabled READ downloadEnabled NOTIFY downloadEnabledChanged)
|
||||||
Q_PROPERTY(QUrl url READ url WRITE setUrl NOTIFY urlChanged)
|
Q_PROPERTY(QUrl url READ url WRITE setUrl NOTIFY urlChanged)
|
||||||
|
Q_PROPERTY(QString targetFilePath READ targetFilePath WRITE setTargetFilePath NOTIFY targetFilePathChanged)
|
||||||
Q_PROPERTY(bool probeUrl READ probeUrl WRITE setProbeUrl NOTIFY probeUrlChanged)
|
Q_PROPERTY(bool probeUrl READ probeUrl WRITE setProbeUrl NOTIFY probeUrlChanged)
|
||||||
Q_PROPERTY(bool finished READ finished NOTIFY finishedChanged)
|
Q_PROPERTY(bool finished READ finished NOTIFY finishedChanged)
|
||||||
Q_PROPERTY(bool error READ error NOTIFY errorChanged)
|
Q_PROPERTY(bool error READ error NOTIFY errorChanged)
|
||||||
Q_PROPERTY(QString name READ name NOTIFY nameChanged)
|
Q_PROPERTY(QString name READ name NOTIFY nameChanged)
|
||||||
Q_PROPERTY(QString completeBaseName READ completeBaseName NOTIFY nameChanged)
|
Q_PROPERTY(QString completeBaseName READ completeBaseName NOTIFY nameChanged)
|
||||||
Q_PROPERTY(int progress READ progress NOTIFY progressChanged)
|
Q_PROPERTY(int progress READ progress NOTIFY progressChanged)
|
||||||
Q_PROPERTY(QString tempFile READ tempFile NOTIFY tempFileChanged)
|
Q_PROPERTY(QString outputFile READ outputFile NOTIFY outputFileChanged)
|
||||||
Q_PROPERTY(QDateTime lastModified READ lastModified NOTIFY lastModifiedChanged)
|
Q_PROPERTY(QDateTime lastModified READ lastModified NOTIFY lastModifiedChanged)
|
||||||
Q_PROPERTY(bool available READ available NOTIFY availableChanged)
|
Q_PROPERTY(bool available READ available NOTIFY availableChanged)
|
||||||
|
|
||||||
@@ -32,12 +33,14 @@ public:
|
|||||||
|
|
||||||
void setUrl(const QUrl &url);
|
void setUrl(const QUrl &url);
|
||||||
QUrl url() const;
|
QUrl url() const;
|
||||||
|
void setTargetFilePath(const QString &path);
|
||||||
|
QString targetFilePath() const;
|
||||||
bool finished() const;
|
bool finished() const;
|
||||||
bool error() const;
|
bool error() const;
|
||||||
QString name() const;
|
QString name() const;
|
||||||
QString completeBaseName() const;
|
QString completeBaseName() const;
|
||||||
int progress() const;
|
int progress() const;
|
||||||
QString tempFile() const;
|
QString outputFile() const;
|
||||||
QDateTime lastModified() const;
|
QDateTime lastModified() const;
|
||||||
bool available() const;
|
bool available() const;
|
||||||
void setDownloadEnabled(bool value);
|
void setDownloadEnabled(bool value);
|
||||||
@@ -55,7 +58,7 @@ signals:
|
|||||||
void nameChanged();
|
void nameChanged();
|
||||||
void urlChanged();
|
void urlChanged();
|
||||||
void progressChanged();
|
void progressChanged();
|
||||||
void tempFileChanged();
|
void outputFileChanged();
|
||||||
void downloadFailed();
|
void downloadFailed();
|
||||||
void lastModifiedChanged();
|
void lastModifiedChanged();
|
||||||
void availableChanged();
|
void availableChanged();
|
||||||
@@ -64,21 +67,24 @@ signals:
|
|||||||
void downloadStarting();
|
void downloadStarting();
|
||||||
void downloadCanceled();
|
void downloadCanceled();
|
||||||
void probeUrlChanged();
|
void probeUrlChanged();
|
||||||
|
void targetFilePathChanged();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void doProbeUrl();
|
void doProbeUrl();
|
||||||
|
bool deleteFileAtTheEnd() const;
|
||||||
|
|
||||||
QUrl m_url;
|
QUrl m_url;
|
||||||
bool m_probeUrl = false;
|
bool m_probeUrl = false;
|
||||||
bool m_finished = false;
|
bool m_finished = false;
|
||||||
bool m_error = false;
|
bool m_error = false;
|
||||||
int m_progress = 0;
|
int m_progress = 0;
|
||||||
QFile m_tempFile;
|
QFile m_outputFile;
|
||||||
QDateTime m_lastModified;
|
QDateTime m_lastModified;
|
||||||
bool m_available = false;
|
bool m_available = false;
|
||||||
|
|
||||||
QNetworkReply *m_reply = nullptr;
|
QNetworkReply *m_reply = nullptr;
|
||||||
bool m_downloadEnabled = false;
|
bool m_downloadEnabled = false;
|
||||||
|
QString m_targetFilePath;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace QmlDesigner
|
} // namespace QmlDesigner
|
||||||
|
@@ -104,13 +104,13 @@ void FileExtractor::browse()
|
|||||||
emit targetFolderExistsChanged();
|
emit targetFolderExistsChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
void FileExtractor::setSourceFile(QString &sourceFilePath)
|
void FileExtractor::setSourceFile(const QString &sourceFilePath)
|
||||||
{
|
{
|
||||||
m_sourceFile = Utils::FilePath::fromString(sourceFilePath);
|
m_sourceFile = Utils::FilePath::fromString(sourceFilePath);
|
||||||
emit targetFolderExistsChanged();
|
emit targetFolderExistsChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
void FileExtractor::setArchiveName(QString &filePath)
|
void FileExtractor::setArchiveName(const QString &filePath)
|
||||||
{
|
{
|
||||||
m_archiveName = filePath;
|
m_archiveName = filePath;
|
||||||
emit targetFolderExistsChanged();
|
emit targetFolderExistsChanged();
|
||||||
|
@@ -36,8 +36,8 @@ public:
|
|||||||
|
|
||||||
QString targetPath() const;
|
QString targetPath() const;
|
||||||
void setTargetPath(const QString &path);
|
void setTargetPath(const QString &path);
|
||||||
void setSourceFile(QString &sourceFilePath);
|
void setSourceFile(const QString &sourceFilePath);
|
||||||
void setArchiveName(QString &filePath);
|
void setArchiveName(const QString &filePath);
|
||||||
const QString detailedText() const;
|
const QString detailedText() const;
|
||||||
bool finished() const;
|
bool finished() const;
|
||||||
QString currentFile() const;
|
QString currentFile() const;
|
||||||
|
132
src/plugins/qmldesigner/utils/multifiledownloader.cpp
Normal file
132
src/plugins/qmldesigner/utils/multifiledownloader.cpp
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
// Copyright (C) 2023 The Qt Company Ltd.
|
||||||
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
|
||||||
|
#include "multifiledownloader.h"
|
||||||
|
#include "filedownloader.h"
|
||||||
|
|
||||||
|
namespace QmlDesigner {
|
||||||
|
|
||||||
|
MultiFileDownloader::MultiFileDownloader(QObject *parent)
|
||||||
|
: QObject(parent)
|
||||||
|
{}
|
||||||
|
|
||||||
|
MultiFileDownloader::~MultiFileDownloader()
|
||||||
|
{}
|
||||||
|
|
||||||
|
void MultiFileDownloader::setDownloader(FileDownloader *downloader)
|
||||||
|
{
|
||||||
|
m_downloader = downloader;
|
||||||
|
|
||||||
|
QObject::connect(this, &MultiFileDownloader::downloadStarting, [this]() {
|
||||||
|
m_nextFile = 0;
|
||||||
|
if (m_files.length() > 0)
|
||||||
|
m_downloader->start();
|
||||||
|
});
|
||||||
|
|
||||||
|
QObject::connect(m_downloader, &FileDownloader::progressChanged, this, [this]() {
|
||||||
|
m_progress = (m_nextFile + m_downloader->progress()) / m_files.count();
|
||||||
|
});
|
||||||
|
|
||||||
|
QObject::connect(m_downloader, &FileDownloader::downloadFailed, this, [this]() {
|
||||||
|
m_failed = true;
|
||||||
|
emit downloadFailed();
|
||||||
|
});
|
||||||
|
|
||||||
|
QObject::connect(m_downloader, &FileDownloader::downloadCanceled, this, [this]() {
|
||||||
|
m_canceled = true;
|
||||||
|
emit downloadCanceled();
|
||||||
|
});
|
||||||
|
|
||||||
|
QObject::connect(m_downloader, &FileDownloader::finishedChanged, this, [this]() {
|
||||||
|
switchToNextFile();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void MultiFileDownloader::start()
|
||||||
|
{
|
||||||
|
emit downloadStarting();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MultiFileDownloader::cancel()
|
||||||
|
{
|
||||||
|
m_canceled = true;
|
||||||
|
m_downloader->cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MultiFileDownloader::setBaseUrl(const QUrl &baseUrl)
|
||||||
|
{
|
||||||
|
if (m_baseUrl != baseUrl) {
|
||||||
|
m_baseUrl = baseUrl;
|
||||||
|
emit baseUrlChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QUrl MultiFileDownloader::baseUrl() const
|
||||||
|
{
|
||||||
|
return m_baseUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MultiFileDownloader::finished() const
|
||||||
|
{
|
||||||
|
return m_finished;
|
||||||
|
}
|
||||||
|
|
||||||
|
int MultiFileDownloader::progress() const
|
||||||
|
{
|
||||||
|
return m_progress;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MultiFileDownloader::setTargetDirPath(const QString &path)
|
||||||
|
{
|
||||||
|
m_targetDirPath = path;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString MultiFileDownloader::targetDirPath() const
|
||||||
|
{
|
||||||
|
return m_targetDirPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString MultiFileDownloader::nextUrl() const
|
||||||
|
{
|
||||||
|
if (m_nextFile >= m_files.length())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
return m_baseUrl.toString() + "/" + m_files[m_nextFile];
|
||||||
|
}
|
||||||
|
|
||||||
|
QString MultiFileDownloader::nextTargetPath() const
|
||||||
|
{
|
||||||
|
if (m_nextFile >= m_files.length())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
return m_targetDirPath + "/" + m_files[m_nextFile];
|
||||||
|
}
|
||||||
|
|
||||||
|
void MultiFileDownloader::setFiles(const QStringList &files)
|
||||||
|
{
|
||||||
|
m_files = files;
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList MultiFileDownloader::files() const
|
||||||
|
{
|
||||||
|
return m_files;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MultiFileDownloader::switchToNextFile()
|
||||||
|
{
|
||||||
|
++m_nextFile;
|
||||||
|
|
||||||
|
if (m_nextFile < m_files.length()) {
|
||||||
|
if (m_canceled) {
|
||||||
|
emit downloadCanceled();
|
||||||
|
} else {
|
||||||
|
emit nextUrlChanged();
|
||||||
|
emit nextTargetPathChanged();
|
||||||
|
m_downloader->start();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
m_finished = true;
|
||||||
|
emit finishedChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace QmlDesigner
|
77
src/plugins/qmldesigner/utils/multifiledownloader.h
Normal file
77
src/plugins/qmldesigner/utils/multifiledownloader.h
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
// Copyright (C) 2023 The Qt Company Ltd.
|
||||||
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QUrl>
|
||||||
|
|
||||||
|
namespace QmlDesigner {
|
||||||
|
|
||||||
|
class FileDownloader;
|
||||||
|
|
||||||
|
class MultiFileDownloader : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
Q_PROPERTY(FileDownloader *downloader WRITE setDownloader)
|
||||||
|
Q_PROPERTY(bool finished READ finished NOTIFY finishedChanged)
|
||||||
|
Q_PROPERTY(int progress READ progress NOTIFY progressChanged)
|
||||||
|
Q_PROPERTY(QUrl baseUrl READ baseUrl WRITE setBaseUrl NOTIFY baseUrlChanged)
|
||||||
|
Q_PROPERTY(QString targetDirPath READ targetDirPath WRITE setTargetDirPath NOTIFY targetDirPathChanged)
|
||||||
|
|
||||||
|
Q_PROPERTY(QString nextUrl READ nextUrl NOTIFY nextUrlChanged)
|
||||||
|
Q_PROPERTY(QString nextTargetPath READ nextTargetPath NOTIFY nextTargetPathChanged)
|
||||||
|
Q_PROPERTY(QStringList files READ files WRITE setFiles NOTIFY filesChanged)
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit MultiFileDownloader(QObject *parent = nullptr);
|
||||||
|
|
||||||
|
~MultiFileDownloader();
|
||||||
|
|
||||||
|
void setBaseUrl(const QUrl &url);
|
||||||
|
QUrl baseUrl() const;
|
||||||
|
|
||||||
|
void setTargetDirPath(const QString &path);
|
||||||
|
QString targetDirPath() const;
|
||||||
|
void setDownloader(FileDownloader *downloader);
|
||||||
|
|
||||||
|
bool finished() const;
|
||||||
|
int progress() const;
|
||||||
|
|
||||||
|
QString nextUrl() const;
|
||||||
|
QString nextTargetPath() const;
|
||||||
|
|
||||||
|
void setFiles(const QStringList &files);
|
||||||
|
QStringList files() const;
|
||||||
|
void switchToNextFile();
|
||||||
|
|
||||||
|
Q_INVOKABLE void start();
|
||||||
|
Q_INVOKABLE void cancel();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void finishedChanged();
|
||||||
|
void baseUrlChanged();
|
||||||
|
void progressChanged();
|
||||||
|
void downloadFailed();
|
||||||
|
|
||||||
|
void downloadStarting();
|
||||||
|
void downloadCanceled();
|
||||||
|
void targetDirPathChanged();
|
||||||
|
void nextUrlChanged();
|
||||||
|
void filesChanged();
|
||||||
|
void nextTargetPathChanged();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QUrl m_baseUrl;
|
||||||
|
bool m_finished = false;
|
||||||
|
int m_progress = 0;
|
||||||
|
QString m_targetDirPath;
|
||||||
|
FileDownloader *m_downloader = nullptr;
|
||||||
|
bool m_canceled = false;
|
||||||
|
bool m_failed = false;
|
||||||
|
QStringList m_files;
|
||||||
|
int m_nextFile = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace QmlDesigner
|
||||||
|
|
@@ -89,7 +89,7 @@ void DesignerSettings::fromSettings(QSettings *settings)
|
|||||||
restoreValue(settings, DesignerSettingsKey::EDITOR_ZOOM_FACTOR, 1.0);
|
restoreValue(settings, DesignerSettingsKey::EDITOR_ZOOM_FACTOR, 1.0);
|
||||||
restoreValue(settings, DesignerSettingsKey::ACTIONS_MERGE_TEMPLATE_ENABLED, false);
|
restoreValue(settings, DesignerSettingsKey::ACTIONS_MERGE_TEMPLATE_ENABLED, false);
|
||||||
restoreValue(settings, DesignerSettingsKey::DOWNLOADABLE_BUNDLES_URL,
|
restoreValue(settings, DesignerSettingsKey::DOWNLOADABLE_BUNDLES_URL,
|
||||||
"https://cdn.qt.io/designstudio/bundles/textures");
|
"https://cdn.qt.io/designstudio/bundles");
|
||||||
|
|
||||||
settings->endGroup();
|
settings->endGroup();
|
||||||
settings->endGroup();
|
settings->endGroup();
|
||||||
|
@@ -60,7 +60,7 @@ inline constexpr char SMOOTH_RENDERING[] = "SmoothRendering";
|
|||||||
inline constexpr char OLD_STATES_EDITOR[] = "ForceOldStatesEditor";
|
inline constexpr char OLD_STATES_EDITOR[] = "ForceOldStatesEditor";
|
||||||
inline constexpr char EDITOR_ZOOM_FACTOR[] = "EditorZoomFactor";
|
inline constexpr char EDITOR_ZOOM_FACTOR[] = "EditorZoomFactor";
|
||||||
inline constexpr char ACTIONS_MERGE_TEMPLATE_ENABLED[] = "ActionsMergeTemplateEnabled";
|
inline constexpr char ACTIONS_MERGE_TEMPLATE_ENABLED[] = "ActionsMergeTemplateEnabled";
|
||||||
inline constexpr char DOWNLOADABLE_BUNDLES_URL[] = "DownloadableBundlesUrl";
|
inline constexpr char DOWNLOADABLE_BUNDLES_URL[] = "DownloadableBundlesLocation";
|
||||||
}
|
}
|
||||||
|
|
||||||
class QMLDESIGNERBASE_EXPORT DesignerSettings
|
class QMLDESIGNERBASE_EXPORT DesignerSettings
|
||||||
|
@@ -117,7 +117,7 @@ DataModelDownloader::DataModelDownloader(QObject * /* parent */)
|
|||||||
|
|
||||||
if (m_fileDownloader.finished()) {
|
if (m_fileDownloader.finished()) {
|
||||||
const Utils::FilePath archiveFile = Utils::FilePath::fromString(
|
const Utils::FilePath archiveFile = Utils::FilePath::fromString(
|
||||||
m_fileDownloader.tempFile());
|
m_fileDownloader.outputFile());
|
||||||
QTC_ASSERT(Utils::Archive::supportsFile(archiveFile), return );
|
QTC_ASSERT(Utils::Archive::supportsFile(archiveFile), return );
|
||||||
auto archive = new Utils::Archive(archiveFile, tempFilePath());
|
auto archive = new Utils::Archive(archiveFile, tempFilePath());
|
||||||
QTC_ASSERT(archive->isValid(), delete archive; return );
|
QTC_ASSERT(archive->isValid(), delete archive; return );
|
||||||
|
@@ -40,7 +40,7 @@ Rectangle {
|
|||||||
FileExtractor {
|
FileExtractor {
|
||||||
id: fileExtractor
|
id: fileExtractor
|
||||||
archiveName: root.completeBaseName.length === 0 ? downloader.completeBaseName : root.completeBaseName
|
archiveName: root.completeBaseName.length === 0 ? downloader.completeBaseName : root.completeBaseName
|
||||||
sourceFile: root.tempFile.length === 0 ? downloader.tempFile : root.tempFile
|
sourceFile: root.tempFile.length === 0 ? downloader.outputFile : root.tempFile
|
||||||
}
|
}
|
||||||
|
|
||||||
FileDownloader {
|
FileDownloader {
|
||||||
|
Reference in New Issue
Block a user