Kirigami2

pagepool.cpp
1/*
2 * SPDX-FileCopyrightText: 2019 Marco Martin <mart@kde.org>
3 *
4 * SPDX-License-Identifier: LGPL-2.0-or-later
5 */
6
7#include "pagepool.h"
8
9#include <QDebug>
10#include <QQmlComponent>
11#include <QQmlContext>
12#include <QQmlEngine>
13#include <QQmlProperty>
14
15#include "loggingcategory.h"
16
17PagePool::PagePool(QObject *parent)
18 : QObject(parent)
19{
20}
21
22PagePool::~PagePool()
23{
24}
25
27{
28 return m_lastLoadedUrl;
29}
30
32{
33 return m_lastLoadedItem;
34}
35
37{
38 return m_itemForUrl.values();
39}
40
42{
43 return m_urlForItem.values();
44}
45
46void PagePool::setCachePages(bool cache)
47{
48 if (cache == m_cachePages) {
49 return;
50 }
51
52 if (cache) {
53 clear();
54 }
55
56 m_cachePages = cache;
57 Q_EMIT cachePagesChanged();
58}
59
60bool PagePool::cachePages() const
61{
62 return m_cachePages;
63}
64
66{
67 return loadPageWithProperties(url, QVariantMap(), callback);
68}
69
70QQuickItem *PagePool::loadPageWithProperties(const QString &url, const QVariantMap &properties, QJSValue callback)
71{
72 const auto engine = qmlEngine(this);
73 Q_ASSERT(engine);
74
75 const QUrl actualUrl = resolvedUrl(url);
76
77 auto found = m_itemForUrl.find(actualUrl);
78 if (found != m_itemForUrl.end()) {
79 m_lastLoadedUrl = found.key();
80 m_lastLoadedItem = found.value();
81
82 if (callback.isCallable()) {
83 QJSValueList args = {engine->newQObject(found.value())};
84 callback.call(args);
85 Q_EMIT lastLoadedUrlChanged();
86 Q_EMIT lastLoadedItemChanged();
87 // We could return the item, but for api coherence return null
88 return nullptr;
89
90 } else {
91 Q_EMIT lastLoadedUrlChanged();
92 Q_EMIT lastLoadedItemChanged();
93 return found.value();
94 }
95 }
96
97 QQmlComponent *component = m_componentForUrl.value(actualUrl);
98
99 if (!component) {
100 component = new QQmlComponent(engine, actualUrl, QQmlComponent::PreferSynchronous);
101 }
102
103 if (component->status() == QQmlComponent::Loading) {
104 if (!callback.isCallable()) {
105 component->deleteLater();
106 m_componentForUrl.remove(actualUrl);
107 return nullptr;
108 }
109
110 connect(component, &QQmlComponent::statusChanged, this, [this, engine, component, callback, properties](QQmlComponent::Status status) mutable {
112 qCWarning(KirigamiLog) << component->errors();
113 m_componentForUrl.remove(component->url());
114 component->deleteLater();
115 return;
116 }
117 QQuickItem *item = createFromComponent(component, properties);
118 if (item) {
119 QJSValueList args = {engine->newQObject(item)};
120 callback.call(args);
121 }
122
123 if (m_cachePages) {
124 component->deleteLater();
125 } else {
126 m_componentForUrl[component->url()] = component;
127 }
128 });
129
130 return nullptr;
131
132 } else if (component->status() != QQmlComponent::Ready) {
133 qCWarning(KirigamiLog) << component->errors();
134 return nullptr;
135 }
136
137 QQuickItem *item = createFromComponent(component, properties);
138 if (!item) {
139 return nullptr;
140 }
141
142 if (m_cachePages) {
143 component->deleteLater();
145 m_itemForUrl[component->url()] = item;
146 m_urlForItem[item] = component->url();
147 Q_EMIT itemsChanged();
148 Q_EMIT urlsChanged();
149
150 } else {
151 m_componentForUrl[component->url()] = component;
153 }
154
155 m_lastLoadedUrl = actualUrl;
156 m_lastLoadedItem = item;
157 Q_EMIT lastLoadedUrlChanged();
158 Q_EMIT lastLoadedItemChanged();
159
160 if (callback.isCallable()) {
161 QJSValueList args = {engine->newQObject(item)};
162 callback.call(args);
163 // We could return the item, but for api coherence return null
164 return nullptr;
165 }
166 return item;
167}
168
169QQuickItem *PagePool::createFromComponent(QQmlComponent *component, const QVariantMap &properties)
170{
171 const auto ctx = qmlContext(this);
172 Q_ASSERT(ctx);
173
174 QObject *obj = component->createWithInitialProperties(properties, ctx);
175
176 if (!obj || component->isError()) {
177 qCWarning(KirigamiLog) << component->errors();
178 if (obj) {
179 obj->deleteLater();
180 }
181 return nullptr;
182 }
183
184 QQuickItem *item = qobject_cast<QQuickItem *>(obj);
185 if (!item) {
186 qCWarning(KirigamiLog) << "Storing Non-QQuickItem in PagePool not supported";
187 obj->deleteLater();
188 return nullptr;
189 }
190
191 return item;
192}
193
194QUrl PagePool::resolvedUrl(const QString &stringUrl) const
195{
196 const auto ctx = qmlContext(this);
197 Q_ASSERT(ctx);
198
199 QUrl actualUrl(stringUrl);
200 if (actualUrl.scheme().isEmpty()) {
201 actualUrl = ctx->resolvedUrl(actualUrl);
202 }
203 return actualUrl;
204}
205
207{
208 return url.isLocalFile() || url.scheme().isEmpty() || url.scheme() == QStringLiteral("qrc");
209}
210
212{
213 return m_urlForItem.value(item);
214}
215
217{
218 return m_itemForUrl.value(resolvedUrl(url.toString()), nullptr);
219}
220
221bool PagePool::contains(const QVariant &page) const
222{
223 if (page.canConvert<QQuickItem *>()) {
224 return m_urlForItem.contains(page.value<QQuickItem *>());
225
226 } else if (page.canConvert<QString>()) {
227 const QUrl actualUrl = resolvedUrl(page.value<QString>());
228 return m_itemForUrl.contains(actualUrl);
229
230 } else {
231 return false;
232 }
233}
234
236{
237 if (!contains(page)) {
238 return;
239 }
240
241 QQuickItem *item;
242 if (page.canConvert<QQuickItem *>()) {
243 item = page.value<QQuickItem *>();
244 } else if (page.canConvert<QString>()) {
245 QString url = page.value<QString>();
246 if (url.isEmpty()) {
247 return;
248 }
249 const QUrl actualUrl = resolvedUrl(page.value<QString>());
250
251 item = m_itemForUrl.value(actualUrl);
252 } else {
253 return;
254 }
255
256 if (!item) {
257 return;
258 }
259
260 const QUrl url = m_urlForItem.value(item);
261
262 if (url.isEmpty()) {
263 return;
264 }
265
266 m_itemForUrl.remove(url);
267 m_urlForItem.remove(item);
268 item->deleteLater();
269
270 Q_EMIT itemsChanged();
271 Q_EMIT urlsChanged();
272}
273
275{
276 for (const auto &component : std::as_const(m_componentForUrl)) {
277 component->deleteLater();
278 }
279 m_componentForUrl.clear();
280
281 for (const auto &item : std::as_const(m_itemForUrl)) {
282 // items that had been deparented are safe to delete
283 if (!item->parentItem()) {
284 item->deleteLater();
285 }
287 }
288 m_itemForUrl.clear();
289 m_urlForItem.clear();
290 m_lastLoadedUrl = QUrl();
291 m_lastLoadedItem = nullptr;
292
293 Q_EMIT lastLoadedUrlChanged();
294 Q_EMIT lastLoadedItemChanged();
295 Q_EMIT itemsChanged();
296 Q_EMIT urlsChanged();
297}
298
299#include "moc_pagepool.cpp"
Q_INVOKABLE QQuickItem * pageForUrl(const QUrl &url) const
Definition pagepool.cpp:216
QQuickItem * lastLoadedItem
The last item that was loaded with @loadPage.
Definition pagepool.h:36
QList< QQuickItem * > items
All items loaded/managed by the PagePool.
Definition pagepool.h:42
QList< QUrl > urls
All page URLs loaded/managed by the PagePool.
Definition pagepool.h:48
Q_INVOKABLE void clear()
Deletes all pages managed by the pool.
Definition pagepool.cpp:274
Q_INVOKABLE bool contains(const QVariant &page) const
Definition pagepool.cpp:221
Q_INVOKABLE QQuickItem * loadPage(const QString &url, QJSValue callback=QJSValue())
Returns the instance of the item defined in the QML file identified by url, only one instance will be...
Definition pagepool.cpp:65
Q_INVOKABLE QUrl urlForPage(QQuickItem *item) const
Definition pagepool.cpp:211
Q_INVOKABLE void deletePage(const QVariant &page)
Deletes the page (only if is managed by the pool.
Definition pagepool.cpp:235
bool cachePages
If true (default) the pages will be kept around, will have C++ ownership and only one instance per pa...
Definition pagepool.h:57
QML_ELEMENTQUrl lastLoadedUrl
The last url that was loaded with @loadPage.
Definition pagepool.h:31
Q_INVOKABLE QUrl resolvedUrl(const QString &file) const
Definition pagepool.cpp:194
Q_INVOKABLE bool isLocalUrl(const QUrl &url)
Definition pagepool.cpp:206
Q_SCRIPTABLE CaptureState status()
void clear()
bool contains(const Key &key) const const
iterator end()
iterator find(const Key &key)
bool remove(const Key &key)
T value(const Key &key) const const
QList< T > values() const const
void setObjectOwnership(QObject *object, ObjectOwnership ownership)
QJSValue call(const QJSValueList &args) const const
bool isCallable() const const
Q_EMITQ_EMIT
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
void deleteLater()
QObject * createWithInitialProperties(const QVariantMap &initialProperties, QQmlContext *context)
QList< QQmlError > errors() const const
bool isError() const const
void statusChanged(QQmlComponent::Status status)
QQuickItem * parentItem() const const
bool isEmpty() const const
bool isEmpty() const const
bool isLocalFile() const const
QString scheme() const const
QString toString(FormattingOptions options) const const
bool canConvert() const const
T value() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:18:46 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.