summaryrefslogtreecommitdiffstats
path: root/src/widgets
diff options
context:
space:
mode:
Diffstat (limited to 'src/widgets')
-rw-r--r--src/widgets/widgets/qdockarealayout.cpp82
-rw-r--r--src/widgets/widgets/qdockarealayout_p.h4
-rw-r--r--src/widgets/widgets/qdockwidget.cpp153
-rw-r--r--src/widgets/widgets/qdockwidget_p.h3
-rw-r--r--src/widgets/widgets/qmainwindow.cpp7
-rw-r--r--src/widgets/widgets/qmainwindow.h3
-rw-r--r--src/widgets/widgets/qmainwindowlayout.cpp519
-rw-r--r--src/widgets/widgets/qmainwindowlayout_p.h24
8 files changed, 705 insertions, 90 deletions
diff --git a/src/widgets/widgets/qdockarealayout.cpp b/src/widgets/widgets/qdockarealayout.cpp
index af0f2515fe..bc2259ce90 100644
--- a/src/widgets/widgets/qdockarealayout.cpp
+++ b/src/widgets/widgets/qdockarealayout.cpp
@@ -1,6 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2015 The Qt Company Ltd.
+** Copyright (C) 2015 Olivier Goffart <ogoffart@woboq.com>
** Contact: http://www.qt.io/licensing/
**
** This file is part of the QtWidgets module of the Qt Toolkit.
@@ -1854,20 +1855,6 @@ static Qt::DockWidgetArea toDockWidgetArea(QInternal::DockPosition pos)
return Qt::NoDockWidgetArea;
}
-static QRect constrainedRect(QRect rect, const QRect &desktop)
-{
- if (desktop.isValid()) {
- rect.setWidth(qMin(rect.width(), desktop.width()));
- rect.setHeight(qMin(rect.height(), desktop.height()));
- rect.moveLeft(qMax(rect.left(), desktop.left()));
- rect.moveTop(qMax(rect.top(), desktop.top()));
- rect.moveRight(qMin(rect.right(), desktop.right()));
- rect.moveBottom(qMin(rect.bottom(), desktop.bottom()));
- }
-
- return rect;
-}
-
bool QDockAreaLayoutInfo::restoreState(QDataStream &stream, QList<QDockWidget*> &widgets, bool testing)
{
uchar marker;
@@ -1958,11 +1945,7 @@ bool QDockAreaLayoutInfo::restoreState(QDataStream &stream, QList<QDockWidget*>
#endif
if (!testing) {
QRect r(x, y, w, h);
- QDesktopWidget *desktop = QApplication::desktop();
- if (desktop->isVirtualDesktop())
- r = constrainedRect(r, desktop->screenGeometry(desktop->screenNumber(r.topLeft())));
- else
- r = constrainedRect(r, desktop->screenGeometry(widget));
+ r = QDockAreaLayout::constrainedRect(r, widget);
widget->move(r.topLeft());
widget->resize(r.size());
}
@@ -2075,6 +2058,36 @@ void QDockAreaLayoutInfo::updateSeparatorWidgets() const
#endif //QT_NO_TABBAR
#ifndef QT_NO_TABBAR
+/*! \internal
+ reparent all the widgets contained in this layout portion to the
+ specified parent. This is used to reparent dock widgets and tabbars
+ to the floating window or the main window
+ */
+void QDockAreaLayoutInfo::reparentWidgets(QWidget *parent)
+{
+ if (tabBar)
+ tabBar->setParent(parent);
+
+ for (int i = 0; i < item_list.count(); ++i) {
+ const QDockAreaLayoutItem &item = item_list.at(i);
+ if (item.flags & QDockAreaLayoutItem::GapItem)
+ continue;
+ if (item.skip())
+ continue;
+ if (item.subinfo)
+ item.subinfo->reparentWidgets(parent);
+ if (item.widgetItem) {
+ QWidget *w = item.widgetItem->widget();
+ if (w->parent() != parent) {
+ bool hidden = w->isHidden();
+ w->setParent(parent);
+ if (!hidden)
+ w->show();
+ }
+ }
+ }
+}
+
//returns whether the tabbar is visible or not
bool QDockAreaLayoutInfo::updateTabBar() const
{
@@ -2981,6 +2994,33 @@ QSize QDockAreaLayout::minimumSize() const
return QSize(qMax(row1, row2, row3), qMax(col1, col2, col3));
}
+/*! \internal
+ Try to fit the given rectangle \a rect on the screen which contains
+ the window \a widget.
+ Used to compute the geometry of a dragged a dock widget that should
+ be shown with \a rect, but needs to be visible on the screen
+ */
+QRect QDockAreaLayout::constrainedRect(QRect rect, QWidget* widget)
+{
+ QRect desktop;
+ QDesktopWidget *desktopW = QApplication::desktop();
+ if (desktopW->isVirtualDesktop())
+ desktop = desktopW->screenGeometry(rect.topLeft());
+ else
+ desktop = desktopW->screenGeometry(widget);
+
+ if (desktop.isValid()) {
+ rect.setWidth(qMin(rect.width(), desktop.width()));
+ rect.setHeight(qMin(rect.height(), desktop.height()));
+ rect.moveLeft(qMax(rect.left(), desktop.left()));
+ rect.moveTop(qMax(rect.top(), desktop.top()));
+ rect.moveRight(qMin(rect.right(), desktop.right()));
+ rect.moveBottom(qMin(rect.bottom(), desktop.bottom()));
+ }
+
+ return rect;
+}
+
bool QDockAreaLayout::restoreDockWidget(QDockWidget *dockWidget)
{
QList<int> index = indexOfPlaceHolder(dockWidget->objectName());
@@ -2994,9 +3034,7 @@ bool QDockAreaLayout::restoreDockWidget(QDockWidget *dockWidget)
item.widgetItem = new QDockWidgetItem(dockWidget);
if (placeHolder->window) {
- const QRect screenGeometry =
- QApplication::desktop()->screenGeometry(placeHolder->topLevelRect.center());
- const QRect r = constrainedRect(placeHolder->topLevelRect, screenGeometry);
+ const QRect r = constrainedRect(placeHolder->topLevelRect, dockWidget);
dockWidget->d_func()->setWindowState(true, true, r);
}
dockWidget->setVisible(!placeHolder->hidden);
diff --git a/src/widgets/widgets/qdockarealayout_p.h b/src/widgets/widgets/qdockarealayout_p.h
index 64fae7ebf6..93b005f64f 100644
--- a/src/widgets/widgets/qdockarealayout_p.h
+++ b/src/widgets/widgets/qdockarealayout_p.h
@@ -202,6 +202,7 @@ public:
QTabBar *tabBar;
int tabBarShape;
+ void reparentWidgets(QWidget *p);
bool updateTabBar() const;
void setTabBarShape(int shape);
QSize tabBarMinimumSize() const;
@@ -232,7 +233,8 @@ public:
bool isValid() const;
- enum { DockWidgetStateMarker = 0xfd };
+ enum { DockWidgetStateMarker = 0xfd, FloatingDockWidgetTabMarker = 0xf9 };
+ static QRect constrainedRect(QRect rect, QWidget *widget);
void saveState(QDataStream &stream) const;
bool restoreState(QDataStream &stream, const QList<QDockWidget*> &widgets, bool testing = false);
diff --git a/src/widgets/widgets/qdockwidget.cpp b/src/widgets/widgets/qdockwidget.cpp
index 21b0904cc0..1b7473fbd7 100644
--- a/src/widgets/widgets/qdockwidget.cpp
+++ b/src/widgets/widgets/qdockwidget.cpp
@@ -65,6 +65,18 @@ extern QString qt_setWindowTitle_helperHelper(const QString&, const QWidget*); /
// qmainwindow.cpp
extern QMainWindowLayout *qt_mainwindow_layout(const QMainWindow *window);
+static inline QMainWindowLayout *qt_mainwindow_layout_from_dock(const QDockWidget *dock)
+{
+ const QWidget *p = dock->parentWidget();
+ while (p) {
+ const QMainWindow *window = qobject_cast<const QMainWindow*>(p);
+ if (window)
+ return qt_mainwindow_layout(window);
+ p = p->parentWidget();
+ }
+ return Q_NULLPTR;
+}
+
static inline bool hasFeature(const QDockWidgetPrivate *priv, QDockWidget::DockWidgetFeature feature)
{ return (priv->features & feature) == feature; }
@@ -194,25 +206,40 @@ QDockWidgetLayout::~QDockWidgetLayout()
qDeleteAll(item_list);
}
+/*! \internal
+ Returns true if the dock widget managed by this layout should have a native
+ window decoration or if Qt needs to draw it.
+ */
bool QDockWidgetLayout::nativeWindowDeco() const
{
- return nativeWindowDeco(parentWidget()->isWindow());
+ bool floating = parentWidget()->isWindow();
+ if (!floating && qobject_cast<QDockWidgetGroupWindow*>(parentWidget()->parentWidget()))
+ return wmSupportsNativeWindowDeco();
+ return nativeWindowDeco(floating);
}
-static bool isXcb()
+/*! \internal
+ Returns true if the window manager can draw natively the windows decoration
+ of a dock widget
+ */
+bool QDockWidgetLayout::wmSupportsNativeWindowDeco()
{
+#if defined(Q_OS_WINCE) || defined(Q_OS_ANDROID)
+ return false;
+#else
static const bool xcb = !QGuiApplication::platformName().compare(QLatin1String("xcb"), Qt::CaseInsensitive);
- return xcb;
+ return !xcb;
+#endif
}
+/*! \internal
+ Returns true if the dock widget managed by this layout should have a native
+ window decoration or if Qt needs to draw it. The \a floating parameter
+ overrides the floating current state of the dock widget.
+ */
bool QDockWidgetLayout::nativeWindowDeco(bool floating) const
{
-#if defined(Q_OS_WINCE) || defined(Q_OS_ANDROID)
- Q_UNUSED(floating)
- return false;
-#else
- return !isXcb() && (floating && item_list[QDockWidgetLayout::TitleBar] == 0);
-#endif
+ return wmSupportsNativeWindowDeco() && floating && item_list.at(QDockWidgetLayout::TitleBar) == 0;
}
@@ -611,7 +638,10 @@ void QDockWidget::initStyleOption(QStyleOptionDockWidget *option) const
return;
QDockWidgetLayout *dwlayout = qobject_cast<QDockWidgetLayout*>(layout());
- option->initFrom(this);
+ QDockWidgetGroupWindow *floatingTab = qobject_cast<QDockWidgetGroupWindow*>(parent());
+ // If we are in a floating tab, init from the parent because the attributes and the geometry
+ // of the title bar should be taken from the floating window.
+ option->initFrom(floatingTab && !isFloating() ? parentWidget() : this);
option->rect = dwlayout->titleArea();
option->title = d->fixedWindowTitle;
option->closable = hasFeature(this, QDockWidget::DockWidgetClosable);
@@ -681,14 +711,20 @@ void QDockWidgetPrivate::_q_toggleTopLevel()
q->setFloating(!q->isFloating());
}
+/*! \internal
+ Initialize the drag state structure and remember the position of the click.
+ This is called when the mouse is pressed, but the dock is not yet dragged out.
+
+ \a nca specify that the event comes from NonClientAreaMouseButtonPress
+ */
void QDockWidgetPrivate::initDrag(const QPoint &pos, bool nca)
{
+ Q_Q(QDockWidget);
+
if (state != 0)
return;
- QMainWindow *win = qobject_cast<QMainWindow*>(parent);
- Q_ASSERT(win != 0);
- QMainWindowLayout *layout = qt_mainwindow_layout(win);
+ QMainWindowLayout *layout = qt_mainwindow_layout_from_dock(q);
Q_ASSERT(layout != 0);
if (layout->pluggingWidget != 0) // the main window is animating a docking operation
return;
@@ -702,17 +738,23 @@ void QDockWidgetPrivate::initDrag(const QPoint &pos, bool nca)
state->ctrlDrag = false;
}
-void QDockWidgetPrivate::startDrag()
+/*! \internal
+ Actually start the drag and detach the dockwidget.
+ The \a group parameter is true when we should potentially drag a group of
+ tabbed widgets, and false if the dock widget should always be dragged
+ alone.
+ */
+void QDockWidgetPrivate::startDrag(bool group)
{
Q_Q(QDockWidget);
if (state == 0 || state->dragging)
return;
- QMainWindowLayout *layout = qt_mainwindow_layout(qobject_cast<QMainWindow *>(q->parentWidget()));
+ QMainWindowLayout *layout = qt_mainwindow_layout_from_dock(q);
Q_ASSERT(layout != 0);
- state->widgetItem = layout->unplug(q);
+ state->widgetItem = layout->unplug(q, group);
if (state->widgetItem == 0) {
/* I have a QMainWindow parent, but I was never inserted with
QMainWindow::addDockWidget, so the QMainWindowLayout has no
@@ -728,6 +770,11 @@ void QDockWidgetPrivate::startDrag()
state->dragging = true;
}
+/*! \internal
+ Ends the drag end drop operation of the QDockWidget.
+ The \a abort parameter specifies that it ends because of programmatic state
+ reset rather than mouse release event.
+ */
void QDockWidgetPrivate::endDrag(bool abort)
{
Q_Q(QDockWidget);
@@ -736,29 +783,31 @@ void QDockWidgetPrivate::endDrag(bool abort)
q->releaseMouse();
if (state->dragging) {
- QMainWindowLayout *mwLayout = qt_mainwindow_layout(qobject_cast<QMainWindow *>(q->parentWidget()));
+ QMainWindowLayout *mwLayout = qt_mainwindow_layout_from_dock(q);
Q_ASSERT(mwLayout != 0);
if (abort || !mwLayout->plug(state->widgetItem)) {
if (hasFeature(this, QDockWidget::DockWidgetFloatable)) {
+ // This QDockWidget will now stay in the floating state.
if (state->ownWidgetItem)
delete state->widgetItem;
mwLayout->restore();
- if (isXcb()) {
+ QDockWidgetLayout *dwLayout = qobject_cast<QDockWidgetLayout*>(layout);
+ if (!dwLayout->nativeWindowDeco()) {
// get rid of the X11BypassWindowManager window flag and activate the resizer
Qt::WindowFlags flags = q->windowFlags();
flags &= ~Qt::X11BypassWindowManagerHint;
q->setWindowFlags(flags);
- setResizerActive(true);
+ setResizerActive(q->isFloating());
q->show();
} else {
- QDockWidgetLayout *myLayout
- = qobject_cast<QDockWidgetLayout*>(layout);
- setResizerActive(myLayout->widgetForRole(QDockWidgetLayout::TitleBar) != 0);
+ setResizerActive(false);
}
undockedGeometry = q->geometry();
q->activateWindow();
} else {
+ // The tab was not plugged back in the QMainWindow but the QDockWidget cannot
+ // stay floating, revert to the previous state.
mwLayout->revert(state->widgetItem);
}
}
@@ -782,11 +831,7 @@ bool QDockWidgetPrivate::isAnimating() const
{
Q_Q(const QDockWidget);
- QMainWindow *mainWin = qobject_cast<QMainWindow*>(parent);
- if (mainWin == 0)
- return false;
-
- QMainWindowLayout *mainWinLayout = qt_mainwindow_layout(mainWin);
+ QMainWindowLayout *mainWinLayout = qt_mainwindow_layout_from_dock(q);
if (mainWinLayout == 0)
return false;
@@ -804,12 +849,14 @@ bool QDockWidgetPrivate::mousePressEvent(QMouseEvent *event)
if (!dwLayout->nativeWindowDeco()) {
QRect titleArea = dwLayout->titleArea();
+ QDockWidgetGroupWindow *floatingTab = qobject_cast<QDockWidgetGroupWindow*>(parent);
+
if (event->button() != Qt::LeftButton ||
!titleArea.contains(event->pos()) ||
// check if the tool window is movable... do nothing if it
// is not (but allow moving if the window is floating)
(!hasFeature(this, QDockWidget::DockWidgetMovable) && !q->isFloating()) ||
- qobject_cast<QMainWindow*>(parent) == 0 ||
+ (qobject_cast<QMainWindow*>(parent) == 0 && !floatingTab) ||
isAnimating() || state != 0) {
return false;
}
@@ -853,7 +900,7 @@ bool QDockWidgetPrivate::mouseMoveEvent(QMouseEvent *event)
QDockWidgetLayout *dwlayout
= qobject_cast<QDockWidgetLayout *>(layout);
- QMainWindowLayout *mwlayout = qt_mainwindow_layout(qobject_cast<QMainWindow *>(q->parentWidget()));
+ QMainWindowLayout *mwlayout = qt_mainwindow_layout_from_dock(q);
if (!dwlayout->nativeWindowDeco()) {
if (!state->dragging
&& mwlayout->pluggingWidget == 0
@@ -871,7 +918,12 @@ bool QDockWidgetPrivate::mouseMoveEvent(QMouseEvent *event)
if (state->dragging && !state->nca) {
QPoint pos = event->globalPos() - state->pressPos;
- q->move(pos);
+
+ QDockWidgetGroupWindow *floatingTab = qobject_cast<QDockWidgetGroupWindow*>(parent);
+ if (floatingTab && !q->isFloating())
+ floatingTab->move(pos);
+ else
+ q->move(pos);
if (state && !state->ctrlDrag)
mwlayout->hover(state->widgetItem, event->globalPos());
@@ -902,8 +954,9 @@ void QDockWidgetPrivate::nonClientAreaMouseEvent(QMouseEvent *event)
int fw = q->style()->pixelMetric(QStyle::PM_DockWidgetFrameWidth, 0, q);
- QRect geo = q->geometry();
- QRect titleRect = q->frameGeometry();
+ QWidget *tl = q->topLevelWidget();
+ QRect geo = tl->geometry();
+ QRect titleRect = tl->frameGeometry();
#ifdef Q_DEAD_CODE_FROM_QT4_MAC
if ((features & QDockWidget::DockWidgetVerticalTitleBar)) {
titleRect.setTop(geo.top());
@@ -924,7 +977,7 @@ void QDockWidgetPrivate::nonClientAreaMouseEvent(QMouseEvent *event)
break;
if (state != 0)
break;
- if (qobject_cast<QMainWindow*>(parent) == 0)
+ if (qobject_cast<QMainWindow*>(parent) == 0 && qobject_cast<QDockWidgetGroupWindow*>(parent) == 0)
break;
if (isAnimating())
break;
@@ -958,11 +1011,17 @@ void QDockWidgetPrivate::nonClientAreaMouseEvent(QMouseEvent *event)
}
}
+/*! \internal
+ Called when the QDockWidget or the QDockWidgetGroupWindow is moved
+ */
void QDockWidgetPrivate::moveEvent(QMoveEvent *event)
{
Q_Q(QDockWidget);
- if (state == 0 || !state->dragging || !state->nca || !q->isWindow())
+ if (state == 0 || !state->dragging || !state->nca)
+ return;
+
+ if (!q->isWindow() && qobject_cast<QDockWidgetGroupWindow*>(parent) == 0)
return;
// When the native window frame is being dragged, all we get is these mouse
@@ -971,7 +1030,7 @@ void QDockWidgetPrivate::moveEvent(QMoveEvent *event)
if (state->ctrlDrag)
return;
- QMainWindowLayout *layout = qt_mainwindow_layout(qobject_cast<QMainWindow *>(q->parentWidget()));
+ QMainWindowLayout *layout = qt_mainwindow_layout_from_dock(q);
Q_ASSERT(layout != 0);
QPoint globalMousePos = event->pos() + state->pressPos;
@@ -999,8 +1058,9 @@ void QDockWidgetPrivate::setWindowState(bool floating, bool unplug, const QRect
Q_Q(QDockWidget);
if (!floating && parent) {
- QMainWindowLayout *mwlayout = qt_mainwindow_layout(qobject_cast<QMainWindow *>(q->parentWidget()));
- if (mwlayout && mwlayout->dockWidgetArea(q) == Qt::NoDockWidgetArea)
+ QMainWindowLayout *mwlayout = qt_mainwindow_layout_from_dock(q);
+ if (mwlayout && mwlayout->dockWidgetArea(q) == Qt::NoDockWidgetArea
+ && !qobject_cast<QDockWidgetGroupWindow *>(parent))
return; // this dockwidget can't be redocked
}
@@ -1048,7 +1108,7 @@ void QDockWidgetPrivate::setWindowState(bool floating, bool unplug, const QRect
if (floating != wasFloating) {
emit q->topLevelChanged(floating);
if (!floating && parent) {
- QMainWindowLayout *mwlayout = qt_mainwindow_layout(qobject_cast<QMainWindow *>(q->parentWidget()));
+ QMainWindowLayout *mwlayout = qt_mainwindow_layout_from_dock(q);
if (mwlayout)
emit q->dockLocationChanged(mwlayout->dockWidgetArea(q));
}
@@ -1230,8 +1290,11 @@ void QDockWidget::setFeatures(QDockWidget::DockWidgetFeatures features)
emit featuresChanged(d->features);
update();
if (closableChanged && layout->nativeWindowDeco()) {
- //this ensures the native decoration is drawn
- d->setWindowState(true /*floating*/, true /*unplug*/);
+ QDockWidgetGroupWindow *floatingTab = qobject_cast<QDockWidgetGroupWindow *>(parent());
+ if (floatingTab && !isFloating())
+ floatingTab->adjustFlags();
+ else
+ d->setWindowState(true /*floating*/, true /*unplug*/); //this ensures the native decoration is drawn
}
}
@@ -1323,11 +1386,12 @@ void QDockWidget::changeEvent(QEvent *event)
#endif
#ifndef QT_NO_TABBAR
{
- QMainWindow *win = qobject_cast<QMainWindow*>(parentWidget());
- if (QMainWindowLayout *winLayout = qt_mainwindow_layout(win)) {
+ if (QMainWindowLayout *winLayout = qt_mainwindow_layout_from_dock(this)) {
if (QDockAreaLayoutInfo *info = winLayout->layoutState.dockAreaLayout.info(this))
info->updateTabBar();
}
+ if (QDockWidgetGroupWindow *p = qobject_cast<QDockWidgetGroupWindow *>(parent()))
+ p->adjustFlags();
}
#endif // QT_NO_TABBAR
break;
@@ -1380,7 +1444,7 @@ bool QDockWidget::event(QEvent *event)
Q_D(QDockWidget);
QMainWindow *win = qobject_cast<QMainWindow*>(parentWidget());
- QMainWindowLayout *layout = qt_mainwindow_layout(win);
+ QMainWindowLayout *layout = qt_mainwindow_layout_from_dock(this);
switch (event->type()) {
#ifndef QT_NO_ACTION
@@ -1417,6 +1481,9 @@ bool QDockWidget::event(QEvent *event)
}
if (!isFloating() && layout != 0 && onTop)
layout->raise(this);
+ if (QDockWidgetGroupWindow *p = qobject_cast<QDockWidgetGroupWindow *>(parent()))
+ p->adjustFlags();
+
break;
}
case QEvent::WindowActivate:
diff --git a/src/widgets/widgets/qdockwidget_p.h b/src/widgets/widgets/qdockwidget_p.h
index 752d6519e5..9c68bc5fb1 100644
--- a/src/widgets/widgets/qdockwidget_p.h
+++ b/src/widgets/widgets/qdockwidget_p.h
@@ -108,7 +108,7 @@ public:
void setWindowState(bool floating, bool unplug = false, const QRect &rect = QRect());
void nonClientAreaMouseEvent(QMouseEvent *event);
void initDrag(const QPoint &pos, bool nca);
- void startDrag();
+ void startDrag(bool group = true);
void endDrag(bool abort = false);
void moveEvent(QMoveEvent *event);
@@ -151,6 +151,7 @@ public:
int minimumTitleWidth() const;
int titleHeight() const;
void updateMaxSize();
+ static bool wmSupportsNativeWindowDeco();
bool nativeWindowDeco() const;
bool nativeWindowDeco(bool floating) const;
diff --git a/src/widgets/widgets/qmainwindow.cpp b/src/widgets/widgets/qmainwindow.cpp
index 2a1e8428ab..8eb9050528 100644
--- a/src/widgets/widgets/qmainwindow.cpp
+++ b/src/widgets/widgets/qmainwindow.cpp
@@ -420,6 +420,13 @@ QMainWindow::~QMainWindow()
at the bottom. Implies AllowTabbedDocks. See also
\l setTabPosition().
+ \value GroupedDragging When dragging the titlebar of a dock, all the tabs
+ that are tabbed with it are going to be dragged.
+ Implies AllowTabbedDocks. Does not work well if
+ some QDockWidgets have restrictions in which area
+ they are allowed. (This enum value was added in Qt
+ 5.6.)
+
These options only control how dock widgets may be dropped in a QMainWindow.
They do not re-arrange the dock widgets to conform with the specified
options. For this reason they should be set before any dock widgets
diff --git a/src/widgets/widgets/qmainwindow.h b/src/widgets/widgets/qmainwindow.h
index cbbea74b9b..0bd70c87a6 100644
--- a/src/widgets/widgets/qmainwindow.h
+++ b/src/widgets/widgets/qmainwindow.h
@@ -77,7 +77,8 @@ public:
AllowNestedDocks = 0x02,
AllowTabbedDocks = 0x04,
ForceTabbedDocks = 0x08, // implies AllowTabbedDocks, !AllowNestedDocks
- VerticalTabs = 0x10 // implies AllowTabbedDocks
+ VerticalTabs = 0x10, // implies AllowTabbedDocks
+ GroupedDragging = 0x20 // implies AllowTabbedDocks
};
Q_ENUM(DockOption)
Q_DECLARE_FLAGS(DockOptions, DockOption)
diff --git a/src/widgets/widgets/qmainwindowlayout.cpp b/src/widgets/widgets/qmainwindowlayout.cpp
index 3620aa04fc..d41f3bad19 100644
--- a/src/widgets/widgets/qmainwindowlayout.cpp
+++ b/src/widgets/widgets/qmainwindowlayout.cpp
@@ -1,6 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2015 The Qt Company Ltd.
+** Copyright (C) 2015 Olivier Goffart <ogoffart@woboq.com>
** Contact: http://www.qt.io/licensing/
**
** This file is part of the QtWidgets module of the Qt Toolkit.
@@ -49,6 +50,7 @@
#include <qstatusbar.h>
#include <qstring.h>
#include <qstyle.h>
+#include <qstylepainter.h>
#include <qvarlengtharray.h>
#include <qstack.h>
#include <qmap.h>
@@ -61,6 +63,7 @@
#include <private/qapplication_p.h>
#include <private/qlayoutengine_p.h>
+#include <private/qwidgetresizehandler_p.h>
#ifdef Q_DEAD_CODE_FROM_QT4_MAC
# include <private/qcore_mac_p.h>
# include <private/qt_cocoa_helpers_mac_p.h>
@@ -68,9 +71,7 @@
QT_BEGIN_NAMESPACE
-#ifdef QT_NO_DOCKWIDGET
extern QMainWindowLayout *qt_mainwindow_layout(const QMainWindow *window);
-#endif
/******************************************************************************
** debug
@@ -165,6 +166,242 @@ QDebug operator<<(QDebug debug, const QMainWindowLayout *layout)
#endif // !defined(QT_NO_DOCKWIDGET) && !defined(QT_NO_DEBUG)
/******************************************************************************
+ ** QDockWidgetGroupWindow
+ */
+// QDockWidgetGroupWindow is the floating window containing the tabbed dockwidgets in case several
+// dockwidgets are dragged together (QMainWindow::GroupedDragging feature).
+// QDockWidgetGroupLayout is the layout of that window and use a QDockAreaLayoutInfo to layout
+// the tabs inside it.
+#ifndef QT_NO_DOCKWIDGET
+class QDockWidgetGroupLayout : public QLayout {
+ QDockAreaLayoutInfo info;
+ QWidgetResizeHandler *resizer;
+public:
+ QDockWidgetGroupLayout(QWidget* parent) : QLayout(parent) {
+ setSizeConstraint(QLayout::SetMinAndMaxSize);
+ resizer = new QWidgetResizeHandler(parent);
+ resizer->setMovingEnabled(false);
+ }
+ ~QDockWidgetGroupLayout() {
+ info.deleteAllLayoutItems();
+ }
+
+ void addItem(QLayoutItem*) Q_DECL_OVERRIDE { Q_UNREACHABLE(); }
+ int count() const Q_DECL_OVERRIDE { return 0; }
+ QLayoutItem* itemAt(int index) const Q_DECL_OVERRIDE
+ {
+ int x = 0;
+ return info.itemAt(&x, index);
+ }
+ QLayoutItem* takeAt(int index) Q_DECL_OVERRIDE
+ {
+ int x = 0;
+ return info.takeAt(&x, index);
+ }
+ QSize sizeHint() const Q_DECL_OVERRIDE
+ {
+ int fw = frameWidth();
+ return info.sizeHint() + QSize(fw, fw);
+ }
+ QSize minimumSize() const Q_DECL_OVERRIDE
+ {
+ int fw = frameWidth();
+ return info.minimumSize() + QSize(fw, fw);
+ }
+ QSize maximumSize() const Q_DECL_OVERRIDE
+ {
+ int fw = frameWidth();
+ return info.maximumSize() + QSize(fw, fw);
+ }
+ void setGeometry(const QRect&r) Q_DECL_OVERRIDE
+ {
+ QDockAreaLayoutInfo *li = layoutInfo();
+ if (li->isEmpty()) {
+ static_cast<QDockWidgetGroupWindow *>(parent())->destroyIfEmpty();
+ return;
+ }
+ int fw = frameWidth();
+ li->reparentWidgets(parentWidget());
+ li->rect = r.adjusted(fw, fw, -fw, -fw);
+ li->fitItems();
+ li->apply(false);
+ resizer->setActive(QWidgetResizeHandler::Resize, !nativeWindowDeco());
+ }
+
+ QDockAreaLayoutInfo *layoutInfo() {
+ return &info;
+ }
+
+ bool nativeWindowDeco() const
+ {
+ return QDockWidgetLayout::wmSupportsNativeWindowDeco();
+ }
+
+ int frameWidth() const
+ {
+ return nativeWindowDeco() ? 0 :
+ parentWidget()->style()->pixelMetric(QStyle::PM_DockWidgetFrameWidth, 0, parentWidget());
+ }
+};
+
+// 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()) {
+ case QEvent::Close:
+ // Forward the close to the QDockWidget just as if its close button was pressed
+ if (QDockWidget *dw = topDockWidget()) {
+ e->ignore();
+ dw->close();
+ adjustFlags();
+ }
+ return true;
+ case QEvent::Move:
+ // Let QDockWidgetPrivate::moseEvent handle the dragging
+ if (QDockWidget *dw = topDockWidget())
+ static_cast<QDockWidgetPrivate *>(QObjectPrivate::get(dw))->moveEvent(static_cast<QMoveEvent*>(e));
+ return true;
+ case QEvent::NonClientAreaMouseMove:
+ case QEvent::NonClientAreaMouseButtonPress:
+ case QEvent::NonClientAreaMouseButtonRelease:
+ case QEvent::NonClientAreaMouseButtonDblClick:
+ // Let the QDockWidgetPrivate of the currently visible dock widget handle the drag and drop
+ if (QDockWidget *dw = topDockWidget())
+ static_cast<QDockWidgetPrivate *>(QObjectPrivate::get(dw))->nonClientAreaMouseEvent(static_cast<QMouseEvent*>(e));
+ return true;
+ case QEvent::ChildAdded:
+ if (qobject_cast<QDockWidget *>(static_cast<QChildEvent*>(e)->child()))
+ adjustFlags();
+ break;
+ default:
+ break;
+ }
+ return QWidget::event(e);
+}
+
+void QDockWidgetGroupWindow::paintEvent(QPaintEvent *)
+{
+ QDockWidgetGroupLayout *lay = static_cast<QDockWidgetGroupLayout *>(layout());
+ bool nativeDeco = lay->nativeWindowDeco();
+
+ if (!nativeDeco) {
+ QStyleOptionFrame framOpt;
+ framOpt.init(this);
+ QStylePainter p(this);
+ p.drawPrimitive(QStyle::PE_FrameDockWidget, framOpt);
+ }
+}
+
+QDockAreaLayoutInfo *QDockWidgetGroupWindow::layoutInfo() const
+{
+ return static_cast<QDockWidgetGroupLayout*>(layout())->layoutInfo();
+}
+
+/*! \internal
+ \returns the currently active QDockWidget
+ */
+QDockWidget *QDockWidgetGroupWindow::topDockWidget() const
+{
+ QDockAreaLayoutInfo *info = layoutInfo();
+ QDockWidget *dw = 0;
+ if (info->tabBar && info->tabBar->currentIndex() >= 0) {
+ int i = info->tabIndexToListIndex(info->tabBar->currentIndex());
+ if (i >= 0) {
+ const QDockAreaLayoutItem &item = info->item_list.at(i);
+ if (item.widgetItem)
+ dw = qobject_cast<QDockWidget *>(item.widgetItem->widget());
+ }
+ }
+ if (!dw) {
+ for (int i = 0; !dw && i < info->item_list.count(); ++i) {
+ const QDockAreaLayoutItem &item = info->item_list.at(i);
+ if (item.skip())
+ continue;
+ if (!item.widgetItem)
+ continue;
+ dw = qobject_cast<QDockWidget *>(item.widgetItem->widget());
+ }
+ }
+ return dw;
+}
+
+/*! \internal
+ Destroy this window if there is no more QDockWidget in it.
+ */
+void QDockWidgetGroupWindow::destroyIfEmpty()
+{
+ if (layoutInfo()->isEmpty()) {
+ // Make sure to reparent the possibly floating or hidden QDockWidgets to the parent
+ foreach (QDockWidget *dw,
+ findChildren<QDockWidget *>(QString(), Qt::FindDirectChildrenOnly)) {
+ bool wasFloating = dw->isFloating();
+ bool wasHidden = dw->isHidden();
+ dw->setParent(parentWidget());
+ if (wasFloating) {
+ dw->setFloating(true);
+ } else {
+ // maybe it was hidden, we still have to put it back in the main layout.
+ QMainWindowLayout *ml = qt_mainwindow_layout(static_cast<QMainWindow*>(parentWidget()));
+ Qt::DockWidgetArea area = ml->dockWidgetArea(this);
+ if (area == Qt::NoDockWidgetArea)
+ area = Qt::LeftDockWidgetArea;
+ static_cast<QMainWindow*>(parentWidget())->addDockWidget(area, dw);
+ }
+ if (!wasHidden)
+ dw->show();
+ }
+ foreach (QTabBar *tb, findChildren<QTabBar *>(QString(), Qt::FindDirectChildrenOnly))
+ tb->setParent(parentWidget());
+ deleteLater();
+ }
+}
+
+/*! \internal
+ Sets the flags of this window in accordence 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) {
+ flags |= Qt::CustomizeWindowHint | Qt::WindowTitleHint;
+ if (top->features() & QDockWidget::DockWidgetClosable)
+ flags |= Qt::WindowCloseButtonHint;
+ else
+ flags &= ~Qt::WindowCloseButtonHint;
+ flags &= ~Qt::FramelessWindowHint;
+ } else {
+ flags |= Qt::FramelessWindowHint;
+ }
+ if (oldFlags != flags) {
+ setWindowFlags(flags);
+ show(); // setWindowFlags hides the window
+ }
+
+ setWindowTitle(top->windowTitle());
+ setWindowIcon(top->windowIcon());
+}
+#endif
+
+/******************************************************************************
** QMainWindowLayoutState
*/
@@ -335,8 +572,8 @@ QList<int> QMainWindowLayoutState::indexOf(QWidget *widget) const
#ifndef QT_NO_DOCKWIDGET
// is it a dock widget?
- if (QDockWidget *dockWidget = qobject_cast<QDockWidget *>(widget)) {
- result = dockAreaLayout.indexOf(dockWidget);
+ if (qobject_cast<QDockWidget *>(widget) || qobject_cast<QDockWidgetGroupWindow *>(widget)) {
+ result = dockAreaLayout.indexOf(widget);
if (!result.isEmpty())
result.prepend(1);
return result;
@@ -413,7 +650,8 @@ QList<int> QMainWindowLayoutState::gapIndex(QWidget *widget,
#ifndef QT_NO_DOCKWIDGET
// is it a dock widget?
- if (qobject_cast<QDockWidget *>(widget) != 0) {
+ if (qobject_cast<QDockWidget *>(widget) != 0
+ || qobject_cast<QDockWidgetGroupWindow *>(widget)) {
result = dockAreaLayout.gapIndex(pos);
if (!result.isEmpty())
result.prepend(1);
@@ -440,7 +678,7 @@ bool QMainWindowLayoutState::insertGap(const QList<int> &path, QLayoutItem *item
#ifndef QT_NO_DOCKWIDGET
if (i == 1) {
- Q_ASSERT(qobject_cast<QDockWidget*>(item->widget()) != 0);
+ Q_ASSERT(qobject_cast<QDockWidget*>(item->widget()) || qobject_cast<QDockWidgetGroupWindow*>(item->widget()));
return dockAreaLayout.insertGap(path.mid(1), item);
}
#endif //QT_NO_DOCKWIDGET
@@ -593,6 +831,17 @@ void QMainWindowLayoutState::saveState(QDataStream &stream) const
{
#ifndef QT_NO_DOCKWIDGET
dockAreaLayout.saveState(stream);
+#ifndef QT_NO_TABBAR
+ QList<QDockWidgetGroupWindow *> floatingTabs =
+ mainWindow->findChildren<QDockWidgetGroupWindow *>(QString(), Qt::FindDirectChildrenOnly);
+
+ foreach (QDockWidgetGroupWindow *floating, floatingTabs) {
+ if (floating->layoutInfo()->isEmpty())
+ continue;
+ stream << uchar(QDockAreaLayout::FloatingDockWidgetTabMarker) << floating->geometry();
+ floating->layoutInfo()->saveState(stream);
+ }
+#endif
#endif
#ifndef QT_NO_TOOLBAR
toolBarAreaLayout.saveState(stream);
@@ -638,12 +887,28 @@ bool QMainWindowLayoutState::checkFormat(QDataStream &stream)
case QDockAreaLayout::DockWidgetStateMarker:
{
QList<QDockWidget *> dockWidgets = findChildrenHelper<QDockWidget*>(mainWindow);
+ foreach (QDockWidgetGroupWindow *floating, findChildrenHelper<QDockWidgetGroupWindow*>(mainWindow))
+ dockWidgets += findChildrenHelper<QDockWidget*>(floating);
if (!dockAreaLayout.restoreState(stream, dockWidgets, true /*testing*/)) {
return false;
}
}
break;
-#endif
+#ifndef QT_NO_TABBAR
+ case QDockAreaLayout::FloatingDockWidgetTabMarker:
+ {
+ QRect geom;
+ stream >> geom;
+ QDockAreaLayoutInfo info;
+ QList<QDockWidget *> dockWidgets = findChildrenHelper<QDockWidget*>(mainWindow);
+ foreach (QDockWidgetGroupWindow *floating, findChildrenHelper<QDockWidgetGroupWindow*>(mainWindow))
+ dockWidgets += findChildrenHelper<QDockWidget*>(floating);
+ if (!info.restoreState(stream, dockWidgets, true /* testing*/))
+ return false;
+ }
+ break;
+#endif // QT_NO_TABBAR
+#endif // QT_NO_DOCKWIDGET
default:
//there was an error during the parsing
return false;
@@ -682,6 +947,8 @@ bool QMainWindowLayoutState::restoreState(QDataStream &_stream,
case QDockAreaLayout::DockWidgetStateMarker:
{
QList<QDockWidget *> dockWidgets = findChildrenHelper<QDockWidget*>(mainWindow);
+ foreach (QDockWidgetGroupWindow *floating, findChildrenHelper<QDockWidgetGroupWindow*>(mainWindow))
+ dockWidgets += findChildrenHelper<QDockWidget*>(floating);
if (!dockAreaLayout.restoreState(stream, dockWidgets))
return false;
@@ -702,6 +969,26 @@ bool QMainWindowLayoutState::restoreState(QDataStream &_stream,
}
}
break;
+#ifndef QT_NO_TABBAR
+ case QDockAreaLayout::FloatingDockWidgetTabMarker:
+ {
+ QList<QDockWidget *> dockWidgets = findChildrenHelper<QDockWidget*>(mainWindow);
+ foreach (QDockWidgetGroupWindow *floating, findChildrenHelper<QDockWidgetGroupWindow*>(mainWindow))
+ dockWidgets += findChildrenHelper<QDockWidget*>(floating);
+ QDockWidgetGroupWindow* floatingTab = qt_mainwindow_layout(mainWindow)->createTabbedDockWindow();
+ *floatingTab->layoutInfo() = QDockAreaLayoutInfo(&dockAreaLayout.sep, QInternal::LeftDock,
+ Qt::Horizontal, QTabBar::RoundedSouth, mainWindow);
+ QRect geometry;
+ stream >> geometry;
+ if (!floatingTab->layoutInfo()->restoreState(stream, dockWidgets, false))
+ return false;
+ geometry = QDockAreaLayout::constrainedRect(geometry, floatingTab);
+ floatingTab->move(geometry.topLeft());
+ floatingTab->resize(geometry.size());
+ floatingTab->show();
+ }
+ break;
+#endif // QT_NO_TABBAR
#endif // QT_NO_DOCKWIDGET
#ifndef QT_NO_TOOLBAR
@@ -1223,7 +1510,7 @@ void QMainWindowLayout::splitDockWidget(QDockWidget *after,
invalidate();
}
-Qt::DockWidgetArea QMainWindowLayout::dockWidgetArea(QDockWidget *widget) const
+Qt::DockWidgetArea QMainWindowLayout::dockWidgetArea(QWidget *widget) const
{
QList<int> pathToWidget = layoutState.dockAreaLayout.indexOf(widget);
if (pathToWidget.isEmpty())
@@ -1238,20 +1525,88 @@ void QMainWindowLayout::keepSize(QDockWidget *w)
#ifndef QT_NO_TABBAR
+// Handle custom tooltip, and allow to drag tabs away.
class QMainWindowTabBar : public QTabBar
{
+ QMainWindow *mainWindow;
+ QDockWidget *draggingDock; // Currently dragging (detached) dock widget
public:
- QMainWindowTabBar(QWidget *parent);
+ QMainWindowTabBar(QMainWindow *parent);
protected:
bool event(QEvent *e) Q_DECL_OVERRIDE;
+ void mouseReleaseEvent(QMouseEvent*) Q_DECL_OVERRIDE;
+ void mouseMoveEvent(QMouseEvent*) Q_DECL_OVERRIDE;
+
};
-QMainWindowTabBar::QMainWindowTabBar(QWidget *parent)
- : QTabBar(parent)
+QMainWindowTabBar::QMainWindowTabBar(QMainWindow *parent)
+ : QTabBar(parent), mainWindow(parent), draggingDock(0)
{
setExpanding(false);
}
+void QMainWindowTabBar::mouseMoveEvent(QMouseEvent *e)
+{
+ // The QTabBar handles the moving (reordering) of tabs.
+ // When QTabBarPrivate::dragInProgress is true, and that the mouse is outside of a region
+ // around the QTabBar, we will consider the user wants to drag that QDockWidget away from this
+ // tab area.
+
+ QTabBarPrivate *d = static_cast<QTabBarPrivate*>(d_ptr.data());
+ if (!draggingDock && (mainWindow->dockOptions() & QMainWindow::GroupedDragging)) {
+ int offset = QApplication::startDragDistance() + 1;
+ offset *= 3;
+ QRect r = rect().adjusted(-offset, -offset, offset, offset);
+ if (d->dragInProgress && !r.contains(e->pos()) && d->validIndex(d->pressedIndex)) {
+ QMainWindowLayout* mlayout = qt_mainwindow_layout(mainWindow);
+ QDockAreaLayoutInfo *info = mlayout->dockInfo(this);
+ Q_ASSERT(info);
+ int idx = info->tabIndexToListIndex(d->pressedIndex);
+ const QDockAreaLayoutItem &item = info->item_list.at(idx);
+ if (item.widgetItem
+ && (draggingDock = qobject_cast<QDockWidget *>(item.widgetItem->widget()))) {
+ // We should drag this QDockWidget away by unpluging it.
+ // First cancel the QTabBar's internal move
+ d->moveTabFinished(d->pressedIndex);
+ d->pressedIndex = -1;
+ if (d->movingTab)
+ d->movingTab->setVisible(false);
+ d->dragStartPosition = QPoint();
+
+ // Then starts the drag using QDockWidgetPrivate's API
+ QDockWidgetPrivate *dockPriv = static_cast<QDockWidgetPrivate *>(QObjectPrivate::get(draggingDock));
+ QDockWidgetLayout *dwlayout = static_cast<QDockWidgetLayout *>(draggingDock->layout());
+ dockPriv->initDrag(dwlayout->titleArea().center(), true);
+ dockPriv->startDrag(false);
+ if (dockPriv->state)
+ dockPriv->state->ctrlDrag = e->modifiers() & Qt::ControlModifier;
+ }
+ }
+ }
+
+ if (draggingDock) {
+ QDockWidgetPrivate *dockPriv = static_cast<QDockWidgetPrivate *>(QObjectPrivate::get(draggingDock));
+ if (dockPriv->state && dockPriv->state->dragging) {
+ QPoint pos = e->globalPos() - dockPriv->state->pressPos;
+ draggingDock->move(pos);
+ // move will call QMainWindowLayout::hover
+ }
+ }
+ QTabBar::mouseMoveEvent(e);
+}
+
+void QMainWindowTabBar::mouseReleaseEvent(QMouseEvent *e)
+{
+ if (draggingDock && e->button() == Qt::LeftButton) {
+ QDockWidgetPrivate *dockPriv = static_cast<QDockWidgetPrivate *>(QObjectPrivate::get(draggingDock));
+ if (dockPriv->state && dockPriv->state->dragging) {
+ dockPriv->endDrag();
+ }
+ draggingDock = 0;
+ }
+ QTabBar::mouseReleaseEvent(e);
+}
+
bool QMainWindowTabBar::event(QEvent *e)
{
// show the tooltip if tab is too small to fit label
@@ -1276,7 +1631,7 @@ QTabBar *QMainWindowLayout::getTabBar()
if (!unusedTabBars.isEmpty()) {
result = unusedTabBars.takeLast();
} else {
- result = new QMainWindowTabBar(parentWidget());
+ result = new QMainWindowTabBar(static_cast<QMainWindow *>(parentWidget()));
result->setDrawBase(true);
result->setElideMode(Qt::ElideRight);
result->setDocumentMode(_documentMode);
@@ -1305,12 +1660,30 @@ QWidget *QMainWindowLayout::getSeparatorWidget()
return result;
}
+/*! \internal
+ Returns a pointer QDockAreaLayoutInfo which contains this \a widget directly
+ (in its internal list)
+ */
+QDockAreaLayoutInfo *QMainWindowLayout::dockInfo(QWidget *widget)
+{
+ QDockAreaLayoutInfo *info = layoutState.dockAreaLayout.info(widget);
+ if (info)
+ return info;
+ foreach (QDockWidgetGroupWindow *dwgw,
+ parent()->findChildren<QDockWidgetGroupWindow*>(QString(), Qt::FindDirectChildrenOnly)) {
+ info = dwgw->layoutInfo()->info(widget);
+ if (info)
+ return info;
+ }
+ return 0;
+}
+
void QMainWindowLayout::tabChanged()
{
QTabBar *tb = qobject_cast<QTabBar*>(sender());
if (tb == 0)
return;
- QDockAreaLayoutInfo *info = layoutState.dockAreaLayout.info(tb);
+ QDockAreaLayoutInfo *info = dockInfo(tb);
if (info == 0)
return;
info->apply(false);
@@ -1323,7 +1696,7 @@ void QMainWindowLayout::tabMoved(int from, int to)
{
QTabBar *tb = qobject_cast<QTabBar*>(sender());
Q_ASSERT(tb);
- QDockAreaLayoutInfo *info = layoutState.dockAreaLayout.info(tb);
+ QDockAreaLayoutInfo *info = dockInfo(tb);
Q_ASSERT(info);
info->moveTab(from, to);
@@ -1365,7 +1738,7 @@ bool QMainWindowLayout::endSeparatorMove(const QPoint&)
void QMainWindowLayout::raise(QDockWidget *widget)
{
- QDockAreaLayoutInfo *info = layoutState.dockAreaLayout.info(widget);
+ QDockAreaLayoutInfo *info = dockInfo(widget);
if (info == 0)
return;
#ifndef QT_NO_TABBAR
@@ -1549,6 +1922,8 @@ void QMainWindowLayout::revert(QLayoutItem *widgetItem)
QWidget *widget = widgetItem->widget();
layoutState = savedState;
currentGapPos = layoutState.indexOf(widget);
+ if (currentGapPos.isEmpty())
+ return;
fixToolBarOrientation(widgetItem, currentGapPos.at(1));
layoutState.unplug(currentGapPos);
layoutState.fitLayout();
@@ -1613,6 +1988,42 @@ void QMainWindowLayout::animationFinished(QWidget *widget)
if (widget == pluggingWidget) {
#ifndef QT_NO_DOCKWIDGET
+ 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();
+ QList<int> path = layoutState.dockAreaLayout.indexOf(widget);
+ Q_ASSERT(path.size() >= 2);
+
+ QDockAreaLayoutInfo* parentInfo = layoutState.dockAreaLayout.info(path);
+ Q_ASSERT(parentInfo);
+ if (parentInfo->tabbed) {
+ // merge the two tab widgets
+ int idx = path.last();
+ 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(parentWidget());
+ parentInfo->updateTabBar();
+ parentInfo->setCurrentTabId(currentId);
+ } else {
+ QDockAreaLayoutItem &item = layoutState.dockAreaLayout.item(path);
+ Q_ASSERT(item.widgetItem->widget() == dwgw);
+ delete item.widgetItem;
+ item.widgetItem = 0;
+ item.subinfo = new QDockAreaLayoutInfo(qMove(*info));
+ *info = QDockAreaLayoutInfo();
+ item.subinfo->reparentWidgets(parentWidget());
+ item.subinfo->setTabBarShape(parentInfo->tabBarShape);
+ }
+ dwgw->destroyIfEmpty();
+ }
+
if (QDockWidget *dw = qobject_cast<QDockWidget*>(widget))
dw->d_func()->plug(currentGapRect);
#endif
@@ -1633,7 +2044,7 @@ void QMainWindowLayout::animationFinished(QWidget *widget)
if (qobject_cast<QDockWidget*>(widget) != 0) {
// info() might return null if the widget is destroyed while
// animating but before the animationFinished signal is received.
- if (QDockAreaLayoutInfo *info = layoutState.dockAreaLayout.info(widget))
+ if (QDockAreaLayoutInfo *info = dockInfo(widget))
info->setCurrentTab(widget);
}
#endif
@@ -1777,8 +2188,33 @@ void QMainWindowLayout::setCentralWidget(QWidget *widget)
invalidate();
}
-QLayoutItem *QMainWindowLayout::unplug(QWidget *widget)
-{
+/*! \internal
+ Unplug \a widget (QDockWidget or QToolBar) from it's parent container.
+
+ If \a group is true we might actually unplug the group of tabs this
+ widget is part if QMainWindow::GroupedDragging is set. When \a group
+ is false, the widget itself is always unplugged alone
+
+ \returns the QLayoutItem of the dragged element.
+ The layout item is kept in the layout but set as a gap item.
+ */
+QLayoutItem *QMainWindowLayout::unplug(QWidget *widget, bool group)
+{
+#if !defined(QT_NO_DOCKWIDGET) && !defined(QT_NO_TABBAR)
+ QDockWidgetGroupWindow *floatingParent = qobject_cast<QDockWidgetGroupWindow *>(widget->parentWidget());
+ if (group && floatingParent && !widget->isWindow()) {
+ // 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);
+ } else if (floatingParent) {
+ // We are unplugging a dock widget from a floating window.
+ if (QDockWidget *dw = qobject_cast<QDockWidget*>(widget)) {
+ dw->d_func()->unplug(widget->geometry());
+ return 0;
+ }
+ }
+#endif
QList<int> path = layoutState.indexOf(widget);
if (path.isEmpty())
return 0;
@@ -1792,9 +2228,35 @@ QLayoutItem *QMainWindowLayout::unplug(QWidget *widget)
#ifndef QT_NO_DOCKWIDGET
if (QDockWidget *dw = qobject_cast<QDockWidget*>(widget)) {
- dw->d_func()->unplug(r);
+ Q_ASSERT(path.first() == 1);
+ bool actualGroup = false;
+#ifndef QT_NO_TABBAR
+ 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;
+ }
+ }
+#endif // QT_NO_TABBAR
+ if (!actualGroup) {
+ dw->d_func()->unplug(r);
+ }
}
-#endif
+#endif // QT_NO_DOCKWIDGET
#ifndef QT_NO_TOOLBAR
if (QToolBar *tb = qobject_cast<QToolBar*>(widget)) {
tb->d_func()->unplug(r);
@@ -1841,6 +2303,9 @@ QList<int> QMainWindowLayout::hover(QLayoutItem *widgetItem, const QPoint &mouse
#ifndef QT_NO_DOCKWIDGET
if (QDockWidget *dw = qobject_cast<QDockWidget*>(widget))
allowed = dw->isAreaAllowed(toDockWidgetArea(path.at(1)));
+
+ if (qobject_cast<QDockWidgetGroupWindow *>(widget))
+ allowed = true;
#endif
#ifndef QT_NO_TOOLBAR
if (QToolBar *tb = qobject_cast<QToolBar*>(widget))
@@ -1893,11 +2358,23 @@ QList<int> QMainWindowLayout::hover(QLayoutItem *widgetItem, const QPoint &mouse
return path;
}
+QDockWidgetGroupWindow *QMainWindowLayout::createTabbedDockWindow()
+{
+ QDockWidgetGroupWindow* f = new QDockWidgetGroupWindow(parentWidget(), Qt::Tool);
+ new QDockWidgetGroupLayout(f);
+ return f;
+}
+
void QMainWindowLayout::applyState(QMainWindowLayoutState &newState, bool animate)
{
#ifndef QT_NO_DOCKWIDGET
#ifndef QT_NO_TABBAR
QSet<QTabBar*> used = newState.dockAreaLayout.usedTabBars();
+ foreach (QDockWidgetGroupWindow *dwgw,
+ parent()->findChildren<QDockWidgetGroupWindow*>(QString(), Qt::FindDirectChildrenOnly)) {
+ used += dwgw->layoutInfo()->usedTabBars();
+ }
+
QSet<QTabBar*> retired = usedTabBars - used;
usedTabBars = used;
foreach (QTabBar *tab_bar, retired) {
@@ -1916,6 +2393,8 @@ void QMainWindowLayout::applyState(QMainWindowLayoutState &newState, bool animat
}
}
+ for (int i = 0; i < QInternal::DockCount; ++i)
+ newState.dockAreaLayout.docks[i].reparentWidgets(parentWidget());
#endif // QT_NO_TABBAR
#endif // QT_NO_DOCKWIDGET
diff --git a/src/widgets/widgets/qmainwindowlayout_p.h b/src/widgets/widgets/qmainwindowlayout_p.h
index 8ccb4d303f..9155c5fb23 100644
--- a/src/widgets/widgets/qmainwindowlayout_p.h
+++ b/src/widgets/widgets/qmainwindowlayout_p.h
@@ -77,6 +77,23 @@ QT_BEGIN_NAMESPACE
class QToolBar;
class QRubberBand;
+#ifndef QT_NO_DOCKWIDGET
+class QDockWidgetGroupWindow : public QWidget
+{
+ Q_OBJECT
+public:
+ explicit QDockWidgetGroupWindow(QWidget* parent = 0, Qt::WindowFlags f = 0)
+ : QWidget(parent, f) {}
+ QDockAreaLayoutInfo *layoutInfo() const;
+ QDockWidget *topDockWidget() const;
+ void destroyIfEmpty();
+ void adjustFlags();
+protected:
+ bool event(QEvent *) Q_DECL_OVERRIDE;
+ void paintEvent(QPaintEvent*) Q_DECL_OVERRIDE;
+};
+#endif
+
/* This data structure represents the state of all the tool-bars and dock-widgets. It's value based
so it can be easilly copied into a temporary variable. All operations are performed without moving
any widgets. Only when we are sure we have the desired state, we call apply(), which moves the
@@ -196,10 +213,11 @@ public:
QDockWidget *dockwidget,
Qt::Orientation orientation);
void tabifyDockWidget(QDockWidget *first, QDockWidget *second);
- Qt::DockWidgetArea dockWidgetArea(QDockWidget *dockwidget) const;
+ Qt::DockWidgetArea dockWidgetArea(QWidget* widget) const;
void raise(QDockWidget *widget);
void setVerticalTabsEnabled(bool enabled);
bool restoreDockWidget(QDockWidget *dockwidget);
+ QDockAreaLayoutInfo *dockInfo(QWidget *w);
#ifndef QT_NO_TABBAR
bool _documentMode;
@@ -224,6 +242,8 @@ public:
void setTabShape(QTabWidget::TabShape tabShape);
QTabWidget::TabPosition tabPosition(Qt::DockWidgetArea area) const;
void setTabPosition(Qt::DockWidgetAreas areas, QTabWidget::TabPosition tabPosition);
+
+ QDockWidgetGroupWindow *createTabbedDockWindow();
#endif // QT_NO_TABWIDGET
#endif // QT_NO_TABBAR
@@ -273,7 +293,7 @@ public:
QList<int> hover(QLayoutItem *widgetItem, const QPoint &mousePos);
bool plug(QLayoutItem *widgetItem);
- QLayoutItem *unplug(QWidget *widget);
+ QLayoutItem *unplug(QWidget *widget, bool group = false);
void revert(QLayoutItem *widgetItem);
void updateGapIndicator();
void paintDropIndicator(QPainter *p, QWidget *widget, const QRegion &clip);