diff --git a/src/DockWidgetBase.cpp b/src/DockWidgetBase.cpp index c71089cc..89178bd0 100644 --- a/src/DockWidgetBase.cpp +++ b/src/DockWidgetBase.cpp @@ -544,7 +544,9 @@ void DockWidgetBase::Private::restoreToPreviousPosition() return; } - m_lastPosition.layoutItem()->restorePlaceholder(q, m_lastPosition.m_tabIndex); + MultiSplitterLayout *layout = DockRegistry::self()->layoutForItem(m_lastPosition.layoutItem()); + Q_ASSERT(layout); + layout->restorePlaceholder(m_lastPosition.layoutItem(), m_lastPosition.m_tabIndex); } void DockWidgetBase::Private::maybeRestoreToPreviousPosition() @@ -560,7 +562,7 @@ void DockWidgetBase::Private::maybeRestoreToPreviousPosition() Frame *frame = q->frame(); - if (frame && frame->parentWidget() == layoutItem->parentWidget()) { + if (frame && frame->parentWidget() == DockRegistry::self()->layoutForItem(layoutItem)->multiSplitter()) { // There's a frame already. Means the DockWidget was hidden instead of closed. // Nothing to do, the dock widget will simply be shown qCDebug(placeholder) << Q_FUNC_INFO << "Already had frame."; diff --git a/src/LayoutSaver.cpp b/src/LayoutSaver.cpp index 8f1d4d59..5dfe4ddb 100644 --- a/src/LayoutSaver.cpp +++ b/src/LayoutSaver.cpp @@ -248,7 +248,7 @@ bool LayoutSaver::restoreLayout(const QByteArray &data) layout.scaleSizes(); // Hide all dockwidgets and unparent them from any layout before starting restore - d->m_dockRegistry->clear(d->m_affinityNames, /*deleteStaticAnchors=*/true); + d->m_dockRegistry->clear(d->m_affinityNames); // 1. Restore main windows for (const LayoutSaver::MainWindow &mw : qAsConst(layout.mainWindows)) { @@ -490,22 +490,12 @@ LayoutSaver::MainWindow LayoutSaver::Layout::mainWindowForIndex(int index) const return mainWindows.at(index); } -bool LayoutSaver::Item::isValid(const LayoutSaver::MultiSplitterLayout &layout) const +bool LayoutSaver::Item::isValid(const LayoutSaver::MultiSplitterLayout &) const { if (!frame.isValid()) return false; - const int numAnchors = layout.anchors.size(); - - if (indexOfLeftAnchor < 0 || indexOfTopAnchor < 0 || - indexOfBottomAnchor < 0 || indexOfRightAnchor < 0 || - indexOfLeftAnchor >= numAnchors || indexOfTopAnchor >= numAnchors || - indexOfBottomAnchor >= numAnchors || indexOfRightAnchor >= numAnchors) { - qWarning() << Q_FUNC_INFO << "Invalid anchor indexes" - << indexOfLeftAnchor << indexOfTopAnchor - << indexOfBottomAnchor << indexOfRightAnchor; - return false; - } + // TODO return true; } @@ -646,120 +636,6 @@ void LayoutSaver::DockWidget::fromVariantMap(const QVariantMap &map) lastPosition.fromVariantMap(map.value(QStringLiteral("lastPosition")).toMap()); } -bool LayoutSaver::Anchor::isValid(const LayoutSaver::MultiSplitterLayout &layout) const -{ - const bool isStatic = type != KDDockWidgets::Anchor::Type_None; - const bool isFollowing = indexOfFollowee != -1; - const int numAnchors = layout.anchors.size(); - - if (!geometry.isValid() && !isStatic && !isFollowing) { - qWarning() << Q_FUNC_INFO << "Invalid geometry" << geometry; - return false; - } - - if (indexOfFrom < 0 || indexOfTo < 0 || indexOfFrom == indexOfTo || - indexOfTo >= numAnchors || indexOfFrom >= numAnchors) { - qWarning() << Q_FUNC_INFO << "Invalid indexes" << indexOfFrom << indexOfTo; - return false; - } - - auto &anchorTo = layout.anchors[indexOfTo]; - auto &anchorFrom = layout.anchors[indexOfFrom]; - if (anchorTo.orientation != anchorFrom.orientation || anchorTo.orientation == orientation) { - qWarning() << Q_FUNC_INFO << "Invalid orientation" << anchorTo.orientation << anchorFrom.orientation - << orientation; - return false; - } - - if (orientation != Qt::Vertical && orientation != Qt::Horizontal) { - qWarning() << Q_FUNC_INFO << "Invalid orientation" << orientation; - return false; - } - - if (type != KDDockWidgets::Anchor::Type_None && - type != KDDockWidgets::Anchor::Type_LeftStatic && - type != KDDockWidgets::Anchor::Type_RightStatic && - type != KDDockWidgets::Anchor::Type_TopStatic && - type != KDDockWidgets::Anchor::Type_BottomStatic) { - qWarning() << Q_FUNC_INFO << "Invalid type" << type; - return false; - } - - if (!isStatic && !isFollowing && (side1Items.isEmpty() || side2Items.isEmpty())) { - qWarning() << Q_FUNC_INFO << "Anchor should have items on both sides"; - return false; - } - - return true; -} - -QVariantMap LayoutSaver::Anchor::toVariantMap() const -{ - QVariantMap map; - map.insert(QStringLiteral("objectName"), objectName); - map.insert(QStringLiteral("geometry"), rectToMap(geometry)); - map.insert(QStringLiteral("orientation"), orientation); - map.insert(QStringLiteral("type"), type); - map.insert(QStringLiteral("indexOfFrom"), indexOfFrom); - map.insert(QStringLiteral("indexOfTo"), indexOfTo); - map.insert(QStringLiteral("indexOfFollowee"), indexOfFollowee); - map.insert(QStringLiteral("positionPercentage"), positionPercentage); - - QVariantList side1ItemsV; - QVariantList side2ItemsV; - side1ItemsV.reserve(side1Items.size()); - side2ItemsV.reserve(side2Items.size()); - for (int index : qAsConst(side1Items)) - side1ItemsV.push_back(index); - for (int index : qAsConst(side2Items)) - side2ItemsV.push_back(index); - - map.insert(QStringLiteral("side1Items"), side1ItemsV); - map.insert(QStringLiteral("side2Items"), side2ItemsV); - - return map; -} - -void LayoutSaver::Anchor::fromVariantMap(const QVariantMap &map) -{ - objectName = map.value(QStringLiteral("objectName")).toString(); - geometry = mapToRect(map.value(QStringLiteral("geometry")).toMap()); - - orientation = map.value(QStringLiteral("orientation")).toInt(); - type = map.value(QStringLiteral("type")).toInt(); - indexOfFrom = map.value(QStringLiteral("indexOfFrom")).toInt(); - indexOfTo = map.value(QStringLiteral("indexOfTo")).toInt(); - indexOfFollowee = map.value(QStringLiteral("indexOfFollowee")).toInt(); - positionPercentage = map.value(QStringLiteral("positionPercentage")).toDouble(); - - side1Items.clear(); - side2Items.clear(); - const QVariantList side1ItemsV = map.value(QStringLiteral("side1Items")).toList(); - const QVariantList side2ItemsV = map.value(QStringLiteral("side2Items")).toList(); - side1Items.reserve(side1ItemsV.size()); - side2Items.reserve(side2ItemsV.size()); - for (const QVariant &v : side1ItemsV) - side1Items.push_back(v.toInt()); - for (const QVariant &v : side2ItemsV) - side2Items.push_back(v.toInt()); -} - -void LayoutSaver::Anchor::scaleSizes(const ScalingInfo &scalingInfo) -{ - const QPoint pos = geometry.topLeft(); - - if (isVertical()) { - geometry.moveLeft(int(pos.x() * scalingInfo.widthFactor)); - } else { - geometry.moveTop(int(pos.y() * scalingInfo.heightFactor)); - } -} - -bool LayoutSaver::Anchor::isVertical() const -{ - return orientation == Qt::Vertical; -} - bool LayoutSaver::FloatingWindow::isValid() const { if (!multiSplitterLayout.isValid()) @@ -870,11 +746,6 @@ bool LayoutSaver::MultiSplitterLayout::isValid() const return false; } - for (auto &anchor : anchors) { - if (!anchor.isValid(*this)) - return false; - } - if (!size.isValid()) { qWarning() << Q_FUNC_INFO << "Invalid size"; return false; @@ -886,8 +757,6 @@ bool LayoutSaver::MultiSplitterLayout::isValid() const void LayoutSaver::MultiSplitterLayout::scaleSizes(const ScalingInfo &scalingInfo) { scalingInfo.applyFactorsTo(/*by-ref*/size); - for (LayoutSaver::Anchor &anchor : anchors) - anchor.scaleSizes(scalingInfo); for (LayoutSaver::Item &item : items) item.scaleSizes(scalingInfo); } @@ -896,7 +765,6 @@ QVariantMap LayoutSaver::MultiSplitterLayout::toVariantMap() const { QVariantMap map; - map.insert(QStringLiteral("anchors"), toVariantList(anchors)); map.insert(QStringLiteral("items"), toVariantList(items)); map.insert(QStringLiteral("minSize"), sizeToMap(minSize)); map.insert(QStringLiteral("size"), sizeToMap(size)); @@ -906,7 +774,6 @@ QVariantMap LayoutSaver::MultiSplitterLayout::toVariantMap() const void LayoutSaver::MultiSplitterLayout::fromVariantMap(const QVariantMap &map) { - anchors = fromVariantList(map.value(QStringLiteral("anchors")).toList()); items = fromVariantList(map.value(QStringLiteral("items")).toList()); minSize = mapToSize(map.value(QStringLiteral("minSize")).toMap()); size = mapToSize(map.value(QStringLiteral("size")).toMap()); diff --git a/src/LayoutSaver_p.h b/src/LayoutSaver_p.h index 953d2124..efe5f7a8 100644 --- a/src/LayoutSaver_p.h +++ b/src/LayoutSaver_p.h @@ -33,7 +33,6 @@ #include -#define ANCHOR_MAGIC_MARKER "e520c60e-cf5d-4a30-b1a7-588d2c569851" #define MULTISPLITTER_LAYOUT_MAGIC_MARKER "bac9948e-5f1b-4271-acc5-07f1708e2611" /** @@ -213,37 +212,13 @@ struct LayoutSaver::Item bool isPlaceholder; QRect geometry; QSize minSize; - int indexOfLeftAnchor; + int indexOfLeftAnchor; // TODO: Search and replace. Remove anchors int indexOfTopAnchor; int indexOfRightAnchor; int indexOfBottomAnchor; LayoutSaver::Frame frame; }; -struct LayoutSaver::Anchor -{ - typedef QVector List; - - bool isValid(const LayoutSaver::MultiSplitterLayout &layout) const; - - QVariantMap toVariantMap() const; - void fromVariantMap(const QVariantMap &map); - void scaleSizes(const ScalingInfo &); - - bool isVertical() const; - - QString objectName; - QRect geometry; - double positionPercentage; - int orientation; - int type; - int indexOfFrom; - int indexOfTo; - int indexOfFollowee; - QVector side1Items; - QVector side2Items; -}; - struct LayoutSaver::MultiSplitterLayout { bool isValid() const; @@ -253,7 +228,6 @@ struct LayoutSaver::MultiSplitterLayout QVariantMap toVariantMap() const; void fromVariantMap(const QVariantMap &map); - LayoutSaver::Anchor::List anchors; LayoutSaver::Item::List items; QSize minSize; QSize size; @@ -389,27 +363,6 @@ inline QDataStream &operator>>(QDataStream &ds, LayoutSaver::Placeholder *p) return ds; } -inline QDataStream &operator>>(QDataStream &ds, LayoutSaver::Anchor *a) -{ - QString marker; - - ds >> marker; - if (marker != QLatin1String(ANCHOR_MAGIC_MARKER)) - qWarning() << Q_FUNC_INFO << "Corrupt stream"; - - ds >> a->objectName; - ds >> a->geometry; - ds >> a->orientation; - ds >> a->type; - ds >> a->indexOfFrom; - ds >> a->indexOfTo; - ds >> a->indexOfFollowee; - ds >> a->side1Items; - ds >> a->side2Items; - - return ds; -} - inline QDataStream &operator>>(QDataStream &ds, LayoutSaver::Frame *frame) { int numDockWidgets; @@ -465,7 +418,6 @@ inline QDataStream &operator>>(QDataStream &ds, LayoutSaver::Item *item) inline QDataStream &operator>>(QDataStream &ds, LayoutSaver::MultiSplitterLayout *l) { int numItems; - int numAnchors; QString marker; ds >> marker; @@ -475,10 +427,8 @@ inline QDataStream &operator>>(QDataStream &ds, LayoutSaver::MultiSplitterLayout ds >> l->size; ds >> l->minSize; ds >> numItems; - ds >> numAnchors; l->items.clear(); - l->anchors.clear(); for (int i = 0 ; i < numItems; ++i) { LayoutSaver::Item item; @@ -486,12 +436,6 @@ inline QDataStream &operator>>(QDataStream &ds, LayoutSaver::MultiSplitterLayout l->items.push_back(item); } - for (int i = 0 ; i < numAnchors; ++i) { - LayoutSaver::Anchor a; - ds >> &a; - l->anchors.push_back(a); - } - return ds; } diff --git a/src/private/DockRegistry.cpp b/src/private/DockRegistry.cpp index 120cec25..11bc8bd1 100644 --- a/src/private/DockRegistry.cpp +++ b/src/private/DockRegistry.cpp @@ -315,7 +315,7 @@ QVector DockRegistry::topLevels(bool excludeFloatingDocks) const return windows; } -void DockRegistry::clear(bool deleteStaticAnchors) +void DockRegistry::clear() { for (auto dw : qAsConst(m_dockWidgets)) { dw->forceClose(); @@ -323,17 +323,17 @@ void DockRegistry::clear(bool deleteStaticAnchors) } for (auto mw : qAsConst(m_mainWindows)) - mw->multiSplitterLayout()->clear(deleteStaticAnchors); + mw->multiSplitterLayout()->clear(); qCDebug(restoring) << Q_FUNC_INFO << "; dockwidgets=" << m_dockWidgets.size() << "; nestedwindows=" << m_nestedWindows.size(); } -void DockRegistry::clear(QStringList affinities, bool deleteStaticAnchors) +void DockRegistry::clear(QStringList affinities) { if (affinities.isEmpty()) { // Just clear everything - clear(deleteStaticAnchors); + clear(); return; } @@ -349,7 +349,7 @@ void DockRegistry::clear(QStringList affinities, bool deleteStaticAnchors) for (auto mw : qAsConst(m_mainWindows)) { if (affinities.contains(mw->affinityName())) { - mw->multiSplitterLayout()->clear(deleteStaticAnchors); + mw->multiSplitterLayout()->clear(); } } } diff --git a/src/private/DockRegistry_p.h b/src/private/DockRegistry_p.h index 988ef02d..dc0d1cf7 100644 --- a/src/private/DockRegistry_p.h +++ b/src/private/DockRegistry_p.h @@ -102,12 +102,12 @@ public: * @brief Closes all dock widgets, destroys all FloatingWindow, Item and Anchors. * This is called before restoring a layout. */ - void clear(bool deleteStaticAnchors = false); + void clear(); /** * @brief Closes all dock widgets, destroys all FloatingWindow, Item and Anchors with the specified affinities. */ - void clear(QStringList affinities, bool deleteStaticAnchors = false); + void clear(QStringList affinities); /** * @brief Ensures that all floating DockWidgets have a FloatingWindow as a window. diff --git a/src/private/DropArea.cpp b/src/private/DropArea.cpp index 4e65f555..5466b6b5 100644 --- a/src/private/DropArea.cpp +++ b/src/private/DropArea.cpp @@ -59,18 +59,6 @@ int DropArea::numFrames() const return m_layout->count(); } -Anchor::List DropArea::nonStaticAnchors(bool includePlaceholders) const -{ - auto anchors = m_layout->anchors(); - Anchor::List result; - for (Anchor *anchor : anchors) { - if (!anchor->isStatic() && !(!includePlaceholders && anchor->isFollowing())) - result << anchor; - } - - return result; -} - Frame *DropArea::frameContainingPos(QPoint globalPos) const { const ItemList &items = m_layout->items(); diff --git a/src/private/DropArea_p.h b/src/private/DropArea_p.h index c25ffdea..c2e1bafe 100644 --- a/src/private/DropArea_p.h +++ b/src/private/DropArea_p.h @@ -58,7 +58,6 @@ public: bool drop(QWidgetOrQuick *droppedwindow, KDDockWidgets::Location location, Frame *relativeTo); int numFrames() const; - Anchor::List nonStaticAnchors(bool includePlaceholders = false) const; Frame *frameContainingPos(QPoint globalPos) const; Item *centralFrame() const; DropIndicatorOverlayInterface *dropIndicatorOverlay() const { return m_dropIndicatorOverlay; } diff --git a/src/private/Frame.cpp b/src/private/Frame.cpp index 7255c045..f73d8ff7 100644 --- a/src/private/Frame.cpp +++ b/src/private/Frame.cpp @@ -293,7 +293,7 @@ void Frame::restoreToPreviousPosition() return; } - m_layoutItem->restorePlaceholder(this); + m_layoutItem->setIsVisible(true); } int Frame::currentTabIndex() const diff --git a/src/private/multisplitter/Item.cpp b/src/private/multisplitter/Item.cpp index 7c03d276..10bec17e 100644 --- a/src/private/multisplitter/Item.cpp +++ b/src/private/multisplitter/Item.cpp @@ -749,6 +749,16 @@ void ItemContainer::positionItems() m_childPercentages.clear(); } +void ItemContainer::clear() +{ + for (Item *item : qAsConst(m_children)) { + if (ItemContainer *container = item->asContainer()) + container->clear(); + + delete item; + } +} + void ItemContainer::insertItem(Item *item, int index, bool growItem) { m_children.insert(index, item); diff --git a/src/private/multisplitter/Item_p.h b/src/private/multisplitter/Item_p.h index c2fdc85c..66e2a45f 100644 --- a/src/private/multisplitter/Item_p.h +++ b/src/private/multisplitter/Item_p.h @@ -250,6 +250,8 @@ public: bool isBeingInserted() const; void setBeingInserted(bool); ItemContainer *root() const; + void deserialize() {} + void serialize() {} QWidget *frame() const { return m_widget; } // TODO: rename void setFrame(QWidget *w) { m_widget = w; } // TODO rename @@ -336,6 +338,7 @@ public: QRect suggestedDropRect(Item *newItem, Item *relativeTo, Location) const; void positionItems(); bool isResizing() const { return m_isResizing; } + void clear(); Q_SIGNALS: void itemsChanged(); public: diff --git a/src/private/multisplitter/MultiSplitterLayout.cpp b/src/private/multisplitter/MultiSplitterLayout.cpp index 2fe8687e..15721817 100644 --- a/src/private/multisplitter/MultiSplitterLayout.cpp +++ b/src/private/multisplitter/MultiSplitterLayout.cpp @@ -48,7 +48,7 @@ const QString MultiSplitterLayout::s_magicMarker = QStringLiteral("bac9948e-5f1b MultiSplitterLayout::MultiSplitterLayout(MultiSplitter *parent) : QObject(parent) , m_multiSplitter(parent) - , m_rootItem(new Item()) + , m_rootItem(new ItemContainer()) { Q_ASSERT(parent); @@ -141,57 +141,6 @@ bool MultiSplitterLayout::validateInputs(QWidgetOrQuick *widget, return true; } -std::pair MultiSplitterLayout::boundInterval(int newPos1, Anchor* anchor1, int newPos2, Anchor *anchor2) const -{ - const int bound1 = boundPositionForAnchor(anchor1, Anchor::Side1); - const int bound2 = boundPositionForAnchor(anchor2, Anchor::Side2); - - if (newPos1 >= bound1 && newPos2 <= bound2) { - // Simplest case, it's bounded. - return { newPos1, newPos2 }; - } - - if (newPos1 < bound1) { - // the anchor1 is out of bounds - - const int bythismuch = bound1 - newPos1; - newPos1 = bound1; - newPos2 = newPos2 + bythismuch; - - if (newPos2 > bound2) { - qWarning() << "Adjusted interval still out of bounds. Not enough space. #1" - << "; newPos1=" << newPos1 - << "; newPos2=" << newPos2 - << "; bounds=" << bound1 << bound2 - << "; anchor1=" << anchor1 - << "; anchor2=" << anchor2 - << "; size=" << size(); - } - - return { newPos1, newPos2 }; - } else if (newPos2 > bound2) { - // the anchor2 is out of bounds - - const int bythismuch = newPos2 - bound2; - newPos2 = bound2; - newPos1 = newPos1 - bythismuch; - - if (newPos1 < bound1) { - qWarning() << "Adjusted interval still out of bounds. Not enough space. #2" - << "; newPos1=" << newPos1 - << "; newPos2=" << newPos2 - << "; bounds=" << bound1 << bound2 - << "; anchor1=" << anchor1 - << "; anchor2=" << anchor2 - << "; size=" << size(); - } - - return { newPos1, newPos2 }; - } - - return { newPos1, newPos2 }; -} - void MultiSplitterLayout::addWidget(QWidgetOrQuick *w, Location location, Frame *relativeToWidget, AddingOption option) { auto frame = qobject_cast(w); @@ -227,9 +176,8 @@ void MultiSplitterLayout::addItems_internal(const ItemList &items, bool updateCo updateSizeConstraints(); for (auto item : items) { - item->setLayout(this); if (item->frame()) { - item->setVisible(true); + item->setIsVisible(true); item->frame()->installEventFilter(this); Q_EMIT widgetAdded(item); } @@ -239,192 +187,6 @@ void MultiSplitterLayout::addItems_internal(const ItemList &items, bool updateCo Q_EMIT widgetCountChanged(m_items.size()); } -void MultiSplitterLayout::addAsPlaceholder(DockWidgetBase *dockWidget, Location location, Item *relativeTo) -{ - if (!dockWidget) { - qWarning() << Q_FUNC_INFO << "null dockwidget"; - return; - } - - dockWidget->setParent(nullptr); - - auto result = createTargetAnchorGroup(location, relativeTo); - AnchorGroup targetAnchorGroup = result.first; - - auto frame = Config::self().frameworkWidgetFactory()->createFrame(m_multiSplitter); - auto item = new Item(frame, this); - - targetAnchorGroup.addItem(item); - addItems_internal(ItemList{ item }, false); - - dockWidget->addPlaceholderItem(item); - delete frame; - - updateAnchorFollowing(); - Q_ASSERT(!dockWidget->isVisible()); - maybeCheckSanity(); -} - -void MultiSplitterLayout::ensureEnoughSize(const QWidgetOrQuick *widget, - Location location, const Item *relativeToItem) -{ - const int neededAnchorThickness = isEmpty() ? 0 : Anchor::thickness(/*static=*/ false); - const QSize available = availableSize(); - const QSize widgetMin = { widgetMinLength(widget, Qt::Vertical), widgetMinLength(widget, Qt::Horizontal) }; - const QSize oldSize = m_size; - const int neededWidth = widgetMin.width() - available.width() + neededAnchorThickness; - const int neededHeight = widgetMin.height() - available.height() + neededAnchorThickness; - - QSize newSize = m_size; - if (neededWidth > 0) - newSize.setWidth(newSize.width() + neededWidth); - if (neededHeight > 0) - newSize.setHeight(newSize.height() + neededHeight); - - if (newSize != m_size) - setSize(newSize); - - // Just to make sure: - if (lengthForDrop(widget, location, relativeToItem).isNull()) { - qWarning() << Q_FUNC_INFO << "failed! Please report a bug." - << "; oldAvailable=" << available - << "; newAvailable=" << availableSize() - << "; newSize=" << newSize - << "; m_size=" << m_size - << "; oldSize=" << oldSize - << "; widgetMin=" << widgetMin - << "; isEmpty=" << isEmpty(); - } -} - -void MultiSplitterLayout::ensureAnchorsBounded() -{ - //Ensures all separators are within their bounds, meaning all items obey their min size - positionStaticAnchors(); - ensureItemsMinSize(); -} - -static Anchor::List removeSmallestPath(QVector &paths) -{ - // Removes and returns the smallest list - Anchor::List smallestPath; - int indexOfSmallest = 0; - for (int i = 0, end = paths.size(); i < end; ++i) { - const Anchor::List &path = paths.at(i); - if (path.size() <= smallestPath.size() || smallestPath.isEmpty()) { - smallestPath = path; - indexOfSmallest = i; - } - } - - paths.removeAt(indexOfSmallest); - return smallestPath; -} - -void MultiSplitterLayout::propagateResize(int delta, Anchor *fromAnchor, Anchor::Side direction) -{ - if (delta < 0) - qWarning() << Q_FUNC_INFO << "Invalid delta" << delta << fromAnchor << direction; - - if (delta <= 0 || fromAnchor->isStatic()) - return; - - QVector paths; - collectPaths(paths, fromAnchor, direction); - - for (const Anchor::List &path : qAsConst(paths)) { - qCDebug(sizing) << Q_FUNC_INFO << path; - } - - Anchor::List anchorsThatAlreadyContributed; - anchorsThatAlreadyContributed.push_back(fromAnchor); - - while (!paths.isEmpty()) { - // Get smallest path: - Anchor::List smallestPath = removeSmallestPath(/*by-ref*/paths); - if (smallestPath.size() <= 1) { - // Nothing to do, it has a single anchor, which was already adjusted in addWidget() - continue; - } - - const bool towardsSide1 = direction == Anchor::Side1; - const bool towardsSide2 = !towardsSide1; - - const int sign = towardsSide1 ? -1 : 1; - const int contributionPerAnchor = (delta / (smallestPath.size() - 1)) * sign; // n-1 because the initial anchor already contributed - if (qAbs(contributionPerAnchor) < 5) { - // Too small, don't bother - continue; - } - - // Now make those anchors contribute, skipping the first - for (int i = 1, end = smallestPath.size(); i < end; ++i) { - Anchor *a = smallestPath.at(i); - if (!anchorsThatAlreadyContributed.contains(a)) { - // When moving anchors don't allow widgets to go bellow their min size - const int bound = boundPositionForAnchor(a, direction); - int newPosition = a->position() + contributionPerAnchor; - if ((towardsSide1 && newPosition < bound) || - (towardsSide2 && newPosition > bound)) { - newPosition = bound; - } - - if (a->position() != newPosition) { - a->setPosition(newPosition); - anchorsThatAlreadyContributed.push_back(a); - } - } - } - } -} - -void MultiSplitterLayout::collectPaths(QVector &paths, Anchor *fromAnchor, Anchor::Side direction) -{ - if (fromAnchor->isStatic()) { - // We've finally reached a border anchor, we can stop now. - return; - } - - if (paths.isEmpty()) - paths.push_back({}); - - int currentPathIndex = paths.size() - 1; // Store the index instead of using "Anchor::List ¤tPath = paths.last();" as the references are stable, as the paths vector reallocates - - paths[currentPathIndex].push_back(fromAnchor); - - const ItemList items = fromAnchor->items(direction); - for (int i = 0, end = items.size(); i < end; ++i) { - Anchor *nextAnchor = items[i]->anchorAtSide(direction, fromAnchor->orientation()); - if (i > 0) { - Anchor::List newPath = paths[currentPathIndex]; - paths.push_back(newPath); - } - collectPaths(paths, nextAnchor, direction); - } -} - -void MultiSplitterLayout::resizeItem(Frame *frame, int newSize, Qt::Orientation orientation) -{ - // Used for unit-tests only - Item *item = itemForFrame(frame); - Q_ASSERT(item); - Anchor *a = item->anchorAtSide(Anchor::Side2, orientation); - Q_ASSERT(!a->isStatic()); - const int widgLength = item->length(orientation); - const int delta = newSize - widgLength; - qCDebug(::anchors) << Q_FUNC_INFO << "Old position:" << a->position() << "; old w.geo=" << item->geometry(); - a->setPosition(a->position() + delta); - qCDebug(::anchors) << Q_FUNC_INFO << "New position:" << a->position() << "; new w.geo=" << item->geometry(); -} - -void MultiSplitterLayout::ensureItemsMinSize() -{ - for (Item *item : qAsConst(m_items)) { - item->ensureMinSize(Qt::Vertical); - item->ensureMinSize(Qt::Horizontal); - } -} - QString MultiSplitterLayout::affinityName() const { if (auto ms = multiSplitter()) { @@ -448,18 +210,13 @@ void MultiSplitterLayout::addMultiSplitter(MultiSplitter *sourceMultiSplitter, void MultiSplitterLayout::removeItem(Item *item) { - if (!item || m_inDestructor || !m_items.contains(item)) + if (!item || m_inDestructor) return; - maybeCheckSanity(); - + Q_ASSERT(item != m_rootItem); if (!item->isPlaceholder()) item->frame()->removeEventFilter(this); - AnchorGroup anchorGroup = item->anchorGroup(); - anchorGroup.removeItem(item); - m_items.removeOne(item); - - updateAnchorFollowing(); + item->parentContainer()->removeItem(item); Q_EMIT widgetRemoved(item); Q_EMIT widgetCountChanged(m_items.size()); @@ -485,43 +242,18 @@ Item *MultiSplitterLayout::itemAt(QPoint p) const return nullptr; } -void MultiSplitterLayout::clear(bool alsoDeleteStaticAnchors) +void MultiSplitterLayout::clear() { const int oldCount = count(); const int oldVisibleCount = visibleCount(); - const auto items = m_items; - m_items.clear(); // Clear the item list first, do avoid ~Item() triggering a removal from the list - qDeleteAll(items); - const auto anchors = m_anchors; - m_anchors.clear(); + m_rootItem->clear(); - for (Anchor *anchor : qAsConst(anchors)) { - anchor->clear(); - if (!anchor->isStatic() || alsoDeleteStaticAnchors) { - delete anchor; - } - } - - if (alsoDeleteStaticAnchors) { - m_anchors.clear(); - m_topAnchor = nullptr; - m_bottomAnchor = nullptr; - m_leftAnchor = nullptr; - m_rightAnchor = nullptr; - m_staticAnchorGroup.left = nullptr; - m_staticAnchorGroup.top = nullptr; - m_staticAnchorGroup.right = nullptr; - m_staticAnchorGroup.bottom = nullptr; - } else { - m_anchors = { m_topAnchor, m_bottomAnchor, m_leftAnchor, m_rightAnchor }; - } if (oldCount > 0) Q_EMIT widgetCountChanged(0); if (oldVisibleCount > 0) Q_EMIT visibleWidgetCountChanged(0); - } int MultiSplitterLayout::visibleCount() const @@ -538,8 +270,6 @@ int MultiSplitterLayout::placeholderCount() const return count() - visibleCount(); } - - void MultiSplitterLayout::setAnchorBeingDragged(Anchor *anchor) { m_anchorBeingDragged = anchor; @@ -617,6 +347,11 @@ QVector MultiSplitterLayout::dockWidgets() const return result; } +void MultiSplitterLayout::restorePlaceholder(Item *, int /*tabIndex*/) +{ + // TODO +} + bool MultiSplitterLayout::checkSanity() const { return m_rootItem->checkSanity(); @@ -637,33 +372,11 @@ void MultiSplitterLayout::unrefOldPlaceholders(const Frame::List &framesBeingAdd void MultiSplitterLayout::setSize(QSize size) { - if (size != m_size) { + if (size != this->size()) { + m_rootItem->resize(size); m_resizing = true; - QSize oldSize = m_size; - - if (size.width() < m_minSize.width() || size.height() < m_minSize.height()) { - qWarning() << Q_FUNC_INFO << "new size is smaller than min size. Size=" << size << "; min=" << m_minSize; - return; - } - -#if defined(DOCKS_DEVELOPER_MODE) - if (!m_inCtor && false) { // TODO Uncomment when it passes - QSize minSizeCalculated = QSize(availableLengthForOrientation(Qt::Vertical), availableLengthForOrientation(Qt::Horizontal)); - if (size.width() < minSizeCalculated.width() || size.height() < minSizeCalculated.height()) { - qWarning() << Q_FUNC_INFO << "new size is smaller than min size calculated" << size << minSizeCalculated; - } - } -#endif - - m_size = size; Q_EMIT sizeChanged(size); - - redistributeSpace(oldSize, size); - m_resizing = false; - - if (!m_restoringPlaceholder) { // ensureAnchorsBounded() is run at the end of restorePlaceholder() already. - ensureAnchorsBounded(); - } + m_resizing = false; // TODO: m_resizing needed ? } } @@ -671,10 +384,10 @@ void MultiSplitterLayout::setContentLength(int value, Qt::Orientation o) { if (o == Qt::Vertical) { // Setting the width - setSize({value, m_size.height()}); + setSize({value, size().height()}); } else { // Setting the height - setSize({m_size.width(), value}); + setSize({size().width(), value}); } } @@ -686,9 +399,9 @@ int MultiSplitterLayout::length(Qt::Orientation o) const void MultiSplitterLayout::setMinimumSize(QSize sz) { - if (sz != m_minSize) { - m_minSize = sz; - setSize(m_size.expandedTo(m_minSize)); // Increase size incase we need to + if (sz != m_rootItem->minSize()) { + m_rootItem->setMinSize(sz); + setSize(size().expandedTo(m_rootItem->minSize())); // Increase size incase we need to Q_EMIT minimumSizeChanged(sz); } qCDebug(sizing) << Q_FUNC_INFO << "minSize = " << m_minSize; @@ -725,77 +438,23 @@ bool MultiSplitterLayout::eventFilter(QObject *o, QEvent *e) bool MultiSplitterLayout::deserialize(const LayoutSaver::MultiSplitterLayout &msl) { - clear(true); + clear(); ItemList items; items.reserve(msl.items.size()); for (const auto &i : qAsConst(msl.items)) { - Item *item = Item::deserialize(i, this); - items.push_back(item); + Q_UNUSED(i); + //Item *item = deserialize(); TODO + //items.push_back(item); } m_items = items; // Set the items, so Anchor::deserialize() can set the side1 and side2 items - for (const auto &a : qAsConst(msl.anchors)) { - Anchor *anchor = Anchor::deserialize(a, this); // They auto-register into m_anchors - if (!anchor) - return false; - - if (anchor->type() == Anchor::Type_LeftStatic) { - Q_ASSERT(!m_leftAnchor); - m_leftAnchor = anchor; - } else if (anchor->type() == Anchor::Type_TopStatic) { - Q_ASSERT(!m_topAnchor); - m_topAnchor = anchor; - } else if (anchor->type() == Anchor::Type_RightStatic) { - Q_ASSERT(!m_rightAnchor); - m_rightAnchor = anchor; - } else if (anchor->type() == Anchor::Type_BottomStatic) { - Q_ASSERT(!m_bottomAnchor); - m_bottomAnchor = anchor; - } - } - - m_items.clear(); // Now properly set the items, which installs needed event filters, etc. addItems_internal(items, false, false); // Add the items only after we have the static anchors set - for (Anchor *anchor : qAsConst(m_anchors)) { - int indexFrom = anchor->property("indexFrom").toInt(); - int indexTo = anchor->property("indexTo").toInt(); - int indexFolowee = anchor->property("indexFolowee").toInt(); - anchor->setProperty("indexFrom", QVariant()); - anchor->setProperty("indexTo", QVariant()); - anchor->setProperty("indexFolowee", QVariant()); - - anchor->setFrom(m_anchors.at(indexFrom)); - anchor->setTo(m_anchors.at(indexTo)); - if (indexFolowee != -1) - anchor->setFollowee(m_anchors.at(indexFolowee)); - } - - m_size = msl.size; - m_minSize = msl.minSize; - - // Now that the anchors were created we can add them to the items - for (Item *item : qAsConst(m_items)) { - const int leftIndex = item->property("leftIndex").toInt(); - const int topIndex = item->property("topIndex").toInt(); - const int rightIndex = item->property("rightIndex").toInt(); - const int bottomIndex = item->property("bottomIndex").toInt(); - - AnchorGroup &group = item->anchorGroup(); - group.left = m_anchors.at(leftIndex); - group.top = m_anchors.at(topIndex); - group.right = m_anchors.at(rightIndex); - group.bottom = m_anchors.at(bottomIndex); - - // Clear helper properties - item->setProperty("leftIndex", QVariant()); - item->setProperty("topIndex", QVariant()); - item->setProperty("rightIndex", QVariant()); - item->setProperty("bottomIndex", QVariant()); - } + setSize(msl.size); + setMinimumSize(msl.minSize); if (!m_items.isEmpty()) Q_EMIT widgetCountChanged(m_items.size()); @@ -806,7 +465,7 @@ bool MultiSplitterLayout::deserialize(const LayoutSaver::MultiSplitterLayout &ms // its content size if needed Q_EMIT minimumSizeChanged(m_minSize); - if (m_size != multiSplitter()->size()) { + if (size() != multiSplitter()->size()) { setSize(multiSplitter()->size()); } @@ -820,11 +479,8 @@ LayoutSaver::MultiSplitterLayout MultiSplitterLayout::serialize() const l.size = size(); l.minSize = minimumSize(); - for (Item *item : m_items) - l.items.push_back(item->serialize()); - - for (Anchor *anchor : m_anchors) - l.anchors.push_back(anchor->serialize()); + //for (Item *item : m_items) TODO + //l.items.push_back(item->serialize()); return l; } diff --git a/src/private/multisplitter/MultiSplitterLayout_p.h b/src/private/multisplitter/MultiSplitterLayout_p.h index 373c3c3f..5dd9fe43 100644 --- a/src/private/multisplitter/MultiSplitterLayout_p.h +++ b/src/private/multisplitter/MultiSplitterLayout_p.h @@ -138,7 +138,7 @@ public: * @brief Removes all Items, Anchors and Frames docked in this layout. * DockWidgets are closed but not deleted. */ - void clear(bool alsoDeleteStaticAnchors = false); + void clear(); /** * @brief Returns the number of Item objects in this layout. @@ -237,13 +237,13 @@ public: * @brief returns the contents width. * Usually it's the same width as the respective parent MultiSplitter. */ - int width() const { return m_size.width(); } + int width() const { return size().width(); } /** * @brief returns the contents height. * Usually it's the same height as the respective parent MultiSplitter. */ - int height() const { return m_size.height(); } + int height() const { return size().height(); } /** * @brief returns the layout's minimum size @@ -254,7 +254,7 @@ public: /** * @brief getter for the size */ - QSize size() const { return m_size; } + QSize size() const { return m_rootItem->size(); } // For debug/hardening bool validateInputs(QWidgetOrQuick *widget, KDDockWidgets::Location location, const Frame *relativeToFrame, AddingOption option) const; @@ -296,6 +296,8 @@ public: */ QVector dockWidgets() const; + void restorePlaceholder(Item *, int tabIndex); + struct Length { Length() = default; Length(int side1, int side2) @@ -434,8 +436,7 @@ private: QSize m_minSize = QSize(0, 0); QPointer m_anchorBeingDragged; - QSize m_size; - Item *const m_rootItem; + ItemContainer *const m_rootItem; }; /** diff --git a/src/private/multisplitter/Separator.cpp b/src/private/multisplitter/Separator.cpp index 018c6ca7..8c45698b 100644 --- a/src/private/multisplitter/Separator.cpp +++ b/src/private/multisplitter/Separator.cpp @@ -22,6 +22,7 @@ #include "multisplitter/MultiSplitterLayout_p.h" #include "multisplitter/Anchor_p.h" #include "Logging_p.h" +#include "Item_p.h" using namespace KDDockWidgets; @@ -32,7 +33,7 @@ Separator::Separator(KDDockWidgets::Anchor *anchor, QWidgetAdapter *parent) Q_ASSERT(anchor); setVisible(true); - const int thickness = Anchor::thickness(isStatic()); + const int thickness = Item::separatorThickness(); if (isVertical()) setFixedWidth(thickness); else @@ -44,11 +45,6 @@ bool Separator::isVertical() const return m_anchor->isVertical(); } -bool Separator::isStatic() const -{ - return m_anchor->isStatic(); -} - int Separator::position() const { return isVertical() ? x() : y(); @@ -56,19 +52,16 @@ int Separator::position() const void Separator::onMousePress() { - Q_ASSERT(!m_anchor->isFollowing()); m_anchor->onMousePress(); } void Separator::onMouseMove(QPoint globalPos) { - Q_ASSERT(!m_anchor->isFollowing()); m_anchor->onMouseMoved(parentWidget()->mapFromGlobal(globalPos)); } void Separator::onMouseRelease() { - Q_ASSERT(!m_anchor->isFollowing()); m_anchor->onMouseReleased(); } diff --git a/src/private/multisplitter/Separator_p.h b/src/private/multisplitter/Separator_p.h index c06de27c..72e2a145 100644 --- a/src/private/multisplitter/Separator_p.h +++ b/src/private/multisplitter/Separator_p.h @@ -33,12 +33,10 @@ class DOCKS_EXPORT Separator : public QWidgetAdapter { Q_OBJECT Q_PROPERTY(bool isVertical READ isVertical CONSTANT) - Q_PROPERTY(bool isStatic READ isStatic CONSTANT) //Q_PROPERTY(int position READ position NOTIFY positionChanged) public: explicit Separator(Anchor *anchor, QWidgetAdapter *parent = nullptr); bool isVertical() const; - bool isStatic() const; int position() const; void move(int p); const QPointer anchor() const { return m_anchor; } diff --git a/src/private/widgets/SeparatorWidget.cpp b/src/private/widgets/SeparatorWidget.cpp index 73ace91d..45272fd8 100644 --- a/src/private/widgets/SeparatorWidget.cpp +++ b/src/private/widgets/SeparatorWidget.cpp @@ -60,12 +60,10 @@ void SeparatorWidget::enterEvent(QEvent *) if (!anchor()) return; - if (!isStatic()) { - if (isVertical()) - setCursor(Qt::SizeHorCursor); - else - setCursor(Qt::SizeVerCursor); - } + if (isVertical()) + setCursor(Qt::SizeHorCursor); + else + setCursor(Qt::SizeVerCursor); } void SeparatorWidget::leaveEvent(QEvent *)