This commit is contained in:
Sergio Martins
2020-04-02 19:43:36 +01:00
parent 993ca6e9c6
commit 4734f06046
15 changed files with 71 additions and 612 deletions

View File

@@ -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.";

View File

@@ -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<LayoutSaver::Anchor>(anchors));
map.insert(QStringLiteral("items"), toVariantList<LayoutSaver::Item>(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<LayoutSaver::Anchor>(map.value(QStringLiteral("anchors")).toList());
items = fromVariantList<LayoutSaver::Item>(map.value(QStringLiteral("items")).toList());
minSize = mapToSize(map.value(QStringLiteral("minSize")).toMap());
size = mapToSize(map.value(QStringLiteral("size")).toMap());

View File

@@ -33,7 +33,6 @@
#include <memory>
#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<LayoutSaver::Anchor> 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<int> side1Items;
QVector<int> 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;
}

View File

@@ -315,7 +315,7 @@ QVector<QWidget *> 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();
}
}
}

View File

@@ -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.

View File

@@ -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();

View File

@@ -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; }

View File

@@ -293,7 +293,7 @@ void Frame::restoreToPreviousPosition()
return;
}
m_layoutItem->restorePlaceholder(this);
m_layoutItem->setIsVisible(true);
}
int Frame::currentTabIndex() const

View File

@@ -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);

View File

@@ -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:

View File

@@ -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<int,int> 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<Frame*>(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<Anchor::List> &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<Anchor::List> 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<Anchor::List> &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 &currentPath = 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<DockWidgetBase *> 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;
}

View File

@@ -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<DockWidgetBase*> 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<Anchor> m_anchorBeingDragged;
QSize m_size;
Item *const m_rootItem;
ItemContainer *const m_rootItem;
};
/**

View File

@@ -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();
}

View File

@@ -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> anchor() const { return m_anchor; }

View File

@@ -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 *)