Kirigami2

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

KDE's Doxygen guidelines are available online.