LayoutSaver: Support the user's factory func doing remapping

While restoring a layout, we expect all widgets to exist already,
but we allow the user to create them delayed, by providing
us a factory function.

What we're supporting in this commit is the ability of the user's
factory function returning a dock widget with an ID different
than the one that was requested. We then save that mapping so the
rest of the layout restore works with the new ID.
This commit is contained in:
Sergio Martins
2021-04-23 14:55:01 +01:00
parent b3c2d87a9b
commit aa76dfba02
5 changed files with 61 additions and 5 deletions

View File

@@ -776,11 +776,18 @@ void DockWidgetBase::onCloseEvent(QCloseEvent *e)
DockWidgetBase *DockWidgetBase::deserialize(const LayoutSaver::DockWidget::Ptr &saved) 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) { if (!dw) {
// DockWidget doesn't exist, ask to create it
if (auto factoryFunc = Config::self().dockWidgetFactoryFunc()) { if (auto factoryFunc = Config::self().dockWidgetFactoryFunc()) {
// DockWidget doesn't exist, ask to create it
dw = factoryFunc(saved->uniqueName); 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());
}
} }
} }

View File

@@ -313,7 +313,8 @@ bool LayoutSaver::restoreLayout(const QByteArray &data)
if (!d->matchesAffinity(dw->affinities)) if (!d->matchesAffinity(dw->affinities))
continue; 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); dockWidget->d->lastPositions().deserialize(dw->lastPosition);
} else { } else {
qWarning() << Q_FUNC_INFO << "Couldn't find dock widget" << dw->uniqueName; qWarning() << Q_FUNC_INFO << "Couldn't find dock widget" << dw->uniqueName;

View File

@@ -256,6 +256,11 @@ Frame *DockRegistry::frameInMDIResize() const
return nullptr; return nullptr;
} }
QHash<QString, QString> &DockRegistry::dockWidgetIdRemapping()
{
return m_dockWidgetIdRemapping;
}
MainWindowBase::List DockRegistry::mainWindowsWithAffinity(const QStringList &affinities) const MainWindowBase::List DockRegistry::mainWindowsWithAffinity(const QStringList &affinities) const
{ {
MainWindowBase::List result; MainWindowBase::List result;
@@ -383,13 +388,20 @@ bool DockRegistry::containsMainWindow(const QString &uniqueName) const
return mainWindowByName(uniqueName) != nullptr; 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)) { for (auto dock : qAsConst(m_dockWidgets)) {
if (dock->uniqueName() == name) if (dock->uniqueName() == name)
return dock; 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; return nullptr;
} }

View File

@@ -64,7 +64,8 @@ public:
Q_INVOKABLE bool containsDockWidget(const QString &uniqueName) const; Q_INVOKABLE bool containsDockWidget(const QString &uniqueName) const;
Q_INVOKABLE bool containsMainWindow(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::MainWindowBase *mainWindowByName(const QString &) const;
Q_INVOKABLE KDDockWidgets::MainWindowMDI *mdiMainWindowByName(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 ///@brief Returns the Frame which is being resized in a MDI layout. nullptr if none
Frame *frameInMDIResize() const; 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<QString, QString> &dockWidgetIdRemapping();
Q_SIGNALS: Q_SIGNALS:
/// @brief emitted when a main window or a floating window change screen /// @brief emitted when a main window or a floating window change screen
void windowChangedScreen(QWindow *); void windowChangedScreen(QWindow *);
@@ -243,6 +252,7 @@ private:
QVector<FloatingWindow*> m_floatingWindows; QVector<FloatingWindow*> m_floatingWindows;
QVector<LayoutWidget *> m_layouts; QVector<LayoutWidget *> m_layouts;
QPointer<DockWidgetBase> m_focusedDockWidget; QPointer<DockWidgetBase> m_focusedDockWidget;
QHash<QString, QString> m_dockWidgetIdRemapping;
}; };
} }

View File

@@ -134,6 +134,7 @@ private Q_SLOTS:
void tst_marginsAfterRestore(); void tst_marginsAfterRestore();
void tst_restoreWithNewDockWidgets(); void tst_restoreWithNewDockWidgets();
void tst_restoreWithDockFactory(); void tst_restoreWithDockFactory();
void tst_restoreWithDockFactory2();
void tst_lastFloatingPositionIsRestored(); void tst_lastFloatingPositionIsRestored();
void tst_restoreSimple(); void tst_restoreSimple();
void tst_restoreSimplest(); void tst_restoreSimplest();
@@ -4055,6 +4056,31 @@ void TestDocks::tst_restoreWithDockFactory()
layout->checkSanity(); 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() void TestDocks::tst_addDockWidgetToMainWindow()
{ {
EnsureTopLevelsDeleted e; EnsureTopLevelsDeleted e;