7#include <private/qquickvectorimageplugin_p.h>
9#include <QtCore/qcoreapplication.h>
10#include <QtCore/qdeadlinetimer.h>
11#include <QtCore/qmutex.h>
12#include <QtCore/qrunnable.h>
13#include <QtCore/qthread.h>
14#include <QtCore/qthreadpool.h>
15#include <QtQuick/qquickitem.h>
33QEvent::Type resultEventType()
35 static const QEvent::Type type = QEvent::Type(QEvent::registerEventType());
39QEvent::Type initializeEventType()
41 static const QEvent::Type type = QEvent::Type(QEvent::registerEventType());
48class QQuickAsyncItemBuilderResultEvent :
public QEvent
51 QQuickAsyncItemBuilderResultEvent() : QEvent(resultEventType()) { }
52 ~QQuickAsyncItemBuilderResultEvent() override
54 if (rootItem !=
nullptr)
55 rootItem->deleteLater();
58 QQuickItem *rootItem =
nullptr;
59 QList<std::function<
void()>> deferredCalls;
60 QQuickVectorImageGenerator::ErrorState errorState = QQuickVectorImageGenerator::NoError;
65class QQuickAsyncItemBuilderTask :
public QRunnable
68 QQuickAsyncItemBuilderTask(
const QQuickVectorImageSource &source,
69 QQuickVectorImageGenerator::GeneratorFlags flags,
71 QThread *targetThread,
72 std::vector<std::unique_ptr<QQuickVectorImagePluginGenerator>> pluginGenerators,
77 , m_targetThread(targetThread)
78 , m_pluginGenerators(std::move(pluginGenerators))
79 , m_shared(std::move(shared))
85 auto *result =
new QQuickAsyncItemBuilderResultEvent;
91 generator.setDeferGuiThreadCalls(
true);
92 generator.setCancellationFlag(&m_shared->cancelled);
94 bool generatedWithPlugin =
false;
95 for (
const auto &pluginGenerator : std::as_const(m_pluginGenerators)) {
98 if ((generatedWithPlugin = pluginGenerator->generate(&generator)))
102 if (!generatedWithPlugin && !isCancelled())
103 generator.generate();
105 result->errorState = isCancelled() ? QQuickVectorImageGenerator::Cancelled
106 : generator.errorState();
108 if (result->errorState == QQuickVectorImageGenerator::NoError) {
109 result->rootItem = generator.takeRootItem();
110 result->deferredCalls = generator.takeDeferredCalls();
111 if (result->rootItem !=
nullptr)
112 result->rootItem->moveToThread(m_targetThread);
116 QMutexLocker locker(&m_shared->mutex);
117 if (m_shared->receiver !=
nullptr) {
118 QCoreApplication::postEvent(m_shared->receiver, result);
126 bool isCancelled()
const {
return m_shared->cancelled.load(std::memory_order_relaxed); }
128 QQuickVectorImageSource m_source;
129 QQuickVectorImageGenerator::GeneratorFlags m_flags;
130 QQmlContext *m_context;
131 QThread *m_targetThread;
132 std::vector<std::unique_ptr<QQuickVectorImagePluginGenerator>> m_pluginGenerators;
136int initializationBudgetMs()
138 static const int val =
int(qEnvironmentVariableIntegerValue(
"QT_QUICKVECTORIMAGE_ASYNC_BUDGET_MS")
145QQuickAsyncItemBuilder::QQuickAsyncItemBuilder(
const QQuickVectorImageSource &source,
146 QQuickVectorImageGenerator::GeneratorFlags flags,
147 QQmlContext *context,
153 , m_shared(std::make_shared<QQuickAsyncItemBuilderShared>())
157QQuickAsyncItemBuilder::~QQuickAsyncItemBuilder()
163void QQuickAsyncItemBuilder::start(std::vector<std::unique_ptr<QQuickVectorImagePluginGenerator>> pluginGenerators)
165 Q_ASSERT(m_state == State::Idle);
166 m_state = State::Generating;
169 QMutexLocker locker(&m_shared->mutex);
170 m_shared->receiver =
this;
173 QThreadPool::globalInstance()->start(
new QQuickAsyncItemBuilderTask(m_source,
177 std::move(pluginGenerators),
184void QQuickAsyncItemBuilder::cancel()
189 m_state = State::Cancelled;
190 m_errorState = QQuickVectorImageGenerator::Cancelled;
193 m_deferredCalls.clear();
195 m_rootItem =
nullptr;
198void QQuickAsyncItemBuilder::detach()
200 m_shared->cancelled =
true;
202 QMutexLocker locker(&m_shared->mutex);
203 m_shared->receiver =
nullptr;
207 QCoreApplication::removePostedEvents(
this, resultEventType());
208 QCoreApplication::removePostedEvents(
this, initializeEventType());
211QQuickItem *QQuickAsyncItemBuilder::takeRootItem()
213 return std::exchange(m_rootItem,
nullptr);
216bool QQuickAsyncItemBuilder::event(QEvent *e)
218 if (e->type() == resultEventType()) {
219 auto *result =
static_cast<QQuickAsyncItemBuilderResultEvent *>(e);
220 if (m_state != State::Generating)
223 if (result->errorState != QQuickVectorImageGenerator::NoError
224 || result->rootItem ==
nullptr) {
225 finish(result->errorState != QQuickVectorImageGenerator::NoError
227 : QQuickVectorImageGenerator::LoadFailed);
231 Q_ASSERT(result->rootItem->thread() == thread());
232 m_rootItem = std::exchange(result->rootItem,
nullptr);
233 m_deferredCalls = std::move(result->deferredCalls);
234 m_nextDeferredCall = 0;
235 m_state = State::Initializing;
240 if (e->type() == initializeEventType()) {
245 return QObject::event(e);
250void QQuickAsyncItemBuilder::initializeItems()
252 if (m_state != State::Initializing)
255 QDeadlineTimer deadline(initializationBudgetMs(), Qt::PreciseTimer);
256 while (m_nextDeferredCall < m_deferredCalls.size()) {
258 std::function<
void()> call = std::exchange(m_deferredCalls[m_nextDeferredCall++], {});
261 if (m_state != State::Initializing)
264 if (deadline.hasExpired() && m_nextDeferredCall < m_deferredCalls.size()) {
265 QCoreApplication::postEvent(
this,
new QEvent(initializeEventType()));
270 m_deferredCalls.clear();
271 finish(QQuickVectorImageGenerator::NoError);
274void QQuickAsyncItemBuilder::finish(QQuickVectorImageGenerator::ErrorState errorState)
276 Q_ASSERT(isRunning());
279 m_errorState = errorState;
280 if (errorState != QQuickVectorImageGenerator::NoError) {
281 qCWarning(lcQuickVectorImage) <<
"QQuickAsyncItemBuilder: failed to generate" << m_source
282 <<
"(errorState:" << errorState <<
")";
284 m_rootItem =
nullptr;
287 m_state = State::Finished;
Combined button and popup list for selecting options.
std::atomic< bool > cancelled