Plasma-framework

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

KDE's Doxygen guidelines are available online.