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"