File indexing completed on 2024-04-14 03:53:48
0001 /* 0002 * SPDX-FileCopyrightText: 2019 Marco Martin <mart@kde.org> 0003 * 0004 * SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "pagepool.h" 0008 0009 #include <QDebug> 0010 #include <QQmlComponent> 0011 #include <QQmlContext> 0012 #include <QQmlEngine> 0013 #include <QQmlProperty> 0014 0015 #include "loggingcategory.h" 0016 0017 PagePool::PagePool(QObject *parent) 0018 : QObject(parent) 0019 { 0020 } 0021 0022 PagePool::~PagePool() 0023 { 0024 } 0025 0026 QUrl PagePool::lastLoadedUrl() const 0027 { 0028 return m_lastLoadedUrl; 0029 } 0030 0031 QQuickItem *PagePool::lastLoadedItem() const 0032 { 0033 return m_lastLoadedItem; 0034 } 0035 0036 QList<QQuickItem *> PagePool::items() const 0037 { 0038 return m_itemForUrl.values(); 0039 } 0040 0041 QList<QUrl> PagePool::urls() const 0042 { 0043 return m_urlForItem.values(); 0044 } 0045 0046 void PagePool::setCachePages(bool cache) 0047 { 0048 if (cache == m_cachePages) { 0049 return; 0050 } 0051 0052 if (cache) { 0053 clear(); 0054 } 0055 0056 m_cachePages = cache; 0057 Q_EMIT cachePagesChanged(); 0058 } 0059 0060 bool PagePool::cachePages() const 0061 { 0062 return m_cachePages; 0063 } 0064 0065 QQuickItem *PagePool::loadPage(const QString &url, QJSValue callback) 0066 { 0067 return loadPageWithProperties(url, QVariantMap(), callback); 0068 } 0069 0070 QQuickItem *PagePool::loadPageWithProperties(const QString &url, const QVariantMap &properties, QJSValue callback) 0071 { 0072 const auto engine = qmlEngine(this); 0073 Q_ASSERT(engine); 0074 0075 const QUrl actualUrl = resolvedUrl(url); 0076 0077 auto found = m_itemForUrl.find(actualUrl); 0078 if (found != m_itemForUrl.end()) { 0079 m_lastLoadedUrl = found.key(); 0080 m_lastLoadedItem = found.value(); 0081 0082 if (callback.isCallable()) { 0083 QJSValueList args = {engine->newQObject(found.value())}; 0084 callback.call(args); 0085 Q_EMIT lastLoadedUrlChanged(); 0086 Q_EMIT lastLoadedItemChanged(); 0087 // We could return the item, but for api coherence return null 0088 return nullptr; 0089 0090 } else { 0091 Q_EMIT lastLoadedUrlChanged(); 0092 Q_EMIT lastLoadedItemChanged(); 0093 return found.value(); 0094 } 0095 } 0096 0097 QQmlComponent *component = m_componentForUrl.value(actualUrl); 0098 0099 if (!component) { 0100 component = new QQmlComponent(engine, actualUrl, QQmlComponent::PreferSynchronous); 0101 } 0102 0103 if (component->status() == QQmlComponent::Loading) { 0104 if (!callback.isCallable()) { 0105 component->deleteLater(); 0106 m_componentForUrl.remove(actualUrl); 0107 return nullptr; 0108 } 0109 0110 connect(component, &QQmlComponent::statusChanged, this, [this, engine, component, callback, properties](QQmlComponent::Status status) mutable { 0111 if (status != QQmlComponent::Ready) { 0112 qCWarning(KirigamiLog) << component->errors(); 0113 m_componentForUrl.remove(component->url()); 0114 component->deleteLater(); 0115 return; 0116 } 0117 QQuickItem *item = createFromComponent(component, properties); 0118 if (item) { 0119 QJSValueList args = {engine->newQObject(item)}; 0120 callback.call(args); 0121 } 0122 0123 if (m_cachePages) { 0124 component->deleteLater(); 0125 } else { 0126 m_componentForUrl[component->url()] = component; 0127 } 0128 }); 0129 0130 return nullptr; 0131 0132 } else if (component->status() != QQmlComponent::Ready) { 0133 qCWarning(KirigamiLog) << component->errors(); 0134 return nullptr; 0135 } 0136 0137 QQuickItem *item = createFromComponent(component, properties); 0138 if (!item) { 0139 return nullptr; 0140 } 0141 0142 if (m_cachePages) { 0143 component->deleteLater(); 0144 QQmlEngine::setObjectOwnership(item, QQmlEngine::CppOwnership); 0145 m_itemForUrl[component->url()] = item; 0146 m_urlForItem[item] = component->url(); 0147 Q_EMIT itemsChanged(); 0148 Q_EMIT urlsChanged(); 0149 0150 } else { 0151 m_componentForUrl[component->url()] = component; 0152 QQmlEngine::setObjectOwnership(item, QQmlEngine::JavaScriptOwnership); 0153 } 0154 0155 m_lastLoadedUrl = actualUrl; 0156 m_lastLoadedItem = item; 0157 Q_EMIT lastLoadedUrlChanged(); 0158 Q_EMIT lastLoadedItemChanged(); 0159 0160 if (callback.isCallable()) { 0161 QJSValueList args = {engine->newQObject(item)}; 0162 callback.call(args); 0163 // We could return the item, but for api coherence return null 0164 return nullptr; 0165 } 0166 return item; 0167 } 0168 0169 QQuickItem *PagePool::createFromComponent(QQmlComponent *component, const QVariantMap &properties) 0170 { 0171 const auto ctx = qmlContext(this); 0172 Q_ASSERT(ctx); 0173 0174 QObject *obj = component->createWithInitialProperties(properties, ctx); 0175 0176 if (!obj || component->isError()) { 0177 qCWarning(KirigamiLog) << component->errors(); 0178 if (obj) { 0179 obj->deleteLater(); 0180 } 0181 return nullptr; 0182 } 0183 0184 QQuickItem *item = qobject_cast<QQuickItem *>(obj); 0185 if (!item) { 0186 qCWarning(KirigamiLog) << "Storing Non-QQuickItem in PagePool not supported"; 0187 obj->deleteLater(); 0188 return nullptr; 0189 } 0190 0191 return item; 0192 } 0193 0194 QUrl PagePool::resolvedUrl(const QString &stringUrl) const 0195 { 0196 const auto ctx = qmlContext(this); 0197 Q_ASSERT(ctx); 0198 0199 QUrl actualUrl(stringUrl); 0200 if (actualUrl.scheme().isEmpty()) { 0201 actualUrl = ctx->resolvedUrl(actualUrl); 0202 } 0203 return actualUrl; 0204 } 0205 0206 bool PagePool::isLocalUrl(const QUrl &url) 0207 { 0208 return url.isLocalFile() || url.scheme().isEmpty() || url.scheme() == QStringLiteral("qrc"); 0209 } 0210 0211 QUrl PagePool::urlForPage(QQuickItem *item) const 0212 { 0213 return m_urlForItem.value(item); 0214 } 0215 0216 QQuickItem *PagePool::pageForUrl(const QUrl &url) const 0217 { 0218 return m_itemForUrl.value(resolvedUrl(url.toString()), nullptr); 0219 } 0220 0221 bool PagePool::contains(const QVariant &page) const 0222 { 0223 if (page.canConvert<QQuickItem *>()) { 0224 return m_urlForItem.contains(page.value<QQuickItem *>()); 0225 0226 } else if (page.canConvert<QString>()) { 0227 const QUrl actualUrl = resolvedUrl(page.value<QString>()); 0228 return m_itemForUrl.contains(actualUrl); 0229 0230 } else { 0231 return false; 0232 } 0233 } 0234 0235 void PagePool::deletePage(const QVariant &page) 0236 { 0237 if (!contains(page)) { 0238 return; 0239 } 0240 0241 QQuickItem *item; 0242 if (page.canConvert<QQuickItem *>()) { 0243 item = page.value<QQuickItem *>(); 0244 } else if (page.canConvert<QString>()) { 0245 QString url = page.value<QString>(); 0246 if (url.isEmpty()) { 0247 return; 0248 } 0249 const QUrl actualUrl = resolvedUrl(page.value<QString>()); 0250 0251 item = m_itemForUrl.value(actualUrl); 0252 } else { 0253 return; 0254 } 0255 0256 if (!item) { 0257 return; 0258 } 0259 0260 const QUrl url = m_urlForItem.value(item); 0261 0262 if (url.isEmpty()) { 0263 return; 0264 } 0265 0266 m_itemForUrl.remove(url); 0267 m_urlForItem.remove(item); 0268 item->deleteLater(); 0269 0270 Q_EMIT itemsChanged(); 0271 Q_EMIT urlsChanged(); 0272 } 0273 0274 void PagePool::clear() 0275 { 0276 for (const auto &component : std::as_const(m_componentForUrl)) { 0277 component->deleteLater(); 0278 } 0279 m_componentForUrl.clear(); 0280 0281 for (const auto &item : std::as_const(m_itemForUrl)) { 0282 // items that had been deparented are safe to delete 0283 if (!item->parentItem()) { 0284 item->deleteLater(); 0285 } 0286 QQmlEngine::setObjectOwnership(item, QQmlEngine::JavaScriptOwnership); 0287 } 0288 m_itemForUrl.clear(); 0289 m_urlForItem.clear(); 0290 m_lastLoadedUrl = QUrl(); 0291 m_lastLoadedItem = nullptr; 0292 0293 Q_EMIT lastLoadedUrlChanged(); 0294 Q_EMIT lastLoadedItemChanged(); 0295 Q_EMIT itemsChanged(); 0296 Q_EMIT urlsChanged(); 0297 } 0298 0299 #include "moc_pagepool.cpp"