From 66f620995bdfdc21d1a22e5ec18170ed5ff4d2ed Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Tue, 27 Apr 2021 11:20:30 +0000 Subject: [PATCH] Don't assume m_containment is there The containment property is assigned from qml so it can be bull. Also, if this component is used in a plasmoid that can run also in normal applet mode (i.e. folderview) it will be null in that case. if we don't have a containment assigned yet, just do nothing in the signal handlers as they are supposed to read and write from the containment config BUG:436041 --- .../appletslayout.cpp | 100 ++++++++++++------ .../containmentlayoutmanager/appletslayout.h | 24 ++++- 2 files changed, 90 insertions(+), 34 deletions(-) diff --git a/components/containmentlayoutmanager/appletslayout.cpp b/components/containmentlayoutmanager/appletslayout.cpp index c9cf2ebca..c7ac3d5b2 100644 --- a/components/containmentlayoutmanager/appletslayout.cpp +++ b/components/containmentlayoutmanager/appletslayout.cpp @@ -45,11 +45,18 @@ AppletsLayout::AppletsLayout(QQuickItem *parent) m_saveLayoutTimer->setInterval(100); connect(m_layoutManager, &AbstractLayoutManager::layoutNeedsSaving, m_saveLayoutTimer, QOverload<>::of(&QTimer::start)); connect(m_saveLayoutTimer, &QTimer::timeout, this, [this]() { + // We can't assume m_containment to be valid: if we load in a plasmoid that can run also + // in "applet" mode, m_containment will never be valid + if (!m_containment) { + return; + } // 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 starting up, // don't actually savenow, but we will when Corona::startupCompleted is emitted + if (!m_configKey.isEmpty() && m_containment && m_containment->corona()->isStartupCompleted()) { const QString serializedConfig = m_layoutManager->serializeLayout(); m_containment->config().writeEntry(m_configKey, serializedConfig); + m_containment->config().writeEntry(m_fallbackConfigKey, serializedConfig); // FIXME: something more efficient m_layoutManager->parseLayout(serializedConfig); m_savedSize = size(); @@ -57,44 +64,48 @@ AppletsLayout::AppletsLayout(QQuickItem *parent) } }); - m_configKeyChangeTimer = new QTimer(this); - m_configKeyChangeTimer->setSingleShot(true); - m_configKeyChangeTimer->setInterval(100); - connect(m_configKeyChangeTimer, &QTimer::timeout, this, [this]() { - if (!m_configKey.isEmpty() && m_containment) { - m_layoutManager->parseLayout(m_containment->config().readEntry(m_configKey, "")); - - if (width() > 0 && height() > 0) { + m_layoutChangeTimer = new QTimer(this); + m_layoutChangeTimer->setSingleShot(true); + m_layoutChangeTimer->setInterval(100); + connect(m_layoutChangeTimer, &QTimer::timeout, this, [this]() { + // We can't assume m_containment to be valid: if we load in a plasmoid that can run also + // in "applet" mode, m_containment will never be valid + if (!m_containment) { + return; + } + const QString &serializedConfig = m_containment->config().readEntry(m_configKey, ""); + if ((m_layoutChanges & ConfigKeyChange) && !serializedConfig.isEmpty()) { + if (!m_configKey.isEmpty() && m_containment) { + m_layoutManager->parseLayout(serializedConfig); + + if (width() > 0 && height() > 0) { + m_layoutManager->resetLayoutFromConfig(); + m_savedSize = size(); + } + } + } else if (m_layoutChanges & SizeChange) { + const QRect newGeom(x(), y(), width(), height()); + // The size has been restored from the last one it has been saved: restore that exact same layout + if (newGeom.size() == m_savedSize) { m_layoutManager->resetLayoutFromConfig(); - m_savedSize = size(); + + // If the resize is consequence of a screen resolution change, queue a relayout maintaining the distance between screen edges + } else if (!m_geometryBeforeResolutionChange.isEmpty()) { + m_layoutManager->layoutGeometryChanged(newGeom, m_geometryBeforeResolutionChange); + m_geometryBeforeResolutionChange = QRectF(); + + // Heuristically relayout items only when the plasma startup is fully completed + } else { + polish(); } } + m_layoutChanges = NoChange; }); m_pressAndHoldTimer = new QTimer(this); m_pressAndHoldTimer->setSingleShot(true); connect(m_pressAndHoldTimer, &QTimer::timeout, this, [this]() { setEditMode(true); }); - - m_sizeSyncTimer = new QTimer(this); - m_sizeSyncTimer->setSingleShot(true); - m_sizeSyncTimer->setInterval(150); - connect(m_sizeSyncTimer, &QTimer::timeout, this, [this]() { - const QRect newGeom(x(), y(), width(), height()); - // The size has been restored from the last one it has been saved: restore that exact same layout - if (newGeom.size() == m_savedSize) { - m_layoutManager->resetLayoutFromConfig(); - - // If the resize is consequence of a screen resolution change, queue a relayout maintaining the distance between screen edges - } else if (!m_geometryBeforeResolutionChange.isEmpty()) { - m_layoutManager->layoutGeometryChanged(newGeom, m_geometryBeforeResolutionChange); - m_geometryBeforeResolutionChange = QRectF(); - - // Heuristically relayout items only when the plasma startup is fully completed - } else { - polish(); - } - }); } AppletsLayout::~AppletsLayout() @@ -150,11 +161,30 @@ void AppletsLayout::setConfigKey(const QString &key) m_configKey = key; // Reloading everything from the new config is expansive, event compress it - m_configKeyChangeTimer->start(); + m_layoutChanges |= ConfigKeyChange; + m_layoutChangeTimer->start(); emit configKeyChanged(); } +QString AppletsLayout::fallbackConfigKey() const +{ + return m_fallbackConfigKey; +} + +void AppletsLayout::setFallbackConfigKey(const QString &key) +{ + if (m_fallbackConfigKey == key) { + return; + } + + m_fallbackConfigKey = key; + + + + emit fallbackConfigKeyChanged(); +} + QJSValue AppletsLayout::acceptsAppletCallback() const { return m_acceptsAppletCallback; @@ -455,7 +485,8 @@ void AppletsLayout::geometryChanged(const QRectF &newGeometry, const QRectF &old // Only do a layouting procedure if we received a valid size if (!newGeometry.isEmpty()) { - m_sizeSyncTimer->start(); + m_layoutChanges |= SizeChange; + m_layoutChangeTimer->start(); } QQuickItem::geometryChanged(newGeometry, oldGeometry); @@ -475,7 +506,12 @@ void AppletsLayout::componentComplete() } if (!m_configKey.isEmpty()) { - m_layoutManager->parseLayout(m_containment->config().readEntry(m_configKey, "")); + const QString &serializedConfig = m_containment->config().readEntry(m_configKey, ""); + if (!serializedConfig.isEmpty()) { + m_layoutManager->parseLayout(serializedConfig); + } else { + m_layoutManager->parseLayout(m_containment->config().readEntry(m_fallbackConfigKey, "")); + } } const QList appletObjects = m_containmentItem->property("applets").value>(); diff --git a/components/containmentlayoutmanager/appletslayout.h b/components/containmentlayoutmanager/appletslayout.h index fc8b65afb..c27d9d6f7 100644 --- a/components/containmentlayoutmanager/appletslayout.h +++ b/components/containmentlayoutmanager/appletslayout.h @@ -48,6 +48,11 @@ class AppletsLayout : public QQuickItem Q_PROPERTY(QString configKey READ configKey WRITE setConfigKey NOTIFY configKeyChanged) + // A config key that can be used as fallback when loading and configKey is not found + // Is always a backup of the last used configKey. Useful when the configkey depends + // from the screen size and plasma starts on an "unexpected" size + Q_PROPERTY(QString fallbackConfigKey READ fallbackConfigKey WRITE setFallbackConfigKey NOTIFY fallbackConfigKeyChanged) + Q_PROPERTY(PlasmaQuick::AppletQuickItem *containment READ containment WRITE setContainment NOTIFY containmentChanged) Q_PROPERTY(QJSValue acceptsAppletCallback READ acceptsAppletCallback WRITE setAcceptsAppletCallback NOTIFY acceptsAppletCallbackChanged) @@ -95,6 +100,13 @@ public: }; Q_ENUM(EditModeCondition) + enum LayoutChange { + NoChange = 0, + SizeChange = 1, + ConfigKeyChange = 2 + }; + Q_DECLARE_FLAGS(LayoutChanges, LayoutChange) + AppletsLayout(QQuickItem *parent = nullptr); ~AppletsLayout(); @@ -102,6 +114,9 @@ public: QString configKey() const; void setConfigKey(const QString &key); + QString fallbackConfigKey() const; + void setFallbackConfigKey(const QString &key); + PlasmaQuick::AppletQuickItem *containment() const; void setContainment(PlasmaQuick::AppletQuickItem *containment); @@ -160,6 +175,7 @@ Q_SIGNALS: void appletRefused(QObject *applet, int x, int y); void configKeyChanged(); + void fallbackConfigKeyChanged(); void containmentChanged(); void minimumItemWidthChanged(); void minimumItemHeightChanged(); @@ -194,8 +210,10 @@ private: AppletContainer *createContainerForApplet(PlasmaQuick::AppletQuickItem *appletItem); QString m_configKey; + QString m_fallbackConfigKey; QTimer *m_saveLayoutTimer; - QTimer *m_configKeyChangeTimer; + QTimer *m_layoutChangeTimer; + LayoutChanges m_layoutChanges = NoChange; PlasmaQuick::AppletQuickItem *m_containmentItem = nullptr; Plasma::Containment *m_containment = nullptr; @@ -207,7 +225,6 @@ private: QPointer m_eventManagerToFilter; QTimer *m_pressAndHoldTimer; - QTimer *m_sizeSyncTimer; QJSValue m_acceptsAppletCallback; @@ -223,4 +240,7 @@ private: QPointF m_mouseDownPosition = QPoint(-1, -1); bool m_mouseDownWasEditMode = false; bool m_editMode = false; + bool m_sizeSpecificLayouts = false; }; + +Q_DECLARE_OPERATORS_FOR_FLAGS(AppletsLayout::LayoutChanges)