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