diff --git a/src/DockWidgetBase.cpp b/src/DockWidgetBase.cpp index ab3be108..7c65b3c4 100644 --- a/src/DockWidgetBase.cpp +++ b/src/DockWidgetBase.cpp @@ -776,11 +776,18 @@ void DockWidgetBase::onCloseEvent(QCloseEvent *e) DockWidgetBase *DockWidgetBase::deserialize(const LayoutSaver::DockWidget::Ptr &saved) { - DockWidgetBase *dw = DockRegistry::self()->dockByName(saved->uniqueName); + auto dr = DockRegistry::self(); + DockWidgetBase *dw = dr->dockByName(saved->uniqueName); if (!dw) { + // DockWidget doesn't exist, ask to create it if (auto factoryFunc = Config::self().dockWidgetFactoryFunc()) { - // DockWidget doesn't exist, ask to create it dw = factoryFunc(saved->uniqueName); + if (dw && dw->uniqueName() != saved->uniqueName) { + // Very special case + // The user's factory function returned a dock widget with a different ID. + // We support it. Save the mapping though. + dr->dockWidgetIdRemapping().insert(saved->uniqueName, dw->uniqueName()); + } } } diff --git a/src/LayoutSaver.cpp b/src/LayoutSaver.cpp index 118a411f..e2c20e67 100644 --- a/src/LayoutSaver.cpp +++ b/src/LayoutSaver.cpp @@ -313,7 +313,8 @@ bool LayoutSaver::restoreLayout(const QByteArray &data) if (!d->matchesAffinity(dw->affinities)) continue; - if (DockWidgetBase *dockWidget = d->m_dockRegistry->dockByName(dw->uniqueName)) { + if (DockWidgetBase *dockWidget = + d->m_dockRegistry->dockByName(dw->uniqueName, /*consultRemapping=*/true)) { dockWidget->d->lastPositions().deserialize(dw->lastPosition); } else { qWarning() << Q_FUNC_INFO << "Couldn't find dock widget" << dw->uniqueName; diff --git a/src/private/DockRegistry.cpp b/src/private/DockRegistry.cpp index de774f51..ae7e74ac 100644 --- a/src/private/DockRegistry.cpp +++ b/src/private/DockRegistry.cpp @@ -256,6 +256,11 @@ Frame *DockRegistry::frameInMDIResize() const return nullptr; } +QHash &DockRegistry::dockWidgetIdRemapping() +{ + return m_dockWidgetIdRemapping; +} + MainWindowBase::List DockRegistry::mainWindowsWithAffinity(const QStringList &affinities) const { MainWindowBase::List result; @@ -383,13 +388,20 @@ bool DockRegistry::containsMainWindow(const QString &uniqueName) const return mainWindowByName(uniqueName) != nullptr; } -DockWidgetBase *DockRegistry::dockByName(const QString &name) const +DockWidgetBase *DockRegistry::dockByName(const QString &name, bool consultRemapping) const { for (auto dock : qAsConst(m_dockWidgets)) { if (dock->uniqueName() == name) return dock; } + if (consultRemapping) { + // Name doesn't exist, let's check if it was remapped during a layout restore. + const QString newName = m_dockWidgetIdRemapping.value(name); + if (!newName.isEmpty()) + return dockByName(newName); + } + return nullptr; } diff --git a/src/private/DockRegistry_p.h b/src/private/DockRegistry_p.h index 0ad5bf23..172c9e2d 100644 --- a/src/private/DockRegistry_p.h +++ b/src/private/DockRegistry_p.h @@ -64,7 +64,8 @@ public: Q_INVOKABLE bool containsDockWidget(const QString &uniqueName) const; Q_INVOKABLE bool containsMainWindow(const QString &uniqueName) const; - Q_INVOKABLE KDDockWidgets::DockWidgetBase *dockByName(const QString &) const; + Q_INVOKABLE KDDockWidgets::DockWidgetBase *dockByName(const QString &, + bool consultRemapping = false) const; Q_INVOKABLE KDDockWidgets::MainWindowBase *mainWindowByName(const QString &) const; Q_INVOKABLE KDDockWidgets::MainWindowMDI *mdiMainWindowByName(const QString &) const; @@ -219,6 +220,14 @@ public: ///@brief Returns the Frame which is being resized in a MDI layout. nullptr if none Frame *frameInMDIResize() const; + ///@brief Returns the dock widget id remapping, used by LayoutSaver + /// + /// When LayoutSaver is trying to restore dock widget "foo", but it doesn't exist, it will + /// attempt to call a user provided factory function. That function can however return a dock + /// widget with another ID, such as "bar". When that happens this QHash gets a "foo" : "bar" + /// entry + QHash &dockWidgetIdRemapping(); + Q_SIGNALS: /// @brief emitted when a main window or a floating window change screen void windowChangedScreen(QWindow *); @@ -243,6 +252,7 @@ private: QVector m_floatingWindows; QVector m_layouts; QPointer m_focusedDockWidget; + QHash m_dockWidgetIdRemapping; }; } diff --git a/tests/tst_docks.cpp b/tests/tst_docks.cpp index 478fc78f..c2f17fab 100644 --- a/tests/tst_docks.cpp +++ b/tests/tst_docks.cpp @@ -134,6 +134,7 @@ private Q_SLOTS: void tst_marginsAfterRestore(); void tst_restoreWithNewDockWidgets(); void tst_restoreWithDockFactory(); + void tst_restoreWithDockFactory2(); void tst_lastFloatingPositionIsRestored(); void tst_restoreSimple(); void tst_restoreSimplest(); @@ -4055,6 +4056,31 @@ void TestDocks::tst_restoreWithDockFactory() layout->checkSanity(); } +void TestDocks::tst_restoreWithDockFactory2() +{ + // Teste that the factory function can do id remapping. + // For example, if id "foo" is missing, the factory can return a + // dock widget with id "bar" if it feels like it + + auto m = createMainWindow(QSize(501, 500), MainWindowOption_None); + + auto dock1 = createDockWidget("dw1", new QPushButton("1")); + m->addDockWidget(dock1, Location_OnLeft); + dock1->setFloating(true); + + LayoutSaver saver; + const QByteArray saved = saver.serializeLayout(); + delete dock1; + + DockWidgetFactoryFunc func = [] (const QString &) { + // A factory func which does id remapping + return createDockWidget("dw2", new QPushButton("w"), {}, {}, /*show=*/ false); + }; + + KDDockWidgets::Config::self().setDockWidgetFactoryFunc(func); + saver.restoreLayout(saved); +} + void TestDocks::tst_addDockWidgetToMainWindow() { EnsureTopLevelsDeleted e;