KDDockWidgets API Documentation  1.4
LayoutSaver.cpp
Go to the documentation of this file.
1 /*
2  This file is part of KDDockWidgets.
3 
4  SPDX-FileCopyrightText: 2019-2021 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
5  Author: SĂ©rgio Martins <sergio.martins@kdab.com>
6 
7  SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only
8 
9  Contact KDAB at <info@kdab.com> for commercial licensing options.
10 */
11 
19 #include "LayoutSaver.h"
20 #include "Config.h"
21 #include "MainWindowBase.h"
22 #include "DockWidgetBase.h"
23 #include "FrameworkWidgetFactory.h"
24 
25 #include "private/LayoutSaver_p.h"
26 #include "private/DockRegistry_p.h"
27 #include "private/DockWidgetBase_p.h"
28 #include "private/FloatingWindow_p.h"
29 #include "private/Frame_p.h"
30 #include "private/LayoutWidget_p.h"
31 #include "private/Logging_p.h"
32 #include "private/Position_p.h"
33 
34 #include <qmath.h>
35 #include <QDebug>
36 #include <QFile>
37 
58 using namespace KDDockWidgets;
59 
60 QHash<QString, LayoutSaver::DockWidget::Ptr> LayoutSaver::DockWidget::s_dockWidgets;
61 LayoutSaver::Layout *LayoutSaver::Layout::s_currentLayoutBeingRestored = nullptr;
62 
63 
64 inline InternalRestoreOptions internalRestoreOptions(RestoreOptions options)
65 {
66  if (options == RestoreOption_None) {
67  return InternalRestoreOption::None;
68  } else if (options == RestoreOption_RelativeToMainWindow) {
69  return InternalRestoreOptions(InternalRestoreOption::SkipMainWindowGeometry)
70  | InternalRestoreOption::RelativeFloatingWindowGeometry;
71  } else {
72  qWarning() << Q_FUNC_INFO << "Unknown options" << options;
73  return {};
74  }
75 }
76 
77 bool LayoutSaver::Private::s_restoreInProgress = false;
78 
79 static QVariantList stringListToVariant(const QStringList &strs)
80 {
81  QVariantList variantList;
82  variantList.reserve(strs.size());
83  for (const QString &str : strs)
84  variantList.push_back(str);
85 
86  return variantList;
87 }
88 
89 static QStringList variantToStringList(const QVariantList &variantList)
90 {
91  QStringList stringList;
92  stringList.reserve(variantList.size());
93  for (const QVariant &variant : variantList)
94  stringList.push_back(variant.toString());
95 
96  return stringList;
97 }
98 
99 LayoutSaver::LayoutSaver(RestoreOptions options)
100  : d(new Private(options))
101 {
102 }
103 
105 {
106  delete d;
107 }
108 
109 bool LayoutSaver::saveToFile(const QString &jsonFilename)
110 {
111  const QByteArray data = serializeLayout();
112 
113  QFile f(jsonFilename);
114  if (!f.open(QIODevice::WriteOnly)) {
115  qWarning() << Q_FUNC_INFO << "Failed to open" << jsonFilename << f.errorString();
116  return false;
117  }
118 
119  f.write(data);
120  return true;
121 }
122 
123 bool LayoutSaver::restoreFromFile(const QString &jsonFilename)
124 {
125  QFile f(jsonFilename);
126  if (!f.open(QIODevice::ReadOnly)) {
127  qWarning() << Q_FUNC_INFO << "Failed to open" << jsonFilename << f.errorString();
128  return false;
129  }
130 
131  const QByteArray data = f.readAll();
132  const bool result = restoreLayout(data);
133 
134  return result;
135 }
136 
138 {
139  if (!d->m_dockRegistry->isSane()) {
140  qWarning() << Q_FUNC_INFO << "Refusing to serialize this layout. Check previous warnings.";
141  return {};
142  }
143 
144  LayoutSaver::Layout layout;
145 
146  // Just a simplification. One less type of windows to handle.
147  d->m_dockRegistry->ensureAllFloatingWidgetsAreMorphed();
148 
149  const MainWindowBase::List mainWindows = d->m_dockRegistry->mainwindows();
150  layout.mainWindows.reserve(mainWindows.size());
151  for (MainWindowBase *mainWindow : mainWindows) {
152  if (d->matchesAffinity(mainWindow->affinities()))
153  layout.mainWindows.push_back(mainWindow->serialize());
154  }
155 
156  const QVector<KDDockWidgets::FloatingWindow *> floatingWindows = d->m_dockRegistry->floatingWindows();
157  layout.floatingWindows.reserve(floatingWindows.size());
158  for (KDDockWidgets::FloatingWindow *floatingWindow : floatingWindows) {
159  if (d->matchesAffinity(floatingWindow->affinities()))
160  layout.floatingWindows.push_back(floatingWindow->serialize());
161  }
162 
163  // Closed dock widgets also have interesting things to save, like geometry and placeholder info
164  const DockWidgetBase::List closedDockWidgets = d->m_dockRegistry->closedDockwidgets();
165  layout.closedDockWidgets.reserve(closedDockWidgets.size());
166  for (DockWidgetBase *dockWidget : closedDockWidgets) {
167  if (d->matchesAffinity(dockWidget->affinities()))
168  layout.closedDockWidgets.push_back(dockWidget->d->serialize());
169  }
170 
171  // Save the placeholder info. We do it last, as we also restore it last, since we need all items to be created
172  // before restoring the placeholders
173 
174  const DockWidgetBase::List dockWidgets = d->m_dockRegistry->dockwidgets();
175  layout.allDockWidgets.reserve(dockWidgets.size());
176  for (DockWidgetBase *dockWidget : dockWidgets) {
177  if (d->matchesAffinity(dockWidget->affinities())) {
178  auto dw = dockWidget->d->serialize();
179  dw->lastPosition = dockWidget->d->lastPositions().serialize();
180  layout.allDockWidgets.push_back(dw);
181  }
182  }
183 
184  return layout.toJson();
185 }
186 
188 {
189  d->clearRestoredProperty();
190  if (data.isEmpty())
191  return true;
192 
193  struct FrameCleanup
194  {
195  FrameCleanup(LayoutSaver *saver)
196  : m_saver(saver)
197  {
198  }
199 
200  ~FrameCleanup()
201  {
202  m_saver->d->deleteEmptyFrames();
203  }
204 
205  LayoutSaver *const m_saver;
206  };
207 
208  FrameCleanup cleanup(this);
209  LayoutSaver::Layout layout;
210  if (!layout.fromJson(data)) {
211  qWarning() << Q_FUNC_INFO << "Failed to parse json data";
212  return false;
213  }
214 
215  if (!layout.isValid()) {
216  return false;
217  }
218 
219  layout.scaleSizes(d->m_restoreOptions);
220 
221  d->floatWidgetsWhichSkipRestore(layout.mainWindowNames());
222 
223  Private::RAIIIsRestoring isRestoring;
224 
225  // Hide all dockwidgets and unparent them from any layout before starting restore
226  // We only close the stuff that the loaded JSON knows about. Unknown widgets might be newer.
227 
228  d->m_dockRegistry->clear(d->m_dockRegistry->dockWidgets(layout.dockWidgetsToClose()),
229  d->m_dockRegistry->mainWindows(layout.mainWindowNames()),
230  d->m_affinityNames);
231 
232  // 1. Restore main windows
233  for (const LayoutSaver::MainWindow &mw : qAsConst(layout.mainWindows)) {
234  MainWindowBase *mainWindow = d->m_dockRegistry->mainWindowByName(mw.uniqueName);
235  if (!mainWindow) {
236  if (auto mwFunc = Config::self().mainWindowFactoryFunc()) {
237  mainWindow = mwFunc(mw.uniqueName);
238  } else {
239  qWarning() << "Failed to restore layout create MainWindow with name" << mw.uniqueName << "first";
240  return false;
241  }
242  }
243 
244  if (!d->matchesAffinity(mainWindow->affinities()))
245  continue;
246 
247  if (!(d->m_restoreOptions & InternalRestoreOption::SkipMainWindowGeometry)) {
248  d->deserializeWindowGeometry(mw, mainWindow->window()); // window(), as the MainWindow can be embedded
249  if (mw.windowState != Qt::WindowNoState) {
250  if (auto w = mainWindow->windowHandle()) {
251  w->setWindowState(mw.windowState);
252  }
253  }
254  }
255 
256  if (!mainWindow->deserialize(mw))
257  return false;
258  }
259 
260  // 2. Restore FloatingWindows
261  for (LayoutSaver::FloatingWindow &fw : layout.floatingWindows) {
262  if (!d->matchesAffinity(fw.affinities) || fw.skipsRestore())
263  continue;
264 
265  MainWindowBase *parent = fw.parentIndex == -1 ? nullptr
266  : DockRegistry::self()->mainwindows().at(fw.parentIndex);
267 
268  auto floatingWindow = Config::self().frameworkWidgetFactory()->createFloatingWindow(parent);
269  fw.floatingWindowInstance = floatingWindow;
270  d->deserializeWindowGeometry(fw, floatingWindow);
271  if (!floatingWindow->deserialize(fw)) {
272  qWarning() << Q_FUNC_INFO << "Failed to deserialize floating window";
273  return false;
274  }
275  }
276 
277  // 3. Restore closed dock widgets. They remain closed but acquire geometry and placeholder properties
278  for (const auto &dw : qAsConst(layout.closedDockWidgets)) {
279  if (d->matchesAffinity(dw->affinities)) {
280  DockWidgetBase::deserialize(dw);
281  }
282  }
283 
284  // 4. Restore the placeholder info, now that the Items have been created
285  for (const auto &dw : qAsConst(layout.allDockWidgets)) {
286  if (!d->matchesAffinity(dw->affinities))
287  continue;
288 
289  if (DockWidgetBase *dockWidget =
290  d->m_dockRegistry->dockByName(dw->uniqueName, DockRegistry::DockByNameFlag::ConsultRemapping)) {
291  dockWidget->d->lastPositions().deserialize(dw->lastPosition);
292  } else {
293  qWarning() << Q_FUNC_INFO << "Couldn't find dock widget" << dw->uniqueName;
294  }
295  }
296 
297  return true;
298 }
299 
300 void LayoutSaver::setAffinityNames(const QStringList &affinityNames)
301 {
302  d->m_affinityNames = affinityNames;
303  if (affinityNames.contains(QString())) {
304  // Any window with empty affinity will also be subject to save/restore
305  d->m_affinityNames << QString();
306  }
307 }
308 
309 LayoutSaver::Private *LayoutSaver::dptr() const
310 {
311  return d;
312 }
313 
315 {
316  const DockWidgetBase::List &allDockWidgets = DockRegistry::self()->dockwidgets();
317  DockWidgetBase::List result;
318  result.reserve(allDockWidgets.size());
319  for (DockWidgetBase *dw : allDockWidgets) {
320  if (dw->property("kddockwidget_was_restored").toBool())
321  result.push_back(dw);
322  }
323 
324  return result;
325 }
326 
327 void LayoutSaver::Private::clearRestoredProperty()
328 {
329  const DockWidgetBase::List &allDockWidgets = DockRegistry::self()->dockwidgets();
330  for (DockWidgetBase *dw : allDockWidgets) {
331  dw->setProperty("kddockwidget_was_restored", QVariant());
332  }
333 }
334 
335 template<typename T>
336 void LayoutSaver::Private::deserializeWindowGeometry(const T &saved, QWidgetOrQuick *topLevel)
337 {
338  // Not simply calling QWidget::setGeometry() here.
339  // For QtQuick we need to modify the QWindow's geometry.
340 
341  if (topLevel->isWindow()) {
342  topLevel->setGeometry(saved.geometry);
343  } else {
344  KDDockWidgets::Private::setTopLevelGeometry(saved.geometry, topLevel);
345  }
346 
347  topLevel->setVisible(saved.isVisible);
348 }
349 
350 LayoutSaver::Private::Private(RestoreOptions options)
351  : m_dockRegistry(DockRegistry::self())
352  , m_restoreOptions(internalRestoreOptions(options))
353 {
354 }
355 
356 bool LayoutSaver::Private::matchesAffinity(const QStringList &affinities) const
357 {
358  return m_affinityNames.isEmpty() || affinities.isEmpty()
359  || DockRegistry::self()->affinitiesMatch(m_affinityNames, affinities);
360 }
361 
362 void LayoutSaver::Private::floatWidgetsWhichSkipRestore(const QStringList &mainWindowNames)
363 {
364  // Widgets with the DockWidget::LayoutSaverOption::Skip flag skip restore completely.
365  // If they were visible before they need to remain visible now.
366  // If they were previously docked we need to float them, as the main window they were on will
367  // be loading a new layout.
368 
369  for (MainWindowBase *mw : DockRegistry::self()->mainWindows(mainWindowNames)) {
370  const KDDockWidgets::DockWidgetBase::List docks = mw->layoutWidget()->dockWidgets();
371  for (auto dw : docks) {
372  if (dw->skipsRestore()) {
373  dw->setFloating(true);
374  }
375  }
376  }
377 }
378 
379 void LayoutSaver::Private::deleteEmptyFrames()
380 {
381  // After a restore it can happen that some DockWidgets didn't exist, so weren't restored.
382  // Delete their frame now.
383 
384  for (auto frame : m_dockRegistry->frames()) {
385  if (!frame->beingDeletedLater() && frame->isEmpty() && !frame->isCentralFrame())
386  delete frame;
387  }
388 }
389 
390 std::unique_ptr<QSettings> LayoutSaver::Private::settings() const
391 {
392  auto settings = std::unique_ptr<QSettings>(new QSettings(qApp->organizationName(),
393  qApp->applicationName()));
394  settings->beginGroup(QStringLiteral("KDDockWidgets::LayoutSaver"));
395 
396  return settings;
397 }
398 
400 {
401  return Private::s_restoreInProgress;
402 }
403 
404 bool LayoutSaver::Layout::isValid() const
405 {
406  if (serializationVersion != KDDOCKWIDGETS_SERIALIZATION_VERSION) {
407  qWarning() << Q_FUNC_INFO << "Serialization format is too old"
408  << serializationVersion << "current=" << KDDOCKWIDGETS_SERIALIZATION_VERSION;
409  return false;
410  }
411 
412  for (auto &m : mainWindows) {
413  if (!m.isValid())
414  return false;
415  }
416 
417  for (auto &m : floatingWindows) {
418  if (!m.isValid())
419  return false;
420  }
421 
422  for (auto &m : allDockWidgets) {
423  if (!m->isValid())
424  return false;
425  }
426 
427  return true;
428 }
429 
430 QByteArray LayoutSaver::Layout::toJson() const
431 {
432  QJsonDocument doc = QJsonDocument::fromVariant(toVariantMap());
433  return doc.toJson();
434 }
435 
436 bool LayoutSaver::Layout::fromJson(const QByteArray &jsonData)
437 {
438  QJsonParseError error;
439  QJsonDocument doc = QJsonDocument::fromJson(jsonData, &error);
440  if (error.error == QJsonParseError::NoError) {
441  fromVariantMap(doc.toVariant().toMap());
442  return true;
443  }
444 
445  return false;
446 }
447 
448 QVariantMap LayoutSaver::Layout::toVariantMap() const
449 {
450  QVariantMap map;
451  map.insert(QStringLiteral("serializationVersion"), serializationVersion);
452  map.insert(QStringLiteral("mainWindows"), toVariantList<LayoutSaver::MainWindow>(mainWindows));
453  map.insert(QStringLiteral("floatingWindows"), toVariantList<LayoutSaver::FloatingWindow>(floatingWindows));
454  map.insert(QStringLiteral("closedDockWidgets"), ::dockWidgetNames(closedDockWidgets));
455  map.insert(QStringLiteral("allDockWidgets"), toVariantList(allDockWidgets));
456  map.insert(QStringLiteral("screenInfo"), toVariantList<LayoutSaver::ScreenInfo>(screenInfo));
457 
458  return map;
459 }
460 
461 void LayoutSaver::Layout::fromVariantMap(const QVariantMap &map)
462 {
463  allDockWidgets.clear();
464  const QVariantList dockWidgetsV = map.value(QStringLiteral("allDockWidgets")).toList();
465  for (const QVariant &v : dockWidgetsV) {
466  const QVariantMap dwV = v.toMap();
467  const QString name = dwV.value(QStringLiteral("uniqueName")).toString();
468  auto dw = LayoutSaver::DockWidget::dockWidgetForName(name);
469  dw->fromVariantMap(dwV);
470  allDockWidgets.push_back(dw);
471  }
472 
473  closedDockWidgets.clear();
474  const QVariantList closedDockWidgetsV = map.value(QStringLiteral("closedDockWidgets")).toList();
475  closedDockWidgets.reserve(closedDockWidgetsV.size());
476  for (const QVariant &v : closedDockWidgetsV) {
477  closedDockWidgets.push_back(LayoutSaver::DockWidget::dockWidgetForName(v.toString()));
478  }
479 
480  serializationVersion = map.value(QStringLiteral("serializationVersion")).toInt();
481  mainWindows = fromVariantList<LayoutSaver::MainWindow>(map.value(QStringLiteral("mainWindows")).toList());
482  floatingWindows = fromVariantList<LayoutSaver::FloatingWindow>(map.value(QStringLiteral("floatingWindows")).toList());
483  screenInfo = fromVariantList<LayoutSaver::ScreenInfo>(map.value(QStringLiteral("screenInfo")).toList());
484 }
485 
486 void LayoutSaver::Layout::scaleSizes(InternalRestoreOptions options)
487 {
488  if (mainWindows.isEmpty())
489  return;
490 
491  const bool skipsMainWindowGeometry = options & InternalRestoreOption::SkipMainWindowGeometry;
492  if (!skipsMainWindowGeometry) {
493  // No scaling to do. All windows will be restored with the exact size specified in the
494  // saved JSON layouts.
495  return;
496  }
497 
498  // We won't restore MainWindow's geometry, we use whatever the user has now, meaning
499  // we need to scale all dock widgets inside the layout, as the layout might not have
500  // the same size as specified in the saved JSON layout
501  for (auto &mw : mainWindows)
502  mw.scaleSizes();
503 
504 
505  // MainWindow has a different size than the one in JSON, so we also restore FloatingWindows
506  // relatively to the user set new MainWindow size
507  const bool useRelativeSizesForFloatingWidgets =
508  options & InternalRestoreOption::RelativeFloatingWindowGeometry;
509 
510  if (useRelativeSizesForFloatingWidgets) {
511  for (auto &fw : floatingWindows) {
512  LayoutSaver::MainWindow mw = mainWindowForIndex(fw.parentIndex);
513  if (mw.scalingInfo.isValid())
514  fw.scaleSizes(mw.scalingInfo);
515  }
516  }
517 
518  const ScalingInfo firstScalingInfo = mainWindows.constFirst().scalingInfo;
519  if (firstScalingInfo.isValid()) {
520  for (auto &dw : allDockWidgets) {
521  // TODO: Determine the best main window. This only interesting for closed dock
522  // widget geometry which was previously floating. But they still have some other
523  // main window as parent.
524  dw->scaleSizes(firstScalingInfo);
525  }
526  }
527 }
528 
529 LayoutSaver::MainWindow LayoutSaver::Layout::mainWindowForIndex(int index) const
530 {
531  if (index < 0 || index >= mainWindows.size())
532  return {};
533 
534  return mainWindows.at(index);
535 }
536 
537 LayoutSaver::FloatingWindow LayoutSaver::Layout::floatingWindowForIndex(int index) const
538 {
539  if (index < 0 || index >= floatingWindows.size())
540  return {};
541 
542  return floatingWindows.at(index);
543 }
544 
545 QStringList LayoutSaver::Layout::mainWindowNames() const
546 {
547  QStringList names;
548  names.reserve(mainWindows.size());
549  for (const auto &mw : mainWindows) {
550  names << mw.uniqueName;
551  }
552 
553  return names;
554 }
555 
556 QStringList LayoutSaver::Layout::dockWidgetNames() const
557 {
558  QStringList names;
559  names.reserve(allDockWidgets.size());
560  for (const auto &dw : allDockWidgets) {
561  names << dw->uniqueName;
562  }
563 
564  return names;
565 }
566 
567 QStringList LayoutSaver::Layout::dockWidgetsToClose() const
568 {
569  // Before restoring a layout we close all dock widgets, unless they're a floating window with the DontCloseBeforeRestore flag
570 
571  QStringList names;
572  names.reserve(allDockWidgets.size());
573  auto registry = DockRegistry::self();
574  for (const auto &dw : allDockWidgets) {
575  if (DockWidgetBase *dockWidget = registry->dockByName(dw->uniqueName)) {
576 
577  bool doClose = true;
578 
579  if (dockWidget->skipsRestore()) {
580  if (auto fw = dockWidget->floatingWindow()) {
581  if (fw->allDockWidgetsHave(DockWidgetBase::LayoutSaverOption::Skip)) {
582  // All dock widgets in this floating window skips float, so we can honour it for all.
583  doClose = false;
584  }
585  }
586  }
587 
588  if (doClose)
589  names << dw->uniqueName;
590  }
591  }
592 
593  return names;
594 }
595 
596 bool LayoutSaver::Frame::isValid() const
597 {
598  if (isNull)
599  return true;
600 
601  if (!geometry.isValid()) {
602  qWarning() << Q_FUNC_INFO << "Invalid geometry";
603  return false;
604  }
605 
606  if (id.isEmpty()) {
607  qWarning() << Q_FUNC_INFO << "Invalid id";
608  return false;
609  }
610 
611  if (options > 3) {
612  qWarning() << Q_FUNC_INFO << "Invalid options" << options;
613  return false;
614  }
615 
616  if (!dockWidgets.isEmpty()) {
617  if (currentTabIndex >= dockWidgets.size() || currentTabIndex < 0) {
618  qWarning() << Q_FUNC_INFO << "Invalid tab index" << currentTabIndex << dockWidgets.size();
619  return false;
620  }
621  }
622 
623  for (auto &dw : dockWidgets) {
624  if (!dw->isValid())
625  return false;
626  }
627 
628  return true;
629 }
630 
631 bool LayoutSaver::Frame::hasSingleDockWidget() const
632 {
633  return dockWidgets.size() == 1;
634 }
635 
636 bool LayoutSaver::Frame::skipsRestore() const
637 {
638  return std::all_of(dockWidgets.cbegin(), dockWidgets.cend(), [](LayoutSaver::DockWidget::Ptr dw) {
639  return dw->skipsRestore();
640  });
641 }
642 
643 LayoutSaver::DockWidget::Ptr LayoutSaver::Frame::singleDockWidget() const
644 {
645  if (!hasSingleDockWidget())
646  return {};
647 
648  return dockWidgets.first();
649 }
650 
651 QVariantMap LayoutSaver::Frame::toVariantMap() const
652 {
653  QVariantMap map;
654  map.insert(QStringLiteral("id"), id);
655  map.insert(QStringLiteral("isNull"), isNull);
656  map.insert(QStringLiteral("objectName"), objectName);
657  map.insert(QStringLiteral("geometry"), Layouting::rectToMap(geometry));
658  map.insert(QStringLiteral("options"), options);
659  map.insert(QStringLiteral("currentTabIndex"), currentTabIndex);
660 
661  map.insert(QStringLiteral("dockWidgets"), dockWidgetNames(dockWidgets));
662 
663  return map;
664 }
665 
666 void LayoutSaver::Frame::fromVariantMap(const QVariantMap &map)
667 {
668  if (map.isEmpty()) {
669  isNull = true;
670  dockWidgets.clear();
671  return;
672  }
673 
674  id = map.value(QStringLiteral("id")).toString();
675  isNull = map.value(QStringLiteral("isNull")).toBool();
676  objectName = map.value(QStringLiteral("objectName")).toString();
677  geometry = Layouting::mapToRect(map.value(QStringLiteral("geometry")).toMap());
678  options = static_cast<QFlags<FrameOption>::Int>(map.value(QStringLiteral("options")).toUInt());
679  currentTabIndex = map.value(QStringLiteral("currentTabIndex")).toInt();
680 
681  const QVariantList dockWidgetsV = map.value(QStringLiteral("dockWidgets")).toList();
682 
683  dockWidgets.clear();
684  dockWidgets.reserve(dockWidgetsV.size());
685  for (const auto &variant : dockWidgetsV) {
686  DockWidget::Ptr dw = DockWidget::dockWidgetForName(variant.toString());
687  dockWidgets.push_back(dw);
688  }
689 }
690 
691 bool LayoutSaver::DockWidget::isValid() const
692 {
693  return !uniqueName.isEmpty();
694 }
695 
696 void LayoutSaver::DockWidget::scaleSizes(const ScalingInfo &scalingInfo)
697 {
698  lastPosition.scaleSizes(scalingInfo);
699 }
700 
701 bool LayoutSaver::DockWidget::skipsRestore() const
702 {
703  if (DockWidgetBase *dw = DockRegistry::self()->dockByName(uniqueName))
704  return dw->skipsRestore();
705 
706  return false;
707 }
708 
709 QVariantMap LayoutSaver::DockWidget::toVariantMap() const
710 {
711  QVariantMap map;
712  if (!affinities.isEmpty())
713  map.insert(QStringLiteral("affinities"), stringListToVariant(affinities));
714  map.insert(QStringLiteral("uniqueName"), uniqueName);
715  map.insert(QStringLiteral("lastPosition"), lastPosition.toVariantMap());
716 
717  return map;
718 }
719 
720 void LayoutSaver::DockWidget::fromVariantMap(const QVariantMap &map)
721 {
722  affinities = variantToStringList(map.value(QStringLiteral("affinities")).toList());
723 
724  // Compatibility hack. Old json format had a single "affinityName" instead of an "affinities" list:
725  const QString affinityName = map.value(QStringLiteral("affinityName")).toString();
726  if (!affinityName.isEmpty() && !affinities.contains(affinityName)) {
727  affinities.push_back(affinityName);
728  }
729 
730  uniqueName = map.value(QStringLiteral("uniqueName")).toString();
731  lastPosition.fromVariantMap(map.value(QStringLiteral("lastPosition")).toMap());
732 }
733 
734 bool LayoutSaver::FloatingWindow::isValid() const
735 {
736  if (!multiSplitterLayout.isValid())
737  return false;
738 
739  if (!geometry.isValid()) {
740  qWarning() << Q_FUNC_INFO << "Invalid geometry";
741  return false;
742  }
743 
744  return true;
745 }
746 
747 bool LayoutSaver::FloatingWindow::hasSingleDockWidget() const
748 {
749  return multiSplitterLayout.hasSingleDockWidget();
750 }
751 
752 LayoutSaver::DockWidget::Ptr LayoutSaver::FloatingWindow::singleDockWidget() const
753 {
754  return multiSplitterLayout.singleDockWidget();
755 }
756 
757 bool LayoutSaver::FloatingWindow::skipsRestore() const
758 {
759  return multiSplitterLayout.skipsRestore();
760 }
761 
762 void LayoutSaver::FloatingWindow::scaleSizes(const ScalingInfo &scalingInfo)
763 {
764  scalingInfo.applyFactorsTo(/*by-ref*/ geometry);
765 }
766 
767 QVariantMap LayoutSaver::FloatingWindow::toVariantMap() const
768 {
769  QVariantMap map;
770  map.insert(QStringLiteral("multiSplitterLayout"), multiSplitterLayout.toVariantMap());
771  map.insert(QStringLiteral("parentIndex"), parentIndex);
772  map.insert(QStringLiteral("geometry"), Layouting::rectToMap(geometry));
773  map.insert(QStringLiteral("screenIndex"), screenIndex);
774  map.insert(QStringLiteral("screenSize"), Layouting::sizeToMap(screenSize));
775  map.insert(QStringLiteral("isVisible"), isVisible);
776 
777  if (!affinities.isEmpty())
778  map.insert(QStringLiteral("affinities"), stringListToVariant(affinities));
779 
780  return map;
781 }
782 
783 void LayoutSaver::FloatingWindow::fromVariantMap(const QVariantMap &map)
784 {
785  multiSplitterLayout.fromVariantMap(map.value(QStringLiteral("multiSplitterLayout")).toMap());
786  parentIndex = map.value(QStringLiteral("parentIndex")).toInt();
787  geometry = Layouting::mapToRect(map.value(QStringLiteral("geometry")).toMap());
788  screenIndex = map.value(QStringLiteral("screenIndex")).toInt();
789  screenSize = Layouting::mapToSize(map.value(QStringLiteral("screenSize")).toMap());
790  isVisible = map.value(QStringLiteral("isVisible")).toBool();
791  affinities = variantToStringList(map.value(QStringLiteral("affinities")).toList());
792 
793  // Compatibility hack. Old json format had a single "affinityName" instead of an "affinities" list:
794  const QString affinityName = map.value(QStringLiteral("affinityName")).toString();
795  if (!affinityName.isEmpty() && !affinities.contains(affinityName)) {
796  affinities.push_back(affinityName);
797  }
798 }
799 
800 bool LayoutSaver::MainWindow::isValid() const
801 {
802  if (!multiSplitterLayout.isValid())
803  return false;
804 
805  if (options != MainWindowOption_None && options != MainWindowOption_HasCentralFrame) {
806  qWarning() << Q_FUNC_INFO << "Invalid option" << options;
807  return false;
808  }
809 
810  return true;
811 }
812 
813 void LayoutSaver::MainWindow::scaleSizes()
814 {
815  if (scalingInfo.isValid()) {
816  // Doesn't happen, it's called only once
817  Q_ASSERT(false);
818  return;
819  }
820 
821  scalingInfo = ScalingInfo(uniqueName, geometry);
822 }
823 
824 QVariantMap LayoutSaver::MainWindow::toVariantMap() const
825 {
826  QVariantMap map;
827  map.insert(QStringLiteral("options"), int(options));
828  map.insert(QStringLiteral("multiSplitterLayout"), multiSplitterLayout.toVariantMap());
829  map.insert(QStringLiteral("uniqueName"), uniqueName);
830  map.insert(QStringLiteral("geometry"), Layouting::rectToMap(geometry));
831  map.insert(QStringLiteral("screenIndex"), screenIndex);
832  map.insert(QStringLiteral("screenSize"), Layouting::sizeToMap(screenSize));
833  map.insert(QStringLiteral("isVisible"), isVisible);
834  map.insert(QStringLiteral("affinities"), stringListToVariant(affinities));
835  map.insert(QStringLiteral("windowState"), windowState);
836 
838  const QStringList dockWidgets = dockWidgetsPerSideBar.value(loc);
839  if (!dockWidgets.isEmpty())
840  map.insert(QStringLiteral("sidebar-%1").arg(int(loc)), stringListToVariant(dockWidgets));
841  }
842 
843  return map;
844 }
845 
846 void LayoutSaver::MainWindow::fromVariantMap(const QVariantMap &map)
847 {
848  options = KDDockWidgets::MainWindowOptions(map.value(QStringLiteral("options")).toInt());
849  multiSplitterLayout.fromVariantMap(map.value(QStringLiteral("multiSplitterLayout")).toMap());
850  uniqueName = map.value(QStringLiteral("uniqueName")).toString();
851  geometry = Layouting::mapToRect(map.value(QStringLiteral("geometry")).toMap());
852  screenIndex = map.value(QStringLiteral("screenIndex")).toInt();
853  screenSize = Layouting::mapToSize(map.value(QStringLiteral("screenSize")).toMap());
854  isVisible = map.value(QStringLiteral("isVisible")).toBool();
855  affinities = variantToStringList(map.value(QStringLiteral("affinities")).toList());
856  windowState = Qt::WindowState(map.value(QStringLiteral("windowState"), Qt::WindowNoState).toInt());
857 
858  // Compatibility hack. Old json format had a single "affinityName" instead of an "affinities" list:
859  const QString affinityName = map.value(QStringLiteral("affinityName")).toString();
860  if (!affinityName.isEmpty() && !affinities.contains(affinityName)) {
861  affinities.push_back(affinityName);
862  }
863 
864  // Load the SideBars:
865  dockWidgetsPerSideBar.clear();
867  const QVariantList dockWidgets = map.value(QStringLiteral("sidebar-%1").arg(int(loc))).toList();
868  if (!dockWidgets.isEmpty())
869  dockWidgetsPerSideBar.insert(loc, variantToStringList(dockWidgets));
870  }
871 }
872 
873 bool LayoutSaver::MultiSplitter::isValid() const
874 {
875  if (layout.isEmpty())
876  return false;
877 
878  /*if (!size.isValid()) {
879  qWarning() << Q_FUNC_INFO << "Invalid size";
880  return false;
881  }*/
882 
883  return true;
884 }
885 
886 bool LayoutSaver::MultiSplitter::hasSingleDockWidget() const
887 {
888  return frames.size() == 1 && frames.cbegin()->hasSingleDockWidget();
889 }
890 
891 LayoutSaver::DockWidget::Ptr LayoutSaver::MultiSplitter::singleDockWidget() const
892 {
893  if (!hasSingleDockWidget())
894  return {};
895 
896  return frames.cbegin()->singleDockWidget();
897 }
898 
899 bool LayoutSaver::MultiSplitter::skipsRestore() const
900 {
901  return std::all_of(frames.cbegin(), frames.cend(), [](const LayoutSaver::Frame &frame) {
902  return frame.skipsRestore();
903  });
904 }
905 
906 QVariantMap LayoutSaver::MultiSplitter::toVariantMap() const
907 {
908  QVariantMap result;
909  result.insert(QStringLiteral("layout"), layout);
910 
911  QVariantMap framesV;
912  for (auto &frame : frames)
913  framesV.insert(frame.id, frame.toVariantMap());
914 
915  result.insert(QStringLiteral("frames"), framesV);
916  return result;
917 }
918 
919 void LayoutSaver::MultiSplitter::fromVariantMap(const QVariantMap &map)
920 {
921  layout = map.value(QStringLiteral("layout")).toMap();
922  const QVariantMap framesV = map.value(QStringLiteral("frames")).toMap();
923  frames.clear();
924  for (const QVariant &frameV : framesV) {
925  LayoutSaver::Frame frame;
926  frame.fromVariantMap(frameV.toMap());
927  frames.insert(frame.id, frame);
928  }
929 }
930 
931 void LayoutSaver::Position::scaleSizes(const ScalingInfo &scalingInfo)
932 {
933  scalingInfo.applyFactorsTo(/*by-ref*/ lastFloatingGeometry);
934 }
935 
936 QVariantMap LayoutSaver::Position::toVariantMap() const
937 {
938  QVariantMap map;
939  map.insert(QStringLiteral("lastFloatingGeometry"), Layouting::rectToMap(lastFloatingGeometry));
940  map.insert(QStringLiteral("tabIndex"), tabIndex);
941  map.insert(QStringLiteral("wasFloating"), wasFloating);
942  map.insert(QStringLiteral("placeholders"), toVariantList<LayoutSaver::Placeholder>(placeholders));
943 
944  return map;
945 }
946 
947 void LayoutSaver::Position::fromVariantMap(const QVariantMap &map)
948 {
949  lastFloatingGeometry = Layouting::mapToRect(map.value(QStringLiteral("lastFloatingGeometry")).toMap());
950  tabIndex = map.value(QStringLiteral("tabIndex")).toInt();
951  wasFloating = map.value(QStringLiteral("wasFloating")).toBool();
952  placeholders = fromVariantList<LayoutSaver::Placeholder>(map.value(QStringLiteral("placeholders")).toList());
953 }
954 
955 QVariantMap LayoutSaver::ScreenInfo::toVariantMap() const
956 {
957  QVariantMap map;
958  map.insert(QStringLiteral("index"), index);
959  map.insert(QStringLiteral("geometry"), Layouting::rectToMap(geometry));
960  map.insert(QStringLiteral("name"), name);
961  map.insert(QStringLiteral("devicePixelRatio"), devicePixelRatio);
962 
963  return map;
964 }
965 
966 void LayoutSaver::ScreenInfo::fromVariantMap(const QVariantMap &map)
967 {
968  index = map.value(QStringLiteral("index")).toInt();
969  geometry = Layouting::mapToRect(map.value(QStringLiteral("geometry")).toMap());
970  name = map.value(QStringLiteral("name")).toString();
971  devicePixelRatio = map.value(QStringLiteral("devicePixelRatio")).toDouble();
972 }
973 
974 QVariantMap LayoutSaver::Placeholder::toVariantMap() const
975 {
976  QVariantMap map;
977  map.insert(QStringLiteral("isFloatingWindow"), isFloatingWindow);
978  map.insert(QStringLiteral("itemIndex"), itemIndex);
979 
980  if (isFloatingWindow)
981  map.insert(QStringLiteral("indexOfFloatingWindow"), indexOfFloatingWindow);
982  else
983  map.insert(QStringLiteral("mainWindowUniqueName"), mainWindowUniqueName);
984 
985  return map;
986 }
987 
988 void LayoutSaver::Placeholder::fromVariantMap(const QVariantMap &map)
989 {
990  isFloatingWindow = map.value(QStringLiteral("isFloatingWindow")).toBool();
991  indexOfFloatingWindow = map.value(QStringLiteral("indexOfFloatingWindow"), -1).toInt();
992  itemIndex = map.value(QStringLiteral("itemIndex")).toInt();
993  mainWindowUniqueName = map.value(QStringLiteral("mainWindowUniqueName")).toString();
994 }
995 
996 LayoutSaver::ScalingInfo::ScalingInfo(const QString &mainWindowId, QRect savedMainWindowGeo)
997 {
998  auto mainWindow = DockRegistry::self()->mainWindowByName(mainWindowId);
999  if (!mainWindow) {
1000  qWarning() << Q_FUNC_INFO << "Failed to find main window with name" << mainWindowName;
1001  return;
1002  }
1003 
1004  if (!savedMainWindowGeo.isValid() || savedMainWindowGeo.isNull()) {
1005  qWarning() << Q_FUNC_INFO << "Invalid saved main window geometry" << savedMainWindowGeo;
1006  return;
1007  }
1008 
1009  if (!mainWindow->geometry().isValid() || mainWindow->geometry().isNull()) {
1010  qWarning() << Q_FUNC_INFO << "Invalid main window geometry" << mainWindow->geometry();
1011  return;
1012  }
1013 
1014  this->mainWindowName = mainWindowId;
1015  this->savedMainWindowGeometry = savedMainWindowGeo;
1016  realMainWindowGeometry = mainWindow->window()->geometry(); // window() as our main window might be embedded
1017  widthFactor = double(realMainWindowGeometry.width()) / savedMainWindowGeo.width();
1018  heightFactor = double(realMainWindowGeometry.height()) / savedMainWindowGeo.height();
1019 }
1020 
1021 void LayoutSaver::ScalingInfo::translatePos(QPoint &pt) const
1022 {
1023  const int deltaX = pt.x() - savedMainWindowGeometry.x();
1024  const int deltaY = pt.y() - savedMainWindowGeometry.y();
1025 
1026  const double newDeltaX = deltaX * widthFactor;
1027  const double newDeltaY = deltaY * heightFactor;
1028 
1029  pt.setX(qCeil(savedMainWindowGeometry.x() + newDeltaX));
1030  pt.setY(qCeil(savedMainWindowGeometry.y() + newDeltaY));
1031 }
1032 
1033 void LayoutSaver::ScalingInfo::applyFactorsTo(QPoint &pt) const
1034 {
1035  translatePos(pt);
1036 }
1037 
1038 void LayoutSaver::ScalingInfo::applyFactorsTo(QSize &sz) const
1039 {
1040  sz.setWidth(int(widthFactor * sz.width()));
1041  sz.setHeight(int(heightFactor * sz.height()));
1042 }
1043 
1044 void LayoutSaver::ScalingInfo::applyFactorsTo(QRect &rect) const
1045 {
1046  if (rect.isEmpty())
1047  return;
1048 
1049  QPoint pos = rect.topLeft();
1050  QSize size = rect.size();
1051 
1052  applyFactorsTo(/*by-ref*/ size);
1053  applyFactorsTo(/*by-ref*/ pos);
1054 
1055  rect.moveTopLeft(pos);
1056  rect.setSize(size);
1057 }
1058 
1059 LayoutSaver::Private::RAIIIsRestoring::RAIIIsRestoring()
1060 {
1061  LayoutSaver::Private::s_restoreInProgress = true;
1062 }
1063 
1064 LayoutSaver::Private::RAIIIsRestoring::~RAIIIsRestoring()
1065 {
1066  LayoutSaver::Private::s_restoreInProgress = false;
1067 }
KDDockWidgets::LayoutSaver::restoreLayout
bool restoreLayout(const QByteArray &)
restores the layout from a byte array All MainWindows and DockWidgets should have been created before...
Definition: LayoutSaver.cpp:187
KDDockWidgets::LayoutSaver::serializeLayout
QByteArray serializeLayout() const
saves the layout into a byte array
Definition: LayoutSaver.cpp:137
DockWidgetBase.h
The DockWidget base-class that's shared between QtWidgets and QtQuick stack.
QRect::setSize
void setSize(const QSize &size)
QRect::moveTopLeft
void moveTopLeft(const QPoint &position)
QIODevice::errorString
QString errorString() const const
QWidget::window
QWidget * window() const const
LayoutSaver.h
Class to save and restore dockwidget layouts.
QJsonDocument::fromJson
QJsonDocument fromJson(const QByteArray &json, QJsonParseError *error)
QRect::topLeft
QPoint topLeft() const const
QIODevice::WriteOnly
WriteOnly
QRect::size
QSize size() const const
KDDockWidgets::DockWidgetBase::LayoutSaverOption::Skip
@ Skip
The dock widget won't participate in save/restore. Currently only available for floating windows.
QRect
QFile::open
virtual bool open(QIODevice::OpenMode mode) override
QJsonDocument::toVariant
QVariant toVariant() const const
KDDockWidgets::LayoutSaver::~LayoutSaver
~LayoutSaver()
Destructor.
Definition: LayoutSaver.cpp:104
KDDockWidgets::LayoutSaver::dptr
Private * dptr() const
Definition: LayoutSaver.cpp:309
QStringList::contains
bool contains(const QString &str, Qt::CaseSensitivity cs) const const
QVector::push_back
void push_back(const T &value)
QWidget
KDDockWidgets::MainWindowBase::affinities
QStringList affinities
Definition: MainWindowBase.h:58
QRect::width
int width() const const
KDDockWidgets::SideBarLocation::West
@ West
QSize
QList::push_back
void push_back(const T &value)
QPoint::x
int x() const const
QPoint::y
int y() const const
QSize::width
int width() const const
QFlags
KDDockWidgets::MainWindowOption_None
@ MainWindowOption_None
Definition: KDDockWidgets.h:56
QSize::setWidth
void setWidth(int width)
MainWindowBase.h
The MainWindow base-class that's shared between QtWidgets and QtQuick stack.
QList::reserve
void reserve(int alloc)
QJsonParseError
QWidget::setVisible
virtual void setVisible(bool visible)
QList::size
int size() const const
KDDockWidgets::LayoutSaver
LayoutSaver allows to save or restore layouts.
Definition: LayoutSaver.h:55
internalRestoreOptions
InternalRestoreOptions internalRestoreOptions(RestoreOptions options)
Definition: LayoutSaver.cpp:64
KDDockWidgets::LayoutSaver::restoreInProgress
static bool restoreInProgress()
returns whether a restore (restoreLayout) is in progress
Definition: LayoutSaver.cpp:399
KDDockWidgets::LayoutSaver::LayoutSaver
LayoutSaver(RestoreOptions options=RestoreOption_None)
Constructor. Construction on the stack is suggested.
Definition: LayoutSaver.cpp:99
QSize::height
int height() const const
QString
QPoint::setX
void setX(int x)
QPoint::setY
void setY(int y)
QString::isEmpty
bool isEmpty() const const
QRect::isValid
bool isValid() const const
QJsonParseError::NoError
NoError
KDDockWidgets::LayoutSaver::restoreFromFile
bool restoreFromFile(const QString &jsonFilename)
restores the layout from a JSON file
Definition: LayoutSaver.cpp:123
QList::isEmpty
bool isEmpty() const const
Qt::WindowNoState
WindowNoState
QWidget::isWindow
bool isWindow() const const
KDDockWidgets::Config::frameworkWidgetFactory
FrameworkWidgetFactory * frameworkWidgetFactory() const
getter for the framework widget factory
Definition: Config.cpp:143
KDDockWidgets::LayoutSaver::restoredDockWidgets
QVector< DockWidgetBase * > restoredDockWidgets() const
returns a list of dock widgets which were restored since the last restoreLayout() or restoreFromFile(...
Definition: LayoutSaver.cpp:314
QRect::isNull
bool isNull() const const
KDDockWidgets::RestoreOption_None
@ RestoreOption_None
Definition: KDDockWidgets.h:182
KDDockWidgets::SideBarLocation
SideBarLocation
Each main window supports 4 sidebars.
Definition: KDDockWidgets.h:209
QVector::reserve
void reserve(int size)
QString::toDouble
double toDouble(bool *ok) const const
QVariant::toMap
QMap< QString, QVariant > toMap() const const
QJsonDocument
KDDockWidgets::FrameworkWidgetFactory::createFloatingWindow
virtual FloatingWindow * createFloatingWindow(MainWindowBase *parent=nullptr) const =0
Called internally by the framework to create a FloatingWindow Override to provide your own FloatingWi...
KDDockWidgets::SideBarLocation::East
@ East
KDDockWidgets::SideBarLocation::South
@ South
Config.h
Application-wide config to tune certain behaviours of the framework.
QWidget::setGeometry
void setGeometry(int x, int y, int w, int h)
KDDockWidgets::LayoutSaver::setAffinityNames
void setAffinityNames(const QStringList &affinityNames)
Sets the list of affinity names for which restore and save will be applied on. Allows to save/restore...
Definition: LayoutSaver.cpp:300
variantToStringList
static QStringList variantToStringList(const QVariantList &variantList)
Definition: LayoutSaver.cpp:89
QWidget::windowHandle
QWindow * windowHandle() const const
QByteArray::isEmpty
bool isEmpty() const const
QRect::height
int height() const const
QRect::isEmpty
bool isEmpty() const const
KDDockWidgets::SideBarLocation::North
@ North
KDDockWidgets::DockWidgetBase
The DockWidget base-class. DockWidget and DockWidgetBase are only split in two so we can share some c...
Definition: DockWidgetBase.h:61
QSettings
KDDockWidgets::RestoreOption_RelativeToMainWindow
@ RestoreOption_RelativeToMainWindow
Loading layouts won't change the main window geometry and just use whatever the user has at the momen...
Definition: KDDockWidgets.h:183
QJsonDocument::toJson
QByteArray toJson() const const
KDDockWidgets
Definition: Config.cpp:36
stringListToVariant
static QVariantList stringListToVariant(const QStringList &strs)
Definition: LayoutSaver.cpp:79
QVector::size
int size() const const
KDDockWidgets::LayoutSaver::saveToFile
bool saveToFile(const QString &jsonFilename)
saves the layout to JSON file
Definition: LayoutSaver.cpp:109
KDDockWidgets::MainWindowBase
The MainWindow base-class. MainWindow and MainWindowBase are only split in two so we can share some c...
Definition: MainWindowBase.h:56
QIODevice::readAll
QByteArray readAll()
QVector
QJsonDocument::fromVariant
QJsonDocument fromVariant(const QVariant &variant)
QVariant
QHash
QFile
QSize::setHeight
void setHeight(int height)
QList::value
T value(int i) const const
QPoint
QByteArray
QIODevice::write
qint64 write(const char *data, qint64 maxSize)
KDDockWidgets::Config::self
static Config & self()
returns the singleton Config instance
Definition: Config.cpp:82
FrameworkWidgetFactory.h
A factory class for allowing the user to customize some internal widgets.
QStringList
KDDockWidgets::MainWindowOption_HasCentralFrame
@ MainWindowOption_HasCentralFrame
No option set
Definition: KDDockWidgets.h:57

© 2019-2021 Klarälvdalens Datakonsult AB (KDAB)
"The Qt, C++ and OpenGL Experts"
https://www.kdab.com/
KDDockWidgets
Advanced Dock Widget Framework for Qt
https://www.kdab.com/development-resources/qt-tools/kddockwidgets/
Generated on Mon Nov 15 2021 00:17:27 for KDDockWidgets API Documentation by doxygen 1.8.20