File indexing completed on 2024-05-12 05:37:12

0001 /*
0002     SPDX-FileCopyrightText: 2019 Marco Martin <mart@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "appletslayout.h"
0008 #include "appletcontainer.h"
0009 #include "containmentlayoutmanager_debug.h"
0010 #include "gridlayoutmanager.h"
0011 
0012 #include <QGuiApplication>
0013 #include <QQmlContext>
0014 #include <QQmlEngine>
0015 #include <QStyleHints>
0016 #include <QTimer>
0017 
0018 // Plasma
0019 #include <Plasma/Containment>
0020 #include <Plasma/Corona>
0021 #include <PlasmaQuick/AppletQuickItem>
0022 #include <chrono>
0023 
0024 using namespace std::chrono_literals;
0025 
0026 AppletsLayout::AppletsLayout(QQuickItem *parent)
0027     : QQuickItem(parent)
0028 {
0029     m_layoutManager = new GridLayoutManager(this);
0030 
0031     setFlags(QQuickItem::ItemIsFocusScope);
0032     setAcceptedMouseButtons(Qt::LeftButton);
0033     setAcceptTouchEvents(true);
0034 
0035     m_saveLayoutTimer = new QTimer(this);
0036     m_saveLayoutTimer->setSingleShot(true);
0037     m_saveLayoutTimer->setInterval(100ms);
0038     connect(m_layoutManager, &AbstractLayoutManager::layoutNeedsSaving, m_saveLayoutTimer, QOverload<>::of(&QTimer::start));
0039     connect(m_saveLayoutTimer, &QTimer::timeout, this, [this]() {
0040         // We can't assume m_containment to be valid: if we load in a plasmoid that can run also
0041         // in "applet" mode, m_containment will never be valid
0042         if (!m_containment) {
0043             return;
0044         }
0045         // We can't save the layout during bootup, for performance reasons and to avoid race consitions as much as possible, so if we needto save and still
0046         // starting up, don't actually savenow, but we will when Corona::startupCompleted is emitted
0047 
0048         if (!m_configKey.isEmpty() && m_containment && m_containment->corona()->isStartupCompleted()) {
0049             const QString serializedConfig = m_layoutManager->serializeLayout();
0050             m_containment->config().writeEntry(m_configKey, serializedConfig);
0051             m_containment->config().writeEntry(m_fallbackConfigKey, serializedConfig);
0052             // FIXME: something more efficient
0053             m_layoutManager->parseLayout(serializedConfig);
0054             m_savedSize = size();
0055             m_containment->corona()->requireConfigSync();
0056         }
0057     });
0058 
0059     m_layoutChangeTimer = new QTimer(this);
0060     m_layoutChangeTimer->setSingleShot(true);
0061     m_layoutChangeTimer->setInterval(100ms);
0062     connect(m_layoutChangeTimer, &QTimer::timeout, this, [this]() {
0063         // We can't assume m_containment to be valid: if we load in a plasmoid that can run also
0064         // in "applet" mode, m_containment will never be valid
0065         if (!m_containment || width() <= 0 || height() <= 0 || m_relayoutLock) {
0066             return;
0067         }
0068 
0069         const QString &serializedConfig = m_containment->config().readEntry(m_configKey, "");
0070         if ((m_layoutChanges & ConfigKeyChange)) {
0071             if (!m_configKey.isEmpty() && m_containment) {
0072                 m_layoutManager->parseLayout(serializedConfig);
0073                 if (width() > 0 && height() > 0) {
0074                     if (m_geometryBeforeResolutionChange.isEmpty()) {
0075                         m_layoutManager->resetLayoutFromConfig(QRectF(), QRectF());
0076                     } else {
0077                         m_layoutManager->resetLayoutFromConfig(QRectF(x(), y(), width(), height()), m_geometryBeforeResolutionChange);
0078                     }
0079                     m_savedSize = size();
0080                 }
0081             }
0082         } else if (m_layoutChanges & SizeChange) {
0083             const QRect newGeom(x(), y(), width(), height());
0084             // The size has been restored from the last one it has been saved: restore that exact same layout
0085             if (newGeom.size() == m_savedSize) {
0086                 m_layoutManager->resetLayoutFromConfig(QRectF(), QRectF());
0087 
0088                 // If the resize is consequence of a screen resolution change, queue a relayout maintaining the distance between screen edges
0089             } else if (!m_geometryBeforeResolutionChange.isEmpty()) {
0090                 m_layoutManager->layoutGeometryChanged(newGeom, m_geometryBeforeResolutionChange);
0091                 m_geometryBeforeResolutionChange = QRectF();
0092 
0093                 // If the user doesn't move a widget after this is done, the widget positions won't be saved and they will be in the wrong
0094                 // places on next login, so save them now.
0095 
0096                 save();
0097             }
0098         }
0099         m_layoutChanges = NoChange;
0100     });
0101     m_pressAndHoldTimer = new QTimer(this);
0102     m_pressAndHoldTimer->setSingleShot(true);
0103     connect(m_pressAndHoldTimer, &QTimer::timeout, this, [this]() {
0104         setEditMode(true);
0105     });
0106 }
0107 
0108 AppletsLayout::~AppletsLayout()
0109 {
0110 }
0111 
0112 Plasma::Containment *AppletsLayout::containment() const
0113 {
0114     return m_containment;
0115 }
0116 
0117 void AppletsLayout::setContainment(Plasma::Containment *containment)
0118 {
0119     // Forbid changing containment at runtime
0120     if (m_containment || !containment->isContainment()) {
0121         qCWarning(CONTAINMENTLAYOUTMANAGER_DEBUG) << "Error: cannot change the containment to AppletsLayout";
0122         return;
0123     }
0124 
0125     m_containment = containment;
0126 
0127     connect(m_containment, &Plasma::Containment::appletAdded, this, &AppletsLayout::appletAdded);
0128     connect(m_containment, &Plasma::Containment::appletRemoved, this, &AppletsLayout::appletRemoved);
0129 
0130     Q_EMIT containmentChanged();
0131 }
0132 
0133 PlasmaQuick::AppletQuickItem *AppletsLayout::containmentItem() const
0134 {
0135     return m_containmentItem;
0136 }
0137 
0138 void AppletsLayout::setContainmentItem(PlasmaQuick::AppletQuickItem *containmentItem)
0139 {
0140     if (containmentItem == m_containmentItem) {
0141         return;
0142     }
0143 
0144     m_containmentItem = containmentItem;
0145 
0146     Q_EMIT containmentItemChanged();
0147 }
0148 
0149 QString AppletsLayout::configKey() const
0150 {
0151     return m_configKey;
0152 }
0153 
0154 void AppletsLayout::setConfigKey(const QString &key)
0155 {
0156     if (m_configKey == key) {
0157         return;
0158     }
0159 
0160     m_configKey = key;
0161 
0162     // Reloading everything from the new config is expansive, event compress it
0163     m_layoutChanges |= ConfigKeyChange;
0164     m_layoutChangeTimer->start();
0165 
0166     Q_EMIT configKeyChanged();
0167 }
0168 
0169 QString AppletsLayout::fallbackConfigKey() const
0170 {
0171     return m_fallbackConfigKey;
0172 }
0173 
0174 void AppletsLayout::setFallbackConfigKey(const QString &key)
0175 {
0176     if (m_fallbackConfigKey == key) {
0177         return;
0178     }
0179 
0180     m_fallbackConfigKey = key;
0181 
0182     Q_EMIT fallbackConfigKeyChanged();
0183 }
0184 
0185 bool AppletsLayout::relayoutLock() const
0186 {
0187     return m_relayoutLock;
0188 }
0189 
0190 void AppletsLayout::setRelayoutLock(bool lock)
0191 {
0192     if (lock == m_relayoutLock) {
0193         return;
0194     }
0195 
0196     m_relayoutLock = lock;
0197 
0198     if (!lock && m_layoutChanges != NoChange) {
0199         m_layoutChangeTimer->start();
0200     }
0201 
0202     Q_EMIT relayoutLockChanged();
0203 }
0204 
0205 QJSValue AppletsLayout::acceptsAppletCallback() const
0206 {
0207     return m_acceptsAppletCallback;
0208 }
0209 
0210 qreal AppletsLayout::minimumItemWidth() const
0211 {
0212     return m_minimumItemSize.width();
0213 }
0214 
0215 void AppletsLayout::setMinimumItemWidth(qreal width)
0216 {
0217     if (qFuzzyCompare(width, m_minimumItemSize.width())) {
0218         return;
0219     }
0220 
0221     m_minimumItemSize.setWidth(width);
0222 
0223     Q_EMIT minimumItemWidthChanged();
0224 }
0225 
0226 qreal AppletsLayout::minimumItemHeight() const
0227 {
0228     return m_minimumItemSize.height();
0229 }
0230 
0231 void AppletsLayout::setMinimumItemHeight(qreal height)
0232 {
0233     if (qFuzzyCompare(height, m_minimumItemSize.height())) {
0234         return;
0235     }
0236 
0237     m_minimumItemSize.setHeight(height);
0238 
0239     Q_EMIT minimumItemHeightChanged();
0240 }
0241 
0242 qreal AppletsLayout::defaultItemWidth() const
0243 {
0244     return m_defaultItemSize.width();
0245 }
0246 
0247 void AppletsLayout::setDefaultItemWidth(qreal width)
0248 {
0249     if (qFuzzyCompare(width, m_defaultItemSize.width())) {
0250         return;
0251     }
0252 
0253     m_defaultItemSize.setWidth(width);
0254 
0255     Q_EMIT defaultItemWidthChanged();
0256 }
0257 
0258 qreal AppletsLayout::defaultItemHeight() const
0259 {
0260     return m_defaultItemSize.height();
0261 }
0262 
0263 void AppletsLayout::setDefaultItemHeight(qreal height)
0264 {
0265     if (qFuzzyCompare(height, m_defaultItemSize.height())) {
0266         return;
0267     }
0268 
0269     m_defaultItemSize.setHeight(height);
0270 
0271     Q_EMIT defaultItemHeightChanged();
0272 }
0273 
0274 qreal AppletsLayout::cellWidth() const
0275 {
0276     return m_layoutManager->cellSize().width();
0277 }
0278 
0279 void AppletsLayout::setCellWidth(qreal width)
0280 {
0281     if (qFuzzyCompare(width, m_layoutManager->cellSize().width())) {
0282         return;
0283     }
0284 
0285     m_layoutManager->setCellSize(QSizeF(width, m_layoutManager->cellSize().height()));
0286 
0287     Q_EMIT cellWidthChanged();
0288 }
0289 
0290 qreal AppletsLayout::cellHeight() const
0291 {
0292     return m_layoutManager->cellSize().height();
0293 }
0294 
0295 void AppletsLayout::setCellHeight(qreal height)
0296 {
0297     if (qFuzzyCompare(height, m_layoutManager->cellSize().height())) {
0298         return;
0299     }
0300 
0301     m_layoutManager->setCellSize(QSizeF(m_layoutManager->cellSize().width(), height));
0302 
0303     Q_EMIT cellHeightChanged();
0304 }
0305 
0306 void AppletsLayout::setAcceptsAppletCallback(const QJSValue &callback)
0307 {
0308     if (m_acceptsAppletCallback.strictlyEquals(callback)) {
0309         return;
0310     }
0311 
0312     if (!callback.isNull() && !callback.isCallable()) {
0313         return;
0314     }
0315 
0316     m_acceptsAppletCallback = callback;
0317 
0318     Q_EMIT acceptsAppletCallbackChanged();
0319 }
0320 
0321 QQmlComponent *AppletsLayout::appletContainerComponent() const
0322 {
0323     return m_appletContainerComponent;
0324 }
0325 
0326 void AppletsLayout::setAppletContainerComponent(QQmlComponent *component)
0327 {
0328     if (m_appletContainerComponent == component) {
0329         return;
0330     }
0331 
0332     m_appletContainerComponent = component;
0333 
0334     Q_EMIT appletContainerComponentChanged();
0335 }
0336 
0337 AppletsLayout::EditModeCondition AppletsLayout::editModeCondition() const
0338 {
0339     return m_editModeCondition;
0340 }
0341 
0342 void AppletsLayout::setEditModeCondition(AppletsLayout::EditModeCondition condition)
0343 {
0344     if (m_editModeCondition == condition) {
0345         return;
0346     }
0347 
0348     if (m_editModeCondition == Locked) {
0349         setEditMode(false);
0350     }
0351 
0352     m_editModeCondition = condition;
0353 
0354     Q_EMIT editModeConditionChanged();
0355 }
0356 
0357 bool AppletsLayout::editMode() const
0358 {
0359     return m_editMode;
0360 }
0361 
0362 void AppletsLayout::setEditMode(bool editMode)
0363 {
0364     if (m_editMode == editMode) {
0365         return;
0366     }
0367 
0368     m_editMode = editMode;
0369 
0370     Q_EMIT editModeChanged();
0371 }
0372 
0373 ItemContainer *AppletsLayout::placeHolder() const
0374 {
0375     return m_placeHolder;
0376 }
0377 
0378 void AppletsLayout::setPlaceHolder(ItemContainer *placeHolder)
0379 {
0380     if (m_placeHolder == placeHolder) {
0381         return;
0382     }
0383 
0384     m_placeHolder = placeHolder;
0385     m_placeHolder->setParentItem(this);
0386     m_placeHolder->setZ(9999);
0387     m_placeHolder->setOpacity(false);
0388 
0389     Q_EMIT placeHolderChanged();
0390 }
0391 
0392 QQuickItem *AppletsLayout::eventManagerToFilter() const
0393 {
0394     return m_eventManagerToFilter;
0395 }
0396 
0397 void AppletsLayout::setEventManagerToFilter(QQuickItem *item)
0398 {
0399     if (m_eventManagerToFilter == item) {
0400         return;
0401     }
0402 
0403     m_eventManagerToFilter = item;
0404     if (m_eventManagerToFilter) {
0405         m_eventManagerToFilter->setAcceptTouchEvents(true);
0406     }
0407     setFiltersChildMouseEvents(m_eventManagerToFilter);
0408     Q_EMIT eventManagerToFilterChanged();
0409 }
0410 
0411 void AppletsLayout::save()
0412 {
0413     m_saveLayoutTimer->start();
0414 }
0415 
0416 void AppletsLayout::showPlaceHolderAt(const QRectF &geom)
0417 {
0418     if (!m_placeHolder) {
0419         return;
0420     }
0421 
0422     m_placeHolder->setPosition(geom.topLeft());
0423     m_placeHolder->setSize(geom.size());
0424 
0425     m_layoutManager->positionItem(m_placeHolder);
0426 
0427     m_placeHolder->setProperty("opacity", 1);
0428 }
0429 
0430 void AppletsLayout::showPlaceHolderForItem(ItemContainer *item)
0431 {
0432     if (!m_placeHolder) {
0433         return;
0434     }
0435 
0436     m_placeHolder->setPreferredLayoutDirection(item->preferredLayoutDirection());
0437     m_placeHolder->setPosition(item->position());
0438     m_placeHolder->setSize(item->size());
0439 
0440     m_layoutManager->positionItem(m_placeHolder);
0441 
0442     m_placeHolder->setProperty("opacity", 1);
0443 }
0444 
0445 void AppletsLayout::hidePlaceHolder()
0446 {
0447     if (!m_placeHolder) {
0448         return;
0449     }
0450 
0451     m_placeHolder->setProperty("opacity", 0);
0452 }
0453 
0454 bool AppletsLayout::isRectAvailable(qreal x, qreal y, qreal width, qreal height)
0455 {
0456     return m_layoutManager->isRectAvailable(QRectF(x, y, width, height));
0457 }
0458 
0459 bool AppletsLayout::itemIsManaged(ItemContainer *item)
0460 {
0461     if (!item) {
0462         return false;
0463     }
0464 
0465     return m_layoutManager->itemIsManaged(item);
0466 }
0467 
0468 void AppletsLayout::positionItem(ItemContainer *item)
0469 {
0470     if (!item) {
0471         return;
0472     }
0473 
0474     item->setParent(this);
0475     m_layoutManager->positionItemAndAssign(item);
0476 }
0477 
0478 void AppletsLayout::restoreItem(ItemContainer *item)
0479 {
0480     m_layoutManager->restoreItem(item);
0481 }
0482 
0483 void AppletsLayout::releaseSpace(ItemContainer *item)
0484 {
0485     if (!item) {
0486         return;
0487     }
0488 
0489     m_layoutManager->releaseSpace(item);
0490 }
0491 
0492 void AppletsLayout::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
0493 {
0494     // Ignore completely moves without resize
0495     if (newGeometry.size() == oldGeometry.size()) {
0496         QQuickItem::geometryChange(newGeometry, oldGeometry);
0497         return;
0498     }
0499 
0500     // Don't care for anything happening before startup completion
0501     if (!m_containment || !m_containment->corona() || !m_containment->corona()->isStartupCompleted()) {
0502         QQuickItem::geometryChange(newGeometry, oldGeometry);
0503         return;
0504     }
0505 
0506     // Only do a layouting procedure if we received a valid size
0507     if (!newGeometry.isEmpty() && newGeometry != oldGeometry) {
0508         m_layoutChanges |= SizeChange;
0509         m_layoutChangeTimer->start();
0510     }
0511 
0512     QQuickItem::geometryChange(newGeometry, oldGeometry);
0513 }
0514 
0515 void AppletsLayout::updatePolish()
0516 {
0517     m_layoutManager->resetLayout();
0518     m_savedSize = size();
0519 }
0520 
0521 void AppletsLayout::componentComplete()
0522 {
0523     if (!m_containment) {
0524         QQuickItem::componentComplete();
0525         return;
0526     }
0527 
0528     if (!m_configKey.isEmpty()) {
0529         const QString &serializedConfig = m_containment->config().readEntry(m_configKey, "");
0530         if (!serializedConfig.isEmpty()) {
0531             m_layoutManager->parseLayout(serializedConfig);
0532         } else {
0533             m_layoutManager->parseLayout(m_containment->config().readEntry(m_fallbackConfigKey, ""));
0534         }
0535     }
0536 
0537     for (auto *applet : m_containment->applets()) {
0538         PlasmaQuick::AppletQuickItem *appletItem = PlasmaQuick::AppletQuickItem::itemForApplet(applet);
0539 
0540         if (!appletItem) {
0541             continue;
0542         }
0543 
0544         AppletContainer *container = createContainerForApplet(appletItem);
0545         if (width() > 0 && height() > 0) {
0546             m_layoutManager->positionItemAndAssign(container);
0547         }
0548     }
0549 
0550     // layout all extra non applet items
0551     if (width() > 0 && height() > 0) {
0552         for (auto *child : childItems()) {
0553             ItemContainer *item = qobject_cast<ItemContainer *>(child);
0554             if (item && item != m_placeHolder && !m_layoutManager->itemIsManaged(item)) {
0555                 m_layoutManager->positionItemAndAssign(item);
0556             }
0557         }
0558     }
0559 
0560     if (m_containment && m_containment->corona()) {
0561         // We inhibit save during startup, so actually save now that startup is completed
0562         connect(m_containment->corona(), &Plasma::Corona::startupCompleted, this, [this]() {
0563             save();
0564         });
0565         // When the screen geometry changes, we need to know the geometry just before it did, so we can apply out heuristic of keeping the distance with borders
0566         // constant
0567         connect(m_containment->corona(), &Plasma::Corona::screenGeometryChanged, this, [this](int id) {
0568             if (m_containment->screen() == id) {
0569                 m_geometryBeforeResolutionChange = QRectF(x(), y(), width(), height());
0570                 m_layoutChangeTimer->start();
0571             }
0572         });
0573         // If the containment is moved to a different screen, treat it like a resolution change
0574         connect(m_containment, &Plasma::Containment::screenChanged, this, [this]() {
0575             m_geometryBeforeResolutionChange = QRectF(x(), y(), width(), height());
0576             m_layoutChangeTimer->start();
0577         });
0578     }
0579     QQuickItem::componentComplete();
0580 }
0581 
0582 bool AppletsLayout::childMouseEventFilter(QQuickItem *item, QEvent *event)
0583 {
0584     if (item != m_eventManagerToFilter) {
0585         return QQuickItem::childMouseEventFilter(item, event);
0586     }
0587 
0588     switch (event->type()) {
0589     case QEvent::TouchBegin:
0590     case QEvent::TouchUpdate:
0591     case QEvent::TouchCancel:
0592     case QEvent::TouchEnd: {
0593         touchEvent(static_cast<QTouchEvent *>(event));
0594         break;
0595     }
0596 
0597     default:
0598         break;
0599     }
0600 
0601     return QQuickItem::childMouseEventFilter(item, event);
0602 }
0603 
0604 void AppletsLayout::mousePressEvent(QMouseEvent *event)
0605 {
0606     // If any container is in edit mode, accept the press event so we can
0607     // cancel the edit mode. If not, don't accept the event so it can be
0608     // passed on to other parts.
0609     if (const auto children = childItems(); std::none_of(children.begin(), children.end(), [](QQuickItem *child) {
0610             auto container = qobject_cast<ItemContainer *>(child);
0611             return container ? container->editMode() : false;
0612         })) {
0613         event->setAccepted(false);
0614     }
0615 }
0616 
0617 void AppletsLayout::mouseReleaseEvent(QMouseEvent *event)
0618 {
0619     handleReleaseEvent(event->scenePosition());
0620 }
0621 
0622 void AppletsLayout::touchEvent(QTouchEvent *event)
0623 {
0624     const auto &point = *(event->points().cbegin());
0625 
0626     switch (point.state()) {
0627     case QEventPoint::State::Pressed: {
0628         if (!m_editMode) {
0629             if (m_editModeCondition == AppletsLayout::Manual) {
0630                 break;
0631             } else if (m_editModeCondition == AppletsLayout::AfterPressAndHold) {
0632                 m_pressAndHoldTimer->start(QGuiApplication::styleHints()->mousePressAndHoldInterval());
0633             }
0634         }
0635         forceActiveFocus(Qt::MouseFocusReason);
0636         m_touchDownWasEditMode = m_editMode;
0637         m_touchDownPosition = point.scenePosition();
0638         break;
0639     }
0640 
0641     case QEventPoint::State::Updated: {
0642         if (m_editMode) {
0643             break;
0644         }
0645         if (m_editModeCondition == AppletsLayout::Manual) {
0646             break;
0647         } else if (QPointF(point.scenePosition() - m_touchDownPosition).manhattanLength() >= QGuiApplication::styleHints()->startDragDistance()) {
0648             m_pressAndHoldTimer->stop();
0649         }
0650         break;
0651     }
0652 
0653     case QEventPoint::State::Released: {
0654         handleReleaseEvent(point.scenePosition());
0655         break;
0656     }
0657 
0658     default:
0659         break;
0660     }
0661 
0662     return QQuickItem::touchEvent(event); // Ignore the event
0663 }
0664 
0665 void AppletsLayout::touchUngrabEvent()
0666 {
0667     m_pressAndHoldTimer->stop();
0668 }
0669 
0670 void AppletsLayout::appletAdded(Plasma::Applet *applet)
0671 {
0672     PlasmaQuick::AppletQuickItem *appletItem = PlasmaQuick::AppletQuickItem::itemForApplet(applet);
0673 
0674     // maybe even an assert?
0675     if (!appletItem) {
0676         return;
0677     }
0678 
0679     QPointF pos(appletItem->x(), appletItem->y());
0680 
0681     if (m_acceptsAppletCallback.isCallable()) {
0682         QQmlEngine *engine = QQmlEngine::contextForObject(this)->engine();
0683         Q_ASSERT(engine);
0684         QJSValueList args;
0685         args << engine->newQObject(applet) << QJSValue(pos.x()) << QJSValue(pos.y());
0686 
0687         if (!m_acceptsAppletCallback.call(args).toBool()) {
0688             Q_EMIT appletRefused(applet, pos.x(), pos.y());
0689             return;
0690         }
0691     }
0692 
0693     AppletContainer *container = createContainerForApplet(appletItem);
0694     container->setPosition(pos);
0695     container->setVisible(true);
0696 
0697     m_layoutManager->positionItemAndAssign(container);
0698 }
0699 
0700 void AppletsLayout::appletRemoved(Plasma::Applet *applet)
0701 {
0702     PlasmaQuick::AppletQuickItem *appletItem = PlasmaQuick::AppletQuickItem::itemForApplet(applet);
0703 
0704     if (!appletItem) {
0705         return;
0706     }
0707 
0708     AppletContainer *container = m_containerForApplet.value(appletItem);
0709     if (!container) {
0710         return;
0711     }
0712 
0713     m_layoutManager->releaseSpace(container);
0714     m_containerForApplet.remove(appletItem);
0715     appletItem->setParentItem(this);
0716     container->deleteLater();
0717 }
0718 
0719 AppletContainer *AppletsLayout::createContainerForApplet(PlasmaQuick::AppletQuickItem *appletItem)
0720 {
0721     AppletContainer *container = m_containerForApplet.value(appletItem);
0722 
0723     if (container) {
0724         return container;
0725     }
0726 
0727     bool createdFromQml = true;
0728 
0729     if (m_appletContainerComponent) {
0730         QQmlContext *context = QQmlEngine::contextForObject(this);
0731         Q_ASSERT(context);
0732         QObject *instance = m_appletContainerComponent->beginCreate(context);
0733         container = qobject_cast<AppletContainer *>(instance);
0734         if (container) {
0735             container->setParentItem(this);
0736         } else {
0737             qCWarning(CONTAINMENTLAYOUTMANAGER_DEBUG) << "Error: provided component not an AppletContainer instance";
0738             if (instance) {
0739                 instance->deleteLater();
0740             }
0741             createdFromQml = false;
0742         }
0743     }
0744 
0745     if (!container) {
0746         container = new AppletContainer(this);
0747     }
0748 
0749     container->setVisible(false);
0750 
0751     const QSizeF appletSize = appletItem->size();
0752     container->setContentItem(appletItem);
0753 
0754     m_containerForApplet[appletItem] = container;
0755     container->setLayout(this);
0756     container->setKey(QLatin1String("Applet-") + QString::number(appletItem->applet()->id()));
0757 
0758     const bool geometryWasSaved = m_layoutManager->restoreItem(container);
0759 
0760     if (!geometryWasSaved) {
0761         container->setPosition(QPointF(appletItem->x() - container->leftPadding(), appletItem->y() - container->topPadding()));
0762 
0763         if (!appletSize.isEmpty()) {
0764             container->setSize(QSizeF(qMax(m_minimumItemSize.width(), appletSize.width() + container->leftPadding() + container->rightPadding()),
0765                                       qMax(m_minimumItemSize.height(), appletSize.height() + container->topPadding() + container->bottomPadding())));
0766         }
0767     }
0768 
0769     if (m_appletContainerComponent && createdFromQml) {
0770         m_appletContainerComponent->completeCreate();
0771     }
0772 
0773     // NOTE: This has to be done here as we need the component completed to have all the bindings evaluated
0774     if (!geometryWasSaved && appletSize.isEmpty()) {
0775         if (container->initialSize().width() > m_minimumItemSize.width() && container->initialSize().height() > m_minimumItemSize.height()) {
0776             const QSizeF size = m_layoutManager->cellAlignedContainingSize(container->initialSize());
0777             container->setSize(size);
0778         } else {
0779             container->setSize(
0780                 QSizeF(qMax(m_minimumItemSize.width(), m_defaultItemSize.width()), qMax(m_minimumItemSize.height(), m_defaultItemSize.height())));
0781         }
0782     }
0783 
0784     container->setVisible(true);
0785     appletItem->setVisible(true);
0786 
0787     return container;
0788 }
0789 
0790 void AppletsLayout::handleReleaseEvent(const QPointF &scenePosition)
0791 {
0792     if (m_editMode && m_touchDownWasEditMode
0793         && QPointF(scenePosition - m_touchDownPosition).manhattanLength() < QGuiApplication::styleHints()->startDragDistance()) {
0794         setEditMode(false);
0795     }
0796 
0797     m_pressAndHoldTimer->stop();
0798 
0799     if (m_editMode) {
0800         return;
0801     }
0802 
0803     // Click any empty area to exit the edit mode
0804     for (const auto children = childItems(); auto *child : children) {
0805         if (ItemContainer *item = qobject_cast<ItemContainer *>(child); item && item != m_placeHolder) {
0806             item->setEditMode(false);
0807         }
0808     }
0809 }
0810 
0811 #include "moc_appletslayout.cpp"