diff options
author | Olivier Goffart <ogoffart@woboq.com> | 2015-07-05 11:55:09 +0200 |
---|---|---|
committer | Olivier Goffart (Woboq GmbH) <ogoffart@woboq.com> | 2015-08-01 06:48:17 +0000 |
commit | 0e2d8ba7929df52b727c2d31315d1b8f10857b01 (patch) | |
tree | e49dcc264dbcfc1a4f0220eb941ff5c7962f4046 | |
parent | 3ae2387f375798a983b6d052723f10fc88b63da0 (diff) |
QMainWindow: allow dropping QDockWidget to floating docks
In the QMainWindow::GroupedDragging mode, we can have floating
tabs of QDockWidget's, but it was not possible to drop onto
already floating QDockWidgets to tab them.
Task-number: QTBUG-47211
Change-Id: Ic666f6f8816d91a3eed844a6da1eb8698c8c7a0c
Reviewed-by: Friedemann Kleint <Friedemann.Kleint@theqtcompany.com>
Reviewed-by: Paul Olav Tvete <paul.tvete@theqtcompany.com>
-rw-r--r-- | src/widgets/widgets/qdockwidget.cpp | 6 | ||||
-rw-r--r-- | src/widgets/widgets/qmainwindowlayout.cpp | 162 | ||||
-rw-r--r-- | src/widgets/widgets/qmainwindowlayout_p.h | 19 |
3 files changed, 153 insertions, 34 deletions
diff --git a/src/widgets/widgets/qdockwidget.cpp b/src/widgets/widgets/qdockwidget.cpp index 1b7473fbd7..6ea92e4c4d 100644 --- a/src/widgets/widgets/qdockwidget.cpp +++ b/src/widgets/widgets/qdockwidget.cpp @@ -760,7 +760,11 @@ void QDockWidgetPrivate::startDrag(bool group) QMainWindow::addDockWidget, so the QMainWindowLayout has no widget item for me. :( I have to create it myself, and then delete it if I don't get dropped into a dock area. */ - state->widgetItem = new QDockWidgetItem(q); + QDockWidgetGroupWindow *floatingTab = qobject_cast<QDockWidgetGroupWindow*>(parent); + if (floatingTab && !q->isFloating()) + state->widgetItem = new QDockWidgetGroupWindowItem(floatingTab); + else + state->widgetItem = new QDockWidgetItem(q); state->ownWidgetItem = true; } diff --git a/src/widgets/widgets/qmainwindowlayout.cpp b/src/widgets/widgets/qmainwindowlayout.cpp index aa2cf5c999..54e956c4cf 100644 --- a/src/widgets/widgets/qmainwindowlayout.cpp +++ b/src/widgets/widgets/qmainwindowlayout.cpp @@ -244,20 +244,6 @@ public: } }; -// This item will be used in the layout for the gap item. We cannot use QWidgetItem directly -// because QWidgetItem functions return an empty size for widgets are are floating. -class QDockWidgetGroupWindowItem : public QWidgetItem -{ -public: - QDockWidgetGroupWindowItem(QDockWidgetGroupWindow *parent) : QWidgetItem(parent) {} - QSize minimumSize() const Q_DECL_OVERRIDE { return lay()->minimumSize(); } - QSize maximumSize() const Q_DECL_OVERRIDE { return lay()->maximumSize(); } - QSize sizeHint() const Q_DECL_OVERRIDE { return lay()->sizeHint(); } - -private: - QLayout *lay() const { return const_cast<QDockWidgetGroupWindowItem *>(this)->widget()->layout(); } -}; - bool QDockWidgetGroupWindow::event(QEvent *e) { switch (e->type()) { @@ -1934,6 +1920,54 @@ void QMainWindowLayout::revert(QLayoutItem *widgetItem) bool QMainWindowLayout::plug(QLayoutItem *widgetItem) { +#ifndef QT_NO_DOCKWIDGET + if (currentHoveredFloat) { + QWidget *widget = widgetItem->widget(); + QList<int> previousPath = layoutState.indexOf(widget); + if (!previousPath.isEmpty()) + layoutState.remove(previousPath); + // Let's remove the widget from any possible group window + foreach (QDockWidgetGroupWindow *dwgw, + parent()->findChildren<QDockWidgetGroupWindow*>(QString(), Qt::FindDirectChildrenOnly)) { + QList<int> path = dwgw->layoutInfo()->indexOf(widget); + if (!path.isEmpty()) + dwgw->layoutInfo()->remove(path); + } + currentGapRect = QRect(); + + 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(parentLayout->takeAt(parentLayout->indexOf(currentHoveredFloat))); + + dropTo->setParent(floatingTabs); + dropTo->show(); + dropTo->d_func()->plug(QRect()); + currentHoveredFloat = floatingTabs; + } + + 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); + if (!previousPath.isEmpty()) + dwgw->layoutInfo()->remove(previousPath); + dwgw->layoutInfo()->tab(0, widgetItem); + QRect globalRect = dwgw->layoutInfo()->tabContentRect(); + globalRect.moveTopLeft(dwgw->mapToGlobal(globalRect.topLeft())); + pluggingWidget = widget; + widgetAnimator.animate(widget, globalRect, dockOptions & QMainWindow::AnimatedDocks); + return true; + } +#endif + if (!parentWidget()->isVisible() || parentWidget()->isMinimized() || currentGapPos.isEmpty()) return false; @@ -2003,11 +2037,21 @@ void QMainWindowLayout::animationFinished(QWidget *widget) // embedded QDockWidget needs to be plugged back into the QMainWindow layout. savedState.clear(); QDockAreaLayoutInfo* info = dwgw->layoutInfo(); - QList<int> path = layoutState.dockAreaLayout.indexOf(widget); - Q_ASSERT(path.size() >= 2); + QDockAreaLayoutInfo* parentInfo; + QList<int> path; + + if (QDockWidgetGroupWindow *dropTo = qobject_cast<QDockWidgetGroupWindow *>(currentHoveredFloat)) { + parentInfo = dropTo->layoutInfo(); + Q_ASSERT(parentInfo->tabbed); + path = parentInfo->indexOf(widget); + Q_ASSERT(path.size() == 1); + } else { + path = layoutState.dockAreaLayout.indexOf(widget); + Q_ASSERT(path.size() >= 2); + parentInfo = layoutState.dockAreaLayout.info(path); + Q_ASSERT(parentInfo); + } - QDockAreaLayoutInfo* parentInfo = layoutState.dockAreaLayout.info(path); - Q_ASSERT(parentInfo); if (parentInfo->tabbed) { // merge the two tab widgets int idx = path.last(); @@ -2018,7 +2062,7 @@ void QMainWindowLayout::animationFinished(QWidget *widget) std::inserter(parentInfo->item_list, parentInfo->item_list.begin() + idx)); quintptr currentId = info->currentTabId(); *info = QDockAreaLayoutInfo(); - parentInfo->reparentWidgets(parentWidget()); + parentInfo->reparentWidgets(currentHoveredFloat ? currentHoveredFloat.data() : parentWidget()); parentInfo->updateTabBar(); parentInfo->setCurrentTabId(currentId); } else { @@ -2034,8 +2078,13 @@ void QMainWindowLayout::animationFinished(QWidget *widget) dwgw->destroyIfEmpty(); } - if (QDockWidget *dw = qobject_cast<QDockWidget*>(widget)) + if (QDockWidget *dw = qobject_cast<QDockWidget*>(widget)) { + if (currentHoveredFloat) { + dw->setParent(currentHoveredFloat); + dw->show(); + } dw->d_func()->plug(currentGapRect); + } #endif #ifndef QT_NO_TOOLBAR if (QToolBar *tb = qobject_cast<QToolBar*>(widget)) @@ -2045,6 +2094,7 @@ void QMainWindowLayout::animationFinished(QWidget *widget) savedState.clear(); currentGapPos.clear(); pluggingWidget = 0; + currentHoveredFloat = Q_NULLPTR; //applying the state will make sure that the currentGap is updated correctly //and all the geometries (especially the one from the central widget) is correct layoutState.apply(false); @@ -2106,9 +2156,6 @@ QMainWindowLayout::QMainWindowLayout(QMainWindow *mainwindow, QLayout *parentLay #endif // QT_NO_DOCKWIDGET , widgetAnimator(this) , pluggingWidget(0) -#ifndef QT_NO_RUBBERBAND - , gapIndicator(new QRubberBand(QRubberBand::Rectangle, mainwindow)) -#endif //QT_NO_RUBBERBAND #ifdef Q_DEAD_CODE_FROM_QT4_MAC , blockVisiblityCheck(false) #endif @@ -2126,12 +2173,6 @@ QMainWindowLayout::QMainWindowLayout(QMainWindow *mainwindow, QLayout *parentLay tabPositions[i] = QTabWidget::South; #endif #endif // QT_NO_DOCKWIDGET - -#ifndef QT_NO_RUBBERBAND - // For accessibility to identify this special widget. - gapIndicator->setObjectName(QLatin1String("qt_rubberband")); - gapIndicator->hide(); -#endif pluggingWidget = 0; setObjectName(mainwindow->objectName() + QLatin1String("_layout")); @@ -2288,9 +2329,22 @@ QLayoutItem *QMainWindowLayout::unplug(QWidget *widget, bool group) void QMainWindowLayout::updateGapIndicator() { #ifndef QT_NO_RUBBERBAND - gapIndicator->setVisible(!widgetAnimator.animating() && !currentGapPos.isEmpty()); - gapIndicator->setGeometry(currentGapRect); -#endif + if ((!widgetAnimator.animating() && !currentGapPos.isEmpty()) || currentHoveredFloat) { + QWidget *expectedParent = currentHoveredFloat ? currentHoveredFloat.data() : parentWidget(); + if (!gapIndicator) { + gapIndicator = new QRubberBand(QRubberBand::Rectangle, expectedParent); + // For accessibility to identify this special widget. + gapIndicator->setObjectName(QLatin1String("qt_rubberband")); + } else if (gapIndicator->parent() != expectedParent) { + gapIndicator->setParent(expectedParent); + } + gapIndicator->setGeometry(currentHoveredFloat ? currentHoveredFloat->rect() : currentGapRect); + gapIndicator->show(); + gapIndicator->raise(); + } else if (gapIndicator) { + gapIndicator->hide(); + } +#endif //QT_NO_RUBBERBAND } void QMainWindowLayout::hover(QLayoutItem *widgetItem, const QPoint &mousePos) @@ -2300,6 +2354,50 @@ void QMainWindowLayout::hover(QLayoutItem *widgetItem, const QPoint &mousePos) return; QWidget *widget = widgetItem->widget(); + +#ifndef QT_NO_DOCKWIDGET + if ((dockOptions & QMainWindow::GroupedDragging) && (qobject_cast<QDockWidget*>(widget) + || qobject_cast<QDockWidgetGroupWindow *>(widget))) { + + // Check if we are over another floating dock widget + QVarLengthArray<QWidget *, 10> candidates; + foreach (QObject *c, parentWidget()->children()) { + QWidget *w = qobject_cast<QWidget*>(c); + if (!w) + continue; + if (w == widget) + continue; + if (!w->isTopLevel() || !w->isVisible() || w->isMinimized()) + continue; + if (!qobject_cast<QDockWidget*>(w) && !qobject_cast<QDockWidgetGroupWindow *>(w)) + continue; + candidates << w; + if (QDockWidgetGroupWindow *group = qobject_cast<QDockWidgetGroupWindow *>(w)) { + // Sometimes, there are floating QDockWidget that have a QDockWidgetGroupWindow as a parent. + foreach (QObject *c, group->children()) { + if (QDockWidget *dw = qobject_cast<QDockWidget*>(c)) { + if (dw != widget && dw->isFloating() && dw->isVisible() && !dw->isMinimized()) + candidates << dw; + } + } + } + } + foreach (QWidget *w, candidates) { + QWindow *handle1 = widget->windowHandle(); + QWindow *handle2 = w->windowHandle(); + if (handle1 && handle2 && handle1->screen() != handle2->screen()) + continue; + if (!w->geometry().contains(mousePos)) + continue; + + currentHoveredFloat = w; + restore(true); + return; + } + } + currentHoveredFloat = Q_NULLPTR; +#endif //QT_NO_DOCKWIDGET + QPoint pos = parentWidget()->mapFromGlobal(mousePos); if (!savedState.isValid()) diff --git a/src/widgets/widgets/qmainwindowlayout_p.h b/src/widgets/widgets/qmainwindowlayout_p.h index 0c76a0ce90..9a13e5f5ce 100644 --- a/src/widgets/widgets/qmainwindowlayout_p.h +++ b/src/widgets/widgets/qmainwindowlayout_p.h @@ -92,6 +92,20 @@ protected: bool event(QEvent *) Q_DECL_OVERRIDE; void paintEvent(QPaintEvent*) Q_DECL_OVERRIDE; }; + +// This item will be used in the layout for the gap item. We cannot use QWidgetItem directly +// because QWidgetItem functions return an empty size for widgets that are are floating. +class QDockWidgetGroupWindowItem : public QWidgetItem +{ +public: + explicit QDockWidgetGroupWindowItem(QDockWidgetGroupWindow *parent) : QWidgetItem(parent) {} + QSize minimumSize() const Q_DECL_OVERRIDE { return lay()->minimumSize(); } + QSize maximumSize() const Q_DECL_OVERRIDE { return lay()->maximumSize(); } + QSize sizeHint() const Q_DECL_OVERRIDE { return lay()->sizeHint(); } + +private: + QLayout *lay() const { return const_cast<QDockWidgetGroupWindowItem *>(this)->widget()->layout(); } +}; #endif /* This data structure represents the state of all the tool-bars and dock-widgets. It's value based @@ -288,7 +302,10 @@ public: QRect currentGapRect; QWidget *pluggingWidget; #ifndef QT_NO_RUBBERBAND - QRubberBand *gapIndicator; + QPointer<QRubberBand> gapIndicator; +#endif +#ifndef QT_NO_DOCKWIDGET + QPointer<QWidget> currentHoveredFloat; // set when dragging over a floating dock widget #endif void hover(QLayoutItem *widgetItem, const QPoint &mousePos); |