diff options
Diffstat (limited to 'src/assets')
160 files changed, 1294 insertions, 0 deletions
diff --git a/src/assets/CMakeLists.txt b/src/assets/CMakeLists.txt new file mode 100644 index 0000000000..da9c0c14ee --- /dev/null +++ b/src/assets/CMakeLists.txt @@ -0,0 +1,7 @@ +# Copyright (C) 2024 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause +add_subdirectory(icons) + +if (NOT INTEGRITY AND TARGET Qt6::Network AND TARGET Qt6::Concurrent) + add_subdirectory(downloader) +endif() diff --git a/src/assets/downloader/CMakeLists.txt b/src/assets/downloader/CMakeLists.txt new file mode 100644 index 0000000000..1d93b021d8 --- /dev/null +++ b/src/assets/downloader/CMakeLists.txt @@ -0,0 +1,18 @@ +# Copyright (C) 2024 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +qt_internal_add_module(ExamplesAssetDownloaderPrivate + CONFIG_MODULE_NAME examples_asset_downloader + STATIC + EXCEPTIONS + INTERNAL_MODULE + SOURCES + assetdownloader.cpp assetdownloader.h + LIBRARIES + Qt6::Concurrent + Qt6::CorePrivate + Qt6::Network + PUBLIC_LIBRARIES + Qt6::Core +) + diff --git a/src/assets/downloader/assetdownloader.cpp b/src/assets/downloader/assetdownloader.cpp new file mode 100644 index 0000000000..85699cfe73 --- /dev/null +++ b/src/assets/downloader/assetdownloader.cpp @@ -0,0 +1,853 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "assetdownloader.h" + +#include <QtConcurrent/QtConcurrent> +#include <QtCore/QtAssert> +#include <QtCore/private/qzipreader_p.h> + +#include <QCoreApplication> +#include <QFile> +#include <QFuture> +#include <QJsonArray> +#include <QJsonDocument> +#include <QJsonObject> +#include <QLatin1StringView> +#include <QNetworkReply> +#include <QNetworkRequest> + +using QtExamples::AssetDownloader; +using QtExamples::AssetDownloaderPrivate; + +namespace { + +enum class UnzipRule { DeleteArchive, KeepArchive }; + +struct DownloadableAssets +{ + QUrl remoteUrl; + QList<QUrl> files; +}; + +class Exception : public QException +{ +public: + Exception(const QString &errorString) + : m_errorString(errorString) + {} + + QString message() const { return m_errorString; } + +private: + QString m_errorString; +}; + +class NetworkException : public Exception +{ +public: + NetworkException(QNetworkReply *reply) + : Exception(reply->errorString()) + {} + + void raise() const override { throw *this; } + NetworkException *clone() const override { return new NetworkException(*this); } +}; + +class FileException : public Exception +{ +public: + FileException(QFile *file) + : Exception(QString::fromUtf8("%1 \"%2\"").arg(file->errorString(), file->fileName())) + {} + + void raise() const override { throw *this; } + FileException *clone() const override { return new FileException(*this); } +}; + +class FileRemoveException : public Exception +{ +public: + FileRemoveException(QFile *file) + : Exception(QString::fromLatin1("Unable to remove file \"%1\"") + .arg(QFileInfo(file->fileName()).absoluteFilePath())) + {} + + void raise() const override { throw *this; } + FileRemoveException *clone() const override { return new FileRemoveException(*this); } +}; + +class FileCopyException : public Exception +{ +public: + FileCopyException(const QString &src, const QString &dst) + : Exception(QString::fromLatin1("Unable to copy file \"%1\" to \"%2\"") + .arg(QFileInfo(src).absoluteFilePath(), QFileInfo(dst).absoluteFilePath())) + {} + + void raise() const override { throw *this; } + FileCopyException *clone() const override { return new FileCopyException(*this); } +}; + +class DirException : public Exception +{ +public: + DirException(const QDir &dir) + : Exception(QString::fromLatin1("Cannot create directory %1").arg(dir.absolutePath())) + {} + + void raise() const override { throw *this; } + DirException *clone() const override { return new DirException(*this); } +}; + +class AssetFileException : public Exception +{ +public: + AssetFileException() + : Exception(QString::fromUtf8("Asset file is broken.")) + {} + void raise() const override { throw *this; } + AssetFileException *clone() const override { return new AssetFileException(*this); } +}; + +bool isWritableDir(const QDir &dir) +{ + if (dir.exists()) { + QTemporaryFile file(dir.filePath(QString::fromLatin1("tmp"))); + return file.open(); + } + return false; +} + +bool sameFileContent(const QFileInfo &first, const QFileInfo &second) +{ + if (first.exists() ^ second.exists()) + return false; + + if (first.size() != second.size()) + return false; + + QFile firstFile(first.absoluteFilePath()); + QFile secondFile(second.absoluteFilePath()); + + if (firstFile.open(QFile::ReadOnly) && secondFile.open(QFile::ReadOnly)) { + char char1; + char char2; + int readBytes1 = 0; + int readBytes2 = 0; + while (!firstFile.atEnd()) { + readBytes1 = firstFile.read(&char1, 1); + readBytes2 = secondFile.read(&char2, 1); + if (readBytes1 != readBytes2 || readBytes1 != 1) + return false; + if (char1 != char2) + return false; + } + return true; + } + + return false; +} + +bool createDirectory(const QDir &dir) +{ + if (dir.exists()) + return true; + + if (!createDirectory(dir.absoluteFilePath(QString::fromUtf8("..")))) + return false; + + return dir.mkpath(QString::fromUtf8(".")); +} + +bool canBeALocalBaseDir(QDir &dir) +{ + if (dir.exists()) { + if (dir.isEmpty()) + return isWritableDir(dir); + return true; + } else { + return createDirectory(dir) && isWritableDir(dir); + } + return false; +} + +bool unzip(const QString &fileName, const QDir &directory, UnzipRule rule) +{ + QZipReader zipFile(fileName); + const bool zipExtracted = zipFile.extractAll(directory.absolutePath()); + zipFile.close(); + if (zipExtracted && (rule == UnzipRule::DeleteArchive)) + QFile::remove(fileName); + return zipExtracted; +} + +void ensureDirOrThrow(const QDir &dir) +{ + if (createDirectory(dir)) + return; + + throw DirException(dir); +} + +void ensureRemoveFileOrThrow(QFile &file) +{ + if (!file.exists() || file.remove()) + return; + + throw FileRemoveException(&file); +} + +bool shouldDownloadEverything(const QList<QUrl> &assetFiles, const QDir &expectedDir) +{ + return std::any_of(assetFiles.begin(), assetFiles.end(), [&](const QUrl &imgPath) -> bool { + const QString localPath = expectedDir.absoluteFilePath(imgPath.toString()); + return !QFileInfo::exists(localPath); + }); +} + +QString pathFromUrl(const QUrl &url) +{ + return url.isLocalFile() ? url.toLocalFile() : url.toString(); +} + +DownloadableAssets readAssetsFile(const QByteArray &assetFileContent) +{ + DownloadableAssets result; + QJsonObject json = QJsonDocument::fromJson(assetFileContent).object(); + + const QJsonArray assetsArray = json[u"assets"].toArray(); + for (const QJsonValue &asset : assetsArray) + result.files.append(asset.toString()); + + result.remoteUrl = json[u"url"].toString(); + + if (result.files.isEmpty() || result.remoteUrl.isEmpty()) + throw AssetFileException{}; + return result; +} + +QList<QUrl> filterDownloadableAssets(const QList<QUrl> &assetFiles, const QDir &expectedDir) +{ + QList<QUrl> downloadList; + std::copy_if(assetFiles.begin(), + assetFiles.end(), + std::back_inserter(downloadList), + [&](const QUrl &imgPath) { + const QString tempFilePath = expectedDir.absoluteFilePath(imgPath.toString()); + return !QFileInfo::exists(tempFilePath); + }); + return downloadList; +} + +} // namespace + +class QtExamples::AssetDownloaderPrivate +{ + AssetDownloader *q_ptr = nullptr; + Q_DECLARE_PUBLIC(AssetDownloader) + +public: + using State = AssetDownloader::State; + + AssetDownloaderPrivate(AssetDownloader *parent); + ~AssetDownloaderPrivate(); + + void setupBase(); + void setBaseLocalDir(const QDir &dir); + void moveAllAssets(); + void setAllDownloadsCount(int count); + void setCompletedDownloadsCount(int count); + void finalizeDownload(); + void setState(State state); + + QFuture<QByteArray> download(const QUrl &url); + QFuture<QUrl> downloadToFile(const QUrl &url, const QUrl &destPath); + QFuture<QByteArray> downloadOrRead(const QUrl &url, const QUrl &localFile); + QFuture<DownloadableAssets> downloadAssets(const DownloadableAssets &assets); + QFuture<DownloadableAssets> maybeReadZipFile(const DownloadableAssets &assets); + + State state = State::NotDownloadedState; + int allDownloadsCount = 0; + std::unique_ptr<QTemporaryDir> providedTempDir; + QDir baseLocal; + QDir tempDir; + int completedDownloadsCount = 0; + double progress = 0; + bool downloadStarted = false; + DownloadableAssets downloadableAssets; + QNetworkAccessManager *networkManager = nullptr; + QFutureWatcher<QByteArray> *downloadWatcher = nullptr; + QFutureWatcher<QUrl> *fileDownloadWatcher = nullptr; + + QString jsonFileName; + QString zipFileName; + + QDir preferredLocalDownloadDir; + QUrl offlineAssetsFilePath; + + QUrl downloadBase = {QString::fromLatin1("https://download.qt.io/learning/examples/")}; +}; + +AssetDownloaderPrivate::AssetDownloaderPrivate(AssetDownloader *parent) + : q_ptr(parent) + , networkManager(new QNetworkAccessManager(parent)) + , downloadWatcher(new QFutureWatcher<QByteArray>(parent)) + , fileDownloadWatcher(new QFutureWatcher<QUrl>(parent)) + , preferredLocalDownloadDir( + QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation)) +{ + QObject::connect(downloadWatcher, + &QFutureWatcherBase::progressValueChanged, + parent, + &AssetDownloader::setProgressPercent); + + QObject::connect(fileDownloadWatcher, + &QFutureWatcherBase::progressValueChanged, + parent, + &AssetDownloader::setProgressPercent); +} + +AssetDownloaderPrivate::~AssetDownloaderPrivate() +{ + downloadWatcher->cancel(); + fileDownloadWatcher->cancel(); + downloadWatcher->waitForFinished(); + fileDownloadWatcher->waitForFinished(); +} + +void AssetDownloaderPrivate::setupBase() +{ + providedTempDir.reset(new QTemporaryDir{}); + Q_ASSERT_X(providedTempDir->isValid(), + "AssetDownloader::setupBase()", + "Cannot create a temporary directory!"); + + if (canBeALocalBaseDir(preferredLocalDownloadDir)) { + setBaseLocalDir(preferredLocalDownloadDir); + } else { + qWarning().noquote() << "AssetDownloader: Cannot set \"" << preferredLocalDownloadDir + << "\" as a local download directory!"; + setBaseLocalDir(QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation)); + } + + tempDir = providedTempDir->path(); +} + +void AssetDownloaderPrivate::setBaseLocalDir(const QDir &dir) +{ + Q_Q(AssetDownloader); + if (baseLocal != dir) { + baseLocal = dir; + emit q->localDownloadDirChanged(QUrl::fromLocalFile(baseLocal.absolutePath())); + } +} + +void AssetDownloaderPrivate::moveAllAssets() +{ + if (tempDir != baseLocal && !tempDir.isEmpty()) { + try { + setState(State::MovingFilesState); + QtConcurrent::blockingMap(downloadableAssets.files, [this](const QUrl &asset) { + QFile srcFile(tempDir.absoluteFilePath(asset.toString())); + QFile dstFile(baseLocal.absoluteFilePath(asset.toString())); + QFileInfo srcFileInfo(srcFile.fileName()); + QFileInfo dstFileInfo(dstFile.fileName()); + + ensureRemoveFileOrThrow(dstFile); + ensureDirOrThrow(dstFileInfo.absolutePath()); + + if (!srcFile.copy(dstFile.fileName())) { + if (!sameFileContent(srcFileInfo, dstFileInfo)) + throw FileCopyException(srcFile.fileName(), dstFile.fileName()); + } + }); + } catch (const Exception &exception) { + qWarning() << exception.message(); + setBaseLocalDir(tempDir); + } catch (const std::exception &exception) { + qWarning() << exception.what(); + setBaseLocalDir(tempDir); + } + } +} + +void AssetDownloaderPrivate::setAllDownloadsCount(int count) +{ + Q_Q(AssetDownloader); + if (allDownloadsCount != count) { + allDownloadsCount = count; + emit q->allDownloadsCountChanged(allDownloadsCount); + } +} + +void AssetDownloaderPrivate::setCompletedDownloadsCount(int count) +{ + Q_Q(AssetDownloader); + if (completedDownloadsCount != count) { + completedDownloadsCount = count; + emit q->completedDownloadsCountChanged(completedDownloadsCount); + } +} + +void AssetDownloaderPrivate::finalizeDownload() +{ + Q_Q(AssetDownloader); + if (downloadStarted) { + downloadStarted = false; + emit q->downloadFinished(); + } +} + +void AssetDownloaderPrivate::setState(State state) +{ + Q_Q(AssetDownloader); + if (this->state != state) { + this->state = state; + emit q->stateChanged(this->state); + } +} + +QFuture<QByteArray> AssetDownloaderPrivate::download(const QUrl &url) +{ + Q_Q(AssetDownloader); + Q_ASSERT_X(QThread::currentThread() != q->thread(), + "AssetDownloader::download()", + "Method called from wrong thread"); + + QSharedPointer<QPromise<QByteArray>> promise(new QPromise<QByteArray>); + QFuture<QByteArray> result = promise->future(); + promise->start(); + + QNetworkReply *reply = networkManager->get(QNetworkRequest(url)); + QObject::connect(reply, &QNetworkReply::finished, reply, [reply, promise]() { + if (reply->error() == QNetworkReply::NoError) + promise->addResult(reply->readAll()); + else + promise->setException(NetworkException(reply)); + promise->finish(); + reply->deleteLater(); + }); + + QObject::connect(reply, + &QNetworkReply::downloadProgress, + reply, + [promise, reply](qint64 bytesReceived, qint64 totalBytes) { + if (promise->isCanceled()) { + reply->abort(); + return; + } + int progress = (totalBytes > 0) ? 100.0 * bytesReceived / totalBytes : 0; + promise->setProgressRange(0, 100); + promise->setProgressValue(progress); + }); + + downloadWatcher->setFuture(result); + emit q->downloadingFileChanged(url); + + return result; +} + +QFuture<QUrl> AssetDownloaderPrivate::downloadToFile(const QUrl &url, const QUrl &destPath) +{ + Q_Q(AssetDownloader); + Q_ASSERT_X(QThread::currentThread() != q->thread(), + "AssetDownloader::download()", + "Method called from wrong thread"); + + QSharedPointer<QPromise<QUrl>> promise(new QPromise<QUrl>); + QFuture<QUrl> result = promise->future(); + promise->start(); + + QFileInfo fileInfo(pathFromUrl(destPath)); + createDirectory(fileInfo.absolutePath()); + + QSharedPointer<QFile> file(new QFile(fileInfo.absoluteFilePath())); + + if (!file->open(QFile::WriteOnly)) { + promise->setException(FileException(file.data())); + promise->finish(); + return result; + } + + QNetworkReply *reply = networkManager->get(QNetworkRequest(url), file.data()); + QObject::connect(reply, &QNetworkReply::finished, reply, [reply, promise, destPath, file]() { + if (reply->error() == QNetworkReply::NoError) + promise->addResult(destPath); + else + promise->setException(NetworkException(reply)); + + const qint64 bytesAvailable = reply->bytesAvailable(); + if (bytesAvailable) + file->write(reply->read(bytesAvailable)); + file->close(); + promise->finish(); + reply->deleteLater(); + }); + + QObject::connect(reply, &QNetworkReply::readyRead, reply, [promise, reply, file]() { + if (promise->isCanceled()) { + reply->abort(); + return; + } + const qint64 available = reply->bytesAvailable(); + file->write(reply->read(available)); + }); + + QObject::connect(reply, + &QNetworkReply::downloadProgress, + reply, + [promise, reply](qint64 bytesReceived, qint64 totalBytes) { + if (promise->isCanceled()) { + reply->abort(); + return; + } + int progress = (totalBytes > 0) ? 100.0 * bytesReceived / totalBytes : 0; + promise->setProgressRange(0, 100); + promise->setProgressValue(progress); + }); + + fileDownloadWatcher->setFuture(result); + emit q->downloadingFileChanged(url); + + return result; +} + +QFuture<QByteArray> AssetDownloaderPrivate::downloadOrRead(const QUrl &url, const QUrl &localFile) +{ + Q_Q(AssetDownloader); + Q_ASSERT_X(QThread::currentThread() != q->thread(), + "AssetDownloader::downloadOrRead()", + "Method called from wrong thread"); + + QSharedPointer<QPromise<QByteArray>> promise(new QPromise<QByteArray>); + QFuture<QByteArray> futureResult = promise->future(); + promise->start(); + + // Precheck local file + if (!localFile.isEmpty()) { + QFile file(pathFromUrl(localFile)); + if (!file.open(QIODevice::ReadOnly)) + qWarning() << "Cannot open local file" << localFile; + } + + download(url) + .then([promise](const QByteArray &content) { + if (promise->isCanceled()) + return; + + promise->addResult(content); + promise->finish(); + }) + .onFailed([promise, url, localFile](const QException &downloadException) { + QFile file(pathFromUrl(localFile)); + qWarning() << "Cannot download" << url; + + if (promise->isCanceled()) + return; + + if (localFile.isEmpty()) { + qWarning() << "Also there is no local file as a replacement"; + promise->setException(downloadException); + } else if (!file.open(QIODevice::ReadOnly)) { + qWarning() << "Also failed to open" << localFile; + promise->setException(FileException(&file)); + } else { + promise->addResult(file.readAll()); + } + promise->finish(); + }); + + return futureResult; +} + +/*! + * \internal + * Schedules \a assets for downloading. + * Returns a future representing the files which are not downloaded. + * \note call this method on the AssetDownloader thread + */ +QFuture<DownloadableAssets> AssetDownloaderPrivate::downloadAssets(const DownloadableAssets &assets) +{ + Q_Q(AssetDownloader); + + Q_ASSERT_X(QThread::currentThread() != q->thread(), + "AssetDownloader::downloadAssets()", + "Method called from wrong thread"); + + QSharedPointer<QPromise<DownloadableAssets>> promise(new QPromise<DownloadableAssets>); + QFuture<DownloadableAssets> future = promise->future(); + + promise->start(); + + if (assets.files.isEmpty()) { + promise->addResult(assets); + promise->finish(); + return future; + } + + setCompletedDownloadsCount(0); + setAllDownloadsCount(assets.files.size()); + setState(State::DownloadingFilesState); + + QFuture<void> schedule = QtConcurrent::run([] {}); + + for (const QUrl &assetFile : assets.files) { + const QUrl url = assets.remoteUrl.resolved(assetFile); + const QUrl dstFilePath = tempDir.absoluteFilePath(assetFile.toString()); + schedule = schedule + .then(q, + [this, url, dstFilePath] { return downloadToFile(url, dstFilePath); }) + .unwrap() + .then(q, + [this, promise]([[maybe_unused]] const QUrl &url) { + if (promise->isCanceled()) + return; + setCompletedDownloadsCount(completedDownloadsCount + 1); + }) + .onFailed([](const Exception &exception) { + qWarning() << "Download failed" << exception.message(); + }); + ; + } + schedule = schedule + .then([assets, promise, tempDir = tempDir]() { + if (promise->isCanceled()) + return; + + DownloadableAssets result = assets; + result.files = filterDownloadableAssets(result.files, tempDir); + promise->addResult(result); + promise->finish(); + }) + .onFailed([promise](const QException &exception) { + promise->setException(exception); + promise->finish(); + }); + return future; +} + +QFuture<DownloadableAssets> AssetDownloaderPrivate::maybeReadZipFile(const DownloadableAssets &assets) +{ + Q_Q(AssetDownloader); + Q_ASSERT_X(QThread::currentThread() != q->thread(), + "AssetDownloader::maybeReadZipFile()", + "Method called from wrong thread"); + + QSharedPointer<QPromise<DownloadableAssets>> promise(new QPromise<DownloadableAssets>); + QFuture<DownloadableAssets> future = promise->future(); + + promise->start(); + + if (assets.files.isEmpty() || zipFileName.isEmpty()) { + promise->addResult(assets); + promise->finish(); + return future; + } + + setState(State::DownloadingZipState); + + const QUrl zipUrl = downloadBase.resolved(zipFileName); + const QUrl dstUrl = tempDir.filePath(zipFileName); + downloadToFile(zipUrl, dstUrl) + .then(q, + [this, promise](const QUrl &downloadedFile) -> QUrl { + if (promise->isCanceled()) + return {}; + + setState(State::ExtractingZipState); + return downloadedFile; + }) + .then(QtFuture::Launch::Async, + [promise, zipFileName = zipFileName, tempDir = tempDir](const QUrl &downloadedFile) { + if (promise->isCanceled()) + return; + + unzip(pathFromUrl(downloadedFile), tempDir, UnzipRule::KeepArchive); + }) + .then([promise, assets, tempDir = tempDir]() { + if (promise->isCanceled()) + return; + + DownloadableAssets result = assets; + result.files = filterDownloadableAssets(result.files, tempDir); + promise->addResult(result); + promise->finish(); + }) + .onFailed([promise, assets](const Exception &exception) { + qWarning() << "ZipFile failed" << exception.message(); + promise->addResult(assets); + promise->finish(); + }); + + return future; +} + +AssetDownloader::AssetDownloader(QObject *parent) + : QObject(parent) + , d_ptr(new AssetDownloaderPrivate(this)) +{} + +AssetDownloader::~AssetDownloader() = default; + +QUrl AssetDownloader::downloadBase() const +{ + Q_D(const AssetDownloader); + return d->downloadBase; +} + +void AssetDownloader::setDownloadBase(const QUrl &downloadBase) +{ + Q_D(AssetDownloader); + if (d->downloadBase != downloadBase) { + d->downloadBase = downloadBase; + emit downloadBaseChanged(d->downloadBase); + } +} + +int AssetDownloader::allDownloadsCount() const +{ + Q_D(const AssetDownloader); + return d->allDownloadsCount; +} + +int AssetDownloader::completedDownloadsCount() const +{ + Q_D(const AssetDownloader); + return d->completedDownloadsCount; +} + +QUrl AssetDownloader::localDownloadDir() const +{ + Q_D(const AssetDownloader); + return QUrl::fromLocalFile(d->baseLocal.absolutePath()); +} + +double AssetDownloader::progress() const +{ + Q_D(const AssetDownloader); + return d->progress; +} + +QUrl AssetDownloader::preferredLocalDownloadDir() const +{ + Q_D(const AssetDownloader); + return QUrl::fromLocalFile(d->preferredLocalDownloadDir.absolutePath()); +} + +void AssetDownloader::setPreferredLocalDownloadDir(const QUrl &localDir) +{ + Q_D(AssetDownloader); + + if (!localDir.isLocalFile()) + qWarning() << "preferredLocalDownloadDir Should be a local directory"; + + const QString path = pathFromUrl(localDir); + if (d->preferredLocalDownloadDir != path) { + d->preferredLocalDownloadDir.setPath(path); + emit preferredLocalDownloadDirChanged(preferredLocalDownloadDir()); + } +} + +QUrl AssetDownloader::offlineAssetsFilePath() const +{ + Q_D(const AssetDownloader); + return d->offlineAssetsFilePath; +} + +void AssetDownloader::setOfflineAssetsFilePath(const QUrl &offlineAssetsFilePath) +{ + Q_D(AssetDownloader); + if (d->offlineAssetsFilePath != offlineAssetsFilePath) { + d->offlineAssetsFilePath = offlineAssetsFilePath; + emit offlineAssetsFilePathChanged(d->offlineAssetsFilePath); + } +} + +QString AssetDownloader::jsonFileName() const +{ + Q_D(const AssetDownloader); + return d->jsonFileName; +} + +void AssetDownloader::setJsonFileName(const QString &jsonFileName) +{ + Q_D(AssetDownloader); + if (d->jsonFileName != jsonFileName) { + d->jsonFileName = jsonFileName; + emit jsonFileNameChanged(d->jsonFileName); + } +} + +QString AssetDownloader::zipFileName() const +{ + Q_D(const AssetDownloader); + return d->zipFileName; +} + +void AssetDownloader::setZipFileName(const QString &zipFileName) +{ + Q_D(AssetDownloader); + if (d->zipFileName != zipFileName) { + d->zipFileName = zipFileName; + emit zipFileNameChanged(d->zipFileName); + } +} + +AssetDownloader::State AssetDownloader::state() const +{ + Q_D(const AssetDownloader); + return d->state; +} + +void AssetDownloader::start() +{ + Q_D(AssetDownloader); + if (d->downloadStarted) + return; + + d->setState(State::NotDownloadedState); + d->setupBase(); + + d->downloadOrRead(d->downloadBase.resolved(d->jsonFileName), d->offlineAssetsFilePath) + .then(QtFuture::Launch::Async, &readAssetsFile) + .then(this, + [this, d](const DownloadableAssets &assets) { + d->downloadStarted = true; + d->downloadableAssets = assets; + emit downloadStarted(); + return assets; + }) + .then(QtFuture::Launch::Async, + [localDir = d->baseLocal](DownloadableAssets assets) -> DownloadableAssets { + if (shouldDownloadEverything(assets.files, localDir)) + return assets; + return {}; + }) + .then(this, [d](const DownloadableAssets &assets) { return d->maybeReadZipFile(assets); }) + .unwrap() + .then(this, [d](const DownloadableAssets &assets) { return d->downloadAssets(assets); }) + .unwrap() + .then(this, [d](const DownloadableAssets &) { return d->moveAllAssets(); }) + .then(this, + [d]() { + d->setState(State::DownloadedState); + d->finalizeDownload(); + }) + .onFailed(this, [d](const Exception &exception) { + qWarning() << "Exception: " << exception.message(); + d->setState(State::ErrorState); + d->finalizeDownload(); + }); + ; +} + +void AssetDownloader::setProgressPercent(int progress) +{ + Q_D(AssetDownloader); + const double progressInRange = 0.01 * progress; + if (d->progress != progressInRange) { + d->progress = progressInRange; + emit progressChanged(d->progress); + } +} diff --git a/src/assets/downloader/assetdownloader.h b/src/assets/downloader/assetdownloader.h new file mode 100644 index 0000000000..70b710ff19 --- /dev/null +++ b/src/assets/downloader/assetdownloader.h @@ -0,0 +1,150 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef ASSETDOWNLOADER_H +#define ASSETDOWNLOADER_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/QObject> +#include <QtCore/QScopedPointer> +#include <QtCore/QUrl> + +QT_BEGIN_NAMESPACE + +namespace QtExamples { + +class AssetDownloaderPrivate; +class AssetDownloader : public QObject +{ + Q_OBJECT + + Q_PROPERTY( + QUrl downloadBase + READ downloadBase + WRITE setDownloadBase + NOTIFY downloadBaseChanged) + + Q_PROPERTY( + int completedDownloadsCount + READ completedDownloadsCount + NOTIFY completedDownloadsCountChanged) + + Q_PROPERTY( + int allDownloadsCount + READ allDownloadsCount + NOTIFY allDownloadsCountChanged) + + Q_PROPERTY( + double progress + READ progress + NOTIFY progressChanged) + + Q_PROPERTY( + QUrl preferredLocalDownloadDir + READ preferredLocalDownloadDir + WRITE setPreferredLocalDownloadDir + NOTIFY preferredLocalDownloadDirChanged) + + Q_PROPERTY( + QUrl localDownloadDir + READ localDownloadDir + NOTIFY localDownloadDirChanged) + + Q_PROPERTY( + QUrl offlineAssetsFilePath + READ offlineAssetsFilePath + WRITE setOfflineAssetsFilePath + NOTIFY offlineAssetsFilePathChanged) + + Q_PROPERTY( + QString jsonFileName + READ jsonFileName + WRITE setJsonFileName + NOTIFY jsonFileNameChanged) + + Q_PROPERTY( + QString zipFileName + READ zipFileName + WRITE setZipFileName + NOTIFY zipFileNameChanged) + + Q_PROPERTY(State state READ state NOTIFY stateChanged) + +public: + enum class State { + NotDownloadedState, + DownloadingZipState, + ExtractingZipState, + DownloadingFilesState, + MovingFilesState, + DownloadedState, + ErrorState + }; + Q_ENUM(State) + + AssetDownloader(QObject *parent = nullptr); + ~AssetDownloader(); + + QUrl downloadBase() const; + void setDownloadBase(const QUrl &downloadBase); + + int allDownloadsCount() const; + int completedDownloadsCount() const; + QUrl localDownloadDir() const; + double progress() const; + + QUrl preferredLocalDownloadDir() const; + void setPreferredLocalDownloadDir(const QUrl &localDir); + + QUrl offlineAssetsFilePath() const; + void setOfflineAssetsFilePath(const QUrl &offlineAssetsFilePath); + + QString jsonFileName() const; + void setJsonFileName(const QString &jsonFileName); + + QString zipFileName() const; + void setZipFileName(const QString &zipFileName); + + State state() const; + +public Q_SLOTS: + void start(); + +private Q_SLOTS: + void setProgressPercent(int); + +Q_SIGNALS: + void stateChanged(State); + void downloadBaseChanged(const QUrl &); + void allDownloadsCountChanged(int count); + void downloadStarted(); + void completedDownloadsCountChanged(int count); + void downloadFinished(); + void downloadingFileChanged(const QUrl &url); + void preferredLocalDownloadDirChanged(const QUrl &url); + void localDownloadDirChanged(const QUrl &url); + void progressChanged(double progress); + void offlineAssetsFilePathChanged(const QUrl &); + void jsonFileNameChanged(const QString &); + void zipFileNameChanged(const QString &); + +private: + Q_DECLARE_PRIVATE(AssetDownloader) + QScopedPointer<AssetDownloaderPrivate> d_ptr; +}; + +} // namespace QtExamples + +QT_END_NAMESPACE + +#endif // ASSETDOWNLOADER_H diff --git a/src/assets/icons/128x128/document-new.png b/src/assets/icons/128x128/document-new.png Binary files differnew file mode 100644 index 0000000000..8d86a4827a --- /dev/null +++ b/src/assets/icons/128x128/document-new.png diff --git a/src/assets/icons/128x128/document-open.png b/src/assets/icons/128x128/document-open.png Binary files differnew file mode 100644 index 0000000000..2183dbbea6 --- /dev/null +++ b/src/assets/icons/128x128/document-open.png diff --git a/src/assets/icons/128x128/document-print.png b/src/assets/icons/128x128/document-print.png Binary files differnew file mode 100644 index 0000000000..9e7378aab2 --- /dev/null +++ b/src/assets/icons/128x128/document-print.png diff --git a/src/assets/icons/128x128/document-save.png b/src/assets/icons/128x128/document-save.png Binary files differnew file mode 100644 index 0000000000..e8b2840643 --- /dev/null +++ b/src/assets/icons/128x128/document-save.png diff --git a/src/assets/icons/128x128/edit-copy.png b/src/assets/icons/128x128/edit-copy.png Binary files differnew file mode 100644 index 0000000000..7585f4baa0 --- /dev/null +++ b/src/assets/icons/128x128/edit-copy.png diff --git a/src/assets/icons/128x128/edit-cut.png b/src/assets/icons/128x128/edit-cut.png Binary files differnew file mode 100644 index 0000000000..51ede2fe37 --- /dev/null +++ b/src/assets/icons/128x128/edit-cut.png diff --git a/src/assets/icons/128x128/edit-delete.png b/src/assets/icons/128x128/edit-delete.png Binary files differnew file mode 100644 index 0000000000..bdf785c828 --- /dev/null +++ b/src/assets/icons/128x128/edit-delete.png diff --git a/src/assets/icons/128x128/edit-paste.png b/src/assets/icons/128x128/edit-paste.png Binary files differnew file mode 100644 index 0000000000..690ffa172d --- /dev/null +++ b/src/assets/icons/128x128/edit-paste.png diff --git a/src/assets/icons/128x128/edit-redo.png b/src/assets/icons/128x128/edit-redo.png Binary files differnew file mode 100644 index 0000000000..f1c97f71c2 --- /dev/null +++ b/src/assets/icons/128x128/edit-redo.png diff --git a/src/assets/icons/128x128/edit-undo.png b/src/assets/icons/128x128/edit-undo.png Binary files differnew file mode 100644 index 0000000000..e728cbf6e0 --- /dev/null +++ b/src/assets/icons/128x128/edit-undo.png diff --git a/src/assets/icons/128x128/format-justify-center.png b/src/assets/icons/128x128/format-justify-center.png Binary files differnew file mode 100644 index 0000000000..44ceb2af4d --- /dev/null +++ b/src/assets/icons/128x128/format-justify-center.png diff --git a/src/assets/icons/128x128/format-justify-fill.png b/src/assets/icons/128x128/format-justify-fill.png Binary files differnew file mode 100644 index 0000000000..b99a850704 --- /dev/null +++ b/src/assets/icons/128x128/format-justify-fill.png diff --git a/src/assets/icons/128x128/format-justify-left.png b/src/assets/icons/128x128/format-justify-left.png Binary files differnew file mode 100644 index 0000000000..2b63887b49 --- /dev/null +++ b/src/assets/icons/128x128/format-justify-left.png diff --git a/src/assets/icons/128x128/format-justify-right.png b/src/assets/icons/128x128/format-justify-right.png Binary files differnew file mode 100644 index 0000000000..6c61889d59 --- /dev/null +++ b/src/assets/icons/128x128/format-justify-right.png diff --git a/src/assets/icons/128x128/format-text-bold.png b/src/assets/icons/128x128/format-text-bold.png Binary files differnew file mode 100644 index 0000000000..96a5ca88a2 --- /dev/null +++ b/src/assets/icons/128x128/format-text-bold.png diff --git a/src/assets/icons/128x128/format-text-italic.png b/src/assets/icons/128x128/format-text-italic.png Binary files differnew file mode 100644 index 0000000000..2bb71b4a4d --- /dev/null +++ b/src/assets/icons/128x128/format-text-italic.png diff --git a/src/assets/icons/128x128/format-text-underline.png b/src/assets/icons/128x128/format-text-underline.png Binary files differnew file mode 100644 index 0000000000..ecf6830c92 --- /dev/null +++ b/src/assets/icons/128x128/format-text-underline.png diff --git a/src/assets/icons/128x128@2/document-new@2x.png b/src/assets/icons/128x128@2/document-new@2x.png Binary files differnew file mode 100644 index 0000000000..32776b51a9 --- /dev/null +++ b/src/assets/icons/128x128@2/document-new@2x.png diff --git a/src/assets/icons/128x128@2/document-open@2x.png b/src/assets/icons/128x128@2/document-open@2x.png Binary files differnew file mode 100644 index 0000000000..06e188b93b --- /dev/null +++ b/src/assets/icons/128x128@2/document-open@2x.png diff --git a/src/assets/icons/128x128@2/document-print@2x.png b/src/assets/icons/128x128@2/document-print@2x.png Binary files differnew file mode 100644 index 0000000000..644e3c149a --- /dev/null +++ b/src/assets/icons/128x128@2/document-print@2x.png diff --git a/src/assets/icons/128x128@2/document-save@2x.png b/src/assets/icons/128x128@2/document-save@2x.png Binary files differnew file mode 100644 index 0000000000..16fa70493a --- /dev/null +++ b/src/assets/icons/128x128@2/document-save@2x.png diff --git a/src/assets/icons/128x128@2/edit-copy@2x.png b/src/assets/icons/128x128@2/edit-copy@2x.png Binary files differnew file mode 100644 index 0000000000..b18bead117 --- /dev/null +++ b/src/assets/icons/128x128@2/edit-copy@2x.png diff --git a/src/assets/icons/128x128@2/edit-cut@2x.png b/src/assets/icons/128x128@2/edit-cut@2x.png Binary files differnew file mode 100644 index 0000000000..d9454cebf1 --- /dev/null +++ b/src/assets/icons/128x128@2/edit-cut@2x.png diff --git a/src/assets/icons/128x128@2/edit-delete@2x.png b/src/assets/icons/128x128@2/edit-delete@2x.png Binary files differnew file mode 100644 index 0000000000..4081cdb2ca --- /dev/null +++ b/src/assets/icons/128x128@2/edit-delete@2x.png diff --git a/src/assets/icons/128x128@2/edit-paste@2x.png b/src/assets/icons/128x128@2/edit-paste@2x.png Binary files differnew file mode 100644 index 0000000000..3358426818 --- /dev/null +++ b/src/assets/icons/128x128@2/edit-paste@2x.png diff --git a/src/assets/icons/128x128@2/edit-redo@2x.png b/src/assets/icons/128x128@2/edit-redo@2x.png Binary files differnew file mode 100644 index 0000000000..e28b28542c --- /dev/null +++ b/src/assets/icons/128x128@2/edit-redo@2x.png diff --git a/src/assets/icons/128x128@2/edit-undo@2x.png b/src/assets/icons/128x128@2/edit-undo@2x.png Binary files differnew file mode 100644 index 0000000000..fe10f57a39 --- /dev/null +++ b/src/assets/icons/128x128@2/edit-undo@2x.png diff --git a/src/assets/icons/128x128@2/format-justify-center@2x.png b/src/assets/icons/128x128@2/format-justify-center@2x.png Binary files differnew file mode 100644 index 0000000000..d4ad74b0d0 --- /dev/null +++ b/src/assets/icons/128x128@2/format-justify-center@2x.png diff --git a/src/assets/icons/128x128@2/format-justify-fill@2x.png b/src/assets/icons/128x128@2/format-justify-fill@2x.png Binary files differnew file mode 100644 index 0000000000..bf0dd84bbb --- /dev/null +++ b/src/assets/icons/128x128@2/format-justify-fill@2x.png diff --git a/src/assets/icons/128x128@2/format-justify-left@2x.png b/src/assets/icons/128x128@2/format-justify-left@2x.png Binary files differnew file mode 100644 index 0000000000..dde68c8514 --- /dev/null +++ b/src/assets/icons/128x128@2/format-justify-left@2x.png diff --git a/src/assets/icons/128x128@2/format-justify-right@2x.png b/src/assets/icons/128x128@2/format-justify-right@2x.png Binary files differnew file mode 100644 index 0000000000..8a5e7518bd --- /dev/null +++ b/src/assets/icons/128x128@2/format-justify-right@2x.png diff --git a/src/assets/icons/128x128@2/format-text-bold@2x.png b/src/assets/icons/128x128@2/format-text-bold@2x.png Binary files differnew file mode 100644 index 0000000000..665d3ce37b --- /dev/null +++ b/src/assets/icons/128x128@2/format-text-bold@2x.png diff --git a/src/assets/icons/128x128@2/format-text-italic@2x.png b/src/assets/icons/128x128@2/format-text-italic@2x.png Binary files differnew file mode 100644 index 0000000000..4b6846a6b9 --- /dev/null +++ b/src/assets/icons/128x128@2/format-text-italic@2x.png diff --git a/src/assets/icons/128x128@2/format-text-underline@2x.png b/src/assets/icons/128x128@2/format-text-underline@2x.png Binary files differnew file mode 100644 index 0000000000..601f73216a --- /dev/null +++ b/src/assets/icons/128x128@2/format-text-underline@2x.png diff --git a/src/assets/icons/16x16/document-new.png b/src/assets/icons/16x16/document-new.png Binary files differnew file mode 100644 index 0000000000..893e7e1aec --- /dev/null +++ b/src/assets/icons/16x16/document-new.png diff --git a/src/assets/icons/16x16/document-open.png b/src/assets/icons/16x16/document-open.png Binary files differnew file mode 100644 index 0000000000..b07906f40b --- /dev/null +++ b/src/assets/icons/16x16/document-open.png diff --git a/src/assets/icons/16x16/document-print.png b/src/assets/icons/16x16/document-print.png Binary files differnew file mode 100644 index 0000000000..9341060076 --- /dev/null +++ b/src/assets/icons/16x16/document-print.png diff --git a/src/assets/icons/16x16/document-save.png b/src/assets/icons/16x16/document-save.png Binary files differnew file mode 100644 index 0000000000..6238718191 --- /dev/null +++ b/src/assets/icons/16x16/document-save.png diff --git a/src/assets/icons/16x16/edit-copy.png b/src/assets/icons/16x16/edit-copy.png Binary files differnew file mode 100644 index 0000000000..585f5bfc8d --- /dev/null +++ b/src/assets/icons/16x16/edit-copy.png diff --git a/src/assets/icons/16x16/edit-cut.png b/src/assets/icons/16x16/edit-cut.png Binary files differnew file mode 100644 index 0000000000..661ef1ad03 --- /dev/null +++ b/src/assets/icons/16x16/edit-cut.png diff --git a/src/assets/icons/16x16/edit-delete.png b/src/assets/icons/16x16/edit-delete.png Binary files differnew file mode 100644 index 0000000000..7b5998df8a --- /dev/null +++ b/src/assets/icons/16x16/edit-delete.png diff --git a/src/assets/icons/16x16/edit-paste.png b/src/assets/icons/16x16/edit-paste.png Binary files differnew file mode 100644 index 0000000000..6318a22caf --- /dev/null +++ b/src/assets/icons/16x16/edit-paste.png diff --git a/src/assets/icons/16x16/edit-redo.png b/src/assets/icons/16x16/edit-redo.png Binary files differnew file mode 100644 index 0000000000..7eb10fe899 --- /dev/null +++ b/src/assets/icons/16x16/edit-redo.png diff --git a/src/assets/icons/16x16/edit-undo.png b/src/assets/icons/16x16/edit-undo.png Binary files differnew file mode 100644 index 0000000000..108712547c --- /dev/null +++ b/src/assets/icons/16x16/edit-undo.png diff --git a/src/assets/icons/16x16/format-justify-center.png b/src/assets/icons/16x16/format-justify-center.png Binary files differnew file mode 100644 index 0000000000..6b0951fa5d --- /dev/null +++ b/src/assets/icons/16x16/format-justify-center.png diff --git a/src/assets/icons/16x16/format-justify-fill.png b/src/assets/icons/16x16/format-justify-fill.png Binary files differnew file mode 100644 index 0000000000..6e1c10d7c4 --- /dev/null +++ b/src/assets/icons/16x16/format-justify-fill.png diff --git a/src/assets/icons/16x16/format-justify-left.png b/src/assets/icons/16x16/format-justify-left.png Binary files differnew file mode 100644 index 0000000000..9dfdc89b68 --- /dev/null +++ b/src/assets/icons/16x16/format-justify-left.png diff --git a/src/assets/icons/16x16/format-justify-right.png b/src/assets/icons/16x16/format-justify-right.png Binary files differnew file mode 100644 index 0000000000..36a52081f1 --- /dev/null +++ b/src/assets/icons/16x16/format-justify-right.png diff --git a/src/assets/icons/16x16/format-text-bold.png b/src/assets/icons/16x16/format-text-bold.png Binary files differnew file mode 100644 index 0000000000..a079317a94 --- /dev/null +++ b/src/assets/icons/16x16/format-text-bold.png diff --git a/src/assets/icons/16x16/format-text-italic.png b/src/assets/icons/16x16/format-text-italic.png Binary files differnew file mode 100644 index 0000000000..04202b2842 --- /dev/null +++ b/src/assets/icons/16x16/format-text-italic.png diff --git a/src/assets/icons/16x16/format-text-underline.png b/src/assets/icons/16x16/format-text-underline.png Binary files differnew file mode 100644 index 0000000000..a80368212d --- /dev/null +++ b/src/assets/icons/16x16/format-text-underline.png diff --git a/src/assets/icons/16x16@2/document-new@2x.png b/src/assets/icons/16x16@2/document-new@2x.png Binary files differnew file mode 100644 index 0000000000..482ae52024 --- /dev/null +++ b/src/assets/icons/16x16@2/document-new@2x.png diff --git a/src/assets/icons/16x16@2/document-open@2x.png b/src/assets/icons/16x16@2/document-open@2x.png Binary files differnew file mode 100644 index 0000000000..9858b146f4 --- /dev/null +++ b/src/assets/icons/16x16@2/document-open@2x.png diff --git a/src/assets/icons/16x16@2/document-print@2x.png b/src/assets/icons/16x16@2/document-print@2x.png Binary files differnew file mode 100644 index 0000000000..1672ec5897 --- /dev/null +++ b/src/assets/icons/16x16@2/document-print@2x.png diff --git a/src/assets/icons/16x16@2/document-save@2x.png b/src/assets/icons/16x16@2/document-save@2x.png Binary files differnew file mode 100644 index 0000000000..f04de74673 --- /dev/null +++ b/src/assets/icons/16x16@2/document-save@2x.png diff --git a/src/assets/icons/16x16@2/edit-copy@2x.png b/src/assets/icons/16x16@2/edit-copy@2x.png Binary files differnew file mode 100644 index 0000000000..bbb34cc4c2 --- /dev/null +++ b/src/assets/icons/16x16@2/edit-copy@2x.png diff --git a/src/assets/icons/16x16@2/edit-cut@2x.png b/src/assets/icons/16x16@2/edit-cut@2x.png Binary files differnew file mode 100644 index 0000000000..d89ef6c016 --- /dev/null +++ b/src/assets/icons/16x16@2/edit-cut@2x.png diff --git a/src/assets/icons/16x16@2/edit-delete@2x.png b/src/assets/icons/16x16@2/edit-delete@2x.png Binary files differnew file mode 100644 index 0000000000..4c97ee2495 --- /dev/null +++ b/src/assets/icons/16x16@2/edit-delete@2x.png diff --git a/src/assets/icons/16x16@2/edit-paste@2x.png b/src/assets/icons/16x16@2/edit-paste@2x.png Binary files differnew file mode 100644 index 0000000000..299fa77686 --- /dev/null +++ b/src/assets/icons/16x16@2/edit-paste@2x.png diff --git a/src/assets/icons/16x16@2/edit-redo@2x.png b/src/assets/icons/16x16@2/edit-redo@2x.png Binary files differnew file mode 100644 index 0000000000..4f8849c711 --- /dev/null +++ b/src/assets/icons/16x16@2/edit-redo@2x.png diff --git a/src/assets/icons/16x16@2/edit-undo@2x.png b/src/assets/icons/16x16@2/edit-undo@2x.png Binary files differnew file mode 100644 index 0000000000..b3d366c53f --- /dev/null +++ b/src/assets/icons/16x16@2/edit-undo@2x.png diff --git a/src/assets/icons/16x16@2/format-justify-center@2x.png b/src/assets/icons/16x16@2/format-justify-center@2x.png Binary files differnew file mode 100644 index 0000000000..80c3afd9a6 --- /dev/null +++ b/src/assets/icons/16x16@2/format-justify-center@2x.png diff --git a/src/assets/icons/16x16@2/format-justify-fill@2x.png b/src/assets/icons/16x16@2/format-justify-fill@2x.png Binary files differnew file mode 100644 index 0000000000..33589ea25d --- /dev/null +++ b/src/assets/icons/16x16@2/format-justify-fill@2x.png diff --git a/src/assets/icons/16x16@2/format-justify-left@2x.png b/src/assets/icons/16x16@2/format-justify-left@2x.png Binary files differnew file mode 100644 index 0000000000..ba02821135 --- /dev/null +++ b/src/assets/icons/16x16@2/format-justify-left@2x.png diff --git a/src/assets/icons/16x16@2/format-justify-right@2x.png b/src/assets/icons/16x16@2/format-justify-right@2x.png Binary files differnew file mode 100644 index 0000000000..8e15d0cb44 --- /dev/null +++ b/src/assets/icons/16x16@2/format-justify-right@2x.png diff --git a/src/assets/icons/16x16@2/format-text-bold@2x.png b/src/assets/icons/16x16@2/format-text-bold@2x.png Binary files differnew file mode 100644 index 0000000000..754efdd975 --- /dev/null +++ b/src/assets/icons/16x16@2/format-text-bold@2x.png diff --git a/src/assets/icons/16x16@2/format-text-italic@2x.png b/src/assets/icons/16x16@2/format-text-italic@2x.png Binary files differnew file mode 100644 index 0000000000..6db31a4f69 --- /dev/null +++ b/src/assets/icons/16x16@2/format-text-italic@2x.png diff --git a/src/assets/icons/16x16@2/format-text-underline@2x.png b/src/assets/icons/16x16@2/format-text-underline@2x.png Binary files differnew file mode 100644 index 0000000000..977cde9d97 --- /dev/null +++ b/src/assets/icons/16x16@2/format-text-underline@2x.png diff --git a/src/assets/icons/256x256/document-new.png b/src/assets/icons/256x256/document-new.png Binary files differnew file mode 100644 index 0000000000..32776b51a9 --- /dev/null +++ b/src/assets/icons/256x256/document-new.png diff --git a/src/assets/icons/256x256/document-open.png b/src/assets/icons/256x256/document-open.png Binary files differnew file mode 100644 index 0000000000..06e188b93b --- /dev/null +++ b/src/assets/icons/256x256/document-open.png diff --git a/src/assets/icons/256x256/document-print.png b/src/assets/icons/256x256/document-print.png Binary files differnew file mode 100644 index 0000000000..644e3c149a --- /dev/null +++ b/src/assets/icons/256x256/document-print.png diff --git a/src/assets/icons/256x256/document-save.png b/src/assets/icons/256x256/document-save.png Binary files differnew file mode 100644 index 0000000000..16fa70493a --- /dev/null +++ b/src/assets/icons/256x256/document-save.png diff --git a/src/assets/icons/256x256/edit-copy.png b/src/assets/icons/256x256/edit-copy.png Binary files differnew file mode 100644 index 0000000000..b18bead117 --- /dev/null +++ b/src/assets/icons/256x256/edit-copy.png diff --git a/src/assets/icons/256x256/edit-cut.png b/src/assets/icons/256x256/edit-cut.png Binary files differnew file mode 100644 index 0000000000..d9454cebf1 --- /dev/null +++ b/src/assets/icons/256x256/edit-cut.png diff --git a/src/assets/icons/256x256/edit-delete.png b/src/assets/icons/256x256/edit-delete.png Binary files differnew file mode 100644 index 0000000000..4081cdb2ca --- /dev/null +++ b/src/assets/icons/256x256/edit-delete.png diff --git a/src/assets/icons/256x256/edit-paste.png b/src/assets/icons/256x256/edit-paste.png Binary files differnew file mode 100644 index 0000000000..3358426818 --- /dev/null +++ b/src/assets/icons/256x256/edit-paste.png diff --git a/src/assets/icons/256x256/edit-redo.png b/src/assets/icons/256x256/edit-redo.png Binary files differnew file mode 100644 index 0000000000..e28b28542c --- /dev/null +++ b/src/assets/icons/256x256/edit-redo.png diff --git a/src/assets/icons/256x256/edit-undo.png b/src/assets/icons/256x256/edit-undo.png Binary files differnew file mode 100644 index 0000000000..fe10f57a39 --- /dev/null +++ b/src/assets/icons/256x256/edit-undo.png diff --git a/src/assets/icons/256x256/format-justify-center.png b/src/assets/icons/256x256/format-justify-center.png Binary files differnew file mode 100644 index 0000000000..d4ad74b0d0 --- /dev/null +++ b/src/assets/icons/256x256/format-justify-center.png diff --git a/src/assets/icons/256x256/format-justify-fill.png b/src/assets/icons/256x256/format-justify-fill.png Binary files differnew file mode 100644 index 0000000000..bf0dd84bbb --- /dev/null +++ b/src/assets/icons/256x256/format-justify-fill.png diff --git a/src/assets/icons/256x256/format-justify-left.png b/src/assets/icons/256x256/format-justify-left.png Binary files differnew file mode 100644 index 0000000000..dde68c8514 --- /dev/null +++ b/src/assets/icons/256x256/format-justify-left.png diff --git a/src/assets/icons/256x256/format-justify-right.png b/src/assets/icons/256x256/format-justify-right.png Binary files differnew file mode 100644 index 0000000000..8a5e7518bd --- /dev/null +++ b/src/assets/icons/256x256/format-justify-right.png diff --git a/src/assets/icons/256x256/format-text-bold.png b/src/assets/icons/256x256/format-text-bold.png Binary files differnew file mode 100644 index 0000000000..665d3ce37b --- /dev/null +++ b/src/assets/icons/256x256/format-text-bold.png diff --git a/src/assets/icons/256x256/format-text-italic.png b/src/assets/icons/256x256/format-text-italic.png Binary files differnew file mode 100644 index 0000000000..4b6846a6b9 --- /dev/null +++ b/src/assets/icons/256x256/format-text-italic.png diff --git a/src/assets/icons/256x256/format-text-underline.png b/src/assets/icons/256x256/format-text-underline.png Binary files differnew file mode 100644 index 0000000000..601f73216a --- /dev/null +++ b/src/assets/icons/256x256/format-text-underline.png diff --git a/src/assets/icons/256x256@2/document-new@2x.png b/src/assets/icons/256x256@2/document-new@2x.png Binary files differnew file mode 100644 index 0000000000..bfec6d0e6d --- /dev/null +++ b/src/assets/icons/256x256@2/document-new@2x.png diff --git a/src/assets/icons/256x256@2/document-open@2x.png b/src/assets/icons/256x256@2/document-open@2x.png Binary files differnew file mode 100644 index 0000000000..630a05f622 --- /dev/null +++ b/src/assets/icons/256x256@2/document-open@2x.png diff --git a/src/assets/icons/256x256@2/document-print@2x.png b/src/assets/icons/256x256@2/document-print@2x.png Binary files differnew file mode 100644 index 0000000000..c8611c31c4 --- /dev/null +++ b/src/assets/icons/256x256@2/document-print@2x.png diff --git a/src/assets/icons/256x256@2/document-save@2x.png b/src/assets/icons/256x256@2/document-save@2x.png Binary files differnew file mode 100644 index 0000000000..6f46095981 --- /dev/null +++ b/src/assets/icons/256x256@2/document-save@2x.png diff --git a/src/assets/icons/256x256@2/edit-copy@2x.png b/src/assets/icons/256x256@2/edit-copy@2x.png Binary files differnew file mode 100644 index 0000000000..2f350041a0 --- /dev/null +++ b/src/assets/icons/256x256@2/edit-copy@2x.png diff --git a/src/assets/icons/256x256@2/edit-cut@2x.png b/src/assets/icons/256x256@2/edit-cut@2x.png Binary files differnew file mode 100644 index 0000000000..e11cf6d234 --- /dev/null +++ b/src/assets/icons/256x256@2/edit-cut@2x.png diff --git a/src/assets/icons/256x256@2/edit-delete@2x.png b/src/assets/icons/256x256@2/edit-delete@2x.png Binary files differnew file mode 100644 index 0000000000..efe6b90bf5 --- /dev/null +++ b/src/assets/icons/256x256@2/edit-delete@2x.png diff --git a/src/assets/icons/256x256@2/edit-paste@2x.png b/src/assets/icons/256x256@2/edit-paste@2x.png Binary files differnew file mode 100644 index 0000000000..32f54b3959 --- /dev/null +++ b/src/assets/icons/256x256@2/edit-paste@2x.png diff --git a/src/assets/icons/256x256@2/edit-redo@2x.png b/src/assets/icons/256x256@2/edit-redo@2x.png Binary files differnew file mode 100644 index 0000000000..1f6e366535 --- /dev/null +++ b/src/assets/icons/256x256@2/edit-redo@2x.png diff --git a/src/assets/icons/256x256@2/edit-undo@2x.png b/src/assets/icons/256x256@2/edit-undo@2x.png Binary files differnew file mode 100644 index 0000000000..980ed37062 --- /dev/null +++ b/src/assets/icons/256x256@2/edit-undo@2x.png diff --git a/src/assets/icons/256x256@2/format-justify-center@2x.png b/src/assets/icons/256x256@2/format-justify-center@2x.png Binary files differnew file mode 100644 index 0000000000..af7044ddee --- /dev/null +++ b/src/assets/icons/256x256@2/format-justify-center@2x.png diff --git a/src/assets/icons/256x256@2/format-justify-fill@2x.png b/src/assets/icons/256x256@2/format-justify-fill@2x.png Binary files differnew file mode 100644 index 0000000000..da14563bd6 --- /dev/null +++ b/src/assets/icons/256x256@2/format-justify-fill@2x.png diff --git a/src/assets/icons/256x256@2/format-justify-left@2x.png b/src/assets/icons/256x256@2/format-justify-left@2x.png Binary files differnew file mode 100644 index 0000000000..c1025bf010 --- /dev/null +++ b/src/assets/icons/256x256@2/format-justify-left@2x.png diff --git a/src/assets/icons/256x256@2/format-justify-right@2x.png b/src/assets/icons/256x256@2/format-justify-right@2x.png Binary files differnew file mode 100644 index 0000000000..3a07e06e0f --- /dev/null +++ b/src/assets/icons/256x256@2/format-justify-right@2x.png diff --git a/src/assets/icons/256x256@2/format-text-bold@2x.png b/src/assets/icons/256x256@2/format-text-bold@2x.png Binary files differnew file mode 100644 index 0000000000..b0f4cb0995 --- /dev/null +++ b/src/assets/icons/256x256@2/format-text-bold@2x.png diff --git a/src/assets/icons/256x256@2/format-text-italic@2x.png b/src/assets/icons/256x256@2/format-text-italic@2x.png Binary files differnew file mode 100644 index 0000000000..85f0cfc1d6 --- /dev/null +++ b/src/assets/icons/256x256@2/format-text-italic@2x.png diff --git a/src/assets/icons/256x256@2/format-text-underline@2x.png b/src/assets/icons/256x256@2/format-text-underline@2x.png Binary files differnew file mode 100644 index 0000000000..51ee0aa778 --- /dev/null +++ b/src/assets/icons/256x256@2/format-text-underline@2x.png diff --git a/src/assets/icons/32x32/document-new.png b/src/assets/icons/32x32/document-new.png Binary files differnew file mode 100644 index 0000000000..482ae52024 --- /dev/null +++ b/src/assets/icons/32x32/document-new.png diff --git a/src/assets/icons/32x32/document-open.png b/src/assets/icons/32x32/document-open.png Binary files differnew file mode 100644 index 0000000000..9858b146f4 --- /dev/null +++ b/src/assets/icons/32x32/document-open.png diff --git a/src/assets/icons/32x32/document-print.png b/src/assets/icons/32x32/document-print.png Binary files differnew file mode 100644 index 0000000000..1672ec5897 --- /dev/null +++ b/src/assets/icons/32x32/document-print.png diff --git a/src/assets/icons/32x32/document-save.png b/src/assets/icons/32x32/document-save.png Binary files differnew file mode 100644 index 0000000000..f04de74673 --- /dev/null +++ b/src/assets/icons/32x32/document-save.png diff --git a/src/assets/icons/32x32/edit-copy.png b/src/assets/icons/32x32/edit-copy.png Binary files differnew file mode 100644 index 0000000000..bbb34cc4c2 --- /dev/null +++ b/src/assets/icons/32x32/edit-copy.png diff --git a/src/assets/icons/32x32/edit-cut.png b/src/assets/icons/32x32/edit-cut.png Binary files differnew file mode 100644 index 0000000000..d89ef6c016 --- /dev/null +++ b/src/assets/icons/32x32/edit-cut.png diff --git a/src/assets/icons/32x32/edit-delete.png b/src/assets/icons/32x32/edit-delete.png Binary files differnew file mode 100644 index 0000000000..4c97ee2495 --- /dev/null +++ b/src/assets/icons/32x32/edit-delete.png diff --git a/src/assets/icons/32x32/edit-paste.png b/src/assets/icons/32x32/edit-paste.png Binary files differnew file mode 100644 index 0000000000..299fa77686 --- /dev/null +++ b/src/assets/icons/32x32/edit-paste.png diff --git a/src/assets/icons/32x32/edit-redo.png b/src/assets/icons/32x32/edit-redo.png Binary files differnew file mode 100644 index 0000000000..4f8849c711 --- /dev/null +++ b/src/assets/icons/32x32/edit-redo.png diff --git a/src/assets/icons/32x32/edit-undo.png b/src/assets/icons/32x32/edit-undo.png Binary files differnew file mode 100644 index 0000000000..b3d366c53f --- /dev/null +++ b/src/assets/icons/32x32/edit-undo.png diff --git a/src/assets/icons/32x32/format-justify-center.png b/src/assets/icons/32x32/format-justify-center.png Binary files differnew file mode 100644 index 0000000000..80c3afd9a6 --- /dev/null +++ b/src/assets/icons/32x32/format-justify-center.png diff --git a/src/assets/icons/32x32/format-justify-fill.png b/src/assets/icons/32x32/format-justify-fill.png Binary files differnew file mode 100644 index 0000000000..33589ea25d --- /dev/null +++ b/src/assets/icons/32x32/format-justify-fill.png diff --git a/src/assets/icons/32x32/format-justify-left.png b/src/assets/icons/32x32/format-justify-left.png Binary files differnew file mode 100644 index 0000000000..ba02821135 --- /dev/null +++ b/src/assets/icons/32x32/format-justify-left.png diff --git a/src/assets/icons/32x32/format-justify-right.png b/src/assets/icons/32x32/format-justify-right.png Binary files differnew file mode 100644 index 0000000000..8e15d0cb44 --- /dev/null +++ b/src/assets/icons/32x32/format-justify-right.png diff --git a/src/assets/icons/32x32/format-text-bold.png b/src/assets/icons/32x32/format-text-bold.png Binary files differnew file mode 100644 index 0000000000..754efdd975 --- /dev/null +++ b/src/assets/icons/32x32/format-text-bold.png diff --git a/src/assets/icons/32x32/format-text-italic.png b/src/assets/icons/32x32/format-text-italic.png Binary files differnew file mode 100644 index 0000000000..6db31a4f69 --- /dev/null +++ b/src/assets/icons/32x32/format-text-italic.png diff --git a/src/assets/icons/32x32/format-text-underline.png b/src/assets/icons/32x32/format-text-underline.png Binary files differnew file mode 100644 index 0000000000..977cde9d97 --- /dev/null +++ b/src/assets/icons/32x32/format-text-underline.png diff --git a/src/assets/icons/32x32@2/document-new@2x.png b/src/assets/icons/32x32@2/document-new@2x.png Binary files differnew file mode 100644 index 0000000000..c924576061 --- /dev/null +++ b/src/assets/icons/32x32@2/document-new@2x.png diff --git a/src/assets/icons/32x32@2/document-open@2x.png b/src/assets/icons/32x32@2/document-open@2x.png Binary files differnew file mode 100644 index 0000000000..68e75b549a --- /dev/null +++ b/src/assets/icons/32x32@2/document-open@2x.png diff --git a/src/assets/icons/32x32@2/document-print@2x.png b/src/assets/icons/32x32@2/document-print@2x.png Binary files differnew file mode 100644 index 0000000000..b784336739 --- /dev/null +++ b/src/assets/icons/32x32@2/document-print@2x.png diff --git a/src/assets/icons/32x32@2/document-save@2x.png b/src/assets/icons/32x32@2/document-save@2x.png Binary files differnew file mode 100644 index 0000000000..f4cca4b323 --- /dev/null +++ b/src/assets/icons/32x32@2/document-save@2x.png diff --git a/src/assets/icons/32x32@2/edit-copy@2x.png b/src/assets/icons/32x32@2/edit-copy@2x.png Binary files differnew file mode 100644 index 0000000000..9690d6bb04 --- /dev/null +++ b/src/assets/icons/32x32@2/edit-copy@2x.png diff --git a/src/assets/icons/32x32@2/edit-cut@2x.png b/src/assets/icons/32x32@2/edit-cut@2x.png Binary files differnew file mode 100644 index 0000000000..408b0ae19b --- /dev/null +++ b/src/assets/icons/32x32@2/edit-cut@2x.png diff --git a/src/assets/icons/32x32@2/edit-delete@2x.png b/src/assets/icons/32x32@2/edit-delete@2x.png Binary files differnew file mode 100644 index 0000000000..58abfc1fa5 --- /dev/null +++ b/src/assets/icons/32x32@2/edit-delete@2x.png diff --git a/src/assets/icons/32x32@2/edit-paste@2x.png b/src/assets/icons/32x32@2/edit-paste@2x.png Binary files differnew file mode 100644 index 0000000000..b8c288f6c7 --- /dev/null +++ b/src/assets/icons/32x32@2/edit-paste@2x.png diff --git a/src/assets/icons/32x32@2/edit-redo@2x.png b/src/assets/icons/32x32@2/edit-redo@2x.png Binary files differnew file mode 100644 index 0000000000..89fcd33c30 --- /dev/null +++ b/src/assets/icons/32x32@2/edit-redo@2x.png diff --git a/src/assets/icons/32x32@2/edit-undo@2x.png b/src/assets/icons/32x32@2/edit-undo@2x.png Binary files differnew file mode 100644 index 0000000000..6f7ad2cb40 --- /dev/null +++ b/src/assets/icons/32x32@2/edit-undo@2x.png diff --git a/src/assets/icons/32x32@2/format-justify-center@2x.png b/src/assets/icons/32x32@2/format-justify-center@2x.png Binary files differnew file mode 100644 index 0000000000..9b2cc1ed16 --- /dev/null +++ b/src/assets/icons/32x32@2/format-justify-center@2x.png diff --git a/src/assets/icons/32x32@2/format-justify-fill@2x.png b/src/assets/icons/32x32@2/format-justify-fill@2x.png Binary files differnew file mode 100644 index 0000000000..1212e9f761 --- /dev/null +++ b/src/assets/icons/32x32@2/format-justify-fill@2x.png diff --git a/src/assets/icons/32x32@2/format-justify-left@2x.png b/src/assets/icons/32x32@2/format-justify-left@2x.png Binary files differnew file mode 100644 index 0000000000..8c0eca3037 --- /dev/null +++ b/src/assets/icons/32x32@2/format-justify-left@2x.png diff --git a/src/assets/icons/32x32@2/format-justify-right@2x.png b/src/assets/icons/32x32@2/format-justify-right@2x.png Binary files differnew file mode 100644 index 0000000000..fb0ed70252 --- /dev/null +++ b/src/assets/icons/32x32@2/format-justify-right@2x.png diff --git a/src/assets/icons/32x32@2/format-text-bold@2x.png b/src/assets/icons/32x32@2/format-text-bold@2x.png Binary files differnew file mode 100644 index 0000000000..0e67ead0b8 --- /dev/null +++ b/src/assets/icons/32x32@2/format-text-bold@2x.png diff --git a/src/assets/icons/32x32@2/format-text-italic@2x.png b/src/assets/icons/32x32@2/format-text-italic@2x.png Binary files differnew file mode 100644 index 0000000000..f746f8956f --- /dev/null +++ b/src/assets/icons/32x32@2/format-text-italic@2x.png diff --git a/src/assets/icons/32x32@2/format-text-underline@2x.png b/src/assets/icons/32x32@2/format-text-underline@2x.png Binary files differnew file mode 100644 index 0000000000..47d6fced02 --- /dev/null +++ b/src/assets/icons/32x32@2/format-text-underline@2x.png diff --git a/src/assets/icons/CMakeLists.txt b/src/assets/icons/CMakeLists.txt new file mode 100644 index 0000000000..f5adb229d8 --- /dev/null +++ b/src/assets/icons/CMakeLists.txt @@ -0,0 +1,174 @@ +# Copyright (C) 2023 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +qt_internal_add_module(ExampleIconsPrivate + CONFIG_MODULE_NAME example_icons + STATIC + INTERNAL_MODULE + NO_GENERATE_CPP_EXPORTS +) + +set(icons_resource_files + index.theme + 16x16/document-new.png + 16x16/document-open.png + 16x16/document-print.png + 16x16/document-save.png + 16x16/edit-copy.png + 16x16/edit-cut.png + 16x16/edit-delete.png + 16x16/edit-paste.png + 16x16/edit-redo.png + 16x16/edit-undo.png + 16x16/format-justify-center.png + 16x16/format-justify-fill.png + 16x16/format-justify-left.png + 16x16/format-justify-right.png + 16x16/format-text-bold.png + 16x16/format-text-italic.png + 16x16/format-text-underline.png + 16x16@2/document-new@2x.png + 16x16@2/document-open@2x.png + 16x16@2/document-print@2x.png + 16x16@2/document-save@2x.png + 16x16@2/edit-copy@2x.png + 16x16@2/edit-cut@2x.png + 16x16@2/edit-delete@2x.png + 16x16@2/edit-paste@2x.png + 16x16@2/edit-redo@2x.png + 16x16@2/edit-undo@2x.png + 16x16@2/format-justify-center@2x.png + 16x16@2/format-justify-fill@2x.png + 16x16@2/format-justify-left@2x.png + 16x16@2/format-justify-right@2x.png + 16x16@2/format-text-bold@2x.png + 16x16@2/format-text-italic@2x.png + 16x16@2/format-text-underline@2x.png + 32x32/document-new.png + 32x32/document-open.png + 32x32/document-print.png + 32x32/document-save.png + 32x32/edit-copy.png + 32x32/edit-cut.png + 32x32/edit-delete.png + 32x32/edit-paste.png + 32x32/edit-redo.png + 32x32/edit-undo.png + 32x32/format-justify-center.png + 32x32/format-justify-fill.png + 32x32/format-justify-left.png + 32x32/format-justify-right.png + 32x32/format-text-bold.png + 32x32/format-text-italic.png + 32x32/format-text-underline.png + 32x32@2/document-new@2x.png + 32x32@2/document-open@2x.png + 32x32@2/document-print@2x.png + 32x32@2/document-save@2x.png + 32x32@2/edit-copy@2x.png + 32x32@2/edit-cut@2x.png + 32x32@2/edit-delete@2x.png + 32x32@2/edit-paste@2x.png + 32x32@2/edit-redo@2x.png + 32x32@2/edit-undo@2x.png + 32x32@2/format-justify-center@2x.png + 32x32@2/format-justify-fill@2x.png + 32x32@2/format-justify-left@2x.png + 32x32@2/format-justify-right@2x.png + 32x32@2/format-text-bold@2x.png + 32x32@2/format-text-italic@2x.png + 32x32@2/format-text-underline@2x.png + 128x128/document-new.png + 128x128/document-open.png + 128x128/document-print.png + 128x128/document-save.png + 128x128/edit-copy.png + 128x128/edit-cut.png + 128x128/edit-delete.png + 128x128/edit-paste.png + 128x128/edit-redo.png + 128x128/edit-undo.png + 128x128/format-justify-center.png + 128x128/format-justify-fill.png + 128x128/format-justify-left.png + 128x128/format-justify-right.png + 128x128/format-text-bold.png + 128x128/format-text-italic.png + 128x128/format-text-underline.png + 128x128@2/document-new@2x.png + 128x128@2/document-open@2x.png + 128x128@2/document-print@2x.png + 128x128@2/document-save@2x.png + 128x128@2/edit-copy@2x.png + 128x128@2/edit-cut@2x.png + 128x128@2/edit-delete@2x.png + 128x128@2/edit-paste@2x.png + 128x128@2/edit-redo@2x.png + 128x128@2/edit-undo@2x.png + 128x128@2/format-justify-center@2x.png + 128x128@2/format-justify-fill@2x.png + 128x128@2/format-justify-left@2x.png + 128x128@2/format-justify-right@2x.png + 128x128@2/format-text-bold@2x.png + 128x128@2/format-text-italic@2x.png + 128x128@2/format-text-underline@2x.png + 256x256/document-new.png + 256x256/document-open.png + 256x256/document-print.png + 256x256/document-save.png + 256x256/edit-copy.png + 256x256/edit-cut.png + 256x256/edit-delete.png + 256x256/edit-paste.png + 256x256/edit-redo.png + 256x256/edit-undo.png + 256x256/format-justify-center.png + 256x256/format-justify-fill.png + 256x256/format-justify-left.png + 256x256/format-justify-right.png + 256x256/format-text-bold.png + 256x256/format-text-italic.png + 256x256/format-text-underline.png + 256x256@2/document-new@2x.png + 256x256@2/document-open@2x.png + 256x256@2/document-print@2x.png + 256x256@2/document-save@2x.png + 256x256@2/edit-copy@2x.png + 256x256@2/edit-cut@2x.png + 256x256@2/edit-delete@2x.png + 256x256@2/edit-paste@2x.png + 256x256@2/edit-redo@2x.png + 256x256@2/edit-undo@2x.png + 256x256@2/format-justify-center@2x.png + 256x256@2/format-justify-fill@2x.png + 256x256@2/format-justify-left@2x.png + 256x256@2/format-justify-right@2x.png + 256x256@2/format-text-bold@2x.png + 256x256@2/format-text-italic@2x.png + 256x256@2/format-text-underline@2x.png + scalable/document-new.svg + scalable/document-open.svg + scalable/document-print.svg + scalable/document-save.svg + scalable/edit-copy.svg + scalable/edit-cut.svg + scalable/edit-delete.svg + scalable/edit-paste.svg + scalable/edit-redo.svg + scalable/edit-undo.svg + scalable/format-justify-center.svg + scalable/format-justify-fill.svg + scalable/format-justify-left.svg + scalable/format-justify-right.svg + scalable/format-text-bold.svg + scalable/format-text-italic.svg + scalable/format-text-underline.svg +) + +qt_internal_add_resource(ExampleIconsPrivate "example_icons" + PREFIX + "/qt-project.org/icons/example_icons" + FILES + ${icons_resource_files} +) + diff --git a/src/assets/icons/README b/src/assets/icons/README new file mode 100644 index 0000000000..26d94e9ff1 --- /dev/null +++ b/src/assets/icons/README @@ -0,0 +1,29 @@ +Copyright (C) 2023 The Qt Company Ltd. +SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only + +Setting up a project for using Example icon library + +1. Add ExampleIconsPrivate component to your project CMakeList.txt file + ... + find_package(Qt6 + REQUIRED COMPONENTS Core Gui Widgets ExampleIconsPrivate + ) + + target_link_libraries(imageviewer PRIVATE + Qt6::Core + Qt6::Gui + Qt6::Widgets + Qt6::ExampleIconsPrivate + ) + ... + +2. Load the theme + ... + QIcon::setThemeSearchPaths(QIcon::themeSearchPaths() << u":/qt-project.org/icons"_s); + QIcon::setFallbackThemeName(u"example_icons"_s); + ... + +3. Use the icons + ... + const QIcon openIcon = QIcon::fromTheme("document-open"); + ... diff --git a/src/assets/icons/index.theme b/src/assets/icons/index.theme new file mode 100644 index 0000000000..e389719e01 --- /dev/null +++ b/src/assets/icons/index.theme @@ -0,0 +1,46 @@ +[Icon Theme] +Name=example_icons + +Directories=16x16,16x16@2,32x32,32x32@2,128x128,128x128@2,256x256,256x256@2,scalable + +[16x16] +Size=16 +Type=Fixed + +[16x16@2] +Size=16 +Scale=2 +Type=Fixed + +[32x32] +Size=32 +Type=Fixed + +[32x32@2] +Size=32 +Scale=2 +Type=Fixed + +[128x128] +Size=128 +Type=Fixed + +[128x128@2] +Size=128 +Scale=2 +Type=Fixed + +[256x256] +Size=256 +Type=Fixed + +[256x256@2] +Size=256 +Scale=2 +Type=Fixed + +[scalable] +Size=512 +Type=Scalable +MinSize=16 +MaxSize=512 diff --git a/src/assets/icons/scalable/document-new.svg b/src/assets/icons/scalable/document-new.svg new file mode 100644 index 0000000000..b926a7b0e1 --- /dev/null +++ b/src/assets/icons/scalable/document-new.svg @@ -0,0 +1 @@ +<?xml version="1.0" encoding="UTF-8"?><svg id="Outlined_icons" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path id="iconDesktopApp_New" d="m15,13c0,.28-.22.5-.5.5h-2v2c0,.28-.22.5-.5.5s-.5-.22-.5-.5v-2h-2c-.28,0-.5-.22-.5-.5s.22-.5.5-.5h2v-2c0-.28.22-.5.5-.5s.5.22.5.5v2h2c.28,0,.5.22.5.5Zm-12,2h5v1H3c-1.1,0-2-.9-2-2V2C1,.9,1.9,0,3,0h7c1.14,1.14,2.93,2.93,4,4v5h-1v-4h-2c-1.1,0-2-.9-2-2V1H3c-.55,0-1,.45-1,1v12c0,.55.45,1,1,1ZM10,1.41v1.59c0,.55.45,1,1,1h1.59l-.82-.82-1.76-1.76Z"/></svg>
\ No newline at end of file diff --git a/src/assets/icons/scalable/document-open.svg b/src/assets/icons/scalable/document-open.svg new file mode 100644 index 0000000000..778c1b7c6e --- /dev/null +++ b/src/assets/icons/scalable/document-open.svg @@ -0,0 +1 @@ +<?xml version="1.0" encoding="UTF-8"?><svg id="Outlined_icons" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path id="iconDesktopApp_Open" d="m14.5,5h-1.5v-1c0-1.1-.9-2-2-2h-5L4,0h-2C.9,0,0,.9,0,2v12c0,1.1.9,2,2,2h9s0,0,0,0c.12,0,.25-.01.36-.03.98-.16,1.94-.94,2.24-1.87l2.29-7.19c.33-1.05-.29-1.91-1.39-1.91ZM1,14V2c0-.55.45-1,1-1h1.59l1.71,1.71c.19.19.44.29.71.29h5c.55,0,1,.45,1,1v1h-5.5c-1.1,0-2.27.86-2.61,1.91l-2.29,7.19c-.09.28-.11.54-.07.78-.32-.17-.54-.49-.54-.87Zm13.94-7.4l-2.29,7.19c-.2.63-.99,1.21-1.65,1.21H3c-.2,0-.36-.05-.43-.15-.07-.1-.07-.26-.01-.45l2.29-7.19c.2-.63.99-1.21,1.65-1.21h8c.2,0,.36.05.43.15.07.1.07.26.01.45Z"/></svg>
\ No newline at end of file diff --git a/src/assets/icons/scalable/document-print.svg b/src/assets/icons/scalable/document-print.svg new file mode 100644 index 0000000000..fb8436af81 --- /dev/null +++ b/src/assets/icons/scalable/document-print.svg @@ -0,0 +1 @@ +<?xml version="1.0" encoding="UTF-8"?><svg id="Outlined_icons" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path id="iconDesktopApp_Print" d="m14,4h-2v-2c0-1.1-.9-2-2-2h-4c-1.1,0-2,.9-2,2v2h-2c-1.1,0-2,.9-2,2v4c0,1.1.9,2,2,2h2v2c0,1.1.9,2,2,2h4c1.1,0,2-.9,2-2v-2h2c1.1,0,2-.9,2-2v-4c0-1.1-.9-2-2-2ZM5,2c0-.55.45-1,1-1h4c.55,0,1,.45,1,1v2h-6v-2Zm5,13h-4c-.55,0-1-.45-1-1v-5h6v5c0,.55-.45,1-1,1Zm5-5c0,.55-.45,1-1,1h-2v-2h.5c.28,0,.5-.22.5-.5s-.22-.5-.5-.5h-.5s-8,0-8,0h0s-.5,0-.5,0c-.28,0-.5.22-.5.5s.22.5.5.5h.5v2h-2c-.55,0-1-.45-1-1v-4c0-.55.45-1,1-1h12c.55,0,1,.45,1,1v4Zm-5,1.5c0,.28-.22.5-.5.5h-3c-.28,0-.5-.22-.5-.5s.22-.5.5-.5h3c.28,0,.5.22.5.5Zm0,2c0,.28-.22.5-.5.5h-3c-.28,0-.5-.22-.5-.5s.22-.5.5-.5h3c.28,0,.5.22.5.5Z"/></svg>
\ No newline at end of file diff --git a/src/assets/icons/scalable/document-save.svg b/src/assets/icons/scalable/document-save.svg new file mode 100644 index 0000000000..03675f4dab --- /dev/null +++ b/src/assets/icons/scalable/document-save.svg @@ -0,0 +1 @@ +<?xml version="1.0" encoding="UTF-8"?><svg id="Outlined_icons" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path id="iconDesktopApp_Save" d="m16,11h0V2.29c0-1.26-1.02-2.29-2.29-2.29H2.29C1.02,0,0,1.02,0,2.29v8.71h0v1h0v1.71c0,1.26,1.02,2.29,2.29,2.29h11.43c1.26,0,2.29-1.02,2.29-2.29v-1.71h0v-1ZM5.71,1h5.29v4.5c0,.28-.22.5-.5.5h-5c-.28,0-.5-.22-.5-.5V1h.71ZM1,2.29c0-.71.58-1.29,1.29-1.29h1.71v4.5c0,.83.67,1.5,1.5,1.5h5c.83,0,1.5-.67,1.5-1.5V1h1.71c.71,0,1.29.58,1.29,1.29v8.71H1V2.29Zm14,11.43c0,.71-.58,1.29-1.29,1.29H2.29c-.71,0-1.29-.58-1.29-1.29v-1.71h14v1.71Z"/></svg>
\ No newline at end of file diff --git a/src/assets/icons/scalable/edit-copy.svg b/src/assets/icons/scalable/edit-copy.svg new file mode 100644 index 0000000000..db53ff1162 --- /dev/null +++ b/src/assets/icons/scalable/edit-copy.svg @@ -0,0 +1 @@ +<?xml version="1.0" encoding="UTF-8"?><svg id="Outlined_icons" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path id="iconDesktopApp_Copy" d="m12,0h-5c-1.1,0-2,.9-2,2h1c0-.55.45-1,1-1h4v2c0,1.1.9,2,2,2h2v6c0,.55-.45,1-1,1h-2v1h2c1.1,0,2-.9,2-2v-7c-1.07-1.07-2.86-2.86-4-4Zm1,4c-.55,0-1-.45-1-1v-1.59l1.29,1.29,1.29,1.29h-1.59Zm-6-1H2c-1.1,0-2,.9-2,2v9c0,1.1.9,2,2,2h7c1.1,0,2-.9,2-2v-7c-1.07-1.07-2.86-2.86-4-4Zm3,6v5c0,.55-.45,1-1,1H2c-.55,0-1-.45-1-1V5c0-.55.45-1,1-1h4v2c0,1.1.9,2,2,2h2v1Zm-2-2c-.55,0-1-.45-1-1v-1.59l1.29,1.29,1.29,1.29h-1.59Z"/></svg>
\ No newline at end of file diff --git a/src/assets/icons/scalable/edit-cut.svg b/src/assets/icons/scalable/edit-cut.svg new file mode 100644 index 0000000000..7f75d0b829 --- /dev/null +++ b/src/assets/icons/scalable/edit-cut.svg @@ -0,0 +1 @@ +<?xml version="1.0" encoding="UTF-8"?><svg id="Outlined_icons" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path id="iconDesktopApp_Cut" d="m15.22,12.05l-7.39-3.55,7.39-3.55c.25-.12.35-.42.23-.67-.12-.25-.42-.35-.67-.23l-8.12,3.9-2.48-1.19c1.07-.46,1.81-1.52,1.81-2.75,0-1.66-1.34-3-3-3S0,2.34,0,4c0,1.28.81,2.36,1.93,2.8.1.05.22.1.35.15l3.23,1.55-3.23,1.55c-.1.05-.18.09-.26.13-1.18.41-2.02,1.51-2.02,2.82,0,1.66,1.34,3,3,3s3-1.34,3-3c0-1.24-.75-2.29-1.81-2.75l2.48-1.19,8.12,3.9c.07.03.14.05.22.05.19,0,.37-.1.45-.28.12-.25.01-.55-.23-.67ZM3,2c1.1,0,2,.9,2,2s-.9,2-2,2-2-.9-2-2,.9-2,2-2Zm2,11c0,1.1-.9,2-2,2s-2-.9-2-2,.9-2,2-2,2,.9,2,2Z"/></svg>
\ No newline at end of file diff --git a/src/assets/icons/scalable/edit-delete.svg b/src/assets/icons/scalable/edit-delete.svg new file mode 100644 index 0000000000..15d1a9c7fd --- /dev/null +++ b/src/assets/icons/scalable/edit-delete.svg @@ -0,0 +1 @@ +<?xml version="1.0" encoding="UTF-8"?><svg id="Outlined_icons" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path id="iconDesktopApp_Delete" d="m8,0C3.58,0,0,3.58,0,8s3.58,8,8,8,8-3.58,8-8S12.42,0,8,0Zm0,15c-3.86,0-7-3.14-7-7S4.14,1,8,1s7,3.14,7,7-3.14,7-7,7Zm2.83-9.12l-2.12,2.12,2.12,2.12c.2.2.2.51,0,.71-.1.1-.23.15-.35.15s-.26-.05-.35-.15l-2.12-2.12-2.12,2.12c-.1.1-.23.15-.35.15s-.26-.05-.35-.15c-.2-.2-.2-.51,0-.71l2.12-2.12-2.12-2.12c-.2-.2-.2-.51,0-.71s.51-.2.71,0l2.12,2.12,2.12-2.12c.2-.2.51-.2.71,0s.2.51,0,.71Z"/></svg>
\ No newline at end of file diff --git a/src/assets/icons/scalable/edit-paste.svg b/src/assets/icons/scalable/edit-paste.svg new file mode 100644 index 0000000000..57e94d917d --- /dev/null +++ b/src/assets/icons/scalable/edit-paste.svg @@ -0,0 +1 @@ +<?xml version="1.0" encoding="UTF-8"?><svg id="Outlined_icons" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path id="iconDesktopApp_Paste" d="m14.09,6.09c-.71-.74-1.44-1.5-2.09-2.09h-3.78c-1.23,0-2.22.9-2.22,2v8c0,1.1.99,2,2.22,2h5.56c1.23,0,2.22-.9,2.22-2v-6c-.52-.52-1.21-1.21-1.91-1.91Zm-2.09-.68l1.38,1.38c.41.43.8.83,1.18,1.21h-1.56c-.55,0-1-.45-1-1v-1.59Zm3,5.59v3c0,.55-.55,1-1.22,1h-5.56c-.67,0-1.22-.45-1.22-1V6c0-.55.55-1,1.22-1h2.78v2c0,1.1.9,2,2,2h2v2Zm-6.5,0h5c.28,0,.5.22.5.5s-.22.5-.5.5h-5c-.28,0-.5-.22-.5-.5s.22-.5.5-.5ZM5.5,3h1c.83,0,1.5-.67,1.5-1.5v-.5h2c.55,0,1,.45,1,1v1h1v-1C12,.9,11.1,0,10,0h-2s-4,0-4,0h0s-2,0-2,0C.9,0,0,.9,0,2v10c0,1.1.9,2,2,2h3v-1h-3c-.55,0-1-.45-1-1V2c0-.55.45-1,1-1h2v.5c0,.83.67,1.5,1.5,1.5Zm1.5-1.5c0,.28-.22.5-.5.5h-1c-.28,0-.5-.22-.5-.5v-.5h2v.5Zm7,12c0,.28-.22.5-.5.5h-5c-.28,0-.5-.22-.5-.5s.22-.5.5-.5h5c.28,0,.5.22.5.5Z"/></svg>
\ No newline at end of file diff --git a/src/assets/icons/scalable/edit-redo.svg b/src/assets/icons/scalable/edit-redo.svg new file mode 100644 index 0000000000..92d60e1dd8 --- /dev/null +++ b/src/assets/icons/scalable/edit-redo.svg @@ -0,0 +1 @@ +<?xml version="1.0" encoding="UTF-8"?><svg id="Outlined_icons" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path id="iconDesktopApp_Redo" d="m15.85,4.68l-3.54-3.54c-.2-.2-.51-.2-.71,0s-.2.51,0,.71l2.68,2.68H2.5c-1.38,0-2.5,1.12-2.5,2.5v4c0,1.38,1.12,2.5,2.5,2.5h7c.28,0,.5-.22.5-.5s-.22-.5-.5-.5H2.5c-.83,0-1.5-.67-1.5-1.5v-4c0-.83.67-1.5,1.5-1.5h11.79l-2.68,2.68c-.2.2-.2.51,0,.71.1.1.23.15.35.15s.26-.05.35-.15l3.54-3.54c.2-.2.2-.51,0-.71Z"/></svg>
\ No newline at end of file diff --git a/src/assets/icons/scalable/edit-undo.svg b/src/assets/icons/scalable/edit-undo.svg new file mode 100644 index 0000000000..91731bb86f --- /dev/null +++ b/src/assets/icons/scalable/edit-undo.svg @@ -0,0 +1 @@ +<?xml version="1.0" encoding="UTF-8"?><svg id="Outlined_icons" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path id="iconDesktopApp_Undo" d="m13.5,4.54H1.71l2.68-2.68c.2-.2.2-.51,0-.71s-.51-.2-.71,0L.15,4.68c-.2.2-.2.51,0,.71l3.54,3.54c.1.1.23.15.35.15s.26-.05.35-.15c.2-.2.2-.51,0-.71l-2.68-2.68h11.79c.83,0,1.5.67,1.5,1.5v4c0,.83-.67,1.5-1.5,1.5h-7c-.28,0-.5.22-.5.5s.22.5.5.5h7c1.38,0,2.5-1.12,2.5-2.5v-4c0-1.38-1.12-2.5-2.5-2.5Z"/></svg>
\ No newline at end of file diff --git a/src/assets/icons/scalable/format-justify-center.svg b/src/assets/icons/scalable/format-justify-center.svg new file mode 100644 index 0000000000..9822c95f2f --- /dev/null +++ b/src/assets/icons/scalable/format-justify-center.svg @@ -0,0 +1 @@ +<?xml version="1.0" encoding="UTF-8"?><svg id="Outlined_icons" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path id="iconTextEditor_alignCenter" d="m15.5,8H.5c-.28,0-.5-.22-.5-.5h0c0-.28.22-.5.5-.5h15c.28,0,.5.22.5.5h0c0,.28-.22.5-.5.5Zm-2,3.5h0c0-.28-.22-.5-.5-.5H3c-.28,0-.5.22-.5.5h0c0,.28.22.5.5.5h10c.28,0,.5-.22.5-.5Zm2.5,4h0c0-.28-.22-.5-.5-.5H.5c-.28,0-.5.22-.5.5h0c0,.28.22.5.5.5h15c.28,0,.5-.22.5-.5Zm-2.5-12h0c0-.28-.22-.5-.5-.5H3c-.28,0-.5.22-.5.5h0c0,.28.22.5.5.5h10c.28,0,.5-.22.5-.5Z"/></svg>
\ No newline at end of file diff --git a/src/assets/icons/scalable/format-justify-fill.svg b/src/assets/icons/scalable/format-justify-fill.svg new file mode 100644 index 0000000000..2fa7ddfa40 --- /dev/null +++ b/src/assets/icons/scalable/format-justify-fill.svg @@ -0,0 +1 @@ +<?xml version="1.0" encoding="UTF-8"?><svg id="Outlined_icons" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path id="iconTextEditor_alignJustify" d="m15.5,8H.5c-.28,0-.5-.22-.5-.5h0c0-.28.22-.5.5-.5h15c.28,0,.5.22.5.5h0c0,.28-.22.5-.5.5Zm.5,3.5h0c0-.28-.22-.5-.5-.5H.5c-.28,0-.5.22-.5.5h0c0,.28.22.5.5.5h15c.28,0,.5-.22.5-.5Zm0,4h0c0-.28-.22-.5-.5-.5H.5c-.28,0-.5.22-.5.5h0c0,.28.22.5.5.5h15c.28,0,.5-.22.5-.5Zm0-12h0c0-.28-.22-.5-.5-.5H.5c-.28,0-.5.22-.5.5h0c0,.28.22.5.5.5h15c.28,0,.5-.22.5-.5Z"/></svg>
\ No newline at end of file diff --git a/src/assets/icons/scalable/format-justify-left.svg b/src/assets/icons/scalable/format-justify-left.svg new file mode 100644 index 0000000000..99d666428d --- /dev/null +++ b/src/assets/icons/scalable/format-justify-left.svg @@ -0,0 +1 @@ +<?xml version="1.0" encoding="UTF-8"?><svg id="Outlined_icons" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path id="iconTextEditor_alignLeft" d="m15.5,8H.5c-.28,0-.5-.22-.5-.5h0c0-.28.22-.5.5-.5h15c.28,0,.5.22.5.5h0c0,.28-.22.5-.5.5Zm-4.5,3.5h0c0-.28-.22-.5-.5-.5H.5c-.28,0-.5.22-.5.5h0c0,.28.22.5.5.5h10c.28,0,.5-.22.5-.5Zm5,4h0c0-.28-.22-.5-.5-.5H.5c-.28,0-.5.22-.5.5h0c0,.28.22.5.5.5h15c.28,0,.5-.22.5-.5ZM11,3.5h0c0-.28-.22-.5-.5-.5H.5c-.28,0-.5.22-.5.5h0c0,.28.22.5.5.5h10c.28,0,.5-.22.5-.5Z"/></svg>
\ No newline at end of file diff --git a/src/assets/icons/scalable/format-justify-right.svg b/src/assets/icons/scalable/format-justify-right.svg new file mode 100644 index 0000000000..7041f5e3f8 --- /dev/null +++ b/src/assets/icons/scalable/format-justify-right.svg @@ -0,0 +1 @@ +<?xml version="1.0" encoding="UTF-8"?><svg id="Outlined_icons" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path id="iconTextEditor_alignRight" d="m15.5,8H.5c-.28,0-.5-.22-.5-.5h0c0-.28.22-.5.5-.5h15c.28,0,.5.22.5.5h0c0,.28-.22.5-.5.5Zm.5,3.5h0c0-.28-.22-.5-.5-.5H5.5c-.28,0-.5.22-.5.5h0c0,.28.22.5.5.5h10c.28,0,.5-.22.5-.5Zm0,4h0c0-.28-.22-.5-.5-.5H.5c-.28,0-.5.22-.5.5h0c0,.28.22.5.5.5h15c.28,0,.5-.22.5-.5Zm0-12h0c0-.28-.22-.5-.5-.5H5.5c-.28,0-.5.22-.5.5h0c0,.28.22.5.5.5h10c.28,0,.5-.22.5-.5Z"/></svg>
\ No newline at end of file diff --git a/src/assets/icons/scalable/format-text-bold.svg b/src/assets/icons/scalable/format-text-bold.svg new file mode 100644 index 0000000000..c0f43e0a69 --- /dev/null +++ b/src/assets/icons/scalable/format-text-bold.svg @@ -0,0 +1 @@ +<?xml version="1.0" encoding="UTF-8"?><svg id="Outlined_icons" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path id="iconTextEditor_Bold" d="m12.06,7.75c.65-.36,1.13-.81,1.44-1.35.3-.55.46-1.29.46-2.24,0-1.45-.4-2.5-1.21-3.16-.81-.66-2.03-.99-3.67-.99H3v16h6.26c3.39,0,5.09-1.51,5.09-4.53,0-1.88-.76-3.12-2.29-3.71ZM5.59,2.24h3.34c1.59,0,2.38.74,2.38,2.22,0,1.57-.76,2.36-2.29,2.36h-3.43V2.24Zm5.45,10.98c-.43.36-1.07.54-1.93.54h-3.53v-4.74h3.48c.75,0,1.37.17,1.87.53.5.35.75.96.75,1.83s-.21,1.49-.64,1.85Z"/></svg>
\ No newline at end of file diff --git a/src/assets/icons/scalable/format-text-italic.svg b/src/assets/icons/scalable/format-text-italic.svg new file mode 100644 index 0000000000..43df7ca54d --- /dev/null +++ b/src/assets/icons/scalable/format-text-italic.svg @@ -0,0 +1 @@ +<?xml version="1.0" encoding="UTF-8"?><svg id="Outlined_icons" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><polygon id="iconTextEditor_Italic" points="6.63 0 6.18 2.01 8.18 2.01 5.45 13.99 3.45 13.99 3 16 9.56 16 10.01 13.99 8.01 13.99 10.74 2.01 12.74 2.01 13.19 0 6.63 0"/></svg>
\ No newline at end of file diff --git a/src/assets/icons/scalable/format-text-underline.svg b/src/assets/icons/scalable/format-text-underline.svg new file mode 100644 index 0000000000..62778fc579 --- /dev/null +++ b/src/assets/icons/scalable/format-text-underline.svg @@ -0,0 +1 @@ +<?xml version="1.0" encoding="UTF-8"?><svg id="Outlined_icons" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path id="iconTextEditor_Underline" d="m3.38,11.88c-.92-.75-1.38-1.94-1.38-3.57V0h2.44v8.35c0,1.79,1.02,2.69,3.06,2.69s3.06-.9,3.06-2.69V0h2.44v8.31c0,1.63-.46,2.82-1.39,3.57-.92.75-2.3,1.12-4.13,1.12s-3.2-.37-4.11-1.12Zm10.12,3.12H1.5c-.28,0-.5.22-.5.5s.22.5.5.5h12c.28,0,.5-.22.5-.5s-.22-.5-.5-.5Z"/></svg>
\ No newline at end of file |