Libplasma

appletquickitem.cpp
1/*
2 SPDX-FileCopyrightText: 2014 Marco Martin <mart@kde.org>
3
4 SPDX-License-Identifier: LGPL-2.0-or-later
5*/
6
7#include "appletquickitem.h"
8#include "applet.h"
9#include "appletcontext_p.h"
10#include "appletquickitem_p.h"
11#include "configview.h"
12#include "containment.h"
13#include "debug_p.h"
14#include "plasma_version.h"
15#include "plasmoid/containmentitem.h"
16#include "plasmoid/plasmoiditem.h"
17#include "plasmoid/wallpaperitem.h"
18#include "plasmoidattached_p.h"
19#include "sharedqmlengine.h"
20
21#include <QJsonArray>
22#include <QQmlContext>
23#include <QQmlExpression>
24#include <QQmlProperty>
25#include <QQuickWindow>
26#include <QRandomGenerator>
27
28#include <QDebug>
29
30#include <KLocalizedString>
31
32#include <Plasma/Applet>
33#include <Plasma/Containment>
34#include <Plasma/Corona>
35#include <qquickitem.h>
36
37namespace PlasmaQuick
38{
39
41AppletQuickItemPrivate::PreloadPolicy AppletQuickItemPrivate::s_preloadPolicy = AppletQuickItemPrivate::Uninitialized;
42
43AppletQuickItemPrivate::AppletQuickItemPrivate(AppletQuickItem *item)
44 : q(item)
45 , switchWidth(-1)
46 , switchHeight(-1)
47 , initComplete(false)
48 , compactRepresentationCheckGuard(false)
49{
50 if (s_preloadPolicy == Uninitialized) {
51 // default as Adaptive
52 s_preloadPolicy = Adaptive;
53
54 if (qEnvironmentVariableIsSet("PLASMA_PRELOAD_POLICY")) {
55 const QString policy = qEnvironmentVariable("PLASMA_PRELOAD_POLICY");
56 if (policy.compare(QLatin1String("aggressive"), Qt::CaseInsensitive) == 0) {
57 s_preloadPolicy = Aggressive;
58 } else if (policy.compare(QLatin1String("none"), Qt::CaseInsensitive) == 0) {
59 s_preloadPolicy = None;
60 }
61 }
62
63 qCInfo(LOG_PLASMAQUICK) << "Applet preload policy set to" << s_preloadPolicy;
64 }
65}
66
67int AppletQuickItemPrivate::preloadWeight() const
68{
69 int defaultWeight;
70 const QStringList provides = applet->pluginMetaData().value(u"X-Plasma-Provides", QStringList());
71
72 // some applet types we want a bigger weight
73 if (provides.contains(QLatin1String("org.kde.plasma.launchermenu"))) {
74 defaultWeight = DefaultLauncherPreloadWeight;
75 } else {
76 defaultWeight = DefaultPreloadWeight;
77 }
78 // default widgets to be barely preloaded
79 return qBound(
80 0,
81 applet->config().readEntry(QStringLiteral("PreloadWeight"), qMax(defaultWeight, applet->pluginMetaData().value(u"X-Plasma-PreloadWeight", 0))),
82 100);
83}
84
85QObject *AppletQuickItemPrivate::searchLayoutAttached(QObject *parent) const
86{
87 QObject *layout = nullptr;
88 // Search a child that has the needed Layout properties
89 // HACK: here we are not type safe, but is the only way to access to a pointer of Layout
90 const auto lstChildren = parent->children();
91 for (QObject *child : lstChildren) {
92 // find for the needed property of Layout: minimum/maximum/preferred sizes and fillWidth/fillHeight
93 /* clang-format off */
94 if (child->property("minimumWidth").isValid()
95 && child->property("minimumHeight").isValid()
96 && child->property("preferredWidth").isValid()
97 && child->property("preferredHeight").isValid()
98 && child->property("maximumWidth").isValid()
99 && child->property("maximumHeight").isValid()
100 && child->property("fillWidth").isValid()
101 && child->property("fillHeight").isValid()) { /* clang-format on */
102 layout = child;
103 break;
104 }
105 }
106 return layout;
107}
108
109void AppletQuickItemPrivate::connectLayoutAttached(QObject *item)
110{
111 // Extract the representation's Layout, if any
112 if (!item) {
113 return;
114 }
115
116 QObject *layout = searchLayoutAttached(item);
117
118 // if the compact repr doesn't export a Layout.* attached property,
119 // reset our own with default values
120 if (!layout) {
121 if (ownLayout) {
122 ownLayout->setProperty("minimumWidth", 0);
123 ownLayout->setProperty("minimumHeight", 0);
124 ownLayout->setProperty("preferredWidth", -1);
125 ownLayout->setProperty("preferredHeight", -1);
126 ownLayout->setProperty("maximumWidth", std::numeric_limits<qreal>::infinity());
127 ownLayout->setProperty("maximumHeight", std::numeric_limits<qreal>::infinity());
128 ownLayout->setProperty("fillWidth", false);
129 ownLayout->setProperty("fillHeight", false);
130 }
131 return;
132 }
133
134 // propagate all the size hints
135 propagateSizeHint("minimumWidth");
136 propagateSizeHint("minimumHeight");
137 propagateSizeHint("preferredWidth");
138 propagateSizeHint("preferredHeight");
139 propagateSizeHint("maximumWidth");
140 propagateSizeHint("maximumHeight");
141 propagateSizeHint("fillWidth");
142 propagateSizeHint("fillHeight");
143
144 QObject *newOwnLayout = searchLayoutAttached(q);
145
146 // this should never happen, since we ask to create it if doesn't exists
147 if (!newOwnLayout) {
148 return;
149 }
150
151 // if the representation didn't change, don't do anything
152 if (representationLayout == layout) {
153 return;
154 }
155
156 if (representationLayout) {
157 QObject::disconnect(representationLayout, nullptr, q, nullptr);
158 }
159
160 // Here we can't use the new connect syntax because we can't link against QtQuick layouts
161 QObject::connect(layout, SIGNAL(minimumWidthChanged()), q, SLOT(minimumWidthChanged()));
162 QObject::connect(layout, SIGNAL(minimumHeightChanged()), q, SLOT(minimumHeightChanged()));
163
164 QObject::connect(layout, SIGNAL(preferredWidthChanged()), q, SLOT(preferredWidthChanged()));
165 QObject::connect(layout, SIGNAL(preferredHeightChanged()), q, SLOT(preferredHeightChanged()));
166
167 QObject::connect(layout, SIGNAL(maximumWidthChanged()), q, SLOT(maximumWidthChanged()));
168 QObject::connect(layout, SIGNAL(maximumHeightChanged()), q, SLOT(maximumHeightChanged()));
169
170 QObject::connect(layout, SIGNAL(fillWidthChanged()), q, SLOT(fillWidthChanged()));
171 QObject::connect(layout, SIGNAL(fillHeightChanged()), q, SLOT(fillHeightChanged()));
172
173 representationLayout = layout;
174 ownLayout = newOwnLayout;
175
176 propagateSizeHint("minimumWidth");
177 propagateSizeHint("minimumHeight");
178 propagateSizeHint("preferredWidth");
179 propagateSizeHint("preferredHeight");
180 propagateSizeHint("maximumWidth");
181 propagateSizeHint("maximumHeight");
182 propagateSizeHint("fillWidth");
183 propagateSizeHint("fillHeight");
184}
185
186void AppletQuickItemPrivate::propagateSizeHint(const QByteArray &layoutProperty)
187{
188 if (ownLayout && representationLayout) {
189 ownLayout->setProperty(layoutProperty.constData(), representationLayout->property(layoutProperty.constData()).toReal());
190 }
191}
192
193QQuickItem *AppletQuickItemPrivate::createCompactRepresentationItem()
194{
195 if (!compactRepresentation) {
196 return nullptr;
197 }
198
199 if (compactRepresentationItem) {
200 return compactRepresentationItem;
201 }
202
203 QVariantHash initialProperties;
204 initialProperties[QStringLiteral("parent")] = QVariant::fromValue(q);
205 initialProperties[QStringLiteral("plasmoidItem")] = QVariant::fromValue(q);
206
207 compactRepresentationItem = qobject_cast<QQuickItem *>(qmlObject->createObjectFromComponent(compactRepresentation, qmlContext(q), initialProperties));
208
209 Q_EMIT q->compactRepresentationItemChanged(compactRepresentationItem);
210
211 return compactRepresentationItem;
212}
213
214QQuickItem *AppletQuickItemPrivate::createFullRepresentationItem()
215{
216 if (fullRepresentationItem) {
217 return fullRepresentationItem;
218 }
219
220 if (fullRepresentation && fullRepresentation != qmlObject->mainComponent()) {
221 QVariantHash initialProperties;
222 initialProperties[QStringLiteral("parent")] = QVariant();
223 fullRepresentationItem = qobject_cast<QQuickItem *>(qmlObject->createObjectFromComponent(fullRepresentation, qmlContext(q), initialProperties));
224 }
225
226 if (!fullRepresentationItem) {
227 return nullptr;
228 }
229
230 Q_EMIT q->fullRepresentationItemChanged(fullRepresentationItem);
231
232 return fullRepresentationItem;
233}
234
235QQuickItem *AppletQuickItemPrivate::createCompactRepresentationExpanderItem()
236{
237 if (!compactRepresentationExpander) {
238 return nullptr;
239 }
240
241 if (compactRepresentationExpanderItem) {
242 return compactRepresentationExpanderItem;
243 }
244
245 compactRepresentationExpanderItem = qobject_cast<QQuickItem *>(qmlObject->createObjectFromComponent(compactRepresentationExpander, qmlContext(q)));
246
247 if (!compactRepresentationExpanderItem) {
248 return nullptr;
249 }
250
251 compactRepresentationExpanderItem->setProperty("compactRepresentation", QVariant::fromValue<QObject *>(createCompactRepresentationItem()));
252 compactRepresentationExpanderItem->setProperty("plasmoidItem", QVariant::fromValue(q));
253
254 return compactRepresentationExpanderItem;
255}
256
257bool AppletQuickItemPrivate::appletShouldBeExpanded() const
258{
259 if (applet->isContainment()) {
260 return true;
261
262 } else {
263 if (!fullRepresentation) {
264 // If a full representation wasn't specified, the onle and only representation of the plasmoid are our
265 // direct contents, so we consider it always expanded
266 return true;
267 }
268 if (switchWidth > 0 && switchHeight > 0) {
269 // This code checks against the following edge case:
270 // The compact representation preferred size is bigger than both the switch
271 // size, and the full representation preferred size.
272 // this can cause in the panel (when is quite big) an infinite resize loop, because
273 // the applet size is bigger than the switch size, then it switches to full
274 // representaiton that has a smaller hint. this causes a resize that will make it
275 // switch to compact representation, making it grow again and switch again
276 if (compactRepresentationItem && fullRepresentationItem) {
277 QObject *compactLayout = searchLayoutAttached(compactRepresentationItem);
278 QObject *fullLayout = searchLayoutAttached(fullRepresentationItem);
279 if (compactLayout && fullLayout) {
280 QSizeF compactPreferred = {compactLayout->property("preferredWidth").toReal(), compactLayout->property("preferredHeight").toReal()};
281 QSizeF fullPreferred = {fullLayout->property("preferredWidth").toReal(), fullLayout->property("preferredHeight").toReal()};
282
283 if ((compactPreferred.width() > fullPreferred.width() && compactPreferred.width() > switchWidth) ||
284 (compactPreferred.height() > fullPreferred.height() && compactPreferred.height() > switchHeight)) {
285 return false;
286 }
287 }
288 }
289 return q->width() > switchWidth && q->height() > switchHeight;
290
291 // if a size to switch wasn't set, determine what representation to always chose
292 } else {
293 // preferred representation set?
294 if (preferredRepresentation) {
295 return preferredRepresentation == fullRepresentation;
296 // Otherwise, base on FormFactor
297 } else {
298 return (applet->formFactor() != Plasma::Types::Horizontal && applet->formFactor() != Plasma::Types::Vertical);
299 }
300 }
301 }
302}
303
304void AppletQuickItemPrivate::preloadForExpansion()
305{
306 qint64 time = 0;
307 if (QLoggingCategory::defaultCategory()->isInfoEnabled()) {
309 }
310
311 if (!createFullRepresentationItem()) {
312 return;
313 }
314
315 // When not already expanded, also preload the expander
316 if (!appletShouldBeExpanded() && !applet->isContainment() && (!preferredRepresentation || preferredRepresentation != fullRepresentation)) {
317 createCompactRepresentationExpanderItem();
318 }
319
320 if (!appletShouldBeExpanded() && compactRepresentationExpanderItem) {
321 compactRepresentationExpanderItem->setProperty("fullRepresentation", QVariant::fromValue<QObject *>(createFullRepresentationItem()));
322 } else if (fullRepresentationItem) {
323 fullRepresentationItem->setProperty("parent", QVariant::fromValue<QObject *>(q));
324 }
325
326 // preallocate nodes
327 if (fullRepresentationItem && fullRepresentationItem->window()) {
328 fullRepresentationItem->window()->create();
329 }
330
331 qCDebug(LOG_PLASMAQUICK) << "Applet" << applet->title() << "loaded after" << (QDateTime::currentMSecsSinceEpoch() - time) << "msec";
332}
333
334void AppletQuickItemPrivate::anchorsFillParent(QQuickItem *item, QQuickItem *parent)
335{
336 if (item->parentItem() != parent) {
337 return;
338 }
339 // just set once, don't bind
340 QQmlProperty::write(item, QStringLiteral("anchors.fill"), QVariant::fromValue<QObject *>(parent));
341}
342
343void AppletQuickItemPrivate::compactRepresentationCheck()
344{
345 if (!initComplete) {
346 return;
347 }
348
349 // ignore 0 sizes;
350 if (q->width() <= 0 || q->height() <= 0) {
351 return;
352 }
353
354 // ignore if this widget is being checked somewhere above
355 if (compactRepresentationCheckGuard) {
356 return;
357 }
358
359 bool full = appletShouldBeExpanded();
360
361 if ((full && fullRepresentationItem && fullRepresentationItem == currentRepresentationItem)
362 || (!full && compactRepresentationItem && compactRepresentationItem == currentRepresentationItem)) {
363 return;
364 }
365
366 compactRepresentationCheckGuard = true;
367
368 // Expanded
369 if (full) {
370 QQuickItem *item = createFullRepresentationItem();
371
372 if (item) {
373 // unwire with the expander
374 if (compactRepresentationExpanderItem) {
375 compactRepresentationExpanderItem->setProperty("fullRepresentation", QVariant());
376 compactRepresentationExpanderItem->setProperty("compactRepresentation", QVariant());
377 compactRepresentationExpanderItem->setVisible(false);
378 }
379
380 const bool fullRepresentationWasVisible = fullRepresentationItem->parentItem() == q;
381
382 // the fullrepresentation being the complete AppletItem is actually allowed when the main ui
383 // is child of the root item (like many panel applets)
384 if (item != q) {
385 item->setParentItem(q);
386 anchorsFillParent(item, q);
387 }
388
389 if (compactRepresentationItem) {
390 compactRepresentationItem->setVisible(false);
391 }
392
393 currentRepresentationItem = item;
394 connectLayoutAttached(item);
395
396 if (!expanded && !fullRepresentationWasVisible) {
397 expanded = true;
398 Q_EMIT q->expandedChanged(true);
399 }
400 }
401
402 } else {
403 // Icon
404 QQuickItem *compactItem = createCompactRepresentationItem();
405 QQuickItem *compactExpanderItem = createCompactRepresentationExpanderItem();
406
407 if (compactItem && compactExpanderItem) {
408 // set the root item as the main visible item
409 compactItem->setVisible(true);
410 compactExpanderItem->setParentItem(q);
411 compactExpanderItem->setVisible(true);
412 anchorsFillParent(compactExpanderItem, q);
413
414 // only reparent full representation to null if it was parented to the applet
415 // if it was already in the expander, leave it where it is
416 const bool fullRepresentationWasVisible = fullRepresentationItem && fullRepresentationItem->parentItem() == q;
417 if (fullRepresentationItem && fullRepresentationWasVisible) {
418 fullRepresentationItem->setProperty("parent", QVariant());
419 }
420
421 compactExpanderItem->setProperty("compactRepresentation", QVariant::fromValue<QObject *>(compactItem));
422 // The actual full representation will be connected when created
423 compactExpanderItem->setProperty("fullRepresentation", QVariant());
424
425 currentRepresentationItem = compactItem;
426 connectLayoutAttached(compactItem);
427
428 // set Expanded to false only if the expanded cause was the full representation
429 // in the applet item, not if the full representation was in the popup and the popup was open
430 if (expanded && fullRepresentationWasVisible) {
431 expanded = false;
432 Q_EMIT q->expandedChanged(false);
433 }
434 }
435 }
436
437 compactRepresentationCheckGuard = false;
438}
439
440void AppletQuickItemPrivate::minimumWidthChanged()
441{
442 propagateSizeHint("minimumWidth");
443}
444
445void AppletQuickItemPrivate::minimumHeightChanged()
446{
447 propagateSizeHint("minimumHeight");
448}
449
450void AppletQuickItemPrivate::preferredWidthChanged()
451{
452 propagateSizeHint("preferredWidth");
453}
454
455void AppletQuickItemPrivate::preferredHeightChanged()
456{
457 propagateSizeHint("preferredHeight");
458}
459
460void AppletQuickItemPrivate::maximumWidthChanged()
461{
462 propagateSizeHint("maximumWidth");
463}
464
465void AppletQuickItemPrivate::maximumHeightChanged()
466{
467 propagateSizeHint("maximumHeight");
468}
469
470void AppletQuickItemPrivate::fillWidthChanged()
471{
472 propagateSizeHint("fillWidth");
473}
474
475void AppletQuickItemPrivate::fillHeightChanged()
476{
477 propagateSizeHint("fillHeight");
478}
479
480AppletQuickItem::AppletQuickItem(QQuickItem *parent)
481 : QQuickItem(parent)
482 , d(new AppletQuickItemPrivate(this))
483{
484}
485
486AppletQuickItem::~AppletQuickItem()
487{
488 AppletQuickItemPrivate::s_itemsForApplet.remove(d->applet);
489 // decrease weight
490 if (d->s_preloadPolicy >= AppletQuickItemPrivate::Adaptive) {
491 d->applet->config().writeEntry(QStringLiteral("PreloadWeight"), qMax(0, d->preloadWeight() - AppletQuickItemPrivate::PreloadWeightDecrement));
492 }
493
494 // Here the order is important
495 delete d->compactRepresentationItem;
496 delete d->fullRepresentationItem;
497 delete d->compactRepresentationExpanderItem;
498 delete d;
499}
500
501bool AppletQuickItem::hasItemForApplet(Plasma::Applet *applet)
502{
503 return AppletQuickItemPrivate::s_itemsForApplet.contains(applet);
504}
505
506AppletQuickItem *AppletQuickItem::itemForApplet(Plasma::Applet *applet)
507{
508 if (!applet) {
509 return nullptr;
510 }
511
512 // TODO: move somewhere else? in plasmacore import?
513 if (AppletQuickItemPrivate::s_itemsForApplet.isEmpty()) {
514 const char *uri = "org.kde.plasma.plasmoid";
515 qmlRegisterExtendedType<Plasma::Applet, PlasmoidAttached>(uri, 2, 0, "Plasmoid");
516 qmlRegisterExtendedType<Plasma::Containment, ContainmentAttached>(uri, 2, 0, "Containment");
517
518 qmlRegisterType<PlasmoidItem>(uri, 2, 0, "PlasmoidItem");
519 qmlRegisterType<ContainmentItem>(uri, 2, 0, "ContainmentItem");
520 qmlRegisterType<WallpaperItem>(uri, 2, 0, "WallpaperItem");
521 qmlRegisterAnonymousType<Plasma::Corona>("org.kde.plasma.plasmoid", 1);
522 }
523 auto it = AppletQuickItemPrivate::s_itemsForApplet.constFind(applet);
524 if (it != AppletQuickItemPrivate::s_itemsForApplet.constEnd()) {
525 return it.value();
526 }
527
528 // Don't try to create applet items when the app is closing
529 if (qApp->closingDown() || applet->destroyed()) {
530 return nullptr;
531 }
532
533 Plasma::Containment *pc = qobject_cast<Plasma::Containment *>(applet);
534 auto *qmlObject = new PlasmaQuick::SharedQmlEngine(applet, applet);
535 qmlObject->engine()->setProperty("_kirigamiTheme", QStringLiteral("KirigamiPlasmaStyle"));
536 qmlObject->setInitializationDelayed(true);
537 qmlObject->setTranslationDomain(applet->translationDomain());
538
539 AppletQuickItem *item = nullptr;
540 qmlObject->setSource(applet->mainScript());
541
542 Q_ASSERT(qmlObject->mainComponent());
543
544 if (pc && pc->isContainment()) {
545 item = qobject_cast<ContainmentItem *>(qmlObject->rootObject());
546 if (!item && !qmlObject->mainComponent()->isError()) {
547 applet->setLaunchErrorMessage(i18n("The root item of %1 must be of type ContainmentItem", applet->mainScript().toString()));
548 }
549 } else {
550 item = qobject_cast<PlasmoidItem *>(qmlObject->rootObject());
551 if (!item && !qmlObject->mainComponent()->isError()) {
552 applet->setLaunchErrorMessage(i18n("The root item of %1 must be of type PlasmoidItem", applet->mainScript().toString()));
553 }
554 }
555
556 if (!item || qmlObject->mainComponent()->isError() || applet->failedToLaunch()) {
557 QString reason;
558 QString compactReason;
559 QJsonObject errorData;
560 errorData[QStringLiteral("appletName")] = i18n("Unknown Applet");
561 errorData[QStringLiteral("isDebugMode")] = qEnvironmentVariableIntValue("PLASMA_ENABLE_QML_DEBUG") != 0;
562
563 if (applet->sourceValid()) {
564 const QString versionString = applet->pluginMetaData().value(u"X-Plasma-API-Minimum-Version");
566 if (!versionString.isEmpty()) {
567 version = QVersionNumber::fromString(versionString);
568 }
569
570 bool versionMismatch = false;
571 const int plasma_version_major = 6; // TODO: as soon PLASMA_VERSION_MAJOR is actually 6, use directly that
572 if (version.isNull()) {
573 reason = i18n(
574 "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 "
575 "an updated version.",
576 plasma_version_major);
577 compactReason = i18n("%1 is not compatible with Plasma %2", applet->pluginMetaData().name(), plasma_version_major);
578 versionMismatch = true;
579 } else if (version.majorVersion() < plasma_version_major) {
580 reason =
581 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.",
582 version.majorVersion(),
583 plasma_version_major);
584 compactReason = i18n("%1 is not compatible with Plasma %2", applet->pluginMetaData().name(), plasma_version_major);
585 versionMismatch = true;
586 } else if (version.majorVersion() > plasma_version_major || version.minorVersion() > PLASMA_VERSION_MINOR) {
587 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.",
588 versionString,
589 plasma_version_major);
590 compactReason = i18n("%1 is not compatible with Plasma %2", applet->pluginMetaData().name(), plasma_version_major);
591 versionMismatch = true;
592 } else if (applet->failedToLaunch()) {
593 reason = applet->launchErrorMessage();
594 compactReason = reason;
595 } else {
596 compactReason = i18n("Sorry! There was an error loading %1.", applet->pluginMetaData().name());
597 }
598 errorData[QStringLiteral("errors")] = QJsonArray::fromStringList({reason});
599 if (compactReason != QString()) {
600 errorData[QStringLiteral("compactError")] = compactReason;
601 }
602
603 if (!versionMismatch) {
604 const auto errors = qmlObject->mainComponent()->errors();
606 for (const QQmlError &error : errors) {
607 reason += error.toString() + QLatin1Char('\n');
608 errorList << error.toString();
609 }
610 errorData[QStringLiteral("errors")] = QJsonArray::fromStringList(errorList);
611 }
612 errorData[QStringLiteral("appletName")] = applet->pluginMetaData().name();
613 reason += i18n("Error loading QML file: %1 %2", qmlObject->mainComponent()->url().toString(), reason);
614 } else {
615 const auto pluginId = applet->pluginMetaData().pluginId();
616 reason = i18n("Error loading Applet: package %1 does not exist.", pluginId);
617 errorData[QStringLiteral("errors")] = QJsonArray::fromStringList({reason});
618 compactReason = i18n("Sorry! There was an error loading %1.", pluginId);
619 errorData[QStringLiteral("compactError")] = compactReason;
620 }
621
622 qCWarning(LOG_PLASMAQUICK) << "error when loading applet" << applet->pluginMetaData().pluginId()
623 << errorData[QStringLiteral("errors")].toVariant().toStringList();
624
625 qmlObject->setSource(applet->containment()->corona()->kPackage().fileUrl("appleterror"));
626
627 applet->setHasConfigurationInterface(false);
628 // even the error message QML may fail
629 if (qmlObject->mainComponent()->isError()) {
630 return nullptr;
631 }
632
633 item = qobject_cast<PlasmoidItem *>(qmlObject->rootObject());
634
635 applet->setLaunchErrorMessage(reason);
636 if (item) {
637 item->setProperty("errorInformation", errorData);
638 } else {
639 // In this case the error message loaded correctly, but was not a PlasmoidItem, bail out
640 qCWarning(LOG_PLASMAQUICK) << "Applet Error message is not of type PlasmoidItem"
641 << applet->containment()->corona()->kPackage().fileUrl("appleterror");
642 return nullptr;
643 }
644 }
645
646 AppletQuickItemPrivate::s_itemsForApplet[applet] = item;
647 qmlObject->setInitializationDelayed(false);
648 qmlObject->completeInitialization();
649
650 // A normal applet has UI ready as soon as is loaded, a containment, only when also the wallpaper is loaded
651 if (!pc || !pc->isContainment()) {
654 }
655
656 item->setProperty("_plasma_applet", QVariant::fromValue(applet));
657 item->d->applet = applet;
658 item->d->qmlObject = qmlObject;
659
660 if (!qEnvironmentVariableIntValue("PLASMA_NO_CONTEXTPROPERTIES")) {
661 qmlObject->rootContext()->setContextProperty(QStringLiteral("plasmoid"), applet);
662 }
663
664 QObject::connect(applet, &Plasma::Applet::appletDeleted, item, [qmlObject](Plasma::Applet *applet) {
665 // Deleting qmlObject will also delete the instantiated plasmoidItem
666 // deleteing just the plasmoiditem will cause a double deletion when qmlObject
667 // gets deleted by applet deletion
668 if (qmlObject->parent() == applet) {
669 // appletDelete can also be emitted by a containment for one of its children
670 delete qmlObject;
671 AppletQuickItemPrivate::s_itemsForApplet.remove(applet);
672 }
673 });
674
675 applet->setProperty("_plasmoid", QVariant::fromValue(item));
676 return item;
677}
678
679Plasma::Applet *AppletQuickItem::applet() const
680{
681 return d->applet;
682}
683
684void AppletQuickItem::init()
685{
686 if (!d->applet) {
687 // This can happen only if the client QML code declares a PlasmoidItem somewhere else than the root object
688 return;
689 }
690 if (d->initComplete) {
691 return;
692 }
693
694 if (d->applet->containment()) {
695 if (d->applet->containment()->corona()) {
696 d->coronaPackage = d->applet->containment()->corona()->kPackage();
697 }
698 }
699
700 // Initialize the main QML file
701 QQmlEngine *engine = d->qmlObject->engine().get();
702
703 // If no fullRepresentation was defined, we won't create compact and expander either.
704 // The only representation available are whatever items defined directly inside PlasmoidItem {}
705 // default compactRepresentation is a simple icon provided by the shell package
706 if (!d->compactRepresentation && d->fullRepresentation) {
707 d->compactRepresentation = new QQmlComponent(engine, this);
708 d->compactRepresentation->loadUrl(d->coronaPackage.fileUrl("defaultcompactrepresentation"));
709 Q_EMIT compactRepresentationChanged(d->compactRepresentation);
710 }
711
712 // default compactRepresentationExpander is the popup in which fullRepresentation goes
713 if (!d->compactRepresentationExpander && d->fullRepresentation) {
714 d->compactRepresentationExpander = new QQmlComponent(engine, this);
715 QUrl compactExpanderUrl = d->applet->containment()->compactApplet();
716 if (compactExpanderUrl.isEmpty()) {
717 compactExpanderUrl = d->coronaPackage.fileUrl("compactapplet");
718 }
719
720 d->compactRepresentationExpander->loadUrl(compactExpanderUrl);
721 }
722
723 d->initComplete = true;
724 d->compactRepresentationCheck();
725 qmlObject()->engine()->rootContext()->setBaseUrl(qmlObject()->source());
726
727 // if we're expanded we don't care about preloading because it will already be the case
728 // as well as for containments
729 if (d->applet->isContainment() || d->expanded || d->preferredRepresentation == d->fullRepresentation) {
730 return;
731 }
732
733 if (!d->applet->isContainment() && d->applet->containment()) {
734 connect(d->applet->containment(), &Plasma::Containment::uiReadyChanged, this, [this](bool uiReady) {
735 if (uiReady && d->s_preloadPolicy >= AppletQuickItemPrivate::Adaptive) {
736 const int preloadWeight = d->preloadWeight();
737 qCDebug(LOG_PLASMAQUICK) << "New Applet " << d->applet->title() << "with a weight of" << preloadWeight;
738
739 // don't preload applets less then a certain weight
740 if (d->s_preloadPolicy >= AppletQuickItemPrivate::Aggressive || preloadWeight >= AppletQuickItemPrivate::DelayedPreloadWeight) {
741 // spread the creation over a random delay to make it look
742 // plasma started already, and load the popup in the background
743 // without big noticeable freezes, the bigger the weight the smaller is likely
744 // to be the delay, smaller minimum walue, smaller spread
745 const int min = (100 - preloadWeight) * 20;
746 const int max = (100 - preloadWeight) * 100;
747 const int delay = QRandomGenerator::global()->bounded((max + 1) - min) + min;
748 QTimer::singleShot(delay, this, [this, delay]() {
749 qCDebug(LOG_PLASMAQUICK) << "Delayed preload of " << d->applet->title() << "after" << (qreal)delay / 1000 << "seconds";
750 d->preloadForExpansion();
751 });
752 }
753 }
754 });
755 }
756}
757
758void AppletQuickItem::classBegin()
759{
761 AppletContext *ac = qobject_cast<AppletContext *>(QQmlEngine::contextForObject(this)->parentContext());
762 if (!ac) {
763 qCWarning(LOG_PLASMAQUICK) << "Detected a PlasmoidItem which is not the root QML item: this is not supported.";
764 return;
765 }
766 d->applet = ac->applet();
767 d->qmlObject = ac->sharedQmlEngine();
768}
769
770void AppletQuickItem::componentComplete()
771{
773 init();
774}
775
776int AppletQuickItem::switchWidth() const
777{
778 return d->switchWidth;
779}
780
781void AppletQuickItem::setSwitchWidth(int width)
782{
783 if (d->switchWidth == width) {
784 return;
785 }
786
787 d->switchWidth = width;
788 d->compactRepresentationCheck();
789 Q_EMIT switchWidthChanged(width);
790}
791
792int AppletQuickItem::switchHeight() const
793{
794 return d->switchHeight;
795}
796
797void AppletQuickItem::setSwitchHeight(int height)
798{
799 if (d->switchHeight == height) {
800 return;
801 }
802
803 d->switchHeight = height;
804 d->compactRepresentationCheck();
805 Q_EMIT switchHeightChanged(height);
806}
807
808QQmlComponent *AppletQuickItem::compactRepresentation()
809{
810 return d->compactRepresentation;
811}
812
813void AppletQuickItem::setCompactRepresentation(QQmlComponent *component)
814{
815 if (d->compactRepresentation == component) {
816 return;
817 }
818
819 d->compactRepresentation = component;
820 Q_EMIT compactRepresentationChanged(component);
821}
822
823QQmlComponent *AppletQuickItem::fullRepresentation()
824{
825 return d->fullRepresentation;
826}
827
828void AppletQuickItem::setFullRepresentation(QQmlComponent *component)
829{
830 if (d->fullRepresentation == component) {
831 return;
832 }
833
834 d->fullRepresentation = component;
835 Q_EMIT fullRepresentationChanged(component);
836}
837
838QQmlComponent *AppletQuickItem::preferredRepresentation()
839{
840 return d->preferredRepresentation;
841}
842
843void AppletQuickItem::setPreferredRepresentation(QQmlComponent *component)
844{
845 if (d->preferredRepresentation == component) {
846 return;
847 }
848
849 d->preferredRepresentation = component;
850 Q_EMIT preferredRepresentationChanged(component);
851 d->compactRepresentationCheck();
852}
853
854bool AppletQuickItem::isExpanded() const
855{
856 return d->applet->isContainment() || !d->fullRepresentation || d->expanded;
857}
858
859void AppletQuickItem::setExpanded(bool expanded)
860{
861 if (d->expanded == expanded) {
862 return;
863 }
864
865 if (expanded) {
866 d->preloadForExpansion();
867 // increase on open, ignore containments
868 if (d->s_preloadPolicy >= AppletQuickItemPrivate::Adaptive && !d->applet->isContainment()) {
869 const int newWeight = qMin(d->preloadWeight() + AppletQuickItemPrivate::PreloadWeightIncrement, 100);
870 d->applet->config().writeEntry(QStringLiteral("PreloadWeight"), newWeight);
871 qCDebug(LOG_PLASMAQUICK) << "Increasing score for" << d->applet->title() << "to" << newWeight;
872 }
873 }
874
875 d->expanded = expanded;
876
877 Q_EMIT expandedChanged(expanded);
878}
879
880bool AppletQuickItem::isActivationTogglesExpanded() const
881{
882 return d->activationTogglesExpanded;
883}
884
885void AppletQuickItem::setActivationTogglesExpanded(bool activationTogglesExpanded)
886{
887 if (d->activationTogglesExpanded == activationTogglesExpanded) {
888 return;
889 }
890 d->activationTogglesExpanded = activationTogglesExpanded;
891 Q_EMIT activationTogglesExpandedChanged(activationTogglesExpanded);
892}
893
894bool AppletQuickItem::hideOnWindowDeactivate() const
895{
896 return d->hideOnWindowDeactivate;
897}
898
899void AppletQuickItem::setHideOnWindowDeactivate(bool hide)
900{
901 if (d->hideOnWindowDeactivate == hide) {
902 return;
903 }
904 d->hideOnWindowDeactivate = hide;
905 Q_EMIT hideOnWindowDeactivateChanged(hide);
906}
907
908bool AppletQuickItem::preloadFullRepresentation() const
909{
910 return d->preloadFullRepresentation;
911}
912
913void AppletQuickItem::setPreloadFullRepresentation(bool preload)
914{
915 if (d->preloadFullRepresentation == preload) {
916 return;
917 }
918
919 d->preloadFullRepresentation = preload;
920 d->createFullRepresentationItem();
921
922 Q_EMIT preloadFullRepresentationChanged(preload);
923}
924
925bool AppletQuickItem::expandedOnDragHover() const
926{
927 return d->expandedOnDragHover;
928}
929
930void AppletQuickItem::setExpandedOnDragHover(bool expandedOnDragHover)
931{
932 if (expandedOnDragHover == d->expandedOnDragHover) {
933 return;
934 }
935
936 d->expandedOnDragHover = expandedOnDragHover;
937 Q_EMIT expandedOnDragHoverChanged(expandedOnDragHover);
938}
939
940////////////Internals
941
942PlasmaQuick::SharedQmlEngine *AppletQuickItem::qmlObject()
943{
944 return d->qmlObject;
945}
946
947QQuickItem *AppletQuickItem::compactRepresentationItem()
948{
949 return d->compactRepresentationItem;
950}
951
952QQuickItem *AppletQuickItem::fullRepresentationItem()
953{
954 return d->fullRepresentationItem;
955}
956
957void AppletQuickItem::childEvent(QChildEvent *event)
958{
959 // Added child may be QQuickLayoutAttached
960 if (event->added() && !d->ownLayout && d->currentRepresentationItem) {
961 // Child has not yet finished initialization at this point
962 QTimer::singleShot(0, this, [this]() {
963 if (!d->ownLayout) {
964 d->connectLayoutAttached(d->currentRepresentationItem);
965 }
966 });
967 }
968
970}
971
972void AppletQuickItem::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
973{
974 QQuickItem::geometryChange(newGeometry, oldGeometry);
975 d->compactRepresentationCheck();
976}
977}
978
979#include "moc_appletquickitem.cpp"
QUrl fileUrl(const QByteArray &key, const QString &filename=QString()) const
QString pluginId() const
bool value(QStringView key, bool defaultValue) const
QString name() const
An object that instantiates an entire QML context, with its own declarative engine.
The base Applet class.
Definition applet.h:64
void updateConstraints(Constraints constraints=AllConstraints)
Called when any of the geometry constraints have been updated.
Definition applet.cpp:266
void setHasConfigurationInterface(bool hasInterface)
Sets whether or not this applet provides a user interface for configuring the applet.
Definition applet.cpp:780
bool failedToLaunch() const
If for some reason, the applet fails to get up on its feet (the library couldn't be loaded,...
Definition applet.cpp:462
@ UiReadyConstraint
The ui has been completely loaded.
Definition applet.h:220
bool isContainment
True if this applet is a Containment and is acting as one, such as a desktop or a panel.
Definition applet.h:200
QString translationDomain() const
The translation domain for this applet.
Definition applet.cpp:877
void appletDeleted(Plasma::Applet *applet)
Emitted when the applet is deleted.
void setLaunchErrorMessage(const QString &reason=QString())
Call this method when the applet fails to launch properly.
Definition applet.cpp:169
bool destroyed() const
Definition applet.cpp:233
Plasma::Containment * containment
The Containment managing this applet.
Definition applet.h:189
void flushPendingConstraintsEvents()
Sends all pending constraints updates to the applet.
Definition applet.cpp:533
KPluginMetaData pluginMetaData() const
Definition applet.cpp:391
QString launchErrorMessage() const
If for some reason, the applet fails to get up on its feet (the library couldn't be loaded,...
Definition applet.cpp:457
The base class for plugins that provide backgrounds and applet grouping containers.
Definition containment.h:47
void uiReadyChanged(bool uiReady)
Emitted when the ui has been fully loaded and is fully working.
Plasma::Corona * corona
The corona for this contaiment.
Definition containment.h:59
@ Vertical
The applet is constrained horizontally, but can expand vertically.
Definition plasma.h:53
@ Horizontal
The applet is constrained vertically, but can expand horizontally.
Definition plasma.h:51
QString i18n(const char *text, const TYPE &arg...)
AKONADI_CALENDAR_EXPORT KCalendarCore::Event::Ptr event(const Akonadi::Item &item)
KCOREADDONS_EXPORT QString versionString()
KCOREADDONS_EXPORT unsigned int version()
void errorList(QWidget *parent, const QString &text, const QStringList &strlist, const QString &title=QString(), Options options=Notify)
void error(QWidget *parent, const QString &text, const QString &title, const KGuiItem &buttonOk, Options options=Notify)
The EdgeEventForwarder class This class forwards edge events to be replayed within the given margin T...
Definition action.h:20
QCA_EXPORT void init()
const char * constData() const const
qint64 currentMSecsSinceEpoch()
const_iterator constFind(const Key &key) const const
bool contains(const Key &key) const const
bool remove(const Key &key)
QJsonArray fromStringList(const QStringList &list)
T value(qsizetype i) const const
QLoggingCategory * defaultCategory()
virtual void childEvent(QChildEvent *event)
const QObjectList & children() const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
bool disconnect(const QMetaObject::Connection &connection)
QVariant property(const char *name) const const
bool setProperty(const char *name, QVariant &&value)
void setBaseUrl(const QUrl &baseUrl)
QQmlContext * contextForObject(const QObject *object)
QQmlContext * rootContext() const const
bool write(QObject *object, const QString &name, const QVariant &value)
virtual void classBegin() override
virtual void componentComplete() override
virtual void geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
QQuickItem * parentItem() const const
void setVisible(bool)
qreal height() const const
qreal width() const const
int compare(QLatin1StringView s1, const QString &s2, Qt::CaseSensitivity cs)
bool isEmpty() const const
bool contains(QLatin1StringView str, Qt::CaseSensitivity cs) const const
CaseInsensitive
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
bool isEmpty() const const
QString toString(FormattingOptions options) const const
QVariant fromValue(T &&value)
qreal toReal(bool *ok) const const
QVersionNumber fromString(QAnyStringView string, qsizetype *suffixIndex)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Sat Dec 21 2024 17:01:35 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.