diff --git a/demo/mainwindow.cpp b/demo/mainwindow.cpp index 0c436ac..32d5383 100644 --- a/demo/mainwindow.cpp +++ b/demo/mainwindow.cpp @@ -2,6 +2,8 @@ #include "ui_mainwindow.h" +#include + #include #include #include @@ -10,6 +12,7 @@ #include #include #include +#include #include "DockManager.h" #include "DockWidget.h" @@ -40,6 +43,7 @@ static ads::CDockWidget* createLongTextLabelDockWidget(QMenu* ViewMenu) ads::CDockWidget* DockWidget = new ads::CDockWidget(QString("Label %1").arg(LabelCount++)); DockWidget->setWidget(l); + DockWidget->setObjectName(DockWidget->windowTitle()); ViewMenu->addAction(DockWidget->toggleViewAction()); return DockWidget; } @@ -50,6 +54,7 @@ static ads::CDockWidget* createCalendarDockWidget(QMenu* ViewMenu) QCalendarWidget* w = new QCalendarWidget(); ads::CDockWidget* DockWidget = new ads::CDockWidget(QString("Calendar %1").arg(CalendarCount++)); DockWidget->setWidget(w); + DockWidget->setObjectName(DockWidget->windowTitle()); ViewMenu->addAction(DockWidget->toggleViewAction()); return DockWidget; } @@ -64,6 +69,7 @@ static ads::CDockWidget* createFileSystemTreeDockWidget(QMenu* ViewMenu) w->setModel(m); ads::CDockWidget* DockWidget = new ads::CDockWidget(QString("Filesystem %1").arg(FileSystemCount++)); DockWidget->setWidget(w); + DockWidget->setObjectName(DockWidget->windowTitle()); ViewMenu->addAction(DockWidget->toggleViewAction()); return DockWidget; } @@ -113,3 +119,29 @@ void MainWindow::createContent() } +void MainWindow::closeEvent(QCloseEvent* event) +{ + QSettings Settings("Settings.ini", QSettings::IniFormat); + Settings.setValue("mainWindow/Geometry", saveGeometry()); + Settings.setValue("mainWindow/DockingState", m_DockManager->saveState()); + QMainWindow::closeEvent(event); +} + + +void MainWindow::on_actionSaveState_triggered(bool) +{ + std::cout << "MainWindow::on_actionSaveState_triggered" << std::endl; + QSettings Settings("Settings.ini", QSettings::IniFormat); + Settings.setValue("mainWindow/Geometry", saveGeometry()); + Settings.setValue("mainWindow/DockingState", m_DockManager->saveState()); +} + + +void MainWindow::on_actionRestoreState_triggered(bool) +{ + std::cout << "MainWindow::on_actionRestoreState_triggered" << std::endl; + QSettings Settings("Settings.ini", QSettings::IniFormat); + restoreGeometry(Settings.value("mainWindow/Geometry").toByteArray()); + m_DockManager->restoreState(Settings.value("mainWindow/DockingState").toByteArray()); +} + diff --git a/demo/mainwindow.h b/demo/mainwindow.h index 8a30425..0cec326 100644 --- a/demo/mainwindow.h +++ b/demo/mainwindow.h @@ -11,7 +11,8 @@ class MainWindow; class MainWindow : public QMainWindow { Q_OBJECT - +protected: + virtual void closeEvent(QCloseEvent* event) override; public: explicit MainWindow(QWidget *parent = 0); virtual ~MainWindow(); @@ -20,6 +21,10 @@ private: Ui::MainWindow *ui; ads::CDockManager* m_DockManager; void createContent(); + +private slots: + void on_actionSaveState_triggered(bool); + void on_actionRestoreState_triggered(bool); }; #endif // MAINWINDOW_H diff --git a/demo/mainwindow.ui b/demo/mainwindow.ui index 8a80726..11d4336 100644 --- a/demo/mainwindow.ui +++ b/demo/mainwindow.ui @@ -29,6 +29,8 @@ File + + @@ -49,6 +51,16 @@ Exit + + + Save State + + + + + Restore State + + diff --git a/src/DockAreaWidget.cpp b/src/DockAreaWidget.cpp index 0f57778..b16a04e 100644 --- a/src/DockAreaWidget.cpp +++ b/src/DockAreaWidget.cpp @@ -706,6 +706,17 @@ void CDockAreaWidget::onDockWidgetViewToggled(bool Open) auto DockWidget = dynamic_cast(sender()); } + +//============================================================================ +void CDockAreaWidget::saveState(QDataStream& stream) const +{ + stream << d->ContentsLayout->count() << d->ContentsLayout->currentIndex(); + for (int i = 0; i < d->ContentsLayout->count(); ++i) + { + dockWidget(i)->saveState(stream); + } +} + } // namespace ads //--------------------------------------------------------------------------- diff --git a/src/DockAreaWidget.h b/src/DockAreaWidget.h index d95bc44..b344ce1 100644 --- a/src/DockAreaWidget.h +++ b/src/DockAreaWidget.h @@ -161,6 +161,11 @@ public: */ void setCurrentDockWidget(CDockWidget* DockWidget); + /** + * Saves the state into the given stream + */ + void saveState(QDataStream& Stream) const; + public slots: /** * This sets the index position of the current tab page. diff --git a/src/DockContainerWidget.cpp b/src/DockContainerWidget.cpp index 21743d2..8cc6fa9 100644 --- a/src/DockContainerWidget.cpp +++ b/src/DockContainerWidget.cpp @@ -41,6 +41,7 @@ #include "DockWidget.h" #include "FloatingDockContainer.h" #include "DockOverlay.h" +#include "DockStateSerialization.h" #include "ads_globals.h" #include @@ -113,6 +114,16 @@ struct DockContainerWidgetPrivate * Adds new dock areas to the internal dock area list */ void addDockAreasToList(const QList NewDockAreas); + + /** + * Save state of child nodes + */ + void saveChildNodesState(QDataStream& Stream, QWidget* Widget); + + /** + * Restore state of child nodes + */ + void restoreChildNodes(QDataStream& Stream, QWidget* Parent); }; // struct DockContainerWidgetPrivate @@ -284,6 +295,74 @@ void DockContainerWidgetPrivate::addDockAreasToList(const QList(Widget); + if (Splitter) + { + stream << NodeSplitter << Splitter->orientation() << Splitter->count(); + std::cout << "NodeSplitter " << Splitter->orientation() << std::endl; + for (int i = 0; i < Splitter->count(); ++i) + { + saveChildNodesState(stream, Splitter->widget(i)); + } + } + else + { + CDockAreaWidget* DockArea = dynamic_cast(Widget); + if (DockArea) + { + std::cout << "NodeDockArea " << std::endl; + DockArea->saveState(stream); + } + } +} + + +//============================================================================ +void DockContainerWidgetPrivate::restoreChildNodes(QDataStream& stream, QWidget* Parent) +{ + int NodeType; + stream >> NodeType; + QSplitter* ParentSplitter = dynamic_cast(Parent); + if (NodeSplitter == NodeType) + { + int Orientation; + int Count; + stream >> Orientation >> Count; + std::cout << "Restore NodeSplitter " << Orientation << std::endl; + QSplitter* Splitter = internal::newSplitter((Qt::Orientation)Orientation); + if (ParentSplitter) + { + ParentSplitter->addWidget(Splitter); + } + else + { + Parent->layout()->addWidget(Splitter); + } + for (int i = 0; i < Count; ++i) + { + restoreChildNodes(stream, Splitter); + } + } + else + { + std::cout << "Restore NodeDockArea " << std::endl; + CDockAreaWidget* DockArea = new CDockAreaWidget(DockManager, _this); + if (ParentSplitter) + { + ParentSplitter->addWidget(DockArea); + } + else + { + Parent->layout()->addWidget(DockArea); + } + DockAreas.append(DockArea); + } +} + + //============================================================================ CDockAreaWidget* DockContainerWidgetPrivate::dockWidgetIntoContainer(DockWidgetArea area, CDockWidget* Dockwidget) @@ -301,7 +380,7 @@ void DockContainerWidgetPrivate::addDockArea(CDockAreaWidget* NewDockArea, DockW auto InsertParam = internal::dockAreaInsertParameters(area); if (DockAreas.isEmpty()) { - Layout->addWidget(NewDockArea, 0, 0); + _this->layout()->addWidget(NewDockArea); } else if (DockAreas.count() == 1) { @@ -604,6 +683,51 @@ QList CDockContainerWidget::openedDockAreas() const } +//============================================================================ +void CDockContainerWidget::saveState(QDataStream& stream) const +{ + std::cout << "CDockContainerWidget::saveState" << std::endl; + stream << isFloating(); + if (isFloating()) + { + CFloatingDockContainer* FloatingWidget = internal::findParent(this); + stream << FloatingWidget->saveGeometry(); + } + + QWidget* RootChild = d->Layout->itemAt(0)->widget(); + if (RootChild) + { + d->saveChildNodesState(stream, RootChild); + } +} + + +//============================================================================ +bool CDockContainerWidget::restoreState(QDataStream& stream) +{ + bool IsFloating; + stream >> IsFloating; + if (isFloating()) + { + std::cout << "Restore floating widget" << std::endl; + CFloatingDockContainer* FloatingWidget = internal::findParent(this); + QByteArray Geometry; + stream >> Geometry; + FloatingWidget->restoreGeometry(Geometry); + FloatingWidget->show(); + } + + QWidget* RootChild = d->Layout->itemAt(0)->widget(); + if (RootChild) + { + d->DockAreas.clear(); + delete RootChild; + } + d->restoreChildNodes(stream, this); + return true; +} + + } // namespace ads //--------------------------------------------------------------------------- diff --git a/src/DockContainerWidget.h b/src/DockContainerWidget.h index 7688111..278a504 100644 --- a/src/DockContainerWidget.h +++ b/src/DockContainerWidget.h @@ -134,6 +134,16 @@ public: */ bool isFloating() const; + /** + * Saves the state into the given stream + */ + void saveState(QDataStream& Stream) const; + + /** + * Restores the state from given stream + */ + bool restoreState(QDataStream& Stream); + signals: /** * This signal is emitted if one or multiple dock areas has been added to diff --git a/src/DockManager.cpp b/src/DockManager.cpp index 6aab8b1..e40766f 100644 --- a/src/DockManager.cpp +++ b/src/DockManager.cpp @@ -40,6 +40,13 @@ namespace ads { + +// sentinel values used to validate state data +enum VersionMarkers +{ + VersionMarker = 0xff +}; + /** * Private data class of CDockManager class (pimpl) */ @@ -55,6 +62,11 @@ struct DockManagerPrivate * Private data constructor */ DockManagerPrivate(CDockManager* _public); + + /** + * Restores a non existing container from stream + */ + bool restoreContainer(QDataStream& Stream); }; // struct DockManagerPrivate @@ -66,6 +78,14 @@ DockManagerPrivate::DockManagerPrivate(CDockManager* _public) : } +//============================================================================ +bool DockManagerPrivate::restoreContainer(QDataStream& Stream) +{ + std::cout << "restoreContainer" << std::endl; + CFloatingDockContainer* FloatingWidget = new CFloatingDockContainer(_this); +} + + //============================================================================ CDockManager::CDockManager(QWidget *parent) : CDockContainerWidget(this, parent), @@ -160,6 +180,61 @@ unsigned int CDockManager::zOrderIndex() const { return 0; } + + +//============================================================================ +QByteArray CDockManager::saveState(int version) const +{ + QByteArray data; + QDataStream stream(&data, QIODevice::WriteOnly); + stream << VersionMarker; + stream << version; + + stream << d->Containers.count(); + for (auto Container : d->Containers) + { + Container->saveState(stream); + } + return data; +} + + +//============================================================================ +bool CDockManager::restoreState(const QByteArray &state, int version) +{ + if (state.isEmpty()) + { + return false; + } + QByteArray sd = state; + QDataStream stream(&sd, QIODevice::ReadOnly); + + int marker; + int v; + stream >> marker; + stream >> v; + + if (stream.status() != QDataStream::Ok || marker != VersionMarker || v != version) + { + return false; + } + + int ContainerCount; + stream >> ContainerCount; + std::cout << "ContainerCount " << ContainerCount << std::endl; + for (int i = 0; i < ContainerCount; ++i) + { + if (i >= d->Containers.count()) + { + CFloatingDockContainer* FloatingWidget = new CFloatingDockContainer(this); + } + + std::cout << "d->Containers[i]->restoreState " << i << std::endl; + d->Containers[i]->restoreState(stream); + } + + return true; +} } // namespace ads //--------------------------------------------------------------------------- diff --git a/src/DockManager.h b/src/DockManager.h index 37dba49..5192646 100644 --- a/src/DockManager.h +++ b/src/DockManager.h @@ -114,6 +114,20 @@ public: * any floating widget */ virtual unsigned int zOrderIndex() const; + + /** + * Saves the current state of the dockmanger and all its dock widgets + */ + QByteArray saveState(int version = 0) const; + + /** + * Restores the state of this dockmanagers dockwidgets. + * The version number is compared with that stored in state. If they do + * not match, the dockmanager's state is left unchanged, and this function + * returns false; otherwise, the state is restored, and this function + * returns true. + */ + bool restoreState(const QByteArray &state, int version = 0); }; // class DockManager } // namespace ads //----------------------------------------------------------------------------- diff --git a/src/DockStateSerialization.cpp b/src/DockStateSerialization.cpp new file mode 100644 index 0000000..e69de29 diff --git a/src/DockStateSerialization.h b/src/DockStateSerialization.h new file mode 100644 index 0000000..698f6e5 --- /dev/null +++ b/src/DockStateSerialization.h @@ -0,0 +1,44 @@ +#ifndef DockStateSerializationH +#define DockStateSerializationH +/******************************************************************************* +** QtAdcancedDockingSystem +** Copyright (C) 2017 Uwe Kindler +** +** This program is free software: you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +******************************************************************************/ + + +//============================================================================ +/// \file DockStateSerialization.h +/// \author Uwe Kindler +/// \date 26.02.2017 +/// \brief +//============================================================================ + + +//============================================================================ +// INCLUDES +//============================================================================ + + +namespace ads +{ +enum eDockTreeNodeType +{ + NodeSplitter, + NodeDockArea +}; +} // namespace ads +//----------------------------------------------------------------------------- +#endif // DockManagerH diff --git a/src/DockWidget.cpp b/src/DockWidget.cpp index 4dcb2e3..30b82e3 100644 --- a/src/DockWidget.cpp +++ b/src/DockWidget.cpp @@ -63,23 +63,12 @@ struct DockWidgetPrivate CDockAreaWidget* DockArea = nullptr; QAction* ToggleViewAction; bool Closed = false; - struct CapturedState - { - QString DockTreePosition; - QRect GlobalGeometry; - QPointer DockContainer; - } CapturedState; /** * Private data constructor */ DockWidgetPrivate(CDockWidget* _public); - /** - * Saves the current state into CapturedState variable - */ - void capturedState(); - /** * Show dock widget */ @@ -116,40 +105,6 @@ DockWidgetPrivate::DockWidgetPrivate(CDockWidget* _public) : } -//============================================================================ -void DockWidgetPrivate::capturedState() -{ - QString DockTreePosition; - QTextStream stream(&DockTreePosition); - - QPoint GlobalTopLeft = _this->mapToGlobal(_this->geometry().topLeft()); - QRect Rect(GlobalTopLeft, _this->geometry().size()); - CapturedState.GlobalGeometry = Rect; - CapturedState.DockContainer = _this->dockContainer(); - - QWidget* Widget = DockArea; - QSplitter* splitter = internal::findParent(Widget); - QStack SplitterData; - while (splitter) - { - SplitterData.push(QString("%1%2") - .arg((splitter->orientation() == Qt::Horizontal) ? "H" : "V") - .arg(splitter->indexOf(Widget))); - Widget = splitter; - splitter = internal::findParent(Widget); - } - - QString Separator; - while (!SplitterData.isEmpty()) - { - stream << Separator << SplitterData.pop(); - Separator = " "; - } - this->CapturedState.DockTreePosition = DockTreePosition; - std::cout << "SerializedPosition: " << DockTreePosition.toStdString() << std::endl; -} - - //============================================================================ void DockWidgetPrivate::showDockWidget() { @@ -398,6 +353,15 @@ void CDockWidget::setDockArea(CDockAreaWidget* DockArea) } +//============================================================================ +void CDockWidget::saveState(QDataStream& stream) const +{ + std::cout << "CDockWidget::saveState " << objectName().toStdString() + << " closed " << d->Closed << std::endl; + stream << objectName() << d->Closed; +} + + } // namespace ads //--------------------------------------------------------------------------- diff --git a/src/DockWidget.h b/src/DockWidget.h index 5911b94..424eadf 100644 --- a/src/DockWidget.h +++ b/src/DockWidget.h @@ -75,6 +75,11 @@ protected: */ void setToggleViewActionChecked(bool Checked); + /** + * Saves the state into the given stream + */ + void saveState(QDataStream& Stream) const; + public: enum DockWidgetFeature { @@ -167,7 +172,6 @@ public: */ QAction* toggleViewAction() const; - public slots: /** * This property controls whether the dock widget is open or closed. diff --git a/src/FloatingDockContainer.h b/src/FloatingDockContainer.h index 0841a56..38caf37 100644 --- a/src/FloatingDockContainer.h +++ b/src/FloatingDockContainer.h @@ -55,11 +55,6 @@ private slots: void onDockAreasAddedOrRemoved(); void onDockAreaCurrentChanged(int Index); -protected: - /** - * Private constructor that is called from public constructors - */ - CFloatingDockContainer(CDockManager* DockManager); protected: // reimplements QWidget virtual void changeEvent(QEvent *event) override; @@ -71,6 +66,11 @@ protected: // reimplements QWidget virtual bool eventFilter(QObject *watched, QEvent *event) override; public: + /** + * Create empty flatingb widget - required for restore state + */ + CFloatingDockContainer(CDockManager* DockManager); + /** * Create floating widget with the given dock area */