diff --git a/AdvancedDockingSystem/include/ads/ContainerWidget.h b/AdvancedDockingSystem/include/ads/ContainerWidget.h index 82118bf..76787ea 100644 --- a/AdvancedDockingSystem/include/ads/ContainerWidget.h +++ b/AdvancedDockingSystem/include/ads/ContainerWidget.h @@ -102,10 +102,13 @@ private: void addSection(SectionWidget* section); SectionWidget* sectionAt(const QPoint& pos) const; SectionWidget* dropContentOuterHelper(QLayout* l, const InternalContentData& data, Qt::Orientation orientation, bool append); + + // Serialization void saveFloatingWidgets(QDataStream& out) const; void saveSectionWidgets(QDataStream& out, QWidget* widget) const; - bool restoreFloatingWidgets(QDataStream& in, QList& floatings); - bool restoreSectionWidgets(QDataStream& in, QSplitter* currentSplitter, QList& sections); + bool restoreFloatingWidgets(QDataStream& in, int version, QList& floatings); + bool restoreSectionWidgets(QDataStream& in, int version, QSplitter* currentSplitter, QList& sections, QList& contentsToHide); + bool takeContent(const SectionContent::RefPtr& sc, InternalContentData& data); private slots: diff --git a/AdvancedDockingSystem/src/ContainerWidget.cpp b/AdvancedDockingSystem/src/ContainerWidget.cpp index d6ee708..e7232d9 100644 --- a/AdvancedDockingSystem/src/ContainerWidget.cpp +++ b/AdvancedDockingSystem/src/ContainerWidget.cpp @@ -106,7 +106,9 @@ bool ContainerWidget::showSectionContent(const SectionContent::RefPtr& sc) } else { - qDebug() << "TODO Create new SW here and add SC"; + sw = newSectionWidget(); + addSection(sw); + sw->addContent(hsi.data, true); return true; } } @@ -236,6 +238,29 @@ QMenu* ContainerWidget::createContextMenu() const QByteArray ContainerWidget::saveState() const { + /* + # Data Format + + quint32 Magic + quint32 Version + + int Number of floating widgets + LOOP Floating widgets + QString Unique name of content + QByteArray Geometry of floating widget + bool Visibility + + int Number of layout items (Valid values: 0, 1) + IF 0 + int Number of hidden contents + LOOP Contents + QString Unique name of content + ELSEIF 1 + ... todo ... + ENDIF + */ + qDebug() << "Begin save state"; + QByteArray ba; QDataStream out(&ba, QIODevice::WriteOnly); out.setVersion(QDataStream::Qt_4_5); @@ -245,21 +270,67 @@ QByteArray ContainerWidget::saveState() const // Save state of floating contents saveFloatingWidgets(out); - // Walk through layout for splitters - // Well.. there actually shouldn't be more than one - for (int i = 0; i < _mainLayout->count(); ++i) + // Save state of sections and contents + if (_mainLayout->count() <= 0 || _sections.isEmpty()) { - QLayoutItem* li = _mainLayout->itemAt(i); - if (!li->widget()) - continue; - saveSectionWidgets(out, li->widget()); - } + // Looks like the user has hidden all contents and no more sections + // are available. We can simply write a list of all hidden contents. + out << 0; + out << _hiddenSectionContents.count(); + QHashIterator iter(_hiddenSectionContents); + while (iter.hasNext()) + { + iter.next(); + out << iter.value().data.content->uniqueName(); + } + } + else if (_mainLayout->count() == 1) + { + // There should only be one! + out << 1; + QLayoutItem* li = _mainLayout->itemAt(0); + if (!li->widget()) + qWarning() << "Not a widget in _mainLayout, this shouldn't happen."; + else + saveSectionWidgets(out, li->widget()); + + // Safe state of hidden contents, which doesn't have an section association + // or the section association points to a no longer existing section. + QHashIterator iter(_hiddenSectionContents); + int cnt = 0; + while (iter.hasNext()) + { + iter.next(); + if (iter.value().preferredSectionId <= 0 || !SectionWidget::LookupMap.contains(iter.value().preferredSectionId)) + cnt++; + } + out << cnt; + iter.toFront(); + while (iter.hasNext()) + { + iter.next(); + if (iter.value().preferredSectionId <= 0 || !SectionWidget::LookupMap.contains(iter.value().preferredSectionId)) + out << iter.value().data.content->uniqueName(); + } + } + else + { + // More? Oh oh.. something is wrong :-/ + out << -1; + qWarning() << "Oh noooz.. Something went wrong. There are too many items in _mainLayout."; + } + qDebug() << "End save state"; return ba; } bool ContainerWidget::restoreState(const QByteArray& data) { + if (data.isEmpty()) + return false; + + qDebug() << "Begin to restore state"; + QDataStream in(data); in.setVersion(QDataStream::Qt_4_5); @@ -278,18 +349,75 @@ bool ContainerWidget::restoreState(const QByteArray& data) // Restore floating widgets QList floatings; - bool success = restoreFloatingWidgets(in, floatings); + bool success = restoreFloatingWidgets(in, version, floatings); if (!success) { qWarning() << "Could not restore floatings completely"; } - // Restore splitters and section widgets + // Restore splitters, sections and contents QList sections; - success = restoreSectionWidgets(in, NULL, sections); - if (!success) + QList contentsToHide; + + int mode = 0; + in >> mode; + if (mode == 0) { - qWarning() << "Could not restore sections completely"; + // List of hidden contents. There are no sections at all. + int cnt = 0; + in >> cnt; + + // Create dummy section, required to call hideSectionContent() later. + SectionWidget* sw = new SectionWidget(this); + sections.append(sw); + + for (int i = 0; i < cnt; ++i) + { + QString uname; + in >> uname; + + const SectionContent::RefPtr sc = SectionContent::LookupMapByName.value(uname); + if (!sc) + continue; + + InternalContentData data; + if (!takeContent(sc, data)) + qFatal("This should never happen!!!"); + + sw->addContent(data, false); + contentsToHide.append(sc); + } + } + else if (mode == 1) + { + success = restoreSectionWidgets(in, version, NULL, sections, contentsToHide); + if (!success) + qWarning() << "Could not restore sections completely"; + + // Restore lonely hidden contents + int cnt = 0; + in >> cnt; + for (int i = 0; i < cnt; ++i) + { + QString uname; + in >> uname; + const SectionContent::RefPtr sc = SectionContent::LookupMapByName.value(uname); + if (!sc) + continue; + + InternalContentData data; + if (!takeContent(sc, data)) + qFatal("This should never happen!!!"); + + SectionWidget* sw = NULL; + if (sections.size() <= 0) + qFatal("This should never happen, because above a section should have been created."); + else + sw = sections.first(); + + sw->addContent(data, false); + contentsToHide.append(sc); + } } // Handle SectionContent which is not mentioned by deserialized data. @@ -305,6 +433,8 @@ bool ContainerWidget::restoreState(const QByteArray& data) for (int i = 0; i < sections.count(); ++i) for (int j = 0; j < sections.at(i)->contents().count(); ++j) contents.append(sections.at(i)->contents().at(j)); + for (int i = 0; i < contentsToHide.count(); ++i) + contents.append(contentsToHide.at(i)); // Compare restored contents with available contents const QList allContents = SectionContent::LookupMap.values(); @@ -319,22 +449,27 @@ bool ContainerWidget::restoreState(const QByteArray& data) } // What should we do with a drunken sailor.. what should.. erm.. - // .. we do with the left-contents? - // We might need to add them into the hidden-list, if no other sections are available + // What should we do with the left-contents? + // Lets add them to the first found SW or create one, if no SW is available. for (int i = 0; i < leftContents.count(); ++i) { const SectionContent::RefPtr sc = leftContents.at(i); - InternalContentData data; - this->takeContent(sc, data); + SectionWidget* sw = NULL; if (sections.isEmpty()) { - HiddenSectionItem hsi; - hsi.data = data; - _hiddenSectionContents.insert(sc->uid(), hsi); + sw = new SectionWidget(this); + sections.append(sw); + addSection(sw); } else - sections.first()->addContent(sc); + sw = sections.first(); + + InternalContentData data; + if (!takeContent(sc, data)) + sw->addContent(sc); + else + sw->addContent(data, false); } } @@ -348,6 +483,11 @@ bool ContainerWidget::restoreState(const QByteArray& data) qDeleteAll(oldFloatings); qDeleteAll(oldSections); + // Hide all as "hidden" marked contents + for (int i = 0; i < contentsToHide.count(); ++i) + hideSectionContent(contentsToHide.at(i)); + + qDebug() << "End of restore state" << success; return success; } @@ -632,6 +772,15 @@ void ContainerWidget::saveSectionWidgets(QDataStream& out, QWidget* widget) cons } else if ((sw = dynamic_cast(widget)) != NULL) { + // Format (version 1) + // int Object type (SectionWidget=2) + // int Current active index + // int Number of contents (visible + hidden) + // LOOP Contents of section (last int) + // QString Unique name of SectionContent + // bool Visibility + // int Preferred index + const QList& contents = sw->contents(); QList hiddenContents; @@ -664,8 +813,10 @@ void ContainerWidget::saveSectionWidgets(QDataStream& out, QWidget* widget) cons } } -bool ContainerWidget::restoreFloatingWidgets(QDataStream& in, QList& floatings) +bool ContainerWidget::restoreFloatingWidgets(QDataStream& in, int version, QList& floatings) { + Q_UNUSED(version) + int fwCount = 0; in >> fwCount; if (fwCount <= 0) @@ -701,8 +852,11 @@ bool ContainerWidget::restoreFloatingWidgets(QDataStream& in, QList& sections) +bool ContainerWidget::restoreSectionWidgets(QDataStream& in, int version, QSplitter* currentSplitter, QList& sections, QList& contentsToHide) { + if (in.atEnd()) + return true; + int type; in >> type; @@ -716,7 +870,7 @@ bool ContainerWidget::restoreSectionWidgets(QDataStream& in, QSplitter* currentS QSplitter* sp = newSplitter((Qt::Orientation) orientation); for (int i = 0; i < count; ++i) { - if (!restoreSectionWidgets(in, sp, sections)) + if (!restoreSectionWidgets(in, version, sp, sections, contentsToHide)) return false; } if (sp->count() <= 0) @@ -762,19 +916,18 @@ bool ContainerWidget::restoreSectionWidgets(QDataStream& in, QSplitter* currentS qWarning() << "Can not find SectionContent:" << uname; continue; } - InternalContentData data; - this->takeContent(sc, data); - if (visible) - sw->addContent(sc); - else + InternalContentData data; + if (!takeContent(sc, data)) { - HiddenSectionItem hsi; - hsi.preferredSectionId = sw->uid(); - hsi.preferredSectionIndex = preferredIndex; - hsi.data = data; - _hiddenSectionContents.insert(sc->uid(), hsi); + qCritical() << "Can not find InternalContentData of SC, this should never happen!" << sc->uid() << sc->uniqueName(); + sw->addContent(sc); } + else + sw->addContent(data, false); + + if (!visible) + contentsToHide.append(sc); } if (sw->contents().isEmpty()) { diff --git a/AdvancedDockingSystem/src/FloatingWidget.cpp b/AdvancedDockingSystem/src/FloatingWidget.cpp index a478483..4bc2dc8 100644 --- a/AdvancedDockingSystem/src/FloatingWidget.cpp +++ b/AdvancedDockingSystem/src/FloatingWidget.cpp @@ -63,9 +63,11 @@ bool FloatingWidget::takeContent(InternalContentData& data) data.contentWidget = _contentWidget; _titleLayout->removeWidget(_titleWidget); + _titleWidget->setParent(_container); _titleWidget = NULL; layout()->removeWidget(_contentWidget); + _contentWidget->setParent(_container); _contentWidget = NULL; return true; diff --git a/AdvancedDockingSystem/src/SectionContentWidget.cpp b/AdvancedDockingSystem/src/SectionContentWidget.cpp index 60db301..2f44849 100644 --- a/AdvancedDockingSystem/src/SectionContentWidget.cpp +++ b/AdvancedDockingSystem/src/SectionContentWidget.cpp @@ -9,6 +9,8 @@ SectionContentWidget::SectionContentWidget(SectionContent::RefPtr c, QWidget* pa QFrame(parent), _content(c) { + qDebug() << Q_FUNC_INFO; + QBoxLayout* l = new QBoxLayout(QBoxLayout::TopToBottom); l->setContentsMargins(0, 0, 0, 0); l->setSpacing(0); diff --git a/AdvancedDockingSystem/src/SectionTitleWidget.cpp b/AdvancedDockingSystem/src/SectionTitleWidget.cpp index 28434a0..6753beb 100644 --- a/AdvancedDockingSystem/src/SectionTitleWidget.cpp +++ b/AdvancedDockingSystem/src/SectionTitleWidget.cpp @@ -31,6 +31,8 @@ SectionTitleWidget::SectionTitleWidget(SectionContent::RefPtr content, QWidget* _tabMoving(false), _activeTab(false) { + qDebug() << Q_FUNC_INFO; + QBoxLayout* l = new QBoxLayout(QBoxLayout::LeftToRight); l->setContentsMargins(0, 0, 0, 0); l->setSpacing(0); diff --git a/AdvancedDockingSystem/src/SectionWidget.cpp b/AdvancedDockingSystem/src/SectionWidget.cpp index 7be3931..1d94e16 100644 --- a/AdvancedDockingSystem/src/SectionWidget.cpp +++ b/AdvancedDockingSystem/src/SectionWidget.cpp @@ -159,6 +159,9 @@ void SectionWidget::addContent(const InternalContentData& data, bool autoActivat // Switch to newest. else if (autoActivate) setCurrentIndex(_contents.count() - 1); + // Mark is as inactive tab. + else + data.titleWidget->setActiveTab(false); // or: setCurrentIndex(currentIndex()) } bool SectionWidget::takeContent(int uid, InternalContentData& data) @@ -265,8 +268,14 @@ void SectionWidget::moveContent(int from, int to) void SectionWidget::setCurrentIndex(int index) { - // Set active TAB. - qDebug() << Q_FUNC_INFO << index; + if (index < 0 || index > _contents.count() - 1) + { + qWarning() << Q_FUNC_INFO << "Invalid index" << index; + return; + } + qDebug() << Q_FUNC_INFO << index << QString("section=%1; content=%2").arg(_uid).arg(_contents.at(index)->uniqueName()); + + // Set active TAB for (int i = 0; i < _tabsLayout->count(); ++i) { QLayoutItem* item = _tabsLayout->itemAt(i); @@ -283,7 +292,7 @@ void SectionWidget::setCurrentIndex(int index) } } - // Set active CONTENT. + // Set active CONTENT _contentsLayout->setCurrentIndex(index); } diff --git a/README.md b/README.md index 8edcfe4..6114d7c 100644 --- a/README.md +++ b/README.md @@ -37,10 +37,13 @@ Sorted by priority - [x] Restore: Manage new or deleted SectionContent objects, which are not available - [x] Working with outer-edge-drops sometimes leaves empty splitters #BUG - [x] Clean up of unused e.g. count()<=1 QSplitters doesn't work well #BUG -- [ ] Show close button on right corner of SectionWidget. How to safe last section position? -- [ ] Serialize state of `_hiddenSectionContents` +- [x] Show close button on right corner of SectionWidget. How to safe last section position? +- [x] Serialize state of `_hiddenSectionContents` +- [ ] It would be easier when the SectionTitleWidget and SectionContentWidget are created inside the "SectionContent::newSectionContent(..)" method. + This would make sure, that those two objects always exists. - [ ] `ContainerWidget::showSectionContent` needs to insert the SC at the correct preferred position of SW - [ ] Empty splitters, if only 2 or 1 items are in container +- [ ] Restore: Handle out-of-screen geometry for floating widgets ### Some day... - [ ] Drop indicator images should be fully visible over the DropOverlay rectangle