summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/widgets/widgets/qdockarealayout.cpp30
-rw-r--r--src/widgets/widgets/qdockarealayout_p.h3
-rw-r--r--src/widgets/widgets/qdockwidget.cpp7
-rw-r--r--src/widgets/widgets/qmainwindowlayout.cpp461
-rw-r--r--src/widgets/widgets/qmainwindowlayout_p.h14
5 files changed, 354 insertions, 161 deletions
diff --git a/src/widgets/widgets/qdockarealayout.cpp b/src/widgets/widgets/qdockarealayout.cpp
index 27ecc33dba..60f6f6d26d 100644
--- a/src/widgets/widgets/qdockarealayout.cpp
+++ b/src/widgets/widgets/qdockarealayout.cpp
@@ -2418,20 +2418,21 @@ QList<int> QDockAreaLayout::indexOf(QWidget *dockWidget) const
return QList<int>();
}
-QList<int> QDockAreaLayout::gapIndex(const QPoint &pos) const
+QList<int> QDockAreaLayout::gapIndex(const QPoint &pos, bool disallowTabs) const
{
QMainWindow::DockOptions opts = mainWindow->dockOptions();
bool nestingEnabled = opts & QMainWindow::AllowNestedDocks;
QDockAreaLayoutInfo::TabMode tabMode = QDockAreaLayoutInfo::NoTabs;
#ifndef QT_NO_TABBAR
- if (opts & QMainWindow::AllowTabbedDocks
- || opts & QMainWindow::VerticalTabs)
- tabMode = QDockAreaLayoutInfo::AllowTabs;
- if (opts & QMainWindow::ForceTabbedDocks)
- tabMode = QDockAreaLayoutInfo::ForceTabs;
+ if (!disallowTabs) {
+ if (opts & QMainWindow::AllowTabbedDocks || opts & QMainWindow::VerticalTabs)
+ tabMode = QDockAreaLayoutInfo::AllowTabs;
+ if (opts & QMainWindow::ForceTabbedDocks)
+ tabMode = QDockAreaLayoutInfo::ForceTabs;
- if (tabMode == QDockAreaLayoutInfo::ForceTabs)
- nestingEnabled = false;
+ if (tabMode == QDockAreaLayoutInfo::ForceTabs)
+ nestingEnabled = false;
+ }
#endif
@@ -3303,6 +3304,19 @@ int QDockAreaLayout::separatorMove(const QList<int> &separator, const QPoint &or
return delta;
}
+int QDockAreaLayoutInfo::separatorMove(const QList<int> &separator, const QPoint &origin,
+ const QPoint &dest)
+{
+ int delta = 0;
+ int index = separator.last();
+ QDockAreaLayoutInfo *info = this->info(separator);
+ delta = pick(info->o, dest - origin);
+ if (delta != 0)
+ delta = info->separatorMove(index, delta);
+ info->apply(false);
+ return delta;
+}
+
#ifndef QT_NO_TABBAR
// Sets the correct positions for the separator widgets
// Allocates new sepearator widgets with getSeparatorWidget
diff --git a/src/widgets/widgets/qdockarealayout_p.h b/src/widgets/widgets/qdockarealayout_p.h
index e9bd661cbd..e4cdc9f296 100644
--- a/src/widgets/widgets/qdockarealayout_p.h
+++ b/src/widgets/widgets/qdockarealayout_p.h
@@ -182,6 +182,7 @@ public:
const QPoint &mouse) const;
QRegion separatorRegion() const;
int separatorMove(int index, int delta);
+ int separatorMove(const QList<int> &separator, const QPoint &origin, const QPoint &dest);
QLayoutItem *itemAt(int *x, int index) const;
QLayoutItem *takeAt(int *x, int index);
@@ -248,7 +249,7 @@ public:
QList<int> indexOfPlaceHolder(const QString &objectName) const;
QList<int> indexOf(QWidget *dockWidget) const;
- QList<int> gapIndex(const QPoint &pos) const;
+ QList<int> gapIndex(const QPoint &pos, bool disallowTabs) const;
QList<int> findSeparator(const QPoint &pos) const;
QDockAreaLayoutItem &item(const QList<int> &path);
diff --git a/src/widgets/widgets/qdockwidget.cpp b/src/widgets/widgets/qdockwidget.cpp
index 2928d51d20..d25f4d922e 100644
--- a/src/widgets/widgets/qdockwidget.cpp
+++ b/src/widgets/widgets/qdockwidget.cpp
@@ -214,10 +214,9 @@ QDockWidgetLayout::~QDockWidgetLayout()
bool QDockWidgetLayout::nativeWindowDeco() const
{
bool floating = parentWidget()->isWindow();
- if (!floating) {
- if (auto groupWindow = qobject_cast<const QDockWidgetGroupWindow*>(parentWidget()->parentWidget()))
- return groupWindow->hasNativeDecos();
- }
+ if (auto groupWindow =
+ qobject_cast<const QDockWidgetGroupWindow *>(parentWidget()->parentWidget()))
+ floating = floating || groupWindow->tabLayoutInfo();
return nativeWindowDeco(floating);
}
diff --git a/src/widgets/widgets/qmainwindowlayout.cpp b/src/widgets/widgets/qmainwindowlayout.cpp
index 30217733cf..e0269a3f02 100644
--- a/src/widgets/widgets/qmainwindowlayout.cpp
+++ b/src/widgets/widgets/qmainwindowlayout.cpp
@@ -173,13 +173,17 @@ QDebug operator<<(QDebug debug, const QMainWindowLayout *layout)
/******************************************************************************
** QDockWidgetGroupWindow
*/
-// QDockWidgetGroupWindow is the floating window containing the tabbed dockwidgets in case several
-// dockwidgets are dragged together (QMainWindow::GroupedDragging feature).
+// QDockWidgetGroupWindow is the floating window containing several QDockWidgets floating together.
+// (QMainWindow::GroupedDragging feature)
// QDockWidgetGroupLayout is the layout of that window and use a QDockAreaLayoutInfo to layout
-// the tabs inside it.
+// the QDockWidgets inside it.
+// If there is only one QDockWidgets, or all QDockWidgets are tabbed together, it is equivalent
+// of a floating QDockWidget (the title of the QDockWidget is the title of the window). But if there
+// are nested QDockWidget, an additional title bar is there.
#ifndef QT_NO_DOCKWIDGET
-class QDockWidgetGroupLayout : public QLayout {
- QDockAreaLayoutInfo info;
+class QDockWidgetGroupLayout : public QLayout,
+ public QMainWindowLayoutSeparatorHelper<QDockWidgetGroupLayout>
+{
QWidgetResizeHandler *resizer;
public:
QDockWidgetGroupLayout(QDockWidgetGroupWindow* parent) : QLayout(parent) {
@@ -188,7 +192,7 @@ public:
resizer->setMovingEnabled(false);
}
~QDockWidgetGroupLayout() {
- info.deleteAllLayoutItems();
+ layoutState.deleteAllLayoutItems();
}
void addItem(QLayoutItem*) Q_DECL_OVERRIDE { Q_UNREACHABLE(); }
@@ -196,32 +200,43 @@ public:
QLayoutItem* itemAt(int index) const Q_DECL_OVERRIDE
{
int x = 0;
- return info.itemAt(&x, index);
+ return layoutState.itemAt(&x, index);
}
QLayoutItem* takeAt(int index) Q_DECL_OVERRIDE
{
int x = 0;
- return info.takeAt(&x, index);
+ QLayoutItem *ret = layoutState.takeAt(&x, index);
+ if (savedState.rect.isValid() && ret->widget()) {
+ // we need to remove the item also from the saved state to prevent crash
+ QList<int> path = savedState.indexOf(ret->widget());
+ if (!path.isEmpty())
+ savedState.remove(path);
+ // Also, the item may be contained several times as a gap item.
+ path = layoutState.indexOf(ret->widget());
+ if (!path.isEmpty())
+ layoutState.remove(path);
+ }
+ return ret;
}
QSize sizeHint() const Q_DECL_OVERRIDE
{
int fw = frameWidth();
- return info.sizeHint() + QSize(fw, fw);
+ return layoutState.sizeHint() + QSize(fw, fw);
}
QSize minimumSize() const Q_DECL_OVERRIDE
{
int fw = frameWidth();
- return info.minimumSize() + QSize(fw, fw);
+ return layoutState.minimumSize() + QSize(fw, fw);
}
QSize maximumSize() const Q_DECL_OVERRIDE
{
int fw = frameWidth();
- return info.maximumSize() + QSize(fw, fw);
+ return layoutState.maximumSize() + QSize(fw, fw);
}
void setGeometry(const QRect&r) Q_DECL_OVERRIDE
{
groupWindow()->destroyOrHideIfEmpty();
- QDockAreaLayoutInfo *li = layoutInfo();
+ QDockAreaLayoutInfo *li = dockAreaLayoutInfo();
if (li->isEmpty())
return;
int fw = frameWidth();
@@ -231,12 +246,12 @@ public:
li->rect = r.adjusted(fw, fw, -fw, -fw);
li->fitItems();
li->apply(false);
+ if (savedState.rect.isValid())
+ savedState.rect = li->rect;
resizer->setActive(QWidgetResizeHandler::Resize, !nativeWindowDeco());
}
- QDockAreaLayoutInfo *layoutInfo() {
- return &info;
- }
+ QDockAreaLayoutInfo *dockAreaLayoutInfo() { return &layoutState; }
bool nativeWindowDeco() const
{
@@ -253,14 +268,21 @@ public:
{
return static_cast<QDockWidgetGroupWindow *>(parent());
}
+
+ QDockAreaLayoutInfo layoutState;
+ QDockAreaLayoutInfo savedState;
};
bool QDockWidgetGroupWindow::event(QEvent *e)
{
+ auto lay = static_cast<QDockWidgetGroupLayout *>(layout());
+ if (lay && lay->windowEvent(e))
+ return true;
+
switch (e->type()) {
case QEvent::Close:
// Forward the close to the QDockWidget just as if its close button was pressed
- if (QDockWidget *dw = topDockWidget()) {
+ if (QDockWidget *dw = activeTabbedDockWidget()) {
e->ignore();
dw->close();
adjustFlags();
@@ -268,7 +290,7 @@ bool QDockWidgetGroupWindow::event(QEvent *e)
return true;
case QEvent::Move:
// Let QDockWidgetPrivate::moseEvent handle the dragging
- if (QDockWidget *dw = topDockWidget())
+ if (QDockWidget *dw = activeTabbedDockWidget())
static_cast<QDockWidgetPrivate *>(QObjectPrivate::get(dw))->moveEvent(static_cast<QMoveEvent*>(e));
return true;
case QEvent::NonClientAreaMouseMove:
@@ -276,7 +298,7 @@ bool QDockWidgetGroupWindow::event(QEvent *e)
case QEvent::NonClientAreaMouseButtonRelease:
case QEvent::NonClientAreaMouseButtonDblClick:
// Let the QDockWidgetPrivate of the currently visible dock widget handle the drag and drop
- if (QDockWidget *dw = topDockWidget())
+ if (QDockWidget *dw = activeTabbedDockWidget())
static_cast<QDockWidgetPrivate *>(QObjectPrivate::get(dw))->nonClientAreaMouseEvent(static_cast<QMouseEvent*>(e));
return true;
case QEvent::ChildAdded:
@@ -308,17 +330,49 @@ void QDockWidgetGroupWindow::paintEvent(QPaintEvent *)
QDockAreaLayoutInfo *QDockWidgetGroupWindow::layoutInfo() const
{
- return static_cast<QDockWidgetGroupLayout*>(layout())->layoutInfo();
+ return static_cast<QDockWidgetGroupLayout *>(layout())->dockAreaLayoutInfo();
}
/*! \internal
- Returns the currently active QDockWidget.
+ If this is a floating tab bar returns the currently the QDockWidgetGroupWindow that contains
+ tab, otherwise, return nullptr;
+ \note: if there is only one QDockWidget, it's still considered as a floating tab
*/
-QDockWidget *QDockWidgetGroupWindow::topDockWidget() const
+const QDockAreaLayoutInfo *QDockWidgetGroupWindow::tabLayoutInfo() const
+{
+ const QDockAreaLayoutInfo *info = layoutInfo();
+ while (info && !info->tabbed) {
+ // There should be only one tabbed subinfo otherwise we are not a floating tab but a real
+ // window
+ const QDockAreaLayoutInfo *next = nullptr;
+ bool isSingle = false;
+ for (const auto &item : info->item_list) {
+ if (item.skip() || (item.flags & QDockAreaLayoutItem::GapItem))
+ continue;
+ if (next || isSingle) // Two visible things
+ return nullptr;
+ if (item.subinfo)
+ next = item.subinfo;
+ else if (item.widgetItem)
+ isSingle = true;
+ }
+ if (isSingle)
+ return info;
+ info = next;
+ }
+ return info;
+}
+
+/*! \internal
+ If this is a floating tab bar returns the currently active QDockWidget, otherwise nullptr
+ */
+QDockWidget *QDockWidgetGroupWindow::activeTabbedDockWidget() const
{
- QDockAreaLayoutInfo *info = layoutInfo();
- QDockWidget *dw = 0;
+ QDockWidget *dw = nullptr;
#if QT_CONFIG(tabbar)
+ const QDockAreaLayoutInfo *info = tabLayoutInfo();
+ if (!info)
+ return nullptr;
if (info->tabBar && info->tabBar->currentIndex() >= 0) {
int i = info->tabIndexToListIndex(info->tabBar->currentIndex());
if (i >= 0) {
@@ -327,7 +381,6 @@ QDockWidget *QDockWidgetGroupWindow::topDockWidget() const
dw = qobject_cast<QDockWidget *>(item.widgetItem->widget());
}
}
-#endif
if (!dw) {
for (int i = 0; !dw && i < info->item_list.count(); ++i) {
const QDockAreaLayoutItem &item = info->item_list.at(i);
@@ -338,6 +391,7 @@ QDockWidget *QDockWidgetGroupWindow::topDockWidget() const
dw = qobject_cast<QDockWidget *>(item.widgetItem->widget());
}
}
+#endif
return dw;
}
@@ -384,18 +438,18 @@ void QDockWidgetGroupWindow::destroyOrHideIfEmpty()
}
/*! \internal
- Sets the flags of this window in accordence to the capabilities of the dock widgets
+ Sets the flags of this window in accordance to the capabilities of the dock widgets
*/
void QDockWidgetGroupWindow::adjustFlags()
{
- QDockWidget *top = topDockWidget();
- if (!top)
- return;
- const bool nativeDeco = static_cast<QDockWidgetGroupLayout *>(layout())->nativeWindowDeco();
-
Qt::WindowFlags oldFlags = windowFlags();
Qt::WindowFlags flags = oldFlags;
- if (nativeDeco) {
+
+ QDockWidget *top = activeTabbedDockWidget();
+ if (!top) { // nested tabs, show window decoration
+ flags =
+ ((oldFlags & ~Qt::FramelessWindowHint) | Qt::CustomizeWindowHint | Qt::WindowTitleHint);
+ } else if (static_cast<QDockWidgetGroupLayout *>(layout())->nativeWindowDeco()) {
flags |= Qt::CustomizeWindowHint | Qt::WindowTitleHint;
flags.setFlag(Qt::WindowCloseButtonHint, top->features() & QDockWidget::DockWidgetClosable);
flags &= ~Qt::FramelessWindowHint;
@@ -428,21 +482,99 @@ void QDockWidgetGroupWindow::adjustFlags()
show(); // setWindowFlags hides the window
}
- setWindowTitle(top->windowTitle());
- setWindowIcon(top->windowIcon());
+ QWidget *titleBarOf = top ? top : parentWidget();
+ setWindowTitle(titleBarOf->windowTitle());
+ setWindowIcon(titleBarOf->windowIcon());
}
bool QDockWidgetGroupWindow::hasNativeDecos() const
{
+ QDockWidget *dw = activeTabbedDockWidget();
+ if (!dw) // We have a group of nested QDockWidgets (not just floating tabs)
+ return true;
+
if (!QDockWidgetLayout::wmSupportsNativeWindowDeco())
return false;
- if (QDockWidget *dw = topDockWidget())
- return dw->titleBarWidget() == nullptr;
+ return dw->titleBarWidget() == nullptr;
+}
+/*
+ The given widget is hovered over this floating group.
+ This function will save the state and create a gap in the actual state.
+ currentGapRect and currentGapPos will be set.
+ One must call restore() or apply() after this function.
+ Returns true if there was any change in the currentGapPos
+ */
+bool QDockWidgetGroupWindow::hover(QLayoutItem *widgetItem, const QPoint &mousePos)
+{
+ QDockAreaLayoutInfo &savedState = static_cast<QDockWidgetGroupLayout *>(layout())->savedState;
+ if (savedState.isEmpty())
+ savedState = *layoutInfo();
+
+ QMainWindow::DockOptions opts = static_cast<QMainWindow *>(parentWidget())->dockOptions();
+ bool nestingEnabled =
+ (opts & QMainWindow::AllowNestedDocks) && !(opts & QMainWindow::ForceTabbedDocks);
+ QDockAreaLayoutInfo::TabMode tabMode =
+ nestingEnabled ? QDockAreaLayoutInfo::AllowTabs : QDockAreaLayoutInfo::ForceTabs;
+ if (auto group = qobject_cast<QDockWidgetGroupWindow *>(widgetItem->widget())) {
+ if (!group->tabLayoutInfo())
+ tabMode = QDockAreaLayoutInfo::NoTabs;
+ }
+
+ QDockAreaLayoutInfo newState = savedState;
+ if (newState.tabbed) {
+ // insertion into a top-level tab
+ newState.item_list = { QDockAreaLayoutItem(new QDockAreaLayoutInfo(newState)) };
+ newState.item_list.first().size = pick(savedState.o, savedState.rect.size());
+ newState.tabbed = false;
+ newState.tabBar = nullptr;
+ }
+
+ auto newGapPos = newState.gapIndex(mousePos, nestingEnabled, tabMode);
+ Q_ASSERT(!newGapPos.isEmpty());
+ if (newGapPos == currentGapPos)
+ return false; // gap is already there
+ currentGapPos = newGapPos;
+ newState.insertGap(currentGapPos, widgetItem);
+ newState.fitItems();
+ currentGapRect = newState.info(currentGapPos)->itemRect(currentGapPos.last(), true);
+ *layoutInfo() = std::move(newState);
+ layoutInfo()->apply(opts & QMainWindow::AnimatedDocks);
return true;
}
+/*
+ Remove the gap that was created by hover()
+ */
+void QDockWidgetGroupWindow::restore()
+{
+ QDockAreaLayoutInfo &savedState = static_cast<QDockWidgetGroupLayout *>(layout())->savedState;
+ if (!savedState.isEmpty()) {
+ *layoutInfo() = savedState;
+ savedState = QDockAreaLayoutInfo();
+ }
+ currentGapRect = QRect();
+ currentGapPos.clear();
+ adjustFlags();
+ layoutInfo()->fitItems();
+ layoutInfo()->apply(static_cast<QMainWindow *>(parentWidget())->dockOptions()
+ & QMainWindow::AnimatedDocks);
+}
+
+/*
+ Apply the state that was created by hover
+ */
+void QDockWidgetGroupWindow::apply()
+{
+ static_cast<QDockWidgetGroupLayout *>(layout())->savedState.clear();
+ currentGapRect = QRect();
+ layoutInfo()->plug(currentGapPos);
+ currentGapPos.clear();
+ adjustFlags();
+ layoutInfo()->apply(false);
+}
+
#endif
/******************************************************************************
@@ -696,7 +828,14 @@ QList<int> QMainWindowLayoutState::gapIndex(QWidget *widget,
// is it a dock widget?
if (qobject_cast<QDockWidget *>(widget) != 0
|| qobject_cast<QDockWidgetGroupWindow *>(widget)) {
- result = dockAreaLayout.gapIndex(pos);
+ bool disallowTabs = false;
+#if QT_CONFIG(tabbar)
+ if (auto *group = qobject_cast<QDockWidgetGroupWindow *>(widget)) {
+ if (!group->tabLayoutInfo()) // Disallow to drop nested docks as a tab
+ disallowTabs = true;
+ }
+#endif
+ result = dockAreaLayout.gapIndex(pos, disallowTabs);
if (!result.isEmpty())
result.prepend(1);
return result;
@@ -1824,12 +1963,16 @@ void QMainWindowLayout::invalidate()
}
#ifndef QT_NO_DOCKWIDGET
-void QMainWindowLayout::setCurrentHoveredFloat(QWidget *w)
+void QMainWindowLayout::setCurrentHoveredFloat(QDockWidgetGroupWindow *w)
{
if (currentHoveredFloat != w) {
if (currentHoveredFloat) {
disconnect(currentHoveredFloat.data(), &QObject::destroyed,
this, &QMainWindowLayout::updateGapIndicator);
+ if (currentHoveredFloat)
+ currentHoveredFloat->restore();
+ } else if (w) {
+ restore(true);
}
currentHoveredFloat = w;
@@ -1900,52 +2043,30 @@ void QMainWindowLayout::revert(QLayoutItem *widgetItem)
bool QMainWindowLayout::plug(QLayoutItem *widgetItem)
{
-#ifndef QT_NO_DOCKWIDGET
+#if QT_CONFIG(dockwidget) && QT_CONFIG(tabwidget) && QT_CONFIG(tabbar)
if (currentHoveredFloat) {
QWidget *widget = widgetItem->widget();
QList<int> previousPath = layoutState.indexOf(widget);
if (!previousPath.isEmpty())
layoutState.remove(previousPath);
+ previousPath = currentHoveredFloat->layoutInfo()->indexOf(widget);
// Let's remove the widget from any possible group window
foreach (QDockWidgetGroupWindow *dwgw,
parent()->findChildren<QDockWidgetGroupWindow*>(QString(), Qt::FindDirectChildrenOnly)) {
+ if (dwgw == currentHoveredFloat)
+ continue;
QList<int> path = dwgw->layoutInfo()->indexOf(widget);
if (!path.isEmpty())
dwgw->layoutInfo()->remove(path);
}
currentGapRect = QRect();
-#if QT_CONFIG(tabwidget)
- if (QDockWidget *dropTo = qobject_cast<QDockWidget*>(currentHoveredFloat)) {
- //dropping to a normal widget, we mutate it in a QDockWidgetGroupWindow with two tabs
- QDockWidgetGroupWindow *floatingTabs = createTabbedDockWindow();
- floatingTabs->setGeometry(dropTo->geometry());
- QDockAreaLayoutInfo *info = floatingTabs->layoutInfo();
- *info = QDockAreaLayoutInfo(&layoutState.dockAreaLayout.sep, QInternal::LeftDock,
- Qt::Horizontal, QTabBar::RoundedSouth,
- static_cast<QMainWindow*>(parentWidget()));
- info->tabbed = true;
- QLayout *parentLayout = currentHoveredFloat->parentWidget()->layout();
- info->item_list.append(QDockAreaLayoutItem(parentLayout->takeAt(parentLayout->indexOf(currentHoveredFloat))));
-
- dropTo->setParent(floatingTabs);
- dropTo->show();
- dropTo->d_func()->plug(QRect());
- setCurrentHoveredFloat(floatingTabs);
- }
-#endif // QT_CONFIG(tabwidget)
-#if QT_CONFIG(tabbar)
- QDockWidgetGroupWindow *dwgw = qobject_cast<QDockWidgetGroupWindow *>(currentHoveredFloat);
- Q_ASSERT(dwgw);
- Q_ASSERT(dwgw->layoutInfo()->tabbed); // because floating group should always be tabbed
- previousPath = dwgw->layoutInfo()->indexOf(widget);
+ currentHoveredFloat->apply();
if (!previousPath.isEmpty())
- dwgw->layoutInfo()->remove(previousPath);
- dwgw->layoutInfo()->tab(0, widgetItem);
- QRect globalRect = dwgw->layoutInfo()->tabContentRect();
- globalRect.moveTopLeft(dwgw->mapToGlobal(globalRect.topLeft()));
+ currentHoveredFloat->layoutInfo()->remove(previousPath);
+ QRect globalRect = currentHoveredFloat->currentGapRect;
+ globalRect.moveTopLeft(currentHoveredFloat->mapToGlobal(globalRect.topLeft()));
pluggingWidget = widget;
widgetAnimator.animate(widget, globalRect, dockOptions & QMainWindow::AnimatedDocks);
-#endif // QT_CONFIG(tabbar)
return true;
}
#endif
@@ -2014,57 +2135,55 @@ void QMainWindowLayout::animationFinished(QWidget *widget)
if (widget == pluggingWidget) {
#ifndef QT_NO_DOCKWIDGET
+#if QT_CONFIG(tabbar)
if (QDockWidgetGroupWindow *dwgw = qobject_cast<QDockWidgetGroupWindow *>(widget)) {
// When the animated widget was a QDockWidgetGroupWindow, it means each of the
// embedded QDockWidget needs to be plugged back into the QMainWindow layout.
savedState.clear();
- QDockAreaLayoutInfo* info = dwgw->layoutInfo();
- QDockAreaLayoutInfo* parentInfo;
- QList<int> path;
-
- if (QDockWidgetGroupWindow *dropTo = qobject_cast<QDockWidgetGroupWindow *>(currentHoveredFloat)) {
- parentInfo = dropTo->layoutInfo();
-#if QT_CONFIG(tabbar)
- Q_ASSERT(parentInfo->tabbed);
-#endif
- path = parentInfo->indexOf(widget);
- Q_ASSERT(path.size() == 1);
+ QDockAreaLayoutInfo *srcInfo = dwgw->layoutInfo();
+ const QDockAreaLayoutInfo *srcTabInfo = dwgw->tabLayoutInfo();
+ QDockAreaLayoutInfo *dstParentInfo;
+ QList<int> dstPath;
+
+ if (currentHoveredFloat) {
+ dstPath = currentHoveredFloat->layoutInfo()->indexOf(widget);
+ Q_ASSERT(dstPath.size() >= 1);
+ dstParentInfo = currentHoveredFloat->layoutInfo()->info(dstPath);
} else {
- path = layoutState.dockAreaLayout.indexOf(widget);
- Q_ASSERT(path.size() >= 2);
- parentInfo = layoutState.dockAreaLayout.info(path);
- Q_ASSERT(parentInfo);
+ dstPath = layoutState.dockAreaLayout.indexOf(widget);
+ Q_ASSERT(dstPath.size() >= 2);
+ dstParentInfo = layoutState.dockAreaLayout.info(dstPath);
}
-#if QT_CONFIG(tabbar)
- if (parentInfo->tabbed) {
+ Q_ASSERT(dstParentInfo);
+ int idx = dstPath.constLast();
+ Q_ASSERT(dstParentInfo->item_list[idx].widgetItem->widget() == dwgw);
+ if (dstParentInfo->tabbed && srcTabInfo) {
// merge the two tab widgets
- int idx = path.constLast();
- Q_ASSERT(parentInfo->item_list[idx].widgetItem->widget() == dwgw);
- delete parentInfo->item_list[idx].widgetItem;
- parentInfo->item_list.removeAt(idx);
- std::copy(info->item_list.cbegin(), info->item_list.cend(),
- std::inserter(parentInfo->item_list, parentInfo->item_list.begin() + idx));
- quintptr currentId = info->currentTabId();
- *info = QDockAreaLayoutInfo();
- parentInfo->reparentWidgets(currentHoveredFloat ? currentHoveredFloat.data() : parentWidget());
- parentInfo->updateTabBar();
- parentInfo->setCurrentTabId(currentId);
- } else
-#endif // QT_CONFIG(tabbar)
- {
- QDockAreaLayoutItem &item = layoutState.dockAreaLayout.item(path);
+ delete dstParentInfo->item_list[idx].widgetItem;
+ dstParentInfo->item_list.removeAt(idx);
+ std::copy(srcTabInfo->item_list.cbegin(), srcTabInfo->item_list.cend(),
+ std::inserter(dstParentInfo->item_list,
+ dstParentInfo->item_list.begin() + idx));
+ quintptr currentId = srcTabInfo->currentTabId();
+ *srcInfo = QDockAreaLayoutInfo();
+ dstParentInfo->reparentWidgets(currentHoveredFloat ? currentHoveredFloat.data()
+ : parentWidget());
+ dstParentInfo->updateTabBar();
+ dstParentInfo->setCurrentTabId(currentId);
+ } else {
+ QDockAreaLayoutItem &item = dstParentInfo->item_list[idx];
Q_ASSERT(item.widgetItem->widget() == dwgw);
delete item.widgetItem;
item.widgetItem = 0;
- item.subinfo = new QDockAreaLayoutInfo(qMove(*info));
- *info = QDockAreaLayoutInfo();
-#if QT_CONFIG(tabbar)
- item.subinfo->reparentWidgets(parentWidget());
- item.subinfo->setTabBarShape(parentInfo->tabBarShape);
-#endif
+ item.subinfo = new QDockAreaLayoutInfo(std::move(*srcInfo));
+ *srcInfo = QDockAreaLayoutInfo();
+ item.subinfo->reparentWidgets(currentHoveredFloat ? currentHoveredFloat.data()
+ : parentWidget());
+ item.subinfo->setTabBarShape(dstParentInfo->tabBarShape);
}
dwgw->destroyOrHideIfEmpty();
}
+#endif
if (QDockWidget *dw = qobject_cast<QDockWidget*>(widget)) {
dw->setParent(currentHoveredFloat ? currentHoveredFloat.data() : parentWidget());
@@ -2220,6 +2339,36 @@ void QMainWindowLayout::setCentralWidget(QWidget *widget)
invalidate();
}
+#if QT_CONFIG(dockwidget) && QT_CONFIG(tabwidget)
+/*! \internal
+ This helper function is called by QMainWindowLayout::unplug if QMainWindow::GroupedDragging is
+ set and we are dragging the title bar of a non-floating QDockWidget.
+ If one should unplug the whole group, do so and return true, otherwise return false.
+ \a item is pointing to the QLayoutItem that holds the QDockWidget, but will be updated to the
+ QLayoutItem that holds the new QDockWidgetGroupWindow if the group is unplugged.
+*/
+static bool unplugGroup(QMainWindowLayout *layout, QLayoutItem **item,
+ QDockAreaLayoutItem &parentItem)
+{
+ if (!parentItem.subinfo || !parentItem.subinfo->tabbed)
+ return false;
+
+ // The QDockWidget is part of a group of tab and we need to unplug them all.
+
+ QDockWidgetGroupWindow *floatingTabs = layout->createTabbedDockWindow();
+ QDockAreaLayoutInfo *info = floatingTabs->layoutInfo();
+ *info = std::move(*parentItem.subinfo);
+ delete parentItem.subinfo;
+ parentItem.subinfo = nullptr;
+ floatingTabs->setGeometry(info->rect.translated(layout->parentWidget()->pos()));
+ floatingTabs->show();
+ floatingTabs->raise();
+ *item = new QDockWidgetGroupWindowItem(floatingTabs);
+ parentItem.widgetItem = *item;
+ return true;
+}
+#endif
+
/*! \internal
Unplug \a widget (QDockWidget or QToolBar) from it's parent container.
@@ -2233,19 +2382,31 @@ void QMainWindowLayout::setCentralWidget(QWidget *widget)
QLayoutItem *QMainWindowLayout::unplug(QWidget *widget, bool group)
{
#if !defined(QT_NO_DOCKWIDGET) && !defined(QT_NO_TABBAR)
- if (!widget->isWindow() && qobject_cast<const QDockWidgetGroupWindow *>(widget->parentWidget())) {
- if (group) {
+ auto *groupWindow = qobject_cast<const QDockWidgetGroupWindow *>(widget->parentWidget());
+ if (!widget->isWindow() && groupWindow) {
+ if (group && groupWindow->tabLayoutInfo()) {
// We are just dragging a floating window as it, not need to do anything, we just have to
// look up the corresponding QWidgetItem* if it exists
- QList<int> tabbedWindowPath = layoutState.indexOf(widget->parentWidget());
- return tabbedWindowPath.isEmpty() ? 0 : layoutState.item(tabbedWindowPath);
+ if (QDockAreaLayoutInfo *info = dockInfo(widget->parentWidget())) {
+ QList<int> groupWindowPath = info->indexOf(widget->parentWidget());
+ return groupWindowPath.isEmpty() ? nullptr : info->item(groupWindowPath).widgetItem;
+ }
+ return nullptr;
+ }
+ QList<int> path = groupWindow->layoutInfo()->indexOf(widget);
+ QLayoutItem *item = groupWindow->layoutInfo()->item(path).widgetItem;
+ if (group && path.size() > 1
+ && unplugGroup(this, &item,
+ groupWindow->layoutInfo()->item(path.mid(0, path.size() - 1)))) {
+ return item;
} else {
// We are unplugging a dock widget from a floating window.
- if (QDockWidget *dw = qobject_cast<QDockWidget*>(widget)) {
- dw->d_func()->unplug(widget->geometry());
- int index = widget->parentWidget()->layout()->indexOf(widget);
- return widget->parentWidget()->layout()->itemAt(index);
- }
+ QDockWidget *dw = qobject_cast<QDockWidget *>(widget);
+ Q_ASSERT(dw); // cannot be a QDockWidgetGroupWindow because it's not floating.
+ dw->d_func()->unplug(widget->geometry());
+ groupWindow->layoutInfo()->fitItems();
+ groupWindow->layoutInfo()->apply(dockOptions & QMainWindow::AnimatedDocks);
+ return item;
}
}
#endif
@@ -2263,30 +2424,15 @@ QLayoutItem *QMainWindowLayout::unplug(QWidget *widget, bool group)
#ifndef QT_NO_DOCKWIDGET
if (QDockWidget *dw = qobject_cast<QDockWidget*>(widget)) {
Q_ASSERT(path.constFirst() == 1);
- bool actualGroup = false;
#if QT_CONFIG(tabwidget)
- if (group && (dockOptions & QMainWindow::GroupedDragging) && path.size() > 3) {
- QDockAreaLayoutItem &parentItem = layoutState.dockAreaLayout.item(path.mid(1, path.size() - 2));
- if (parentItem.subinfo && parentItem.subinfo->tabbed) {
- // The QDockWidget is part of a group of tab and we need to unplug them all.
- actualGroup = true;
- path.removeLast();
-
- QDockWidgetGroupWindow* floatingTabs = createTabbedDockWindow();
- QDockAreaLayoutInfo* info = floatingTabs->layoutInfo();
- *info = qMove(*parentItem.subinfo);
- delete parentItem.subinfo;
- parentItem.subinfo = 0;
- floatingTabs->setGeometry(info->rect.translated(parentWidget()->pos()));
- floatingTabs->show();
- floatingTabs->raise();
- item = new QDockWidgetGroupWindowItem(floatingTabs);
- parentItem.widgetItem = item;
- savedState = layoutState;
- }
- }
+ if (group && (dockOptions & QMainWindow::GroupedDragging) && path.size() > 3
+ && unplugGroup(this, &item,
+ layoutState.dockAreaLayout.item(path.mid(1, path.size() - 2)))) {
+ path.removeLast();
+ savedState = layoutState;
+ } else
#endif // QT_CONFIG(tabwidget)
- if (!actualGroup) {
+ {
dw->d_func()->unplug(r);
}
}
@@ -2315,11 +2461,11 @@ QLayoutItem *QMainWindowLayout::unplug(QWidget *widget, bool group)
void QMainWindowLayout::updateGapIndicator()
{
#ifndef QT_NO_RUBBERBAND
- if ((!widgetAnimator.animating() && !currentGapPos.isEmpty())
+ if (!widgetAnimator.animating() && (!currentGapPos.isEmpty()
#if QT_CONFIG(dockwidget)
- || currentHoveredFloat
+ || currentHoveredFloat
#endif
- ) {
+ )) {
QWidget *expectedParent =
#if QT_CONFIG(dockwidget)
currentHoveredFloat ? currentHoveredFloat.data() :
@@ -2332,11 +2478,13 @@ void QMainWindowLayout::updateGapIndicator()
} else if (gapIndicator->parent() != expectedParent) {
gapIndicator->setParent(expectedParent);
}
- gapIndicator->setGeometry(
+
#if QT_CONFIG(dockwidget)
- currentHoveredFloat ? currentHoveredFloat->rect() :
+ if (currentHoveredFloat)
+ gapIndicator->setGeometry(currentHoveredFloat->currentGapRect);
+ else
#endif
- currentGapRect);
+ gapIndicator->setGeometry(currentGapRect);
gapIndicator->show();
gapIndicator->raise();
} else if (gapIndicator) {
@@ -2385,8 +2533,31 @@ void QMainWindowLayout::hover(QLayoutItem *widgetItem, const QPoint &mousePos)
if (!w->geometry().contains(mousePos))
continue;
- setCurrentHoveredFloat(w);
- restore(true);
+ if (auto dropTo = qobject_cast<QDockWidget *>(w)) {
+ // dropping to a normal widget, we mutate it in a QDockWidgetGroupWindow with two
+ // tabs
+ QDockWidgetGroupWindow *floatingTabs = createTabbedDockWindow(); // FIXME
+ floatingTabs->setGeometry(dropTo->geometry());
+ QDockAreaLayoutInfo *info = floatingTabs->layoutInfo();
+ *info = QDockAreaLayoutInfo(&layoutState.dockAreaLayout.sep, QInternal::LeftDock,
+ Qt::Horizontal, QTabBar::RoundedSouth,
+ static_cast<QMainWindow *>(parentWidget()));
+ info->tabbed = true;
+ QLayout *parentLayout = dropTo->parentWidget()->layout();
+ info->item_list.append(
+ QDockAreaLayoutItem(parentLayout->takeAt(parentLayout->indexOf(dropTo))));
+
+ dropTo->setParent(floatingTabs);
+ dropTo->show();
+ dropTo->d_func()->plug(QRect());
+ w = floatingTabs;
+ }
+ Q_ASSERT(qobject_cast<QDockWidgetGroupWindow *>(w));
+ auto group = static_cast<QDockWidgetGroupWindow *>(w);
+ if (group->hover(widgetItem, group->mapFromGlobal(mousePos))) {
+ setCurrentHoveredFloat(group);
+ applyState(layoutState); // update the tabbars
+ }
return;
}
}
@@ -2454,7 +2625,7 @@ void QMainWindowLayout::hover(QLayoutItem *widgetItem, const QPoint &mousePos)
#ifndef QT_NO_DOCKWIDGET
parentWidget()->update(layoutState.dockAreaLayout.separatorRegion());
#endif
- layoutState = newState;
+ layoutState = std::move(newState);
applyState(layoutState);
updateGapIndicator();
diff --git a/src/widgets/widgets/qmainwindowlayout_p.h b/src/widgets/widgets/qmainwindowlayout_p.h
index dcd94e3617..cc3b4e9261 100644
--- a/src/widgets/widgets/qmainwindowlayout_p.h
+++ b/src/widgets/widgets/qmainwindowlayout_p.h
@@ -327,11 +327,19 @@ public:
explicit QDockWidgetGroupWindow(QWidget* parent = 0, Qt::WindowFlags f = 0)
: QWidget(parent, f) {}
QDockAreaLayoutInfo *layoutInfo() const;
- QDockWidget *topDockWidget() const;
+ const QDockAreaLayoutInfo *tabLayoutInfo() const;
+ QDockWidget *activeTabbedDockWidget() const;
void destroyOrHideIfEmpty();
void adjustFlags();
bool hasNativeDecos() const;
+ bool hover(QLayoutItem *widgetItem, const QPoint &mousePos);
+ void restore();
+ void apply();
+
+ QRect currentGapRect;
+ QList<int> currentGapPos;
+
protected:
bool event(QEvent *) Q_DECL_OVERRIDE;
void paintEvent(QPaintEvent*) Q_DECL_OVERRIDE;
@@ -543,8 +551,8 @@ public:
QPointer<QRubberBand> gapIndicator;
#endif
#ifndef QT_NO_DOCKWIDGET
- QPointer<QWidget> currentHoveredFloat; // set when dragging over a floating dock widget
- void setCurrentHoveredFloat(QWidget *w);
+ QPointer<QDockWidgetGroupWindow> currentHoveredFloat; // set when dragging over a floating dock widget
+ void setCurrentHoveredFloat(QDockWidgetGroupWindow *w);
#endif
void hover(QLayoutItem *widgetItem, const QPoint &mousePos);