summaryrefslogtreecommitdiffstats
path: root/src/assets
diff options
context:
space:
mode:
Diffstat (limited to 'src/assets')
-rw-r--r--src/assets/CMakeLists.txt7
-rw-r--r--src/assets/downloader/CMakeLists.txt18
-rw-r--r--src/assets/downloader/assetdownloader.cpp853
-rw-r--r--src/assets/downloader/assetdownloader.h150
-rw-r--r--src/assets/icons/128x128/document-new.pngbin0 -> 1258 bytes
-rw-r--r--src/assets/icons/128x128/document-open.pngbin0 -> 1855 bytes
-rw-r--r--src/assets/icons/128x128/document-print.pngbin0 -> 1576 bytes
-rw-r--r--src/assets/icons/128x128/document-save.pngbin0 -> 1298 bytes
-rw-r--r--src/assets/icons/128x128/edit-copy.pngbin0 -> 1480 bytes
-rw-r--r--src/assets/icons/128x128/edit-cut.pngbin0 -> 2012 bytes
-rw-r--r--src/assets/icons/128x128/edit-delete.pngbin0 -> 2665 bytes
-rw-r--r--src/assets/icons/128x128/edit-paste.pngbin0 -> 1892 bytes
-rw-r--r--src/assets/icons/128x128/edit-redo.pngbin0 -> 1239 bytes
-rw-r--r--src/assets/icons/128x128/edit-undo.pngbin0 -> 1233 bytes
-rw-r--r--src/assets/icons/128x128/format-justify-center.pngbin0 -> 663 bytes
-rw-r--r--src/assets/icons/128x128/format-justify-fill.pngbin0 -> 506 bytes
-rw-r--r--src/assets/icons/128x128/format-justify-left.pngbin0 -> 603 bytes
-rw-r--r--src/assets/icons/128x128/format-justify-right.pngbin0 -> 634 bytes
-rw-r--r--src/assets/icons/128x128/format-text-bold.pngbin0 -> 1459 bytes
-rw-r--r--src/assets/icons/128x128/format-text-italic.pngbin0 -> 1158 bytes
-rw-r--r--src/assets/icons/128x128/format-text-underline.pngbin0 -> 1263 bytes
-rw-r--r--src/assets/icons/128x128@2/document-new@2x.pngbin0 -> 2541 bytes
-rw-r--r--src/assets/icons/128x128@2/document-open@2x.pngbin0 -> 3865 bytes
-rw-r--r--src/assets/icons/128x128@2/document-print@2x.pngbin0 -> 3721 bytes
-rw-r--r--src/assets/icons/128x128@2/document-save@2x.pngbin0 -> 2682 bytes
-rw-r--r--src/assets/icons/128x128@2/edit-copy@2x.pngbin0 -> 3467 bytes
-rw-r--r--src/assets/icons/128x128@2/edit-cut@2x.pngbin0 -> 5004 bytes
-rw-r--r--src/assets/icons/128x128@2/edit-delete@2x.pngbin0 -> 5870 bytes
-rw-r--r--src/assets/icons/128x128@2/edit-paste@2x.pngbin0 -> 4005 bytes
-rw-r--r--src/assets/icons/128x128@2/edit-redo@2x.pngbin0 -> 2528 bytes
-rw-r--r--src/assets/icons/128x128@2/edit-undo@2x.pngbin0 -> 2530 bytes
-rw-r--r--src/assets/icons/128x128@2/format-justify-center@2x.pngbin0 -> 1599 bytes
-rw-r--r--src/assets/icons/128x128@2/format-justify-fill@2x.pngbin0 -> 1475 bytes
-rw-r--r--src/assets/icons/128x128@2/format-justify-left@2x.pngbin0 -> 1539 bytes
-rw-r--r--src/assets/icons/128x128@2/format-justify-right@2x.pngbin0 -> 1557 bytes
-rw-r--r--src/assets/icons/128x128@2/format-text-bold@2x.pngbin0 -> 3092 bytes
-rw-r--r--src/assets/icons/128x128@2/format-text-italic@2x.pngbin0 -> 2374 bytes
-rw-r--r--src/assets/icons/128x128@2/format-text-underline@2x.pngbin0 -> 2477 bytes
-rw-r--r--src/assets/icons/16x16/document-new.pngbin0 -> 241 bytes
-rw-r--r--src/assets/icons/16x16/document-open.pngbin0 -> 286 bytes
-rw-r--r--src/assets/icons/16x16/document-print.pngbin0 -> 261 bytes
-rw-r--r--src/assets/icons/16x16/document-save.pngbin0 -> 224 bytes
-rw-r--r--src/assets/icons/16x16/edit-copy.pngbin0 -> 216 bytes
-rw-r--r--src/assets/icons/16x16/edit-cut.pngbin0 -> 267 bytes
-rw-r--r--src/assets/icons/16x16/edit-delete.pngbin0 -> 250 bytes
-rw-r--r--src/assets/icons/16x16/edit-paste.pngbin0 -> 272 bytes
-rw-r--r--src/assets/icons/16x16/edit-redo.pngbin0 -> 253 bytes
-rw-r--r--src/assets/icons/16x16/edit-undo.pngbin0 -> 253 bytes
-rw-r--r--src/assets/icons/16x16/format-justify-center.pngbin0 -> 134 bytes
-rw-r--r--src/assets/icons/16x16/format-justify-fill.pngbin0 -> 122 bytes
-rw-r--r--src/assets/icons/16x16/format-justify-left.pngbin0 -> 128 bytes
-rw-r--r--src/assets/icons/16x16/format-justify-right.pngbin0 -> 134 bytes
-rw-r--r--src/assets/icons/16x16/format-text-bold.pngbin0 -> 264 bytes
-rw-r--r--src/assets/icons/16x16/format-text-italic.pngbin0 -> 255 bytes
-rw-r--r--src/assets/icons/16x16/format-text-underline.pngbin0 -> 186 bytes
-rw-r--r--src/assets/icons/16x16@2/document-new@2x.pngbin0 -> 318 bytes
-rw-r--r--src/assets/icons/16x16@2/document-open@2x.pngbin0 -> 455 bytes
-rw-r--r--src/assets/icons/16x16@2/document-print@2x.pngbin0 -> 393 bytes
-rw-r--r--src/assets/icons/16x16@2/document-save@2x.pngbin0 -> 311 bytes
-rw-r--r--src/assets/icons/16x16@2/edit-copy@2x.pngbin0 -> 333 bytes
-rw-r--r--src/assets/icons/16x16@2/edit-cut@2x.pngbin0 -> 416 bytes
-rw-r--r--src/assets/icons/16x16@2/edit-delete@2x.pngbin0 -> 495 bytes
-rw-r--r--src/assets/icons/16x16@2/edit-paste@2x.pngbin0 -> 464 bytes
-rw-r--r--src/assets/icons/16x16@2/edit-redo@2x.pngbin0 -> 346 bytes
-rw-r--r--src/assets/icons/16x16@2/edit-undo@2x.pngbin0 -> 347 bytes
-rw-r--r--src/assets/icons/16x16@2/format-justify-center@2x.pngbin0 -> 179 bytes
-rw-r--r--src/assets/icons/16x16@2/format-justify-fill@2x.pngbin0 -> 156 bytes
-rw-r--r--src/assets/icons/16x16@2/format-justify-left@2x.pngbin0 -> 171 bytes
-rw-r--r--src/assets/icons/16x16@2/format-justify-right@2x.pngbin0 -> 173 bytes
-rw-r--r--src/assets/icons/16x16@2/format-text-bold@2x.pngbin0 -> 430 bytes
-rw-r--r--src/assets/icons/16x16@2/format-text-italic@2x.pngbin0 -> 391 bytes
-rw-r--r--src/assets/icons/16x16@2/format-text-underline@2x.pngbin0 -> 290 bytes
-rw-r--r--src/assets/icons/256x256/document-new.pngbin0 -> 2541 bytes
-rw-r--r--src/assets/icons/256x256/document-open.pngbin0 -> 3865 bytes
-rw-r--r--src/assets/icons/256x256/document-print.pngbin0 -> 3721 bytes
-rw-r--r--src/assets/icons/256x256/document-save.pngbin0 -> 2682 bytes
-rw-r--r--src/assets/icons/256x256/edit-copy.pngbin0 -> 3467 bytes
-rw-r--r--src/assets/icons/256x256/edit-cut.pngbin0 -> 5004 bytes
-rw-r--r--src/assets/icons/256x256/edit-delete.pngbin0 -> 5870 bytes
-rw-r--r--src/assets/icons/256x256/edit-paste.pngbin0 -> 4005 bytes
-rw-r--r--src/assets/icons/256x256/edit-redo.pngbin0 -> 2528 bytes
-rw-r--r--src/assets/icons/256x256/edit-undo.pngbin0 -> 2530 bytes
-rw-r--r--src/assets/icons/256x256/format-justify-center.pngbin0 -> 1599 bytes
-rw-r--r--src/assets/icons/256x256/format-justify-fill.pngbin0 -> 1475 bytes
-rw-r--r--src/assets/icons/256x256/format-justify-left.pngbin0 -> 1539 bytes
-rw-r--r--src/assets/icons/256x256/format-justify-right.pngbin0 -> 1557 bytes
-rw-r--r--src/assets/icons/256x256/format-text-bold.pngbin0 -> 3092 bytes
-rw-r--r--src/assets/icons/256x256/format-text-italic.pngbin0 -> 2374 bytes
-rw-r--r--src/assets/icons/256x256/format-text-underline.pngbin0 -> 2477 bytes
-rw-r--r--src/assets/icons/256x256@2/document-new@2x.pngbin0 -> 5683 bytes
-rw-r--r--src/assets/icons/256x256@2/document-open@2x.pngbin0 -> 8174 bytes
-rw-r--r--src/assets/icons/256x256@2/document-print@2x.pngbin0 -> 8055 bytes
-rw-r--r--src/assets/icons/256x256@2/document-save@2x.pngbin0 -> 5666 bytes
-rw-r--r--src/assets/icons/256x256@2/edit-copy@2x.pngbin0 -> 7329 bytes
-rw-r--r--src/assets/icons/256x256@2/edit-cut@2x.pngbin0 -> 11388 bytes
-rw-r--r--src/assets/icons/256x256@2/edit-delete@2x.pngbin0 -> 12743 bytes
-rw-r--r--src/assets/icons/256x256@2/edit-paste@2x.pngbin0 -> 8485 bytes
-rw-r--r--src/assets/icons/256x256@2/edit-redo@2x.pngbin0 -> 5378 bytes
-rw-r--r--src/assets/icons/256x256@2/edit-undo@2x.pngbin0 -> 5433 bytes
-rw-r--r--src/assets/icons/256x256@2/format-justify-center@2x.pngbin0 -> 3855 bytes
-rw-r--r--src/assets/icons/256x256@2/format-justify-fill@2x.pngbin0 -> 3681 bytes
-rw-r--r--src/assets/icons/256x256@2/format-justify-left@2x.pngbin0 -> 3775 bytes
-rw-r--r--src/assets/icons/256x256@2/format-justify-right@2x.pngbin0 -> 3756 bytes
-rw-r--r--src/assets/icons/256x256@2/format-text-bold@2x.pngbin0 -> 7046 bytes
-rw-r--r--src/assets/icons/256x256@2/format-text-italic@2x.pngbin0 -> 6224 bytes
-rw-r--r--src/assets/icons/256x256@2/format-text-underline@2x.pngbin0 -> 5168 bytes
-rw-r--r--src/assets/icons/32x32/document-new.pngbin0 -> 318 bytes
-rw-r--r--src/assets/icons/32x32/document-open.pngbin0 -> 455 bytes
-rw-r--r--src/assets/icons/32x32/document-print.pngbin0 -> 393 bytes
-rw-r--r--src/assets/icons/32x32/document-save.pngbin0 -> 311 bytes
-rw-r--r--src/assets/icons/32x32/edit-copy.pngbin0 -> 333 bytes
-rw-r--r--src/assets/icons/32x32/edit-cut.pngbin0 -> 416 bytes
-rw-r--r--src/assets/icons/32x32/edit-delete.pngbin0 -> 495 bytes
-rw-r--r--src/assets/icons/32x32/edit-paste.pngbin0 -> 464 bytes
-rw-r--r--src/assets/icons/32x32/edit-redo.pngbin0 -> 346 bytes
-rw-r--r--src/assets/icons/32x32/edit-undo.pngbin0 -> 347 bytes
-rw-r--r--src/assets/icons/32x32/format-justify-center.pngbin0 -> 179 bytes
-rw-r--r--src/assets/icons/32x32/format-justify-fill.pngbin0 -> 156 bytes
-rw-r--r--src/assets/icons/32x32/format-justify-left.pngbin0 -> 171 bytes
-rw-r--r--src/assets/icons/32x32/format-justify-right.pngbin0 -> 173 bytes
-rw-r--r--src/assets/icons/32x32/format-text-bold.pngbin0 -> 430 bytes
-rw-r--r--src/assets/icons/32x32/format-text-italic.pngbin0 -> 391 bytes
-rw-r--r--src/assets/icons/32x32/format-text-underline.pngbin0 -> 290 bytes
-rw-r--r--src/assets/icons/32x32@2/document-new@2x.pngbin0 -> 588 bytes
-rw-r--r--src/assets/icons/32x32@2/document-open@2x.pngbin0 -> 923 bytes
-rw-r--r--src/assets/icons/32x32@2/document-print@2x.pngbin0 -> 762 bytes
-rw-r--r--src/assets/icons/32x32@2/document-save@2x.pngbin0 -> 565 bytes
-rw-r--r--src/assets/icons/32x32@2/edit-copy@2x.pngbin0 -> 710 bytes
-rw-r--r--src/assets/icons/32x32@2/edit-cut@2x.pngbin0 -> 843 bytes
-rw-r--r--src/assets/icons/32x32@2/edit-delete@2x.pngbin0 -> 954 bytes
-rw-r--r--src/assets/icons/32x32@2/edit-paste@2x.pngbin0 -> 909 bytes
-rw-r--r--src/assets/icons/32x32@2/edit-redo@2x.pngbin0 -> 628 bytes
-rw-r--r--src/assets/icons/32x32@2/edit-undo@2x.pngbin0 -> 608 bytes
-rw-r--r--src/assets/icons/32x32@2/format-justify-center@2x.pngbin0 -> 317 bytes
-rw-r--r--src/assets/icons/32x32@2/format-justify-fill@2x.pngbin0 -> 266 bytes
-rw-r--r--src/assets/icons/32x32@2/format-justify-left@2x.pngbin0 -> 298 bytes
-rw-r--r--src/assets/icons/32x32@2/format-justify-right@2x.pngbin0 -> 305 bytes
-rw-r--r--src/assets/icons/32x32@2/format-text-bold@2x.pngbin0 -> 777 bytes
-rw-r--r--src/assets/icons/32x32@2/format-text-italic@2x.pngbin0 -> 657 bytes
-rw-r--r--src/assets/icons/32x32@2/format-text-underline@2x.pngbin0 -> 577 bytes
-rw-r--r--src/assets/icons/CMakeLists.txt174
-rw-r--r--src/assets/icons/README29
-rw-r--r--src/assets/icons/index.theme46
-rw-r--r--src/assets/icons/scalable/document-new.svg1
-rw-r--r--src/assets/icons/scalable/document-open.svg1
-rw-r--r--src/assets/icons/scalable/document-print.svg1
-rw-r--r--src/assets/icons/scalable/document-save.svg1
-rw-r--r--src/assets/icons/scalable/edit-copy.svg1
-rw-r--r--src/assets/icons/scalable/edit-cut.svg1
-rw-r--r--src/assets/icons/scalable/edit-delete.svg1
-rw-r--r--src/assets/icons/scalable/edit-paste.svg1
-rw-r--r--src/assets/icons/scalable/edit-redo.svg1
-rw-r--r--src/assets/icons/scalable/edit-undo.svg1
-rw-r--r--src/assets/icons/scalable/format-justify-center.svg1
-rw-r--r--src/assets/icons/scalable/format-justify-fill.svg1
-rw-r--r--src/assets/icons/scalable/format-justify-left.svg1
-rw-r--r--src/assets/icons/scalable/format-justify-right.svg1
-rw-r--r--src/assets/icons/scalable/format-text-bold.svg1
-rw-r--r--src/assets/icons/scalable/format-text-italic.svg1
-rw-r--r--src/assets/icons/scalable/format-text-underline.svg1
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
new file mode 100644
index 0000000000..8d86a4827a
--- /dev/null
+++ b/src/assets/icons/128x128/document-new.png
Binary files differ
diff --git a/src/assets/icons/128x128/document-open.png b/src/assets/icons/128x128/document-open.png
new file mode 100644
index 0000000000..2183dbbea6
--- /dev/null
+++ b/src/assets/icons/128x128/document-open.png
Binary files differ
diff --git a/src/assets/icons/128x128/document-print.png b/src/assets/icons/128x128/document-print.png
new file mode 100644
index 0000000000..9e7378aab2
--- /dev/null
+++ b/src/assets/icons/128x128/document-print.png
Binary files differ
diff --git a/src/assets/icons/128x128/document-save.png b/src/assets/icons/128x128/document-save.png
new file mode 100644
index 0000000000..e8b2840643
--- /dev/null
+++ b/src/assets/icons/128x128/document-save.png
Binary files differ
diff --git a/src/assets/icons/128x128/edit-copy.png b/src/assets/icons/128x128/edit-copy.png
new file mode 100644
index 0000000000..7585f4baa0
--- /dev/null
+++ b/src/assets/icons/128x128/edit-copy.png
Binary files differ
diff --git a/src/assets/icons/128x128/edit-cut.png b/src/assets/icons/128x128/edit-cut.png
new file mode 100644
index 0000000000..51ede2fe37
--- /dev/null
+++ b/src/assets/icons/128x128/edit-cut.png
Binary files differ
diff --git a/src/assets/icons/128x128/edit-delete.png b/src/assets/icons/128x128/edit-delete.png
new file mode 100644
index 0000000000..bdf785c828
--- /dev/null
+++ b/src/assets/icons/128x128/edit-delete.png
Binary files differ
diff --git a/src/assets/icons/128x128/edit-paste.png b/src/assets/icons/128x128/edit-paste.png
new file mode 100644
index 0000000000..690ffa172d
--- /dev/null
+++ b/src/assets/icons/128x128/edit-paste.png
Binary files differ
diff --git a/src/assets/icons/128x128/edit-redo.png b/src/assets/icons/128x128/edit-redo.png
new file mode 100644
index 0000000000..f1c97f71c2
--- /dev/null
+++ b/src/assets/icons/128x128/edit-redo.png
Binary files differ
diff --git a/src/assets/icons/128x128/edit-undo.png b/src/assets/icons/128x128/edit-undo.png
new file mode 100644
index 0000000000..e728cbf6e0
--- /dev/null
+++ b/src/assets/icons/128x128/edit-undo.png
Binary files differ
diff --git a/src/assets/icons/128x128/format-justify-center.png b/src/assets/icons/128x128/format-justify-center.png
new file mode 100644
index 0000000000..44ceb2af4d
--- /dev/null
+++ b/src/assets/icons/128x128/format-justify-center.png
Binary files differ
diff --git a/src/assets/icons/128x128/format-justify-fill.png b/src/assets/icons/128x128/format-justify-fill.png
new file mode 100644
index 0000000000..b99a850704
--- /dev/null
+++ b/src/assets/icons/128x128/format-justify-fill.png
Binary files differ
diff --git a/src/assets/icons/128x128/format-justify-left.png b/src/assets/icons/128x128/format-justify-left.png
new file mode 100644
index 0000000000..2b63887b49
--- /dev/null
+++ b/src/assets/icons/128x128/format-justify-left.png
Binary files differ
diff --git a/src/assets/icons/128x128/format-justify-right.png b/src/assets/icons/128x128/format-justify-right.png
new file mode 100644
index 0000000000..6c61889d59
--- /dev/null
+++ b/src/assets/icons/128x128/format-justify-right.png
Binary files differ
diff --git a/src/assets/icons/128x128/format-text-bold.png b/src/assets/icons/128x128/format-text-bold.png
new file mode 100644
index 0000000000..96a5ca88a2
--- /dev/null
+++ b/src/assets/icons/128x128/format-text-bold.png
Binary files differ
diff --git a/src/assets/icons/128x128/format-text-italic.png b/src/assets/icons/128x128/format-text-italic.png
new file mode 100644
index 0000000000..2bb71b4a4d
--- /dev/null
+++ b/src/assets/icons/128x128/format-text-italic.png
Binary files differ
diff --git a/src/assets/icons/128x128/format-text-underline.png b/src/assets/icons/128x128/format-text-underline.png
new file mode 100644
index 0000000000..ecf6830c92
--- /dev/null
+++ b/src/assets/icons/128x128/format-text-underline.png
Binary files differ
diff --git a/src/assets/icons/128x128@2/document-new@2x.png b/src/assets/icons/128x128@2/document-new@2x.png
new file mode 100644
index 0000000000..32776b51a9
--- /dev/null
+++ b/src/assets/icons/128x128@2/document-new@2x.png
Binary files differ
diff --git a/src/assets/icons/128x128@2/document-open@2x.png b/src/assets/icons/128x128@2/document-open@2x.png
new file mode 100644
index 0000000000..06e188b93b
--- /dev/null
+++ b/src/assets/icons/128x128@2/document-open@2x.png
Binary files differ
diff --git a/src/assets/icons/128x128@2/document-print@2x.png b/src/assets/icons/128x128@2/document-print@2x.png
new file mode 100644
index 0000000000..644e3c149a
--- /dev/null
+++ b/src/assets/icons/128x128@2/document-print@2x.png
Binary files differ
diff --git a/src/assets/icons/128x128@2/document-save@2x.png b/src/assets/icons/128x128@2/document-save@2x.png
new file mode 100644
index 0000000000..16fa70493a
--- /dev/null
+++ b/src/assets/icons/128x128@2/document-save@2x.png
Binary files differ
diff --git a/src/assets/icons/128x128@2/edit-copy@2x.png b/src/assets/icons/128x128@2/edit-copy@2x.png
new file mode 100644
index 0000000000..b18bead117
--- /dev/null
+++ b/src/assets/icons/128x128@2/edit-copy@2x.png
Binary files differ
diff --git a/src/assets/icons/128x128@2/edit-cut@2x.png b/src/assets/icons/128x128@2/edit-cut@2x.png
new file mode 100644
index 0000000000..d9454cebf1
--- /dev/null
+++ b/src/assets/icons/128x128@2/edit-cut@2x.png
Binary files differ
diff --git a/src/assets/icons/128x128@2/edit-delete@2x.png b/src/assets/icons/128x128@2/edit-delete@2x.png
new file mode 100644
index 0000000000..4081cdb2ca
--- /dev/null
+++ b/src/assets/icons/128x128@2/edit-delete@2x.png
Binary files differ
diff --git a/src/assets/icons/128x128@2/edit-paste@2x.png b/src/assets/icons/128x128@2/edit-paste@2x.png
new file mode 100644
index 0000000000..3358426818
--- /dev/null
+++ b/src/assets/icons/128x128@2/edit-paste@2x.png
Binary files differ
diff --git a/src/assets/icons/128x128@2/edit-redo@2x.png b/src/assets/icons/128x128@2/edit-redo@2x.png
new file mode 100644
index 0000000000..e28b28542c
--- /dev/null
+++ b/src/assets/icons/128x128@2/edit-redo@2x.png
Binary files differ
diff --git a/src/assets/icons/128x128@2/edit-undo@2x.png b/src/assets/icons/128x128@2/edit-undo@2x.png
new file mode 100644
index 0000000000..fe10f57a39
--- /dev/null
+++ b/src/assets/icons/128x128@2/edit-undo@2x.png
Binary files differ
diff --git a/src/assets/icons/128x128@2/format-justify-center@2x.png b/src/assets/icons/128x128@2/format-justify-center@2x.png
new file mode 100644
index 0000000000..d4ad74b0d0
--- /dev/null
+++ b/src/assets/icons/128x128@2/format-justify-center@2x.png
Binary files differ
diff --git a/src/assets/icons/128x128@2/format-justify-fill@2x.png b/src/assets/icons/128x128@2/format-justify-fill@2x.png
new file mode 100644
index 0000000000..bf0dd84bbb
--- /dev/null
+++ b/src/assets/icons/128x128@2/format-justify-fill@2x.png
Binary files differ
diff --git a/src/assets/icons/128x128@2/format-justify-left@2x.png b/src/assets/icons/128x128@2/format-justify-left@2x.png
new file mode 100644
index 0000000000..dde68c8514
--- /dev/null
+++ b/src/assets/icons/128x128@2/format-justify-left@2x.png
Binary files differ
diff --git a/src/assets/icons/128x128@2/format-justify-right@2x.png b/src/assets/icons/128x128@2/format-justify-right@2x.png
new file mode 100644
index 0000000000..8a5e7518bd
--- /dev/null
+++ b/src/assets/icons/128x128@2/format-justify-right@2x.png
Binary files differ
diff --git a/src/assets/icons/128x128@2/format-text-bold@2x.png b/src/assets/icons/128x128@2/format-text-bold@2x.png
new file mode 100644
index 0000000000..665d3ce37b
--- /dev/null
+++ b/src/assets/icons/128x128@2/format-text-bold@2x.png
Binary files differ
diff --git a/src/assets/icons/128x128@2/format-text-italic@2x.png b/src/assets/icons/128x128@2/format-text-italic@2x.png
new file mode 100644
index 0000000000..4b6846a6b9
--- /dev/null
+++ b/src/assets/icons/128x128@2/format-text-italic@2x.png
Binary files differ
diff --git a/src/assets/icons/128x128@2/format-text-underline@2x.png b/src/assets/icons/128x128@2/format-text-underline@2x.png
new file mode 100644
index 0000000000..601f73216a
--- /dev/null
+++ b/src/assets/icons/128x128@2/format-text-underline@2x.png
Binary files differ
diff --git a/src/assets/icons/16x16/document-new.png b/src/assets/icons/16x16/document-new.png
new file mode 100644
index 0000000000..893e7e1aec
--- /dev/null
+++ b/src/assets/icons/16x16/document-new.png
Binary files differ
diff --git a/src/assets/icons/16x16/document-open.png b/src/assets/icons/16x16/document-open.png
new file mode 100644
index 0000000000..b07906f40b
--- /dev/null
+++ b/src/assets/icons/16x16/document-open.png
Binary files differ
diff --git a/src/assets/icons/16x16/document-print.png b/src/assets/icons/16x16/document-print.png
new file mode 100644
index 0000000000..9341060076
--- /dev/null
+++ b/src/assets/icons/16x16/document-print.png
Binary files differ
diff --git a/src/assets/icons/16x16/document-save.png b/src/assets/icons/16x16/document-save.png
new file mode 100644
index 0000000000..6238718191
--- /dev/null
+++ b/src/assets/icons/16x16/document-save.png
Binary files differ
diff --git a/src/assets/icons/16x16/edit-copy.png b/src/assets/icons/16x16/edit-copy.png
new file mode 100644
index 0000000000..585f5bfc8d
--- /dev/null
+++ b/src/assets/icons/16x16/edit-copy.png
Binary files differ
diff --git a/src/assets/icons/16x16/edit-cut.png b/src/assets/icons/16x16/edit-cut.png
new file mode 100644
index 0000000000..661ef1ad03
--- /dev/null
+++ b/src/assets/icons/16x16/edit-cut.png
Binary files differ
diff --git a/src/assets/icons/16x16/edit-delete.png b/src/assets/icons/16x16/edit-delete.png
new file mode 100644
index 0000000000..7b5998df8a
--- /dev/null
+++ b/src/assets/icons/16x16/edit-delete.png
Binary files differ
diff --git a/src/assets/icons/16x16/edit-paste.png b/src/assets/icons/16x16/edit-paste.png
new file mode 100644
index 0000000000..6318a22caf
--- /dev/null
+++ b/src/assets/icons/16x16/edit-paste.png
Binary files differ
diff --git a/src/assets/icons/16x16/edit-redo.png b/src/assets/icons/16x16/edit-redo.png
new file mode 100644
index 0000000000..7eb10fe899
--- /dev/null
+++ b/src/assets/icons/16x16/edit-redo.png
Binary files differ
diff --git a/src/assets/icons/16x16/edit-undo.png b/src/assets/icons/16x16/edit-undo.png
new file mode 100644
index 0000000000..108712547c
--- /dev/null
+++ b/src/assets/icons/16x16/edit-undo.png
Binary files differ
diff --git a/src/assets/icons/16x16/format-justify-center.png b/src/assets/icons/16x16/format-justify-center.png
new file mode 100644
index 0000000000..6b0951fa5d
--- /dev/null
+++ b/src/assets/icons/16x16/format-justify-center.png
Binary files differ
diff --git a/src/assets/icons/16x16/format-justify-fill.png b/src/assets/icons/16x16/format-justify-fill.png
new file mode 100644
index 0000000000..6e1c10d7c4
--- /dev/null
+++ b/src/assets/icons/16x16/format-justify-fill.png
Binary files differ
diff --git a/src/assets/icons/16x16/format-justify-left.png b/src/assets/icons/16x16/format-justify-left.png
new file mode 100644
index 0000000000..9dfdc89b68
--- /dev/null
+++ b/src/assets/icons/16x16/format-justify-left.png
Binary files differ
diff --git a/src/assets/icons/16x16/format-justify-right.png b/src/assets/icons/16x16/format-justify-right.png
new file mode 100644
index 0000000000..36a52081f1
--- /dev/null
+++ b/src/assets/icons/16x16/format-justify-right.png
Binary files differ
diff --git a/src/assets/icons/16x16/format-text-bold.png b/src/assets/icons/16x16/format-text-bold.png
new file mode 100644
index 0000000000..a079317a94
--- /dev/null
+++ b/src/assets/icons/16x16/format-text-bold.png
Binary files differ
diff --git a/src/assets/icons/16x16/format-text-italic.png b/src/assets/icons/16x16/format-text-italic.png
new file mode 100644
index 0000000000..04202b2842
--- /dev/null
+++ b/src/assets/icons/16x16/format-text-italic.png
Binary files differ
diff --git a/src/assets/icons/16x16/format-text-underline.png b/src/assets/icons/16x16/format-text-underline.png
new file mode 100644
index 0000000000..a80368212d
--- /dev/null
+++ b/src/assets/icons/16x16/format-text-underline.png
Binary files differ
diff --git a/src/assets/icons/16x16@2/document-new@2x.png b/src/assets/icons/16x16@2/document-new@2x.png
new file mode 100644
index 0000000000..482ae52024
--- /dev/null
+++ b/src/assets/icons/16x16@2/document-new@2x.png
Binary files differ
diff --git a/src/assets/icons/16x16@2/document-open@2x.png b/src/assets/icons/16x16@2/document-open@2x.png
new file mode 100644
index 0000000000..9858b146f4
--- /dev/null
+++ b/src/assets/icons/16x16@2/document-open@2x.png
Binary files differ
diff --git a/src/assets/icons/16x16@2/document-print@2x.png b/src/assets/icons/16x16@2/document-print@2x.png
new file mode 100644
index 0000000000..1672ec5897
--- /dev/null
+++ b/src/assets/icons/16x16@2/document-print@2x.png
Binary files differ
diff --git a/src/assets/icons/16x16@2/document-save@2x.png b/src/assets/icons/16x16@2/document-save@2x.png
new file mode 100644
index 0000000000..f04de74673
--- /dev/null
+++ b/src/assets/icons/16x16@2/document-save@2x.png
Binary files differ
diff --git a/src/assets/icons/16x16@2/edit-copy@2x.png b/src/assets/icons/16x16@2/edit-copy@2x.png
new file mode 100644
index 0000000000..bbb34cc4c2
--- /dev/null
+++ b/src/assets/icons/16x16@2/edit-copy@2x.png
Binary files differ
diff --git a/src/assets/icons/16x16@2/edit-cut@2x.png b/src/assets/icons/16x16@2/edit-cut@2x.png
new file mode 100644
index 0000000000..d89ef6c016
--- /dev/null
+++ b/src/assets/icons/16x16@2/edit-cut@2x.png
Binary files differ
diff --git a/src/assets/icons/16x16@2/edit-delete@2x.png b/src/assets/icons/16x16@2/edit-delete@2x.png
new file mode 100644
index 0000000000..4c97ee2495
--- /dev/null
+++ b/src/assets/icons/16x16@2/edit-delete@2x.png
Binary files differ
diff --git a/src/assets/icons/16x16@2/edit-paste@2x.png b/src/assets/icons/16x16@2/edit-paste@2x.png
new file mode 100644
index 0000000000..299fa77686
--- /dev/null
+++ b/src/assets/icons/16x16@2/edit-paste@2x.png
Binary files differ
diff --git a/src/assets/icons/16x16@2/edit-redo@2x.png b/src/assets/icons/16x16@2/edit-redo@2x.png
new file mode 100644
index 0000000000..4f8849c711
--- /dev/null
+++ b/src/assets/icons/16x16@2/edit-redo@2x.png
Binary files differ
diff --git a/src/assets/icons/16x16@2/edit-undo@2x.png b/src/assets/icons/16x16@2/edit-undo@2x.png
new file mode 100644
index 0000000000..b3d366c53f
--- /dev/null
+++ b/src/assets/icons/16x16@2/edit-undo@2x.png
Binary files differ
diff --git a/src/assets/icons/16x16@2/format-justify-center@2x.png b/src/assets/icons/16x16@2/format-justify-center@2x.png
new file mode 100644
index 0000000000..80c3afd9a6
--- /dev/null
+++ b/src/assets/icons/16x16@2/format-justify-center@2x.png
Binary files differ
diff --git a/src/assets/icons/16x16@2/format-justify-fill@2x.png b/src/assets/icons/16x16@2/format-justify-fill@2x.png
new file mode 100644
index 0000000000..33589ea25d
--- /dev/null
+++ b/src/assets/icons/16x16@2/format-justify-fill@2x.png
Binary files differ
diff --git a/src/assets/icons/16x16@2/format-justify-left@2x.png b/src/assets/icons/16x16@2/format-justify-left@2x.png
new file mode 100644
index 0000000000..ba02821135
--- /dev/null
+++ b/src/assets/icons/16x16@2/format-justify-left@2x.png
Binary files differ
diff --git a/src/assets/icons/16x16@2/format-justify-right@2x.png b/src/assets/icons/16x16@2/format-justify-right@2x.png
new file mode 100644
index 0000000000..8e15d0cb44
--- /dev/null
+++ b/src/assets/icons/16x16@2/format-justify-right@2x.png
Binary files differ
diff --git a/src/assets/icons/16x16@2/format-text-bold@2x.png b/src/assets/icons/16x16@2/format-text-bold@2x.png
new file mode 100644
index 0000000000..754efdd975
--- /dev/null
+++ b/src/assets/icons/16x16@2/format-text-bold@2x.png
Binary files differ
diff --git a/src/assets/icons/16x16@2/format-text-italic@2x.png b/src/assets/icons/16x16@2/format-text-italic@2x.png
new file mode 100644
index 0000000000..6db31a4f69
--- /dev/null
+++ b/src/assets/icons/16x16@2/format-text-italic@2x.png
Binary files differ
diff --git a/src/assets/icons/16x16@2/format-text-underline@2x.png b/src/assets/icons/16x16@2/format-text-underline@2x.png
new file mode 100644
index 0000000000..977cde9d97
--- /dev/null
+++ b/src/assets/icons/16x16@2/format-text-underline@2x.png
Binary files differ
diff --git a/src/assets/icons/256x256/document-new.png b/src/assets/icons/256x256/document-new.png
new file mode 100644
index 0000000000..32776b51a9
--- /dev/null
+++ b/src/assets/icons/256x256/document-new.png
Binary files differ
diff --git a/src/assets/icons/256x256/document-open.png b/src/assets/icons/256x256/document-open.png
new file mode 100644
index 0000000000..06e188b93b
--- /dev/null
+++ b/src/assets/icons/256x256/document-open.png
Binary files differ
diff --git a/src/assets/icons/256x256/document-print.png b/src/assets/icons/256x256/document-print.png
new file mode 100644
index 0000000000..644e3c149a
--- /dev/null
+++ b/src/assets/icons/256x256/document-print.png
Binary files differ
diff --git a/src/assets/icons/256x256/document-save.png b/src/assets/icons/256x256/document-save.png
new file mode 100644
index 0000000000..16fa70493a
--- /dev/null
+++ b/src/assets/icons/256x256/document-save.png
Binary files differ
diff --git a/src/assets/icons/256x256/edit-copy.png b/src/assets/icons/256x256/edit-copy.png
new file mode 100644
index 0000000000..b18bead117
--- /dev/null
+++ b/src/assets/icons/256x256/edit-copy.png
Binary files differ
diff --git a/src/assets/icons/256x256/edit-cut.png b/src/assets/icons/256x256/edit-cut.png
new file mode 100644
index 0000000000..d9454cebf1
--- /dev/null
+++ b/src/assets/icons/256x256/edit-cut.png
Binary files differ
diff --git a/src/assets/icons/256x256/edit-delete.png b/src/assets/icons/256x256/edit-delete.png
new file mode 100644
index 0000000000..4081cdb2ca
--- /dev/null
+++ b/src/assets/icons/256x256/edit-delete.png
Binary files differ
diff --git a/src/assets/icons/256x256/edit-paste.png b/src/assets/icons/256x256/edit-paste.png
new file mode 100644
index 0000000000..3358426818
--- /dev/null
+++ b/src/assets/icons/256x256/edit-paste.png
Binary files differ
diff --git a/src/assets/icons/256x256/edit-redo.png b/src/assets/icons/256x256/edit-redo.png
new file mode 100644
index 0000000000..e28b28542c
--- /dev/null
+++ b/src/assets/icons/256x256/edit-redo.png
Binary files differ
diff --git a/src/assets/icons/256x256/edit-undo.png b/src/assets/icons/256x256/edit-undo.png
new file mode 100644
index 0000000000..fe10f57a39
--- /dev/null
+++ b/src/assets/icons/256x256/edit-undo.png
Binary files differ
diff --git a/src/assets/icons/256x256/format-justify-center.png b/src/assets/icons/256x256/format-justify-center.png
new file mode 100644
index 0000000000..d4ad74b0d0
--- /dev/null
+++ b/src/assets/icons/256x256/format-justify-center.png
Binary files differ
diff --git a/src/assets/icons/256x256/format-justify-fill.png b/src/assets/icons/256x256/format-justify-fill.png
new file mode 100644
index 0000000000..bf0dd84bbb
--- /dev/null
+++ b/src/assets/icons/256x256/format-justify-fill.png
Binary files differ
diff --git a/src/assets/icons/256x256/format-justify-left.png b/src/assets/icons/256x256/format-justify-left.png
new file mode 100644
index 0000000000..dde68c8514
--- /dev/null
+++ b/src/assets/icons/256x256/format-justify-left.png
Binary files differ
diff --git a/src/assets/icons/256x256/format-justify-right.png b/src/assets/icons/256x256/format-justify-right.png
new file mode 100644
index 0000000000..8a5e7518bd
--- /dev/null
+++ b/src/assets/icons/256x256/format-justify-right.png
Binary files differ
diff --git a/src/assets/icons/256x256/format-text-bold.png b/src/assets/icons/256x256/format-text-bold.png
new file mode 100644
index 0000000000..665d3ce37b
--- /dev/null
+++ b/src/assets/icons/256x256/format-text-bold.png
Binary files differ
diff --git a/src/assets/icons/256x256/format-text-italic.png b/src/assets/icons/256x256/format-text-italic.png
new file mode 100644
index 0000000000..4b6846a6b9
--- /dev/null
+++ b/src/assets/icons/256x256/format-text-italic.png
Binary files differ
diff --git a/src/assets/icons/256x256/format-text-underline.png b/src/assets/icons/256x256/format-text-underline.png
new file mode 100644
index 0000000000..601f73216a
--- /dev/null
+++ b/src/assets/icons/256x256/format-text-underline.png
Binary files differ
diff --git a/src/assets/icons/256x256@2/document-new@2x.png b/src/assets/icons/256x256@2/document-new@2x.png
new file mode 100644
index 0000000000..bfec6d0e6d
--- /dev/null
+++ b/src/assets/icons/256x256@2/document-new@2x.png
Binary files differ
diff --git a/src/assets/icons/256x256@2/document-open@2x.png b/src/assets/icons/256x256@2/document-open@2x.png
new file mode 100644
index 0000000000..630a05f622
--- /dev/null
+++ b/src/assets/icons/256x256@2/document-open@2x.png
Binary files differ
diff --git a/src/assets/icons/256x256@2/document-print@2x.png b/src/assets/icons/256x256@2/document-print@2x.png
new file mode 100644
index 0000000000..c8611c31c4
--- /dev/null
+++ b/src/assets/icons/256x256@2/document-print@2x.png
Binary files differ
diff --git a/src/assets/icons/256x256@2/document-save@2x.png b/src/assets/icons/256x256@2/document-save@2x.png
new file mode 100644
index 0000000000..6f46095981
--- /dev/null
+++ b/src/assets/icons/256x256@2/document-save@2x.png
Binary files differ
diff --git a/src/assets/icons/256x256@2/edit-copy@2x.png b/src/assets/icons/256x256@2/edit-copy@2x.png
new file mode 100644
index 0000000000..2f350041a0
--- /dev/null
+++ b/src/assets/icons/256x256@2/edit-copy@2x.png
Binary files differ
diff --git a/src/assets/icons/256x256@2/edit-cut@2x.png b/src/assets/icons/256x256@2/edit-cut@2x.png
new file mode 100644
index 0000000000..e11cf6d234
--- /dev/null
+++ b/src/assets/icons/256x256@2/edit-cut@2x.png
Binary files differ
diff --git a/src/assets/icons/256x256@2/edit-delete@2x.png b/src/assets/icons/256x256@2/edit-delete@2x.png
new file mode 100644
index 0000000000..efe6b90bf5
--- /dev/null
+++ b/src/assets/icons/256x256@2/edit-delete@2x.png
Binary files differ
diff --git a/src/assets/icons/256x256@2/edit-paste@2x.png b/src/assets/icons/256x256@2/edit-paste@2x.png
new file mode 100644
index 0000000000..32f54b3959
--- /dev/null
+++ b/src/assets/icons/256x256@2/edit-paste@2x.png
Binary files differ
diff --git a/src/assets/icons/256x256@2/edit-redo@2x.png b/src/assets/icons/256x256@2/edit-redo@2x.png
new file mode 100644
index 0000000000..1f6e366535
--- /dev/null
+++ b/src/assets/icons/256x256@2/edit-redo@2x.png
Binary files differ
diff --git a/src/assets/icons/256x256@2/edit-undo@2x.png b/src/assets/icons/256x256@2/edit-undo@2x.png
new file mode 100644
index 0000000000..980ed37062
--- /dev/null
+++ b/src/assets/icons/256x256@2/edit-undo@2x.png
Binary files differ
diff --git a/src/assets/icons/256x256@2/format-justify-center@2x.png b/src/assets/icons/256x256@2/format-justify-center@2x.png
new file mode 100644
index 0000000000..af7044ddee
--- /dev/null
+++ b/src/assets/icons/256x256@2/format-justify-center@2x.png
Binary files differ
diff --git a/src/assets/icons/256x256@2/format-justify-fill@2x.png b/src/assets/icons/256x256@2/format-justify-fill@2x.png
new file mode 100644
index 0000000000..da14563bd6
--- /dev/null
+++ b/src/assets/icons/256x256@2/format-justify-fill@2x.png
Binary files differ
diff --git a/src/assets/icons/256x256@2/format-justify-left@2x.png b/src/assets/icons/256x256@2/format-justify-left@2x.png
new file mode 100644
index 0000000000..c1025bf010
--- /dev/null
+++ b/src/assets/icons/256x256@2/format-justify-left@2x.png
Binary files differ
diff --git a/src/assets/icons/256x256@2/format-justify-right@2x.png b/src/assets/icons/256x256@2/format-justify-right@2x.png
new file mode 100644
index 0000000000..3a07e06e0f
--- /dev/null
+++ b/src/assets/icons/256x256@2/format-justify-right@2x.png
Binary files differ
diff --git a/src/assets/icons/256x256@2/format-text-bold@2x.png b/src/assets/icons/256x256@2/format-text-bold@2x.png
new file mode 100644
index 0000000000..b0f4cb0995
--- /dev/null
+++ b/src/assets/icons/256x256@2/format-text-bold@2x.png
Binary files differ
diff --git a/src/assets/icons/256x256@2/format-text-italic@2x.png b/src/assets/icons/256x256@2/format-text-italic@2x.png
new file mode 100644
index 0000000000..85f0cfc1d6
--- /dev/null
+++ b/src/assets/icons/256x256@2/format-text-italic@2x.png
Binary files differ
diff --git a/src/assets/icons/256x256@2/format-text-underline@2x.png b/src/assets/icons/256x256@2/format-text-underline@2x.png
new file mode 100644
index 0000000000..51ee0aa778
--- /dev/null
+++ b/src/assets/icons/256x256@2/format-text-underline@2x.png
Binary files differ
diff --git a/src/assets/icons/32x32/document-new.png b/src/assets/icons/32x32/document-new.png
new file mode 100644
index 0000000000..482ae52024
--- /dev/null
+++ b/src/assets/icons/32x32/document-new.png
Binary files differ
diff --git a/src/assets/icons/32x32/document-open.png b/src/assets/icons/32x32/document-open.png
new file mode 100644
index 0000000000..9858b146f4
--- /dev/null
+++ b/src/assets/icons/32x32/document-open.png
Binary files differ
diff --git a/src/assets/icons/32x32/document-print.png b/src/assets/icons/32x32/document-print.png
new file mode 100644
index 0000000000..1672ec5897
--- /dev/null
+++ b/src/assets/icons/32x32/document-print.png
Binary files differ
diff --git a/src/assets/icons/32x32/document-save.png b/src/assets/icons/32x32/document-save.png
new file mode 100644
index 0000000000..f04de74673
--- /dev/null
+++ b/src/assets/icons/32x32/document-save.png
Binary files differ
diff --git a/src/assets/icons/32x32/edit-copy.png b/src/assets/icons/32x32/edit-copy.png
new file mode 100644
index 0000000000..bbb34cc4c2
--- /dev/null
+++ b/src/assets/icons/32x32/edit-copy.png
Binary files differ
diff --git a/src/assets/icons/32x32/edit-cut.png b/src/assets/icons/32x32/edit-cut.png
new file mode 100644
index 0000000000..d89ef6c016
--- /dev/null
+++ b/src/assets/icons/32x32/edit-cut.png
Binary files differ
diff --git a/src/assets/icons/32x32/edit-delete.png b/src/assets/icons/32x32/edit-delete.png
new file mode 100644
index 0000000000..4c97ee2495
--- /dev/null
+++ b/src/assets/icons/32x32/edit-delete.png
Binary files differ
diff --git a/src/assets/icons/32x32/edit-paste.png b/src/assets/icons/32x32/edit-paste.png
new file mode 100644
index 0000000000..299fa77686
--- /dev/null
+++ b/src/assets/icons/32x32/edit-paste.png
Binary files differ
diff --git a/src/assets/icons/32x32/edit-redo.png b/src/assets/icons/32x32/edit-redo.png
new file mode 100644
index 0000000000..4f8849c711
--- /dev/null
+++ b/src/assets/icons/32x32/edit-redo.png
Binary files differ
diff --git a/src/assets/icons/32x32/edit-undo.png b/src/assets/icons/32x32/edit-undo.png
new file mode 100644
index 0000000000..b3d366c53f
--- /dev/null
+++ b/src/assets/icons/32x32/edit-undo.png
Binary files differ
diff --git a/src/assets/icons/32x32/format-justify-center.png b/src/assets/icons/32x32/format-justify-center.png
new file mode 100644
index 0000000000..80c3afd9a6
--- /dev/null
+++ b/src/assets/icons/32x32/format-justify-center.png
Binary files differ
diff --git a/src/assets/icons/32x32/format-justify-fill.png b/src/assets/icons/32x32/format-justify-fill.png
new file mode 100644
index 0000000000..33589ea25d
--- /dev/null
+++ b/src/assets/icons/32x32/format-justify-fill.png
Binary files differ
diff --git a/src/assets/icons/32x32/format-justify-left.png b/src/assets/icons/32x32/format-justify-left.png
new file mode 100644
index 0000000000..ba02821135
--- /dev/null
+++ b/src/assets/icons/32x32/format-justify-left.png
Binary files differ
diff --git a/src/assets/icons/32x32/format-justify-right.png b/src/assets/icons/32x32/format-justify-right.png
new file mode 100644
index 0000000000..8e15d0cb44
--- /dev/null
+++ b/src/assets/icons/32x32/format-justify-right.png
Binary files differ
diff --git a/src/assets/icons/32x32/format-text-bold.png b/src/assets/icons/32x32/format-text-bold.png
new file mode 100644
index 0000000000..754efdd975
--- /dev/null
+++ b/src/assets/icons/32x32/format-text-bold.png
Binary files differ
diff --git a/src/assets/icons/32x32/format-text-italic.png b/src/assets/icons/32x32/format-text-italic.png
new file mode 100644
index 0000000000..6db31a4f69
--- /dev/null
+++ b/src/assets/icons/32x32/format-text-italic.png
Binary files differ
diff --git a/src/assets/icons/32x32/format-text-underline.png b/src/assets/icons/32x32/format-text-underline.png
new file mode 100644
index 0000000000..977cde9d97
--- /dev/null
+++ b/src/assets/icons/32x32/format-text-underline.png
Binary files differ
diff --git a/src/assets/icons/32x32@2/document-new@2x.png b/src/assets/icons/32x32@2/document-new@2x.png
new file mode 100644
index 0000000000..c924576061
--- /dev/null
+++ b/src/assets/icons/32x32@2/document-new@2x.png
Binary files differ
diff --git a/src/assets/icons/32x32@2/document-open@2x.png b/src/assets/icons/32x32@2/document-open@2x.png
new file mode 100644
index 0000000000..68e75b549a
--- /dev/null
+++ b/src/assets/icons/32x32@2/document-open@2x.png
Binary files differ
diff --git a/src/assets/icons/32x32@2/document-print@2x.png b/src/assets/icons/32x32@2/document-print@2x.png
new file mode 100644
index 0000000000..b784336739
--- /dev/null
+++ b/src/assets/icons/32x32@2/document-print@2x.png
Binary files differ
diff --git a/src/assets/icons/32x32@2/document-save@2x.png b/src/assets/icons/32x32@2/document-save@2x.png
new file mode 100644
index 0000000000..f4cca4b323
--- /dev/null
+++ b/src/assets/icons/32x32@2/document-save@2x.png
Binary files differ
diff --git a/src/assets/icons/32x32@2/edit-copy@2x.png b/src/assets/icons/32x32@2/edit-copy@2x.png
new file mode 100644
index 0000000000..9690d6bb04
--- /dev/null
+++ b/src/assets/icons/32x32@2/edit-copy@2x.png
Binary files differ
diff --git a/src/assets/icons/32x32@2/edit-cut@2x.png b/src/assets/icons/32x32@2/edit-cut@2x.png
new file mode 100644
index 0000000000..408b0ae19b
--- /dev/null
+++ b/src/assets/icons/32x32@2/edit-cut@2x.png
Binary files differ
diff --git a/src/assets/icons/32x32@2/edit-delete@2x.png b/src/assets/icons/32x32@2/edit-delete@2x.png
new file mode 100644
index 0000000000..58abfc1fa5
--- /dev/null
+++ b/src/assets/icons/32x32@2/edit-delete@2x.png
Binary files differ
diff --git a/src/assets/icons/32x32@2/edit-paste@2x.png b/src/assets/icons/32x32@2/edit-paste@2x.png
new file mode 100644
index 0000000000..b8c288f6c7
--- /dev/null
+++ b/src/assets/icons/32x32@2/edit-paste@2x.png
Binary files differ
diff --git a/src/assets/icons/32x32@2/edit-redo@2x.png b/src/assets/icons/32x32@2/edit-redo@2x.png
new file mode 100644
index 0000000000..89fcd33c30
--- /dev/null
+++ b/src/assets/icons/32x32@2/edit-redo@2x.png
Binary files differ
diff --git a/src/assets/icons/32x32@2/edit-undo@2x.png b/src/assets/icons/32x32@2/edit-undo@2x.png
new file mode 100644
index 0000000000..6f7ad2cb40
--- /dev/null
+++ b/src/assets/icons/32x32@2/edit-undo@2x.png
Binary files differ
diff --git a/src/assets/icons/32x32@2/format-justify-center@2x.png b/src/assets/icons/32x32@2/format-justify-center@2x.png
new file mode 100644
index 0000000000..9b2cc1ed16
--- /dev/null
+++ b/src/assets/icons/32x32@2/format-justify-center@2x.png
Binary files differ
diff --git a/src/assets/icons/32x32@2/format-justify-fill@2x.png b/src/assets/icons/32x32@2/format-justify-fill@2x.png
new file mode 100644
index 0000000000..1212e9f761
--- /dev/null
+++ b/src/assets/icons/32x32@2/format-justify-fill@2x.png
Binary files differ
diff --git a/src/assets/icons/32x32@2/format-justify-left@2x.png b/src/assets/icons/32x32@2/format-justify-left@2x.png
new file mode 100644
index 0000000000..8c0eca3037
--- /dev/null
+++ b/src/assets/icons/32x32@2/format-justify-left@2x.png
Binary files differ
diff --git a/src/assets/icons/32x32@2/format-justify-right@2x.png b/src/assets/icons/32x32@2/format-justify-right@2x.png
new file mode 100644
index 0000000000..fb0ed70252
--- /dev/null
+++ b/src/assets/icons/32x32@2/format-justify-right@2x.png
Binary files differ
diff --git a/src/assets/icons/32x32@2/format-text-bold@2x.png b/src/assets/icons/32x32@2/format-text-bold@2x.png
new file mode 100644
index 0000000000..0e67ead0b8
--- /dev/null
+++ b/src/assets/icons/32x32@2/format-text-bold@2x.png
Binary files differ
diff --git a/src/assets/icons/32x32@2/format-text-italic@2x.png b/src/assets/icons/32x32@2/format-text-italic@2x.png
new file mode 100644
index 0000000000..f746f8956f
--- /dev/null
+++ b/src/assets/icons/32x32@2/format-text-italic@2x.png
Binary files differ
diff --git a/src/assets/icons/32x32@2/format-text-underline@2x.png b/src/assets/icons/32x32@2/format-text-underline@2x.png
new file mode 100644
index 0000000000..47d6fced02
--- /dev/null
+++ b/src/assets/icons/32x32@2/format-text-underline@2x.png
Binary files differ
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