KDDockWidgets API Documentation  1.5
LayoutSaver.cpp
Go to the documentation of this file.
1 /*
2  This file is part of KDDockWidgets.
3 
4  SPDX-FileCopyrightText: 2019-2022 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  d->floatUnknownWidgets(layout);
223 
224  Private::RAIIIsRestoring isRestoring;
225 
226  // Hide all dockwidgets and unparent them from any layout before starting restore
227  // We only close the stuff that the loaded JSON knows about. Unknown widgets might be newer.
228 
229  d->m_dockRegistry->clear(d->m_dockRegistry->dockWidgets(layout.dockWidgetsToClose()),
230  d->m_dockRegistry->mainWindows(layout.mainWindowNames()),
231  d->m_affinityNames);
232 
233  // 1. Restore main windows
234  for (const LayoutSaver::MainWindow &mw : qAsConst(layout.mainWindows)) {
235  MainWindowBase *mainWindow = d->m_dockRegistry->mainWindowByName(mw.uniqueName);
236  if (!mainWindow) {
237  if (auto mwFunc = Config::self().mainWindowFactoryFunc()) {
238  mainWindow = mwFunc(mw.uniqueName);
239  } else {
240  qWarning() << "Failed to restore layout create MainWindow with name" << mw.uniqueName << "first";
241  return false;
242  }
243  }
244 
245  if (!d->matchesAffinity(mainWindow->affinities()))
246  continue;
247 
248  if (!(d->m_restoreOptions & InternalRestoreOption::SkipMainWindowGeometry)) {
249  d->deserializeWindowGeometry(mw, mainWindow->window()); // window(), as the MainWindow can be embedded
250  if (mw.windowState != Qt::WindowNoState) {
251  if (auto w = mainWindow->windowHandle()) {
252  w->setWindowState(mw.windowState);
253  }
254  }
255  }
256 
257  if (!mainWindow->deserialize(mw))
258  return false;
259  }
260 
261  // 2. Restore FloatingWindows
262  for (LayoutSaver::FloatingWindow &fw : layout.floatingWindows) {
263  if (!d->matchesAffinity(fw.affinities) || fw.skipsRestore())
264  continue;
265 
266  MainWindowBase *parent = fw.parentIndex == -1 ? nullptr
267  : DockRegistry::self()->mainwindows().at(fw.parentIndex);
268 
269  auto floatingWindow = Config::self().frameworkWidgetFactory()->createFloatingWindow(parent);
270  fw.floatingWindowInstance = floatingWindow;
271  d->deserializeWindowGeometry(fw, floatingWindow);
272  if (!floatingWindow->deserialize(fw)) {
273  qWarning() << Q_FUNC_INFO << "Failed to deserialize floating window";
274  return false;
275  }
276  }
277 
278  // 3. Restore closed dock widgets. They remain closed but acquire geometry and placeholder properties
279  for (const auto &dw : qAsConst(layout.closedDockWidgets)) {
280  if (d->matchesAffinity(dw->affinities)) {
281  DockWidgetBase::deserialize(dw);
282  }
283  }
284 
285  // 4. Restore the placeholder info, now that the Items have been created
286  for (const auto &dw : qAsConst(layout.allDockWidgets)) {
287  if (!d->matchesAffinity(dw->affinities))
288  continue;
289 
290  if (DockWidgetBase *dockWidget =
291  d->m_dockRegistry->dockByName(dw->uniqueName, DockRegistry::DockByNameFlag::ConsultRemapping)) {
292  dockWidget->d->lastPositions().deserialize(dw->lastPosition);
293  } else {
294  qWarning() << Q_FUNC_INFO << "Couldn't find dock widget" << dw->uniqueName;
295  }
296  }
297 
298  return true;
299 }
300 
301 void LayoutSaver::setAffinityNames(const QStringList &affinityNames)
302 {
303  d->m_affinityNames = affinityNames;
304  if (affinityNames.contains(QString())) {
305  // Any window with empty affinity will also be subject to save/restore
306  d->m_affinityNames << QString();
307  }
308 }
309 
310 LayoutSaver::Private *LayoutSaver::dptr() const
311 {
312  return d;
313 }
314 
316 {
317  const DockWidgetBase::List &allDockWidgets = DockRegistry::self()->dockwidgets();
318  DockWidgetBase::List result;
319  result.reserve(allDockWidgets.size());
320  for (DockWidgetBase *dw : allDockWidgets) {
321  if (dw->property("kddockwidget_was_restored").toBool())
322  result.push_back(dw);
323  }
324 
325  return result;
326 }
327 
328 void LayoutSaver::Private::clearRestoredProperty()
329 {
330  const DockWidgetBase::List &allDockWidgets = DockRegistry::self()->dockwidgets();
331  for (DockWidgetBase *dw : allDockWidgets) {
332  dw->setProperty("kddockwidget_was_restored", QVariant());
333  }
334 }
335 
336 template<typename T>
337 void LayoutSaver::Private::deserializeWindowGeometry(const T &saved, QWidgetOrQuick *topLevel)
338 {
339  // Not simply calling QWidget::setGeometry() here.
340  // For QtQuick we need to modify the QWindow's geometry.
341 
342  auto windowGeometry = saved.geometry;
343  ::FloatingWindow::ensureRectIsOnScreen(windowGeometry);
344 
345  if (topLevel->isWindow()) {
346  topLevel->setGeometry(windowGeometry);
347  } else {
348  KDDockWidgets::Private::setTopLevelGeometry(windowGeometry, topLevel);
349  }
350 
351  topLevel->setVisible(saved.isVisible);
352 }
353 
354 LayoutSaver::Private::Private(RestoreOptions options)
355  : m_dockRegistry(DockRegistry::self())
356  , m_restoreOptions(internalRestoreOptions(options))
357 {
358 }
359 
360 bool LayoutSaver::Private::matchesAffinity(const QStringList &affinities) const
361 {
362  return m_affinityNames.isEmpty() || affinities.isEmpty()
363  || DockRegistry::self()->affinitiesMatch(m_affinityNames, affinities);
364 }
365 
366 void LayoutSaver::Private::floatWidgetsWhichSkipRestore(const QStringList &mainWindowNames)
367 {
368  // Widgets with the DockWidget::LayoutSaverOption::Skip flag skip restore completely.
369  // If they were visible before they need to remain visible now.
370  // If they were previously docked we need to float them, as the main window they were on will
371  // be loading a new layout.
372 
373  for (MainWindowBase *mw : DockRegistry::self()->mainWindows(mainWindowNames)) {
374  const KDDockWidgets::DockWidgetBase::List docks = mw->layoutWidget()->dockWidgets();
375  for (auto dw : docks) {
376  if (dw->skipsRestore()) {
377  dw->setFloating(true);
378  }
379  }
380  }
381 }
382 
383 void LayoutSaver::Private::floatUnknownWidgets(const LayoutSaver::Layout &layout)
384 {
385  // An old *.json layout file might have not know about existing dock widgets
386  // When restoring such a file, we need to float any visible dock widgets which it doesn't know about
387  // so we can restore the MainWindow layout properly
388 
389  for (MainWindowBase *mw : DockRegistry::self()->mainWindows(layout.mainWindowNames())) {
390  const KDDockWidgets::DockWidgetBase::List docks = mw->layoutWidget()->dockWidgets();
391  for (DockWidgetBase *dw : docks) {
392  if (!layout.containsDockWidget(dw->uniqueName())) {
393  dw->setFloating(true);
394  }
395  }
396  }
397 }
398 
399 void LayoutSaver::Private::deleteEmptyFrames()
400 {
401  // After a restore it can happen that some DockWidgets didn't exist, so weren't restored.
402  // Delete their frame now.
403 
404  for (auto frame : m_dockRegistry->frames()) {
405  if (!frame->beingDeletedLater() && frame->isEmpty() && !frame->isCentralFrame())
406  delete frame;
407  }
408 }
409 
410 std::unique_ptr<QSettings> LayoutSaver::Private::settings() const
411 {
412  auto settings = std::unique_ptr<QSettings>(new QSettings(qApp->organizationName(),
413  qApp->applicationName()));
414  settings->beginGroup(QStringLiteral("KDDockWidgets::LayoutSaver"));
415 
416  return settings;
417 }
418 
420 {
421  return Private::s_restoreInProgress;
422 }
423 
424 bool LayoutSaver::Layout::isValid() const
425 {
426  if (serializationVersion != KDDOCKWIDGETS_SERIALIZATION_VERSION) {
427  qWarning() << Q_FUNC_INFO << "Serialization format is too old"
428  << serializationVersion << "current=" << KDDOCKWIDGETS_SERIALIZATION_VERSION;
429  return false;
430  }
431 
432  for (auto &m : mainWindows) {
433  if (!m.isValid())
434  return false;
435  }
436 
437  for (auto &m : floatingWindows) {
438  if (!m.isValid())
439  return false;
440  }
441 
442  for (auto &m : allDockWidgets) {
443  if (!m->isValid())
444  return false;
445  }
446 
447  return true;
448 }
449 
450 QByteArray LayoutSaver::Layout::toJson() const
451 {
452  QJsonDocument doc = QJsonDocument::fromVariant(toVariantMap());
453  return doc.toJson();
454 }
455 
456 bool LayoutSaver::Layout::fromJson(const QByteArray &jsonData)
457 {
458  QJsonParseError error;
459  QJsonDocument doc = QJsonDocument::fromJson(jsonData, &error);
460  if (error.error == QJsonParseError::NoError) {
461  fromVariantMap(doc.toVariant().toMap());
462  return true;
463  }
464 
465  return false;
466 }
467 
468 QVariantMap LayoutSaver::Layout::toVariantMap() const
469 {
470  QVariantMap map;
471  map.insert(QStringLiteral("serializationVersion"), serializationVersion);
472  map.insert(QStringLiteral("mainWindows"), toVariantList<LayoutSaver::MainWindow>(mainWindows));
473  map.insert(QStringLiteral("floatingWindows"), toVariantList<LayoutSaver::FloatingWindow>(floatingWindows));
474  map.insert(QStringLiteral("closedDockWidgets"), ::dockWidgetNames(closedDockWidgets));
475  map.insert(QStringLiteral("allDockWidgets"), toVariantList(allDockWidgets));
476  map.insert(QStringLiteral("screenInfo"), toVariantList<LayoutSaver::ScreenInfo>(screenInfo));
477 
478  return map;
479 }
480 
481 void LayoutSaver::Layout::fromVariantMap(const QVariantMap &map)
482 {
483  allDockWidgets.clear();
484  const QVariantList dockWidgetsV = map.value(QStringLiteral("allDockWidgets")).toList();
485  for (const QVariant &v : dockWidgetsV) {
486  const QVariantMap dwV = v.toMap();
487  const QString name = dwV.value(QStringLiteral("uniqueName")).toString();
488  auto dw = LayoutSaver::DockWidget::dockWidgetForName(name);
489  dw->fromVariantMap(dwV);
490  allDockWidgets.push_back(dw);
491  }
492 
493  closedDockWidgets.clear();
494  const QVariantList closedDockWidgetsV = map.value(QStringLiteral("closedDockWidgets")).toList();
495  closedDockWidgets.reserve(closedDockWidgetsV.size());
496  for (const QVariant &v : closedDockWidgetsV) {
497  closedDockWidgets.push_back(LayoutSaver::DockWidget::dockWidgetForName(v.toString()));
498  }
499 
500  serializationVersion = map.value(QStringLiteral("serializationVersion")).toInt();
501  mainWindows = fromVariantList<LayoutSaver::MainWindow>(map.value(QStringLiteral("mainWindows")).toList());
502  floatingWindows = fromVariantList<LayoutSaver::FloatingWindow>(map.value(QStringLiteral("floatingWindows")).toList());
503  screenInfo = fromVariantList<LayoutSaver::ScreenInfo>(map.value(QStringLiteral("screenInfo")).toList());
504 }
505 
506 void LayoutSaver::Layout::scaleSizes(InternalRestoreOptions options)
507 {
508  if (mainWindows.isEmpty())
509  return;
510 
511  const bool skipsMainWindowGeometry = options & InternalRestoreOption::SkipMainWindowGeometry;
512  if (!skipsMainWindowGeometry) {
513  // No scaling to do. All windows will be restored with the exact size specified in the
514  // saved JSON layouts.
515  return;
516  }
517 
518  // We won't restore MainWindow's geometry, we use whatever the user has now, meaning
519  // we need to scale all dock widgets inside the layout, as the layout might not have
520  // the same size as specified in the saved JSON layout
521  for (auto &mw : mainWindows)
522  mw.scaleSizes();
523 
524 
525  // MainWindow has a different size than the one in JSON, so we also restore FloatingWindows
526  // relatively to the user set new MainWindow size
527  const bool useRelativeSizesForFloatingWidgets =
528  options & InternalRestoreOption::RelativeFloatingWindowGeometry;
529 
530  if (useRelativeSizesForFloatingWidgets) {
531  for (auto &fw : floatingWindows) {
532  LayoutSaver::MainWindow mw = mainWindowForIndex(fw.parentIndex);
533  if (mw.scalingInfo.isValid())
534  fw.scaleSizes(mw.scalingInfo);
535  }
536  }
537 
538  const ScalingInfo firstScalingInfo = mainWindows.constFirst().scalingInfo;
539  if (firstScalingInfo.isValid()) {
540  for (auto &dw : allDockWidgets) {
541  // TODO: Determine the best main window. This only interesting for closed dock
542  // widget geometry which was previously floating. But they still have some other
543  // main window as parent.
544  dw->scaleSizes(firstScalingInfo);
545  }
546  }
547 }
548 
549 LayoutSaver::MainWindow LayoutSaver::Layout::mainWindowForIndex(int index) const
550 {
551  if (index < 0 || index >= mainWindows.size())
552  return {};
553 
554  return mainWindows.at(index);
555 }
556 
557 LayoutSaver::FloatingWindow LayoutSaver::Layout::floatingWindowForIndex(int index) const
558 {
559  if (index < 0 || index >= floatingWindows.size())
560  return {};
561 
562  return floatingWindows.at(index);
563 }
564 
565 QStringList LayoutSaver::Layout::mainWindowNames() const
566 {
567  QStringList names;
568  names.reserve(mainWindows.size());
569  for (const auto &mw : mainWindows) {
570  names << mw.uniqueName;
571  }
572 
573  return names;
574 }
575 
576 QStringList LayoutSaver::Layout::dockWidgetNames() const
577 {
578  QStringList names;
579  names.reserve(allDockWidgets.size());
580  for (const auto &dw : allDockWidgets) {
581  names << dw->uniqueName;
582  }
583 
584  return names;
585 }
586 
587 QStringList LayoutSaver::Layout::dockWidgetsToClose() const
588 {
589  // Before restoring a layout we close all dock widgets, unless they're a floating window with the DontCloseBeforeRestore flag
590 
591  QStringList names;
592  names.reserve(allDockWidgets.size());
593  auto registry = DockRegistry::self();
594  for (const auto &dw : allDockWidgets) {
595  if (DockWidgetBase *dockWidget = registry->dockByName(dw->uniqueName)) {
596 
597  bool doClose = true;
598 
599  if (dockWidget->skipsRestore()) {
600  if (auto fw = dockWidget->floatingWindow()) {
601  if (fw->allDockWidgetsHave(DockWidgetBase::LayoutSaverOption::Skip)) {
602  // All dock widgets in this floating window skips float, so we can honour it for all.
603  doClose = false;
604  }
605  }
606  }
607 
608  if (doClose)
609  names << dw->uniqueName;
610  }
611  }
612 
613  return names;
614 }
615 
616 bool LayoutSaver::Layout::containsDockWidget(const QString &uniqueName) const
617 {
618  return std::find_if(allDockWidgets.cbegin(), allDockWidgets.cend(), [uniqueName](const std::shared_ptr<LayoutSaver::DockWidget> &dock) {
619  return dock->uniqueName == uniqueName;
620  })
621  != allDockWidgets.cend();
622 }
623 
624 bool LayoutSaver::Frame::isValid() const
625 {
626  if (isNull)
627  return true;
628 
629  if (!geometry.isValid()) {
630  qWarning() << Q_FUNC_INFO << "Invalid geometry";
631  return false;
632  }
633 
634  if (id.isEmpty()) {
635  qWarning() << Q_FUNC_INFO << "Invalid id";
636  return false;
637  }
638 
639  if (!dockWidgets.isEmpty()) {
640  if (currentTabIndex >= dockWidgets.size() || currentTabIndex < 0) {
641  qWarning() << Q_FUNC_INFO << "Invalid tab index" << currentTabIndex << dockWidgets.size();
642  return false;
643  }
644  }
645 
646  for (auto &dw : dockWidgets) {
647  if (!dw->isValid())
648  return false;
649  }
650 
651  return true;
652 }
653 
654 bool LayoutSaver::Frame::hasSingleDockWidget() const
655 {
656  return dockWidgets.size() == 1;
657 }
658 
659 bool LayoutSaver::Frame::skipsRestore() const
660 {
661  return std::all_of(dockWidgets.cbegin(), dockWidgets.cend(), [](LayoutSaver::DockWidget::Ptr dw) {
662  return dw->skipsRestore();
663  });
664 }
665 
666 LayoutSaver::DockWidget::Ptr LayoutSaver::Frame::singleDockWidget() const
667 {
668  if (!hasSingleDockWidget())
669  return {};
670 
671  return dockWidgets.first();
672 }
673 
674 QVariantMap LayoutSaver::Frame::toVariantMap() const
675 {
676  QVariantMap map;
677  map.insert(QStringLiteral("id"), id);
678  map.insert(QStringLiteral("isNull"), isNull);
679  map.insert(QStringLiteral("objectName"), objectName);
680  map.insert(QStringLiteral("geometry"), Layouting::rectToMap(geometry));
681  map.insert(QStringLiteral("options"), options);
682  map.insert(QStringLiteral("currentTabIndex"), currentTabIndex);
683  map.insert(QStringLiteral("mainWindowUniqueName"), mainWindowUniqueName);
684  map.insert(QStringLiteral("dockWidgets"), dockWidgetNames(dockWidgets));
685 
686  return map;
687 }
688 
689 void LayoutSaver::Frame::fromVariantMap(const QVariantMap &map)
690 {
691  if (map.isEmpty()) {
692  isNull = true;
693  dockWidgets.clear();
694  return;
695  }
696 
697  id = map.value(QStringLiteral("id")).toString();
698  isNull = map.value(QStringLiteral("isNull")).toBool();
699  objectName = map.value(QStringLiteral("objectName")).toString();
700  mainWindowUniqueName = map.value(QStringLiteral("mainWindowUniqueName")).toString();
701  geometry = Layouting::mapToRect(map.value(QStringLiteral("geometry")).toMap());
702  options = static_cast<QFlags<FrameOption>::Int>(map.value(QStringLiteral("options")).toUInt());
703  currentTabIndex = map.value(QStringLiteral("currentTabIndex")).toInt();
704 
705  const QVariantList dockWidgetsV = map.value(QStringLiteral("dockWidgets")).toList();
706 
707  dockWidgets.clear();
708  dockWidgets.reserve(dockWidgetsV.size());
709  for (const auto &variant : dockWidgetsV) {
710  DockWidget::Ptr dw = DockWidget::dockWidgetForName(variant.toString());
711  dockWidgets.push_back(dw);
712  }
713 }
714 
715 bool LayoutSaver::DockWidget::isValid() const
716 {
717  return !uniqueName.isEmpty();
718 }
719 
720 void LayoutSaver::DockWidget::scaleSizes(const ScalingInfo &scalingInfo)
721 {
722  lastPosition.scaleSizes(scalingInfo);
723 }
724 
725 bool LayoutSaver::DockWidget::skipsRestore() const
726 {
727  if (DockWidgetBase *dw = DockRegistry::self()->dockByName(uniqueName))
728  return dw->skipsRestore();
729 
730  return false;
731 }
732 
733 QVariantMap LayoutSaver::DockWidget::toVariantMap() const
734 {
735  QVariantMap map;
736  if (!affinities.isEmpty())
737  map.insert(QStringLiteral("affinities"), stringListToVariant(affinities));
738  map.insert(QStringLiteral("uniqueName"), uniqueName);
739  map.insert(QStringLiteral("lastPosition"), lastPosition.toVariantMap());
740 
741  return map;
742 }
743 
744 void LayoutSaver::DockWidget::fromVariantMap(const QVariantMap &map)
745 {
746  affinities = variantToStringList(map.value(QStringLiteral("affinities")).toList());
747 
748  // Compatibility hack. Old json format had a single "affinityName" instead of an "affinities" list:
749  const QString affinityName = map.value(QStringLiteral("affinityName")).toString();
750  if (!affinityName.isEmpty() && !affinities.contains(affinityName)) {
751  affinities.push_back(affinityName);
752  }
753 
754  uniqueName = map.value(QStringLiteral("uniqueName")).toString();
755  lastPosition.fromVariantMap(map.value(QStringLiteral("lastPosition")).toMap());
756 }
757 
758 bool LayoutSaver::FloatingWindow::isValid() const
759 {
760  if (!multiSplitterLayout.isValid())
761  return false;
762 
763  if (!geometry.isValid()) {
764  qWarning() << Q_FUNC_INFO << "Invalid geometry";
765  return false;
766  }
767 
768  return true;
769 }
770 
771 bool LayoutSaver::FloatingWindow::hasSingleDockWidget() const
772 {
773  return multiSplitterLayout.hasSingleDockWidget();
774 }
775 
776 LayoutSaver::DockWidget::Ptr LayoutSaver::FloatingWindow::singleDockWidget() const
777 {
778  return multiSplitterLayout.singleDockWidget();
779 }
780 
781 bool LayoutSaver::FloatingWindow::skipsRestore() const
782 {
783  return multiSplitterLayout.skipsRestore();
784 }
785 
786 void LayoutSaver::FloatingWindow::scaleSizes(const ScalingInfo &scalingInfo)
787 {
788  scalingInfo.applyFactorsTo(/*by-ref*/ geometry);
789 }
790 
791 QVariantMap LayoutSaver::FloatingWindow::toVariantMap() const
792 {
793  QVariantMap map;
794  map.insert(QStringLiteral("multiSplitterLayout"), multiSplitterLayout.toVariantMap());
795  map.insert(QStringLiteral("parentIndex"), parentIndex);
796  map.insert(QStringLiteral("geometry"), Layouting::rectToMap(geometry));
797  map.insert(QStringLiteral("normalGeometry"), Layouting::rectToMap(normalGeometry));
798  map.insert(QStringLiteral("screenIndex"), screenIndex);
799  map.insert(QStringLiteral("screenSize"), Layouting::sizeToMap(screenSize));
800  map.insert(QStringLiteral("isVisible"), isVisible);
801  map.insert(QStringLiteral("windowState"), windowState);
802 
803  if (!affinities.isEmpty())
804  map.insert(QStringLiteral("affinities"), stringListToVariant(affinities));
805 
806  return map;
807 }
808 
809 void LayoutSaver::FloatingWindow::fromVariantMap(const QVariantMap &map)
810 {
811  multiSplitterLayout.fromVariantMap(map.value(QStringLiteral("multiSplitterLayout")).toMap());
812  parentIndex = map.value(QStringLiteral("parentIndex")).toInt();
813  geometry = Layouting::mapToRect(map.value(QStringLiteral("geometry")).toMap());
814  normalGeometry = Layouting::mapToRect(map.value(QStringLiteral("normalGeometry")).toMap());
815  screenIndex = map.value(QStringLiteral("screenIndex")).toInt();
816  screenSize = Layouting::mapToSize(map.value(QStringLiteral("screenSize")).toMap());
817  isVisible = map.value(QStringLiteral("isVisible")).toBool();
818  affinities = variantToStringList(map.value(QStringLiteral("affinities")).toList());
819  windowState = Qt::WindowState(map.value(QStringLiteral("windowState"), Qt::WindowNoState).toInt());
820 
821  // Compatibility hack. Old json format had a single "affinityName" instead of an "affinities" list:
822  const QString affinityName = map.value(QStringLiteral("affinityName")).toString();
823  if (!affinityName.isEmpty() && !affinities.contains(affinityName)) {
824  affinities.push_back(affinityName);
825  }
826 }
827 
828 bool LayoutSaver::MainWindow::isValid() const
829 {
830  if (!multiSplitterLayout.isValid())
831  return false;
832 
833  return true;
834 }
835 
836 void LayoutSaver::MainWindow::scaleSizes()
837 {
838  if (scalingInfo.isValid()) {
839  // Doesn't happen, it's called only once
840  Q_ASSERT(false);
841  return;
842  }
843 
844  scalingInfo = ScalingInfo(uniqueName, geometry, screenIndex);
845 }
846 
847 QVariantMap LayoutSaver::MainWindow::toVariantMap() const
848 {
849  QVariantMap map;
850  map.insert(QStringLiteral("options"), int(options));
851  map.insert(QStringLiteral("multiSplitterLayout"), multiSplitterLayout.toVariantMap());
852  map.insert(QStringLiteral("uniqueName"), uniqueName);
853  map.insert(QStringLiteral("geometry"), Layouting::rectToMap(geometry));
854  map.insert(QStringLiteral("screenIndex"), screenIndex);
855  map.insert(QStringLiteral("screenSize"), Layouting::sizeToMap(screenSize));
856  map.insert(QStringLiteral("isVisible"), isVisible);
857  map.insert(QStringLiteral("affinities"), stringListToVariant(affinities));
858  map.insert(QStringLiteral("windowState"), windowState);
859 
861  const QStringList dockWidgets = dockWidgetsPerSideBar.value(loc);
862  if (!dockWidgets.isEmpty())
863  map.insert(QStringLiteral("sidebar-%1").arg(int(loc)), stringListToVariant(dockWidgets));
864  }
865 
866  return map;
867 }
868 
869 void LayoutSaver::MainWindow::fromVariantMap(const QVariantMap &map)
870 {
871  options = KDDockWidgets::MainWindowOptions(map.value(QStringLiteral("options")).toInt());
872  multiSplitterLayout.fromVariantMap(map.value(QStringLiteral("multiSplitterLayout")).toMap());
873  uniqueName = map.value(QStringLiteral("uniqueName")).toString();
874  geometry = Layouting::mapToRect(map.value(QStringLiteral("geometry")).toMap());
875  screenIndex = map.value(QStringLiteral("screenIndex")).toInt();
876  screenSize = Layouting::mapToSize(map.value(QStringLiteral("screenSize")).toMap());
877  isVisible = map.value(QStringLiteral("isVisible")).toBool();
878  affinities = variantToStringList(map.value(QStringLiteral("affinities")).toList());
879  windowState = Qt::WindowState(map.value(QStringLiteral("windowState"), Qt::WindowNoState).toInt());
880 
881  // Compatibility hack. Old json format had a single "affinityName" instead of an "affinities" list:
882  const QString affinityName = map.value(QStringLiteral("affinityName")).toString();
883  if (!affinityName.isEmpty() && !affinities.contains(affinityName)) {
884  affinities.push_back(affinityName);
885  }
886 
887  // Load the SideBars:
888  dockWidgetsPerSideBar.clear();
890  const QVariantList dockWidgets = map.value(QStringLiteral("sidebar-%1").arg(int(loc))).toList();
891  if (!dockWidgets.isEmpty())
892  dockWidgetsPerSideBar.insert(loc, variantToStringList(dockWidgets));
893  }
894 }
895 
896 bool LayoutSaver::MultiSplitter::isValid() const
897 {
898  if (layout.isEmpty())
899  return false;
900 
901  /*if (!size.isValid()) {
902  qWarning() << Q_FUNC_INFO << "Invalid size";
903  return false;
904  }*/
905 
906  return true;
907 }
908 
909 bool LayoutSaver::MultiSplitter::hasSingleDockWidget() const
910 {
911  return frames.size() == 1 && frames.cbegin()->hasSingleDockWidget();
912 }
913 
914 LayoutSaver::DockWidget::Ptr LayoutSaver::MultiSplitter::singleDockWidget() const
915 {
916  if (!hasSingleDockWidget())
917  return {};
918 
919  return frames.cbegin()->singleDockWidget();
920 }
921 
922 bool LayoutSaver::MultiSplitter::skipsRestore() const
923 {
924  return std::all_of(frames.cbegin(), frames.cend(), [](const LayoutSaver::Frame &frame) {
925  return frame.skipsRestore();
926  });
927 }
928 
929 QVariantMap LayoutSaver::MultiSplitter::toVariantMap() const
930 {
931  QVariantMap result;
932  result.insert(QStringLiteral("layout"), layout);
933 
934  QVariantMap framesV;
935  for (auto &frame : frames)
936  framesV.insert(frame.id, frame.toVariantMap());
937 
938  result.insert(QStringLiteral("frames"), framesV);
939  return result;
940 }
941 
942 void LayoutSaver::MultiSplitter::fromVariantMap(const QVariantMap &map)
943 {
944  layout = map.value(QStringLiteral("layout")).toMap();
945  const QVariantMap framesV = map.value(QStringLiteral("frames")).toMap();
946  frames.clear();
947  for (const QVariant &frameV : framesV) {
948  LayoutSaver::Frame frame;
949  frame.fromVariantMap(frameV.toMap());
950  frames.insert(frame.id, frame);
951  }
952 }
953 
954 void LayoutSaver::Position::scaleSizes(const ScalingInfo &scalingInfo)
955 {
956  scalingInfo.applyFactorsTo(/*by-ref*/ lastFloatingGeometry);
957 }
958 
959 QVariantMap LayoutSaver::Position::toVariantMap() const
960 {
961  QVariantMap map;
962  map.insert(QStringLiteral("lastFloatingGeometry"), Layouting::rectToMap(lastFloatingGeometry));
963  map.insert(QStringLiteral("tabIndex"), tabIndex);
964  map.insert(QStringLiteral("wasFloating"), wasFloating);
965  map.insert(QStringLiteral("placeholders"), toVariantList<LayoutSaver::Placeholder>(placeholders));
966 
967  return map;
968 }
969 
970 void LayoutSaver::Position::fromVariantMap(const QVariantMap &map)
971 {
972  lastFloatingGeometry = Layouting::mapToRect(map.value(QStringLiteral("lastFloatingGeometry")).toMap());
973  tabIndex = map.value(QStringLiteral("tabIndex")).toInt();
974  wasFloating = map.value(QStringLiteral("wasFloating")).toBool();
975  placeholders = fromVariantList<LayoutSaver::Placeholder>(map.value(QStringLiteral("placeholders")).toList());
976 }
977 
978 QVariantMap LayoutSaver::ScreenInfo::toVariantMap() const
979 {
980  QVariantMap map;
981  map.insert(QStringLiteral("index"), index);
982  map.insert(QStringLiteral("geometry"), Layouting::rectToMap(geometry));
983  map.insert(QStringLiteral("name"), name);
984  map.insert(QStringLiteral("devicePixelRatio"), devicePixelRatio);
985 
986  return map;
987 }
988 
989 void LayoutSaver::ScreenInfo::fromVariantMap(const QVariantMap &map)
990 {
991  index = map.value(QStringLiteral("index")).toInt();
992  geometry = Layouting::mapToRect(map.value(QStringLiteral("geometry")).toMap());
993  name = map.value(QStringLiteral("name")).toString();
994  devicePixelRatio = map.value(QStringLiteral("devicePixelRatio")).toDouble();
995 }
996 
997 QVariantMap LayoutSaver::Placeholder::toVariantMap() const
998 {
999  QVariantMap map;
1000  map.insert(QStringLiteral("isFloatingWindow"), isFloatingWindow);
1001  map.insert(QStringLiteral("itemIndex"), itemIndex);
1002 
1003  if (isFloatingWindow)
1004  map.insert(QStringLiteral("indexOfFloatingWindow"), indexOfFloatingWindow);
1005  else
1006  map.insert(QStringLiteral("mainWindowUniqueName"), mainWindowUniqueName);
1007 
1008  return map;
1009 }
1010 
1011 void LayoutSaver::Placeholder::fromVariantMap(const QVariantMap &map)
1012 {
1013  isFloatingWindow = map.value(QStringLiteral("isFloatingWindow")).toBool();
1014  indexOfFloatingWindow = map.value(QStringLiteral("indexOfFloatingWindow"), -1).toInt();
1015  itemIndex = map.value(QStringLiteral("itemIndex")).toInt();
1016  mainWindowUniqueName = map.value(QStringLiteral("mainWindowUniqueName")).toString();
1017 }
1018 
1019 LayoutSaver::ScalingInfo::ScalingInfo(const QString &mainWindowId, QRect savedMainWindowGeo, int screenIndex)
1020 {
1021  auto mainWindow = DockRegistry::self()->mainWindowByName(mainWindowId);
1022  if (!mainWindow) {
1023  qWarning() << Q_FUNC_INFO << "Failed to find main window with name" << mainWindowName;
1024  return;
1025  }
1026 
1027  if (!savedMainWindowGeo.isValid() || savedMainWindowGeo.isNull()) {
1028  qWarning() << Q_FUNC_INFO << "Invalid saved main window geometry" << savedMainWindowGeo;
1029  return;
1030  }
1031 
1032  if (!mainWindow->geometry().isValid() || mainWindow->geometry().isNull()) {
1033  qWarning() << Q_FUNC_INFO << "Invalid main window geometry" << mainWindow->geometry();
1034  return;
1035  }
1036 
1037  const int currentScreenIndex = qApp->screens().indexOf(mainWindow->screen());
1038 
1039  this->mainWindowName = mainWindowId;
1040  this->savedMainWindowGeometry = savedMainWindowGeo;
1041  realMainWindowGeometry = mainWindow->window()->geometry(); // window() as our main window might be embedded
1042  widthFactor = double(realMainWindowGeometry.width()) / savedMainWindowGeo.width();
1043  heightFactor = double(realMainWindowGeometry.height()) / savedMainWindowGeo.height();
1044  mainWindowChangedScreen = currentScreenIndex != screenIndex;
1045 }
1046 
1047 void LayoutSaver::ScalingInfo::translatePos(QPoint &pt) const
1048 {
1049  const int deltaX = pt.x() - savedMainWindowGeometry.x();
1050  const int deltaY = pt.y() - savedMainWindowGeometry.y();
1051 
1052  const double newDeltaX = deltaX * widthFactor;
1053  const double newDeltaY = deltaY * heightFactor;
1054 
1055  pt.setX(qCeil(savedMainWindowGeometry.x() + newDeltaX));
1056  pt.setY(qCeil(savedMainWindowGeometry.y() + newDeltaY));
1057 }
1058 
1059 void LayoutSaver::ScalingInfo::applyFactorsTo(QPoint &pt) const
1060 {
1061  translatePos(pt);
1062 }
1063 
1064 void LayoutSaver::ScalingInfo::applyFactorsTo(QSize &sz) const
1065 {
1066  sz.setWidth(int(widthFactor * sz.width()));
1067  sz.setHeight(int(heightFactor * sz.height()));
1068 }
1069 
1070 void LayoutSaver::ScalingInfo::applyFactorsTo(QRect &rect) const
1071 {
1072  if (rect.isEmpty())
1073  return;
1074 
1075  QPoint pos = rect.topLeft();
1076  QSize size = rect.size();
1077 
1078  applyFactorsTo(/*by-ref*/ size);
1079 
1080 
1081  if (!mainWindowChangedScreen) {
1082  // Don't play with floating window position if the main window changed screen.
1083  // There's too many corner cases that push the floating windows out of bounds, and
1084  // we're not even considering monitors with different HDPI. We can support only the simple case.
1085  // For complex cases we'll try to guarantee the window is placed somewhere reasonable.
1086  applyFactorsTo(/*by-ref*/ pos);
1087  }
1088 
1089  rect.moveTopLeft(pos);
1090  rect.setSize(size);
1091 }
1092 
1093 LayoutSaver::Private::RAIIIsRestoring::RAIIIsRestoring()
1094 {
1095  LayoutSaver::Private::s_restoreInProgress = true;
1096 }
1097 
1098 LayoutSaver::Private::RAIIIsRestoring::~RAIIIsRestoring()
1099 {
1100  LayoutSaver::Private::s_restoreInProgress = false;
1101 }
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:310
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
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:419
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
QString::toInt
int toInt(bool *ok, int base) const const
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
QList::cend
QList::const_iterator cend() const const
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:315
QRect::isNull
bool isNull() const const
KDDockWidgets::RestoreOption_None
@ RestoreOption_None
Definition: KDDockWidgets.h:184
KDDockWidgets::SideBarLocation
SideBarLocation
Each main window supports 4 sidebars.
Definition: KDDockWidgets.h:211
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:301
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
QString::insert
QString & insert(int position, QChar ch)
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:185
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

© 2019-2022 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 Mar 7 2022 02:01:20 for KDDockWidgets API Documentation by doxygen 1.8.20