summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorOlivier Goffart <ogoffart@woboq.com>2015-07-05 11:55:09 +0200
committerOlivier Goffart (Woboq GmbH) <ogoffart@woboq.com>2015-08-01 06:48:17 +0000
commit0e2d8ba7929df52b727c2d31315d1b8f10857b01 (patch)
treee49dcc264dbcfc1a4f0220eb941ff5c7962f4046
parent3ae2387f375798a983b6d052723f10fc88b63da0 (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.cpp6
-rw-r--r--src/widgets/widgets/qmainwindowlayout.cpp162
-rw-r--r--src/widgets/widgets/qmainwindowlayout_p.h19
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);