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"