File indexing completed on 2025-03-09 05:02:40
0001 /* 0002 SPDX-FileCopyrightText: 2014 Marco Martin <mart@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "appletquickitem.h" 0008 #include "applet.h" 0009 #include "appletcontext_p.h" 0010 #include "configview.h" 0011 #include "containment.h" 0012 #include "debug_p.h" 0013 #include "plasma_version.h" 0014 #include "plasmoid/containmentitem.h" 0015 #include "plasmoid/plasmoiditem.h" 0016 #include "plasmoid/wallpaperitem.h" 0017 #include "private/appletquickitem_p.h" 0018 #include "private/plasmoidattached_p.h" 0019 #include "sharedqmlengine.h" 0020 0021 #include <QJsonArray> 0022 #include <QQmlContext> 0023 #include <QQmlExpression> 0024 #include <QQmlProperty> 0025 #include <QQuickWindow> 0026 #include <QRandomGenerator> 0027 0028 #include <QDebug> 0029 0030 #include <KLocalizedString> 0031 0032 #include <Plasma/Applet> 0033 #include <Plasma/Containment> 0034 #include <Plasma/Corona> 0035 0036 namespace PlasmaQuick 0037 { 0038 0039 QHash<Plasma::Applet *, AppletQuickItem *> AppletQuickItemPrivate::s_itemsForApplet = QHash<Plasma::Applet *, AppletQuickItem *>(); 0040 AppletQuickItemPrivate::PreloadPolicy AppletQuickItemPrivate::s_preloadPolicy = AppletQuickItemPrivate::Uninitialized; 0041 0042 AppletQuickItemPrivate::AppletQuickItemPrivate(AppletQuickItem *item) 0043 : q(item) 0044 , switchWidth(-1) 0045 , switchHeight(-1) 0046 , initComplete(false) 0047 , compactRepresentationCheckGuard(false) 0048 { 0049 if (s_preloadPolicy == Uninitialized) { 0050 // default as Adaptive 0051 s_preloadPolicy = Adaptive; 0052 0053 if (qEnvironmentVariableIsSet("PLASMA_PRELOAD_POLICY")) { 0054 const QString policy = QString::fromUtf8(qgetenv("PLASMA_PRELOAD_POLICY")).toLower(); 0055 if (policy == QLatin1String("aggressive")) { 0056 s_preloadPolicy = Aggressive; 0057 } else if (policy == QLatin1String("none")) { 0058 s_preloadPolicy = None; 0059 } 0060 } 0061 0062 qCInfo(LOG_PLASMAQUICK) << "Applet preload policy set to" << s_preloadPolicy; 0063 } 0064 } 0065 0066 int AppletQuickItemPrivate::preloadWeight() const 0067 { 0068 int defaultWeight; 0069 const QStringList provides = applet->pluginMetaData().value(QStringLiteral("X-Plasma-Provides"), QStringList()); 0070 0071 // some applet types we want a bigger weight 0072 if (provides.contains(QLatin1String("org.kde.plasma.launchermenu"))) { 0073 defaultWeight = DefaultLauncherPreloadWeight; 0074 } else { 0075 defaultWeight = DefaultPreloadWeight; 0076 } 0077 // default widgets to be barely preloaded 0078 return qBound(0, 0079 applet->config().readEntry(QStringLiteral("PreloadWeight"), 0080 qMax(defaultWeight, applet->pluginMetaData().value(QStringLiteral("X-Plasma-PreloadWeight"), 0))), 0081 100); 0082 } 0083 0084 QObject *AppletQuickItemPrivate::searchLayoutAttached(QObject *parent) 0085 { 0086 QObject *layout = nullptr; 0087 // Search a child that has the needed Layout properties 0088 // HACK: here we are not type safe, but is the only way to access to a pointer of Layout 0089 const auto lstChildren = parent->children(); 0090 for (QObject *child : lstChildren) { 0091 // find for the needed property of Layout: minimum/maximum/preferred sizes and fillWidth/fillHeight 0092 /* clang-format off */ 0093 if (child->property("minimumWidth").isValid() 0094 && child->property("minimumHeight").isValid() 0095 && child->property("preferredWidth").isValid() 0096 && child->property("preferredHeight").isValid() 0097 && child->property("maximumWidth").isValid() 0098 && child->property("maximumHeight").isValid() 0099 && child->property("fillWidth").isValid() 0100 && child->property("fillHeight").isValid()) { /* clang-format on */ 0101 layout = child; 0102 break; 0103 } 0104 } 0105 return layout; 0106 } 0107 0108 void AppletQuickItemPrivate::connectLayoutAttached(QObject *item) 0109 { 0110 // Extract the representation's Layout, if any 0111 if (!item) { 0112 return; 0113 } 0114 0115 QObject *layout = searchLayoutAttached(item); 0116 0117 // if the compact repr doesn't export a Layout.* attached property, 0118 // reset our own with default values 0119 if (!layout) { 0120 if (ownLayout) { 0121 ownLayout->setProperty("minimumWidth", 0); 0122 ownLayout->setProperty("minimumHeight", 0); 0123 ownLayout->setProperty("preferredWidth", -1); 0124 ownLayout->setProperty("preferredHeight", -1); 0125 ownLayout->setProperty("maximumWidth", std::numeric_limits<qreal>::infinity()); 0126 ownLayout->setProperty("maximumHeight", std::numeric_limits<qreal>::infinity()); 0127 ownLayout->setProperty("fillWidth", false); 0128 ownLayout->setProperty("fillHeight", false); 0129 } 0130 return; 0131 } 0132 0133 // propagate all the size hints 0134 propagateSizeHint("minimumWidth"); 0135 propagateSizeHint("minimumHeight"); 0136 propagateSizeHint("preferredWidth"); 0137 propagateSizeHint("preferredHeight"); 0138 propagateSizeHint("maximumWidth"); 0139 propagateSizeHint("maximumHeight"); 0140 propagateSizeHint("fillWidth"); 0141 propagateSizeHint("fillHeight"); 0142 0143 QObject *newOwnLayout = searchLayoutAttached(q); 0144 0145 // this should never happen, since we ask to create it if doesn't exists 0146 if (!newOwnLayout) { 0147 return; 0148 } 0149 0150 // if the representation didn't change, don't do anything 0151 if (representationLayout == layout) { 0152 return; 0153 } 0154 0155 if (representationLayout) { 0156 QObject::disconnect(representationLayout, nullptr, q, nullptr); 0157 } 0158 0159 // Here we can't use the new connect syntax because we can't link against QtQuick layouts 0160 QObject::connect(layout, SIGNAL(minimumWidthChanged()), q, SLOT(minimumWidthChanged())); 0161 QObject::connect(layout, SIGNAL(minimumHeightChanged()), q, SLOT(minimumHeightChanged())); 0162 0163 QObject::connect(layout, SIGNAL(preferredWidthChanged()), q, SLOT(preferredWidthChanged())); 0164 QObject::connect(layout, SIGNAL(preferredHeightChanged()), q, SLOT(preferredHeightChanged())); 0165 0166 QObject::connect(layout, SIGNAL(maximumWidthChanged()), q, SLOT(maximumWidthChanged())); 0167 QObject::connect(layout, SIGNAL(maximumHeightChanged()), q, SLOT(maximumHeightChanged())); 0168 0169 QObject::connect(layout, SIGNAL(fillWidthChanged()), q, SLOT(fillWidthChanged())); 0170 QObject::connect(layout, SIGNAL(fillHeightChanged()), q, SLOT(fillHeightChanged())); 0171 0172 representationLayout = layout; 0173 ownLayout = newOwnLayout; 0174 0175 propagateSizeHint("minimumWidth"); 0176 propagateSizeHint("minimumHeight"); 0177 propagateSizeHint("preferredWidth"); 0178 propagateSizeHint("preferredHeight"); 0179 propagateSizeHint("maximumWidth"); 0180 propagateSizeHint("maximumHeight"); 0181 propagateSizeHint("fillWidth"); 0182 propagateSizeHint("fillHeight"); 0183 } 0184 0185 void AppletQuickItemPrivate::propagateSizeHint(const QByteArray &layoutProperty) 0186 { 0187 if (ownLayout && representationLayout) { 0188 ownLayout->setProperty(layoutProperty.constData(), representationLayout->property(layoutProperty.constData())); 0189 } 0190 } 0191 0192 QQuickItem *AppletQuickItemPrivate::createCompactRepresentationItem() 0193 { 0194 if (!compactRepresentation) { 0195 return nullptr; 0196 } 0197 0198 if (compactRepresentationItem) { 0199 return compactRepresentationItem; 0200 } 0201 0202 QVariantHash initialProperties; 0203 initialProperties[QStringLiteral("parent")] = QVariant::fromValue(q); 0204 initialProperties[QStringLiteral("plasmoidItem")] = QVariant::fromValue(q); 0205 0206 compactRepresentationItem = qobject_cast<QQuickItem *>(qmlObject->createObjectFromComponent(compactRepresentation, qmlContext(q), initialProperties)); 0207 0208 Q_EMIT q->compactRepresentationItemChanged(compactRepresentationItem); 0209 0210 return compactRepresentationItem; 0211 } 0212 0213 QQuickItem *AppletQuickItemPrivate::createFullRepresentationItem() 0214 { 0215 if (fullRepresentationItem) { 0216 return fullRepresentationItem; 0217 } 0218 0219 if (fullRepresentation && fullRepresentation != qmlObject->mainComponent()) { 0220 QVariantHash initialProperties; 0221 initialProperties[QStringLiteral("parent")] = QVariant(); 0222 fullRepresentationItem = qobject_cast<QQuickItem *>(qmlObject->createObjectFromComponent(fullRepresentation, qmlContext(q), initialProperties)); 0223 } 0224 0225 if (!fullRepresentationItem) { 0226 return nullptr; 0227 } 0228 0229 Q_EMIT q->fullRepresentationItemChanged(fullRepresentationItem); 0230 0231 return fullRepresentationItem; 0232 } 0233 0234 QQuickItem *AppletQuickItemPrivate::createCompactRepresentationExpanderItem() 0235 { 0236 if (!compactRepresentationExpander) { 0237 return nullptr; 0238 } 0239 0240 if (compactRepresentationExpanderItem) { 0241 return compactRepresentationExpanderItem; 0242 } 0243 0244 compactRepresentationExpanderItem = qobject_cast<QQuickItem *>(qmlObject->createObjectFromComponent(compactRepresentationExpander, qmlContext(q))); 0245 0246 if (!compactRepresentationExpanderItem) { 0247 return nullptr; 0248 } 0249 0250 compactRepresentationExpanderItem->setProperty("compactRepresentation", QVariant::fromValue<QObject *>(createCompactRepresentationItem())); 0251 compactRepresentationExpanderItem->setProperty("plasmoidItem", QVariant::fromValue(q)); 0252 0253 return compactRepresentationExpanderItem; 0254 } 0255 0256 bool AppletQuickItemPrivate::appletShouldBeExpanded() const 0257 { 0258 if (applet->isContainment()) { 0259 return true; 0260 0261 } else { 0262 if (!fullRepresentation) { 0263 // If a full representation wasn't specified, the onle and only representation of the plasmoid are our 0264 // direct contents, so we consider it always expanded 0265 return true; 0266 } 0267 if (switchWidth > 0 && switchHeight > 0) { 0268 return q->width() > switchWidth && q->height() > switchHeight; 0269 0270 // if a size to switch wasn't set, determine what representation to always chose 0271 } else { 0272 // preferred representation set? 0273 if (preferredRepresentation) { 0274 return preferredRepresentation == fullRepresentation; 0275 // Otherwise, base on FormFactor 0276 } else { 0277 return (applet->formFactor() != Plasma::Types::Horizontal && applet->formFactor() != Plasma::Types::Vertical); 0278 } 0279 } 0280 } 0281 } 0282 0283 void AppletQuickItemPrivate::preloadForExpansion() 0284 { 0285 qint64 time = 0; 0286 if (QLoggingCategory::defaultCategory()->isInfoEnabled()) { 0287 time = QDateTime::currentMSecsSinceEpoch(); 0288 } 0289 0290 if (!createFullRepresentationItem()) { 0291 return; 0292 } 0293 0294 // When not already expanded, also preload the expander 0295 if (!appletShouldBeExpanded() && !applet->isContainment() && (!preferredRepresentation || preferredRepresentation != fullRepresentation)) { 0296 createCompactRepresentationExpanderItem(); 0297 } 0298 0299 if (!appletShouldBeExpanded() && compactRepresentationExpanderItem) { 0300 compactRepresentationExpanderItem->setProperty("fullRepresentation", QVariant::fromValue<QObject *>(createFullRepresentationItem())); 0301 } else if (fullRepresentationItem) { 0302 fullRepresentationItem->setProperty("parent", QVariant::fromValue<QObject *>(q)); 0303 } 0304 0305 // preallocate nodes 0306 if (fullRepresentationItem && fullRepresentationItem->window()) { 0307 fullRepresentationItem->window()->create(); 0308 } 0309 0310 qCDebug(LOG_PLASMAQUICK) << "Applet" << applet->title() << "loaded after" << (QDateTime::currentMSecsSinceEpoch() - time) << "msec"; 0311 } 0312 0313 void AppletQuickItemPrivate::anchorsFillParent(QQuickItem *item, QQuickItem *parent) 0314 { 0315 if (item->parentItem() != parent) { 0316 return; 0317 } 0318 // just set once, don't bind 0319 QQmlProperty::write(item, QStringLiteral("anchors.fill"), QVariant::fromValue<QObject *>(parent)); 0320 } 0321 0322 void AppletQuickItemPrivate::compactRepresentationCheck() 0323 { 0324 if (!initComplete) { 0325 return; 0326 } 0327 0328 // ignore 0 sizes; 0329 if (q->width() <= 0 || q->height() <= 0) { 0330 return; 0331 } 0332 0333 // ignore if this widget is being checked somewhere above 0334 if (compactRepresentationCheckGuard) { 0335 return; 0336 } 0337 0338 bool full = appletShouldBeExpanded(); 0339 0340 if ((full && fullRepresentationItem && fullRepresentationItem == currentRepresentationItem) 0341 || (!full && compactRepresentationItem && compactRepresentationItem == currentRepresentationItem)) { 0342 return; 0343 } 0344 0345 compactRepresentationCheckGuard = true; 0346 0347 // Expanded 0348 if (full) { 0349 QQuickItem *item = createFullRepresentationItem(); 0350 0351 if (item) { 0352 // unwire with the expander 0353 if (compactRepresentationExpanderItem) { 0354 compactRepresentationExpanderItem->setProperty("fullRepresentation", QVariant()); 0355 compactRepresentationExpanderItem->setProperty("compactRepresentation", QVariant()); 0356 compactRepresentationExpanderItem->setVisible(false); 0357 } 0358 0359 // the fullrepresentation being the complete AppletItem is actually allowed when the main ui 0360 // is child of the root item (like many panel applets) 0361 if (item != q) { 0362 item->setParentItem(q); 0363 anchorsFillParent(item, q); 0364 } 0365 0366 if (compactRepresentationItem) { 0367 compactRepresentationItem->setVisible(false); 0368 } 0369 0370 currentRepresentationItem = item; 0371 connectLayoutAttached(item); 0372 0373 expanded = true; 0374 Q_EMIT q->expandedChanged(true); 0375 } 0376 0377 } else { 0378 // Icon 0379 QQuickItem *compactItem = createCompactRepresentationItem(); 0380 QQuickItem *compactExpanderItem = createCompactRepresentationExpanderItem(); 0381 0382 if (compactItem && compactExpanderItem) { 0383 // set the root item as the main visible item 0384 compactItem->setVisible(true); 0385 compactExpanderItem->setParentItem(q); 0386 compactExpanderItem->setVisible(true); 0387 anchorsFillParent(compactExpanderItem, q); 0388 0389 if (fullRepresentationItem) { 0390 fullRepresentationItem->setProperty("parent", QVariant()); 0391 } 0392 0393 compactExpanderItem->setProperty("compactRepresentation", QVariant::fromValue<QObject *>(compactItem)); 0394 // The actual full representation will be connected when created 0395 compactExpanderItem->setProperty("fullRepresentation", QVariant()); 0396 0397 currentRepresentationItem = compactItem; 0398 connectLayoutAttached(compactItem); 0399 0400 expanded = false; 0401 Q_EMIT q->expandedChanged(false); 0402 } 0403 } 0404 0405 compactRepresentationCheckGuard = false; 0406 } 0407 0408 void AppletQuickItemPrivate::minimumWidthChanged() 0409 { 0410 propagateSizeHint("minimumWidth"); 0411 } 0412 0413 void AppletQuickItemPrivate::minimumHeightChanged() 0414 { 0415 propagateSizeHint("minimumHeight"); 0416 } 0417 0418 void AppletQuickItemPrivate::preferredWidthChanged() 0419 { 0420 propagateSizeHint("preferredWidth"); 0421 } 0422 0423 void AppletQuickItemPrivate::preferredHeightChanged() 0424 { 0425 propagateSizeHint("preferredHeight"); 0426 } 0427 0428 void AppletQuickItemPrivate::maximumWidthChanged() 0429 { 0430 propagateSizeHint("maximumWidth"); 0431 } 0432 0433 void AppletQuickItemPrivate::maximumHeightChanged() 0434 { 0435 propagateSizeHint("maximumHeight"); 0436 } 0437 0438 void AppletQuickItemPrivate::fillWidthChanged() 0439 { 0440 propagateSizeHint("fillWidth"); 0441 } 0442 0443 void AppletQuickItemPrivate::fillHeightChanged() 0444 { 0445 propagateSizeHint("fillHeight"); 0446 } 0447 0448 AppletQuickItem::AppletQuickItem(QQuickItem *parent) 0449 : QQuickItem(parent) 0450 , d(new AppletQuickItemPrivate(this)) 0451 { 0452 } 0453 0454 AppletQuickItem::~AppletQuickItem() 0455 { 0456 AppletQuickItemPrivate::s_itemsForApplet.remove(d->applet); 0457 // decrease weight 0458 if (d->s_preloadPolicy >= AppletQuickItemPrivate::Adaptive) { 0459 d->applet->config().writeEntry(QStringLiteral("PreloadWeight"), qMax(0, d->preloadWeight() - AppletQuickItemPrivate::PreloadWeightDecrement)); 0460 } 0461 0462 // Here the order is important 0463 delete d->compactRepresentationItem; 0464 delete d->fullRepresentationItem; 0465 delete d->compactRepresentationExpanderItem; 0466 delete d; 0467 } 0468 0469 bool AppletQuickItem::hasItemForApplet(Plasma::Applet *applet) 0470 { 0471 return AppletQuickItemPrivate::s_itemsForApplet.contains(applet); 0472 } 0473 0474 AppletQuickItem *AppletQuickItem::itemForApplet(Plasma::Applet *applet) 0475 { 0476 if (!applet) { 0477 return nullptr; 0478 } 0479 0480 // TODO: move somewhere else? in plasmacore import? 0481 if (AppletQuickItemPrivate::s_itemsForApplet.isEmpty()) { 0482 const char *uri = "org.kde.plasma.plasmoid"; 0483 qmlRegisterExtendedType<Plasma::Applet, PlasmoidAttached>(uri, 2, 0, "Plasmoid"); 0484 qmlRegisterExtendedType<Plasma::Containment, ContainmentAttached>(uri, 2, 0, "Containment"); 0485 0486 qmlRegisterType<PlasmoidItem>(uri, 2, 0, "PlasmoidItem"); 0487 qmlRegisterType<ContainmentItem>(uri, 2, 0, "ContainmentItem"); 0488 qmlRegisterType<WallpaperItem>(uri, 2, 0, "WallpaperItem"); 0489 qmlRegisterAnonymousType<Plasma::Corona>("org.kde.plasma.plasmoid", 1); 0490 } 0491 auto it = AppletQuickItemPrivate::s_itemsForApplet.constFind(applet); 0492 if (it != AppletQuickItemPrivate::s_itemsForApplet.constEnd()) { 0493 return it.value(); 0494 } 0495 0496 // Don't try to create applet items when the app is closing 0497 if (qApp->closingDown() || applet->destroyed()) { 0498 return nullptr; 0499 } 0500 0501 Plasma::Containment *pc = qobject_cast<Plasma::Containment *>(applet); 0502 auto *qmlObject = new PlasmaQuick::SharedQmlEngine(applet, applet); 0503 qmlObject->engine()->setProperty("_kirigamiTheme", QStringLiteral("KirigamiPlasmaStyle")); 0504 qmlObject->setInitializationDelayed(true); 0505 if (applet->pluginMetaData().isValid()) { 0506 const QString rootPath = applet->pluginMetaData().value(QStringLiteral("X-Plasma-RootPath")); 0507 if (!rootPath.isEmpty()) { 0508 qmlObject->setTranslationDomain(QLatin1String("plasma_applet_") + rootPath); 0509 } else { 0510 qmlObject->setTranslationDomain(QLatin1String("plasma_applet_") + applet->pluginMetaData().pluginId()); 0511 } 0512 } 0513 0514 AppletQuickItem *item = nullptr; 0515 qmlObject->setSource(applet->kPackage().fileUrl("mainscript")); 0516 if (pc && pc->isContainment()) { 0517 item = qobject_cast<ContainmentItem *>(qmlObject->rootObject()); 0518 if (!item && qmlObject->mainComponent() && !qmlObject->mainComponent()->isError()) { 0519 applet->setLaunchErrorMessage(i18n("The root item of %1 must be of type ContainmentItem", applet->kPackage().fileUrl("mainscript").toString())); 0520 } 0521 } else { 0522 item = qobject_cast<PlasmoidItem *>(qmlObject->rootObject()); 0523 if (!item && qmlObject->mainComponent() && !qmlObject->mainComponent()->isError()) { 0524 applet->setLaunchErrorMessage(i18n("The root item of %1 must be of type PlasmoidItem", applet->kPackage().fileUrl("mainscript").toString())); 0525 } 0526 } 0527 0528 if (!item || !qmlObject->mainComponent() || qmlObject->mainComponent()->isError() || applet->failedToLaunch()) { 0529 QString reason; 0530 QJsonObject errorData; 0531 errorData[QStringLiteral("appletName")] = i18n("Unknown Applet"); 0532 errorData[QStringLiteral("isDebugMode")] = qEnvironmentVariableIntValue("PLASMA_ENABLE_QML_DEBUG") != 0; 0533 0534 const QString versionString = applet->pluginMetaData().value(QStringLiteral("X-Plasma-API-Minimum-Version")); 0535 QVersionNumber version; 0536 if (!versionString.isEmpty()) { 0537 version = QVersionNumber::fromString(versionString); 0538 } 0539 0540 bool versionMismatch = false; 0541 const int plasma_version_major = 6; // TODO: as soon PLASMA_VERSION_MAJOR is actually 6, use directly that 0542 if (version.isNull()) { 0543 reason = i18n( 0544 "This Widget was written for an unknown older version of Plasma and is not compatible with Plasma %1. Please contact the widget's author for " 0545 "an updated version.", 0546 plasma_version_major); 0547 versionMismatch = true; 0548 } else if (version.majorVersion() < plasma_version_major) { 0549 reason = 0550 i18n("This Widget was written for Plasma %1 and is not compatible with Plasma %2. Please contact the widget's author for an updated version.", 0551 version.majorVersion(), 0552 plasma_version_major); 0553 versionMismatch = true; 0554 } else if (version.majorVersion() > plasma_version_major || version.minorVersion() > PLASMA_VERSION_MINOR) { 0555 reason = i18n("This Widget was written for Plasma %1 and is not compatible with Plasma %2. Please update Plasma in order to use the widget.", 0556 versionString, 0557 plasma_version_major); 0558 versionMismatch = true; 0559 } else if (applet->failedToLaunch()) { 0560 reason = applet->launchErrorMessage(); 0561 } 0562 errorData[QStringLiteral("errors")] = QJsonArray::fromStringList({reason}); 0563 if (applet->kPackage().isValid()) { 0564 if (!versionMismatch) { 0565 const auto errors = qmlObject->mainComponent()->errors(); 0566 QStringList errorList; 0567 for (const QQmlError &error : errors) { 0568 reason += error.toString() + QLatin1Char('\n'); 0569 errorList << error.toString(); 0570 } 0571 errorData[QStringLiteral("errors")] = QJsonArray::fromStringList(errorList); 0572 } 0573 errorData[QStringLiteral("appletName")] = applet->pluginMetaData().name(); 0574 reason += i18n("Error loading QML file: %1 %2", qmlObject->mainComponent()->url().toString(), reason); 0575 } else { 0576 // TODO: here also try to detect if the package was a P5 one 0577 reason += i18n("Error loading Applet: package does not exist. %1", applet->launchErrorMessage()); 0578 errorData[QStringLiteral("errors")] = QJsonArray::fromStringList({reason}); 0579 } 0580 0581 qCWarning(LOG_PLASMAQUICK) << "error when loading applet" << applet->pluginMetaData().pluginId() 0582 << errorData[QStringLiteral("errors")].toVariant().toStringList(); 0583 0584 qmlObject->setSource(applet->containment()->corona()->kPackage().fileUrl("appleterror")); 0585 0586 applet->setHasConfigurationInterface(false); 0587 // even the error message QML may fail 0588 if (qmlObject->mainComponent()->isError()) { 0589 return nullptr; 0590 } 0591 0592 item = qobject_cast<PlasmoidItem *>(qmlObject->rootObject()); 0593 0594 applet->setLaunchErrorMessage(reason); 0595 if (item) { 0596 item->setProperty("errorInformation", errorData); 0597 } else { 0598 // In this case the error message loaded correctly, but was not a PlasmoidItem, bail out 0599 qCWarning(LOG_PLASMAQUICK) << "Applet Error message is not of type PlasmoidItem" 0600 << applet->containment()->corona()->kPackage().fileUrl("appleterror"); 0601 return nullptr; 0602 } 0603 } 0604 0605 AppletQuickItemPrivate::s_itemsForApplet[applet] = item; 0606 qmlObject->setInitializationDelayed(false); 0607 qmlObject->completeInitialization(); 0608 0609 // A normal applet has UI ready as soon as is loaded, a containment, only when also the wallpaper is loaded 0610 if (!pc || !pc->isContainment()) { 0611 applet->updateConstraints(Plasma::Applet::UiReadyConstraint); 0612 applet->flushPendingConstraintsEvents(); 0613 } 0614 0615 item->setProperty("_plasma_applet", QVariant::fromValue(applet)); 0616 item->d->applet = applet; 0617 item->d->qmlObject = qmlObject; 0618 0619 if (!qEnvironmentVariableIntValue("PLASMA_NO_CONTEXTPROPERTIES")) { 0620 qmlObject->rootContext()->setContextProperty(QStringLiteral("plasmoid"), applet); 0621 } 0622 0623 applet->connect(applet, &Plasma::Applet::appletDeleted, applet, [](Plasma::Applet *applet) { 0624 delete AppletQuickItemPrivate::s_itemsForApplet[applet]; 0625 AppletQuickItemPrivate::s_itemsForApplet.remove(applet); 0626 }); 0627 0628 applet->setProperty("_plasmoid", QVariant::fromValue(item)); 0629 return item; 0630 } 0631 0632 Plasma::Applet *AppletQuickItem::applet() const 0633 { 0634 return d->applet; 0635 } 0636 0637 void AppletQuickItem::init() 0638 { 0639 if (!d->applet) { 0640 // This can happen only if the client QML code declares a PlasmoidItem somewhere else than the root object 0641 return; 0642 } 0643 0644 d->appletPackage = d->applet->kPackage(); 0645 0646 if (d->applet->containment()) { 0647 if (d->applet->containment()->corona()) { 0648 d->coronaPackage = d->applet->containment()->corona()->kPackage(); 0649 } 0650 0651 d->containmentPackage = d->applet->containment()->kPackage(); 0652 } 0653 0654 // Initialize the main QML file 0655 QQmlEngine *engine = d->qmlObject->engine().get(); 0656 0657 // If no fullRepresentation was defined, we won't create compact and expander either. 0658 // The only representation available are whatever items defined directly inside PlasmoidItem {} 0659 // default compactRepresentation is a simple icon provided by the shell package 0660 if (!d->compactRepresentation && d->fullRepresentation) { 0661 d->compactRepresentation = new QQmlComponent(engine, this); 0662 d->compactRepresentation->loadUrl(d->coronaPackage.fileUrl("defaultcompactrepresentation")); 0663 Q_EMIT compactRepresentationChanged(d->compactRepresentation); 0664 } 0665 0666 // default compactRepresentationExpander is the popup in which fullRepresentation goes 0667 if (!d->compactRepresentationExpander && d->fullRepresentation) { 0668 d->compactRepresentationExpander = new QQmlComponent(engine, this); 0669 QUrl compactExpanderUrl = d->containmentPackage.fileUrl("compactapplet"); 0670 if (compactExpanderUrl.isEmpty()) { 0671 compactExpanderUrl = d->coronaPackage.fileUrl("compactapplet"); 0672 } 0673 0674 d->compactRepresentationExpander->loadUrl(compactExpanderUrl); 0675 } 0676 0677 d->initComplete = true; 0678 d->compactRepresentationCheck(); 0679 qmlObject()->engine()->rootContext()->setBaseUrl(qmlObject()->source()); 0680 0681 // if we're expanded we don't care about preloading because it will already be the case 0682 // as well as for containments 0683 if (d->applet->isContainment() || d->expanded || d->preferredRepresentation == d->fullRepresentation) { 0684 return; 0685 } 0686 0687 if (!d->applet->isContainment() && d->applet->containment()) { 0688 connect(d->applet->containment(), &Plasma::Containment::uiReadyChanged, this, [this](bool uiReady) { 0689 if (uiReady && d->s_preloadPolicy >= AppletQuickItemPrivate::Adaptive) { 0690 const int preloadWeight = d->preloadWeight(); 0691 qCDebug(LOG_PLASMAQUICK) << "New Applet " << d->applet->title() << "with a weight of" << preloadWeight; 0692 0693 // don't preload applets less then a certain weight 0694 if (d->s_preloadPolicy >= AppletQuickItemPrivate::Aggressive || preloadWeight >= AppletQuickItemPrivate::DelayedPreloadWeight) { 0695 // spread the creation over a random delay to make it look 0696 // plasma started already, and load the popup in the background 0697 // without big noticeable freezes, the bigger the weight the smaller is likely 0698 // to be the delay, smaller minimum walue, smaller spread 0699 const int min = (100 - preloadWeight) * 20; 0700 const int max = (100 - preloadWeight) * 100; 0701 const int delay = QRandomGenerator::global()->bounded((max + 1) - min) + min; 0702 QTimer::singleShot(delay, this, [this, delay]() { 0703 qCDebug(LOG_PLASMAQUICK) << "Delayed preload of " << d->applet->title() << "after" << (qreal)delay / 1000 << "seconds"; 0704 d->preloadForExpansion(); 0705 }); 0706 } 0707 } 0708 }); 0709 } 0710 } 0711 0712 void AppletQuickItem::classBegin() 0713 { 0714 QQuickItem::classBegin(); 0715 AppletContext *ac = qobject_cast<AppletContext *>(QQmlEngine::contextForObject(this)->parentContext()); 0716 if (!ac) { 0717 qCWarning(LOG_PLASMAQUICK) << "Detected a PlasmoidItem which is not the root QML item: this is not supported."; 0718 return; 0719 } 0720 d->applet = ac->applet(); 0721 d->qmlObject = ac->sharedQmlEngine(); 0722 } 0723 0724 int AppletQuickItem::switchWidth() const 0725 { 0726 return d->switchWidth; 0727 } 0728 0729 void AppletQuickItem::setSwitchWidth(int width) 0730 { 0731 if (d->switchWidth == width) { 0732 return; 0733 } 0734 0735 d->switchWidth = width; 0736 d->compactRepresentationCheck(); 0737 Q_EMIT switchWidthChanged(width); 0738 } 0739 0740 int AppletQuickItem::switchHeight() const 0741 { 0742 return d->switchHeight; 0743 } 0744 0745 void AppletQuickItem::setSwitchHeight(int height) 0746 { 0747 if (d->switchHeight == height) { 0748 return; 0749 } 0750 0751 d->switchHeight = height; 0752 d->compactRepresentationCheck(); 0753 Q_EMIT switchHeightChanged(height); 0754 } 0755 0756 QQmlComponent *AppletQuickItem::compactRepresentation() 0757 { 0758 return d->compactRepresentation; 0759 } 0760 0761 void AppletQuickItem::setCompactRepresentation(QQmlComponent *component) 0762 { 0763 if (d->compactRepresentation == component) { 0764 return; 0765 } 0766 0767 d->compactRepresentation = component; 0768 Q_EMIT compactRepresentationChanged(component); 0769 } 0770 0771 QQmlComponent *AppletQuickItem::fullRepresentation() 0772 { 0773 return d->fullRepresentation; 0774 } 0775 0776 QObject *AppletQuickItem::testItem() 0777 { 0778 if (!d->testItem) { 0779 const QUrl url(d->appletPackage.fileUrl("test")); 0780 if (url.isEmpty()) { 0781 return nullptr; 0782 } 0783 0784 d->testItem = d->qmlObject->createObjectFromSource(url, qmlContext(this)); 0785 if (d->testItem) { 0786 d->testItem->setProperty("plasmoidItem", QVariant::fromValue<QObject *>(this)); 0787 } 0788 } 0789 0790 return d->testItem; 0791 } 0792 0793 void AppletQuickItem::setFullRepresentation(QQmlComponent *component) 0794 { 0795 if (d->fullRepresentation == component) { 0796 return; 0797 } 0798 0799 d->fullRepresentation = component; 0800 Q_EMIT fullRepresentationChanged(component); 0801 } 0802 0803 QQmlComponent *AppletQuickItem::preferredRepresentation() 0804 { 0805 return d->preferredRepresentation; 0806 } 0807 0808 void AppletQuickItem::setPreferredRepresentation(QQmlComponent *component) 0809 { 0810 if (d->preferredRepresentation == component) { 0811 return; 0812 } 0813 0814 d->preferredRepresentation = component; 0815 Q_EMIT preferredRepresentationChanged(component); 0816 d->compactRepresentationCheck(); 0817 } 0818 0819 bool AppletQuickItem::isExpanded() const 0820 { 0821 return d->applet->isContainment() || !d->fullRepresentation || d->expanded; 0822 } 0823 0824 void AppletQuickItem::setExpanded(bool expanded) 0825 { 0826 if (d->expanded == expanded) { 0827 return; 0828 } 0829 0830 if (expanded) { 0831 d->preloadForExpansion(); 0832 // increase on open, ignore containments 0833 if (d->s_preloadPolicy >= AppletQuickItemPrivate::Adaptive && !d->applet->isContainment()) { 0834 const int newWeight = qMin(d->preloadWeight() + AppletQuickItemPrivate::PreloadWeightIncrement, 100); 0835 d->applet->config().writeEntry(QStringLiteral("PreloadWeight"), newWeight); 0836 qCDebug(LOG_PLASMAQUICK) << "Increasing score for" << d->applet->title() << "to" << newWeight; 0837 } 0838 } 0839 0840 d->expanded = expanded; 0841 0842 Q_EMIT expandedChanged(expanded); 0843 } 0844 0845 bool AppletQuickItem::isActivationTogglesExpanded() const 0846 { 0847 return d->activationTogglesExpanded; 0848 } 0849 0850 void AppletQuickItem::setActivationTogglesExpanded(bool activationTogglesExpanded) 0851 { 0852 if (d->activationTogglesExpanded == activationTogglesExpanded) { 0853 return; 0854 } 0855 d->activationTogglesExpanded = activationTogglesExpanded; 0856 Q_EMIT activationTogglesExpandedChanged(activationTogglesExpanded); 0857 } 0858 0859 bool AppletQuickItem::hideOnWindowDeactivate() const 0860 { 0861 return d->hideOnWindowDeactivate; 0862 } 0863 0864 void AppletQuickItem::setHideOnWindowDeactivate(bool hide) 0865 { 0866 if (d->hideOnWindowDeactivate == hide) { 0867 return; 0868 } 0869 d->hideOnWindowDeactivate = hide; 0870 Q_EMIT hideOnWindowDeactivateChanged(hide); 0871 } 0872 0873 bool AppletQuickItem::preloadFullRepresentation() const 0874 { 0875 return d->preloadFullRepresentation; 0876 } 0877 0878 void AppletQuickItem::setPreloadFullRepresentation(bool preload) 0879 { 0880 if (d->preloadFullRepresentation == preload) { 0881 return; 0882 } 0883 0884 d->preloadFullRepresentation = preload; 0885 d->createFullRepresentationItem(); 0886 0887 Q_EMIT preloadFullRepresentationChanged(preload); 0888 } 0889 0890 ////////////Internals 0891 0892 PlasmaQuick::SharedQmlEngine *AppletQuickItem::qmlObject() 0893 { 0894 return d->qmlObject; 0895 } 0896 0897 QQuickItem *AppletQuickItem::compactRepresentationItem() 0898 { 0899 return d->compactRepresentationItem; 0900 } 0901 0902 QQuickItem *AppletQuickItem::fullRepresentationItem() 0903 { 0904 return d->fullRepresentationItem; 0905 } 0906 0907 void AppletQuickItem::childEvent(QChildEvent *event) 0908 { 0909 // Added child may be QQuickLayoutAttached 0910 if (event->added() && !d->ownLayout && d->currentRepresentationItem) { 0911 // Child has not yet finished initialization at this point 0912 QTimer::singleShot(0, this, [this]() { 0913 if (!d->ownLayout) { 0914 d->connectLayoutAttached(d->currentRepresentationItem); 0915 } 0916 }); 0917 } 0918 0919 QQuickItem::childEvent(event); 0920 } 0921 0922 void AppletQuickItem::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry) 0923 { 0924 QQuickItem::geometryChange(newGeometry, oldGeometry); 0925 d->compactRepresentationCheck(); 0926 } 0927 0928 void AppletQuickItem::itemChange(ItemChange change, const ItemChangeData &value) 0929 { 0930 if (change == QQuickItem::ItemSceneChange) { 0931 // we have a window: create the representations if needed 0932 // also avoid initializing the item more than once, 0933 // since this will cause shortcut to open and close the item immediately 0934 if (value.window && !d->initComplete) { 0935 init(); 0936 } 0937 } 0938 0939 QQuickItem::itemChange(change, value); 0940 } 0941 } 0942 0943 #include "moc_appletquickitem.cpp"