Add QMainWindow::GroupedDragging DockOption

If this setting is enabled, the entire group of docked tabs will
be draggable by the title bar of the group and and individual dock
can be dragged by dragging the tab.

When tabs are detached, the docks that are contained are reparented to
a QDockWidgetGroupWindow.

[ChangeLog][QtWidgets][QMainWindow] Added GroupedDragging as a DockOption
which allow users to drag all the tabs together when dragging the
title of a QDockWidget which is tabbed with others.

Change-Id: I5285685b129770498eb3e4fd5f4556e41225a595
Reviewed-by: Friedemann Kleint <Friedemann.Kleint@theqtcompany.com>
Reviewed-by: Paul Olav Tvete <paul.tvete@theqtcompany.com>
bb10
Olivier Goffart 2015-03-05 11:00:53 +01:00 committed by Olivier Goffart (Woboq GmbH)
parent 84be950e98
commit ac1b6c0145
11 changed files with 717 additions and 97 deletions

View File

@ -248,8 +248,8 @@ void ColorDock::setCustomSizeHint(const QSize &size)
updateGeometry();
}
ColorSwatch::ColorSwatch(const QString &colorName, QWidget *parent, Qt::WindowFlags flags)
: QDockWidget(parent, flags)
ColorSwatch::ColorSwatch(const QString &colorName, QMainWindow *parent, Qt::WindowFlags flags)
: QDockWidget(parent, flags), mainWindow(parent)
{
setObjectName(colorName + QLatin1String(" Dock Widget"));
setWindowTitle(objectName() + QLatin1String(" [*]"));
@ -390,7 +390,6 @@ ColorSwatch::ColorSwatch(const QString &colorName, QWidget *parent, Qt::WindowFl
void ColorSwatch::updateContextMenu()
{
QMainWindow *mainWindow = qobject_cast<QMainWindow *>(parentWidget());
const Qt::DockWidgetArea area = mainWindow->dockWidgetArea(this);
const Qt::DockWidgetAreas areas = allowedAreas();
@ -458,7 +457,6 @@ void ColorSwatch::updateContextMenu()
void ColorSwatch::splitInto(QAction *action)
{
QMainWindow *mainWindow = qobject_cast<QMainWindow *>(parentWidget());
QList<ColorSwatch*> dock_list = mainWindow->findChildren<ColorSwatch*>();
ColorSwatch *target = 0;
foreach (ColorSwatch *dock, dock_list) {
@ -477,7 +475,6 @@ void ColorSwatch::splitInto(QAction *action)
void ColorSwatch::tabInto(QAction *action)
{
QMainWindow *mainWindow = qobject_cast<QMainWindow *>(parentWidget());
QList<ColorSwatch*> dock_list = mainWindow->findChildren<ColorSwatch*>();
ColorSwatch *target = 0;
foreach (ColorSwatch *dock, dock_list) {
@ -525,7 +522,6 @@ void ColorSwatch::place(Qt::DockWidgetArea area, bool p)
{
if (!p) return;
QMainWindow *mainWindow = qobject_cast<QMainWindow *>(parentWidget());
mainWindow->addDockWidget(area, this);
if (allowedAreasActions->isEnabled()) {

View File

@ -70,8 +70,10 @@ class ColorSwatch : public QDockWidget
QAction *windowModifiedAction;
QMainWindow *mainWindow;
public:
explicit ColorSwatch(const QString &colorName, QWidget *parent = 0, Qt::WindowFlags flags = 0);
explicit ColorSwatch(const QString &colorName, QMainWindow *parent = 0, Qt::WindowFlags flags = 0);
QMenu *menu;
void setCustomSizeHint(const QSize &size);

View File

@ -156,6 +156,11 @@ void MainWindow::setupMenuBar()
action->setChecked(dockOptions() & VerticalTabs);
connect(action, SIGNAL(toggled(bool)), this, SLOT(setDockOptions()));
action = mainWindowMenu->addAction(tr("Grouped dragging"));
action->setCheckable(true);
action->setChecked(dockOptions() & GroupedDragging);
connect(action, SIGNAL(toggled(bool)), this, SLOT(setDockOptions()));
QMenu *toolBarMenu = menuBar()->addMenu(tr("Tool bars"));
for (int i = 0; i < toolBars.count(); ++i)
toolBarMenu->addMenu(toolBars.at(i)->menu);
@ -187,6 +192,8 @@ void MainWindow::setDockOptions()
opts |= ForceTabbedDocks;
if (actions.at(4)->isChecked())
opts |= VerticalTabs;
if (actions.at(5)->isChecked())
opts |= GroupedDragging;
QMainWindow::setDockOptions(opts);
}

View File

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

View File

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

View File

@ -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,27 +206,42 @@ 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()
{
static const bool xcb = !QGuiApplication::platformName().compare(QLatin1String("xcb"), Qt::CaseInsensitive);
return xcb;
}
bool QDockWidgetLayout::nativeWindowDeco(bool floating) const
/*! \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)
Q_UNUSED(floating)
return false;
#else
return !isXcb() && (floating && item_list[QDockWidgetLayout::TitleBar] == 0);
static const bool xcb = !QGuiApplication::platformName().compare(QLatin1String("xcb"), Qt::CaseInsensitive);
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
{
return wmSupportsNativeWindowDeco() && floating && item_list.at(QDockWidgetLayout::TitleBar) == 0;
}
void QDockWidgetLayout::addItem(QLayoutItem*)
{
@ -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:

View File

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

View File

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

View File

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

View File

@ -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
@ -164,6 +165,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

View File

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