diff options
Diffstat (limited to 'src/widgets/widgets/qmainwindowlayout.cpp')
-rw-r--r-- | src/widgets/widgets/qmainwindowlayout.cpp | 700 |
1 files changed, 349 insertions, 351 deletions
diff --git a/src/widgets/widgets/qmainwindowlayout.cpp b/src/widgets/widgets/qmainwindowlayout.cpp index 91c012d8d8..1767a8458f 100644 --- a/src/widgets/widgets/qmainwindowlayout.cpp +++ b/src/widgets/widgets/qmainwindowlayout.cpp @@ -77,10 +77,6 @@ #include <private/qapplication_p.h> #include <private/qlayoutengine_p.h> #include <private/qwidgetresizehandler_p.h> -#if 0 // Used to be included in Qt4 for Q_WS_MAC -# include <private/qcore_mac_p.h> -# include <private/qt_cocoa_helpers_mac_p.h> -#endif QT_BEGIN_NAMESPACE @@ -184,13 +180,17 @@ QDebug operator<<(QDebug debug, const QMainWindowLayout *layout) /****************************************************************************** ** QDockWidgetGroupWindow */ -// QDockWidgetGroupWindow is the floating window containing the tabbed dockwidgets in case several -// dockwidgets are dragged together (QMainWindow::GroupedDragging feature). +// QDockWidgetGroupWindow is the floating window containing several QDockWidgets floating together. +// (QMainWindow::GroupedDragging feature) // QDockWidgetGroupLayout is the layout of that window and use a QDockAreaLayoutInfo to layout -// the tabs inside it. +// the QDockWidgets inside it. +// If there is only one QDockWidgets, or all QDockWidgets are tabbed together, it is equivalent +// of a floating QDockWidget (the title of the QDockWidget is the title of the window). But if there +// are nested QDockWidget, an additional title bar is there. #if QT_CONFIG(dockwidget) -class QDockWidgetGroupLayout : public QLayout { - QDockAreaLayoutInfo info; +class QDockWidgetGroupLayout : public QLayout, + public QMainWindowLayoutSeparatorHelper<QDockWidgetGroupLayout> +{ QWidgetResizeHandler *resizer; public: QDockWidgetGroupLayout(QDockWidgetGroupWindow* parent) : QLayout(parent) { @@ -199,7 +199,7 @@ public: resizer->setMovingEnabled(false); } ~QDockWidgetGroupLayout() { - info.deleteAllLayoutItems(); + layoutState.deleteAllLayoutItems(); } void addItem(QLayoutItem*) Q_DECL_OVERRIDE { Q_UNREACHABLE(); } @@ -207,32 +207,43 @@ public: QLayoutItem* itemAt(int index) const Q_DECL_OVERRIDE { int x = 0; - return info.itemAt(&x, index); + return layoutState.itemAt(&x, index); } QLayoutItem* takeAt(int index) Q_DECL_OVERRIDE { int x = 0; - return info.takeAt(&x, index); + QLayoutItem *ret = layoutState.takeAt(&x, index); + if (savedState.rect.isValid() && ret->widget()) { + // we need to remove the item also from the saved state to prevent crash + QList<int> path = savedState.indexOf(ret->widget()); + if (!path.isEmpty()) + savedState.remove(path); + // Also, the item may be contained several times as a gap item. + path = layoutState.indexOf(ret->widget()); + if (!path.isEmpty()) + layoutState.remove(path); + } + return ret; } QSize sizeHint() const Q_DECL_OVERRIDE { int fw = frameWidth(); - return info.sizeHint() + QSize(fw, fw); + return layoutState.sizeHint() + QSize(fw, fw); } QSize minimumSize() const Q_DECL_OVERRIDE { int fw = frameWidth(); - return info.minimumSize() + QSize(fw, fw); + return layoutState.minimumSize() + QSize(fw, fw); } QSize maximumSize() const Q_DECL_OVERRIDE { int fw = frameWidth(); - return info.maximumSize() + QSize(fw, fw); + return layoutState.maximumSize() + QSize(fw, fw); } void setGeometry(const QRect&r) Q_DECL_OVERRIDE { groupWindow()->destroyOrHideIfEmpty(); - QDockAreaLayoutInfo *li = layoutInfo(); + QDockAreaLayoutInfo *li = dockAreaLayoutInfo(); if (li->isEmpty()) return; int fw = frameWidth(); @@ -242,12 +253,12 @@ public: li->rect = r.adjusted(fw, fw, -fw, -fw); li->fitItems(); li->apply(false); + if (savedState.rect.isValid()) + savedState.rect = li->rect; resizer->setActive(QWidgetResizeHandler::Resize, !nativeWindowDeco()); } - QDockAreaLayoutInfo *layoutInfo() { - return &info; - } + QDockAreaLayoutInfo *dockAreaLayoutInfo() { return &layoutState; } bool nativeWindowDeco() const { @@ -264,14 +275,21 @@ public: { return static_cast<QDockWidgetGroupWindow *>(parent()); } + + QDockAreaLayoutInfo layoutState; + QDockAreaLayoutInfo savedState; }; bool QDockWidgetGroupWindow::event(QEvent *e) { + auto lay = static_cast<QDockWidgetGroupLayout *>(layout()); + if (lay && lay->windowEvent(e)) + return true; + switch (e->type()) { case QEvent::Close: // Forward the close to the QDockWidget just as if its close button was pressed - if (QDockWidget *dw = topDockWidget()) { + if (QDockWidget *dw = activeTabbedDockWidget()) { e->ignore(); dw->close(); adjustFlags(); @@ -279,7 +297,7 @@ bool QDockWidgetGroupWindow::event(QEvent *e) return true; case QEvent::Move: // Let QDockWidgetPrivate::moseEvent handle the dragging - if (QDockWidget *dw = topDockWidget()) + if (QDockWidget *dw = activeTabbedDockWidget()) static_cast<QDockWidgetPrivate *>(QObjectPrivate::get(dw))->moveEvent(static_cast<QMoveEvent*>(e)); return true; case QEvent::NonClientAreaMouseMove: @@ -287,7 +305,7 @@ bool QDockWidgetGroupWindow::event(QEvent *e) case QEvent::NonClientAreaMouseButtonRelease: case QEvent::NonClientAreaMouseButtonDblClick: // Let the QDockWidgetPrivate of the currently visible dock widget handle the drag and drop - if (QDockWidget *dw = topDockWidget()) + if (QDockWidget *dw = activeTabbedDockWidget()) static_cast<QDockWidgetPrivate *>(QObjectPrivate::get(dw))->nonClientAreaMouseEvent(static_cast<QMouseEvent*>(e)); return true; case QEvent::ChildAdded: @@ -319,17 +337,49 @@ void QDockWidgetGroupWindow::paintEvent(QPaintEvent *) QDockAreaLayoutInfo *QDockWidgetGroupWindow::layoutInfo() const { - return static_cast<QDockWidgetGroupLayout*>(layout())->layoutInfo(); + return static_cast<QDockWidgetGroupLayout *>(layout())->dockAreaLayoutInfo(); +} + +/*! \internal + If this is a floating tab bar returns the currently the QDockWidgetGroupWindow that contains + tab, otherwise, return nullptr; + \note: if there is only one QDockWidget, it's still considered as a floating tab + */ +const QDockAreaLayoutInfo *QDockWidgetGroupWindow::tabLayoutInfo() const +{ + const QDockAreaLayoutInfo *info = layoutInfo(); + while (info && !info->tabbed) { + // There should be only one tabbed subinfo otherwise we are not a floating tab but a real + // window + const QDockAreaLayoutInfo *next = nullptr; + bool isSingle = false; + for (const auto &item : info->item_list) { + if (item.skip() || (item.flags & QDockAreaLayoutItem::GapItem)) + continue; + if (next || isSingle) // Two visible things + return nullptr; + if (item.subinfo) + next = item.subinfo; + else if (item.widgetItem) + isSingle = true; + } + if (isSingle) + return info; + info = next; + } + return info; } /*! \internal - Returns the currently active QDockWidget. + If this is a floating tab bar returns the currently active QDockWidget, otherwise nullptr */ -QDockWidget *QDockWidgetGroupWindow::topDockWidget() const +QDockWidget *QDockWidgetGroupWindow::activeTabbedDockWidget() const { - QDockAreaLayoutInfo *info = layoutInfo(); - QDockWidget *dw = 0; + QDockWidget *dw = nullptr; #if QT_CONFIG(tabbar) + const QDockAreaLayoutInfo *info = tabLayoutInfo(); + if (!info) + return nullptr; if (info->tabBar && info->tabBar->currentIndex() >= 0) { int i = info->tabIndexToListIndex(info->tabBar->currentIndex()); if (i >= 0) { @@ -338,7 +388,6 @@ QDockWidget *QDockWidgetGroupWindow::topDockWidget() const dw = qobject_cast<QDockWidget *>(item.widgetItem->widget()); } } -#endif if (!dw) { for (int i = 0; !dw && i < info->item_list.count(); ++i) { const QDockAreaLayoutItem &item = info->item_list.at(i); @@ -349,6 +398,7 @@ QDockWidget *QDockWidgetGroupWindow::topDockWidget() const dw = qobject_cast<QDockWidget *>(item.widgetItem->widget()); } } +#endif return dw; } @@ -395,18 +445,18 @@ void QDockWidgetGroupWindow::destroyOrHideIfEmpty() } /*! \internal - Sets the flags of this window in accordence to the capabilities of the dock widgets + Sets the flags of this window in accordance to the capabilities of the dock widgets */ void QDockWidgetGroupWindow::adjustFlags() { - QDockWidget *top = topDockWidget(); - if (!top) - return; - const bool nativeDeco = static_cast<QDockWidgetGroupLayout *>(layout())->nativeWindowDeco(); - Qt::WindowFlags oldFlags = windowFlags(); Qt::WindowFlags flags = oldFlags; - if (nativeDeco) { + + QDockWidget *top = activeTabbedDockWidget(); + if (!top) { // nested tabs, show window decoration + flags = + ((oldFlags & ~Qt::FramelessWindowHint) | Qt::CustomizeWindowHint | Qt::WindowTitleHint); + } else if (static_cast<QDockWidgetGroupLayout *>(layout())->nativeWindowDeco()) { flags |= Qt::CustomizeWindowHint | Qt::WindowTitleHint; flags.setFlag(Qt::WindowCloseButtonHint, top->features() & QDockWidget::DockWidgetClosable); flags &= ~Qt::FramelessWindowHint; @@ -439,21 +489,99 @@ void QDockWidgetGroupWindow::adjustFlags() show(); // setWindowFlags hides the window } - setWindowTitle(top->windowTitle()); - setWindowIcon(top->windowIcon()); + QWidget *titleBarOf = top ? top : parentWidget(); + setWindowTitle(titleBarOf->windowTitle()); + setWindowIcon(titleBarOf->windowIcon()); } bool QDockWidgetGroupWindow::hasNativeDecos() const { + QDockWidget *dw = activeTabbedDockWidget(); + if (!dw) // We have a group of nested QDockWidgets (not just floating tabs) + return true; + if (!QDockWidgetLayout::wmSupportsNativeWindowDeco()) return false; - if (QDockWidget *dw = topDockWidget()) - return dw->titleBarWidget() == nullptr; + return dw->titleBarWidget() == nullptr; +} +/* + The given widget is hovered over this floating group. + This function will save the state and create a gap in the actual state. + currentGapRect and currentGapPos will be set. + One must call restore() or apply() after this function. + Returns true if there was any change in the currentGapPos + */ +bool QDockWidgetGroupWindow::hover(QLayoutItem *widgetItem, const QPoint &mousePos) +{ + QDockAreaLayoutInfo &savedState = static_cast<QDockWidgetGroupLayout *>(layout())->savedState; + if (savedState.isEmpty()) + savedState = *layoutInfo(); + + QMainWindow::DockOptions opts = static_cast<QMainWindow *>(parentWidget())->dockOptions(); + bool nestingEnabled = + (opts & QMainWindow::AllowNestedDocks) && !(opts & QMainWindow::ForceTabbedDocks); + QDockAreaLayoutInfo::TabMode tabMode = + nestingEnabled ? QDockAreaLayoutInfo::AllowTabs : QDockAreaLayoutInfo::ForceTabs; + if (auto group = qobject_cast<QDockWidgetGroupWindow *>(widgetItem->widget())) { + if (!group->tabLayoutInfo()) + tabMode = QDockAreaLayoutInfo::NoTabs; + } + + QDockAreaLayoutInfo newState = savedState; + if (newState.tabbed) { + // insertion into a top-level tab + newState.item_list = { QDockAreaLayoutItem(new QDockAreaLayoutInfo(newState)) }; + newState.item_list.first().size = pick(savedState.o, savedState.rect.size()); + newState.tabbed = false; + newState.tabBar = nullptr; + } + + auto newGapPos = newState.gapIndex(mousePos, nestingEnabled, tabMode); + Q_ASSERT(!newGapPos.isEmpty()); + if (newGapPos == currentGapPos) + return false; // gap is already there + currentGapPos = newGapPos; + newState.insertGap(currentGapPos, widgetItem); + newState.fitItems(); + currentGapRect = newState.info(currentGapPos)->itemRect(currentGapPos.last(), true); + *layoutInfo() = std::move(newState); + layoutInfo()->apply(opts & QMainWindow::AnimatedDocks); return true; } +/* + Remove the gap that was created by hover() + */ +void QDockWidgetGroupWindow::restore() +{ + QDockAreaLayoutInfo &savedState = static_cast<QDockWidgetGroupLayout *>(layout())->savedState; + if (!savedState.isEmpty()) { + *layoutInfo() = savedState; + savedState = QDockAreaLayoutInfo(); + } + currentGapRect = QRect(); + currentGapPos.clear(); + adjustFlags(); + layoutInfo()->fitItems(); + layoutInfo()->apply(static_cast<QMainWindow *>(parentWidget())->dockOptions() + & QMainWindow::AnimatedDocks); +} + +/* + Apply the state that was created by hover + */ +void QDockWidgetGroupWindow::apply() +{ + static_cast<QDockWidgetGroupLayout *>(layout())->savedState.clear(); + currentGapRect = QRect(); + layoutInfo()->plug(currentGapPos); + currentGapPos.clear(); + adjustFlags(); + layoutInfo()->apply(false); +} + #endif /****************************************************************************** @@ -707,7 +835,14 @@ QList<int> QMainWindowLayoutState::gapIndex(QWidget *widget, // is it a dock widget? if (qobject_cast<QDockWidget *>(widget) != 0 || qobject_cast<QDockWidgetGroupWindow *>(widget)) { - result = dockAreaLayout.gapIndex(pos); + bool disallowTabs = false; +#if QT_CONFIG(tabbar) + if (auto *group = qobject_cast<QDockWidgetGroupWindow *>(widget)) { + if (!group->tabLayoutInfo()) // Disallow to drop nested docks as a tab + disallowTabs = true; + } +#endif + result = dockAreaLayout.gapIndex(pos, disallowTabs); if (!result.isEmpty()) result.prepend(1); return result; @@ -1190,14 +1325,7 @@ void QMainWindowLayout::removeToolBar(QToolBar *toolbar) QObject::disconnect(parentWidget(), SIGNAL(toolButtonStyleChanged(Qt::ToolButtonStyle)), toolbar, SLOT(_q_updateToolButtonStyle(Qt::ToolButtonStyle))); -#if 0 // Used to be included in Qt4 for Q_WS_MAC - if (usesHIToolBar(toolbar)) { - removeFromMacToolbar(toolbar); - } else -#endif - { - removeWidget(toolbar); - } + removeWidget(toolbar); } } @@ -1209,25 +1337,17 @@ void QMainWindowLayout::addToolBar(Qt::ToolBarArea area, bool) { validateToolBarArea(area); -#if 0 // Used to be included in Qt4 for Q_WS_MAC - if ((area == Qt::TopToolBarArea) - && layoutState.mainWindow->unifiedTitleAndToolBarOnMac()) { - insertIntoMacToolbar(0, toolbar); - } else -#endif - { - //let's add the toolbar to the layout - addChildWidget(toolbar); - QLayoutItem * item = layoutState.toolBarAreaLayout.addToolBar(toDockPos(area), toolbar); - if (savedState.isValid() && item) { - // copy the toolbar also in the saved state - savedState.toolBarAreaLayout.insertItem(toDockPos(area), item); - } - invalidate(); - - //this ensures that the toolbar has the right window flags (not floating any more) - toolbar->d_func()->updateWindowFlags(false /*floating*/); + // let's add the toolbar to the layout + addChildWidget(toolbar); + QLayoutItem *item = layoutState.toolBarAreaLayout.addToolBar(toDockPos(area), toolbar); + if (savedState.isValid() && item) { + // copy the toolbar also in the saved state + savedState.toolBarAreaLayout.insertItem(toDockPos(area), item); } + invalidate(); + + // this ensures that the toolbar has the right window flags (not floating any more) + toolbar->d_func()->updateWindowFlags(false /*floating*/); } /*! @@ -1235,27 +1355,20 @@ void QMainWindowLayout::addToolBar(Qt::ToolBarArea area, */ void QMainWindowLayout::insertToolBar(QToolBar *before, QToolBar *toolbar) { -#if 0 // Used to be included in Qt4 for Q_WS_MAC - if (usesHIToolBar(before)) { - insertIntoMacToolbar(before, toolbar); - } else -#endif - { - addChildWidget(toolbar); - QLayoutItem * item = layoutState.toolBarAreaLayout.insertToolBar(before, toolbar); - if (savedState.isValid() && item) { - // copy the toolbar also in the saved state - savedState.toolBarAreaLayout.insertItem(before, item); + addChildWidget(toolbar); + QLayoutItem *item = layoutState.toolBarAreaLayout.insertToolBar(before, toolbar); + if (savedState.isValid() && item) { + // copy the toolbar also in the saved state + savedState.toolBarAreaLayout.insertItem(before, item); + } + if (!currentGapPos.isEmpty() && currentGapPos.constFirst() == 0) { + currentGapPos = layoutState.toolBarAreaLayout.currentGapIndex(); + if (!currentGapPos.isEmpty()) { + currentGapPos.prepend(0); + currentGapRect = layoutState.itemRect(currentGapPos); } - if (!currentGapPos.isEmpty() && currentGapPos.constFirst() == 0) { - currentGapPos = layoutState.toolBarAreaLayout.currentGapIndex(); - if (!currentGapPos.isEmpty()) { - currentGapPos.prepend(0); - currentGapRect = layoutState.itemRect(currentGapPos); - } - } - invalidate(); } + invalidate(); } Qt::ToolBarArea QMainWindowLayout::toolBarArea(QToolBar *toolbar) const @@ -1268,12 +1381,6 @@ Qt::ToolBarArea QMainWindowLayout::toolBarArea(QToolBar *toolbar) const case QInternal::BottomDock: return Qt::BottomToolBarArea; default: break; } -#if 0 // Used to be included in Qt4 for Q_WS_MAC - if (pos == QInternal::DockCount) { - if (qtoolbarsInUnifiedToolbarList.contains(toolbar)) - return Qt::TopToolBarArea; - } -#endif return Qt::NoToolBarArea; } @@ -1290,70 +1397,15 @@ void QMainWindowLayout::getStyleOptionInfo(QStyleOptionToolBar *option, QToolBar void QMainWindowLayout::toggleToolBarsVisible() { - bool updateNonUnifiedParts = true; -#if 0 // Used to be included in Qt4 for Q_WS_MAC - if (layoutState.mainWindow->unifiedTitleAndToolBarOnMac()) { - // If we hit this case, someone has pressed the "toolbar button" which will - // toggle the unified toolbar visibility, because that's what the user wants. - // We might be in a situation where someone has hidden all the toolbars - // beforehand (maybe in construction), but now they've hit this button and - // and are expecting the items to show. What do we do? - // 1) Check the visibility of all the toolbars, if one is visible, do nothing, this - // preserves what people would expect (these toolbars were visible when I clicked last time). - // 2) If NONE are visible, then show them all. Again, this preserves the user expectation - // of, "I want to see the toolbars." The user may get more toolbars than expected, but this - // is better seeing nothing. - // Don't worry about any of this if we are going invisible. This does mean we may get - // into issues when switching into and out of fullscreen mode, but this is probably minor. - // If we ever need to do hiding, that would have to be taken care of after the unified toolbar - // has finished hiding. - // People can of course handle the QEvent::ToolBarChange event themselves and do - // WHATEVER they want if they don't like what we are doing (though the unified toolbar - // will fire regardless). - - // Check if we REALLY need to update the geometry below. If we only have items in the - // unified toolbar, all the docks will be empty, so there's very little point - // in doing the geometry as Apple will do it (we also avoid flicker in Cocoa as well). - // FWIW, layoutState.toolBarAreaLayout.visible and the state of the unified toolbar - // visibility can get out of sync. I really don't think it's a big issue. It is kept - // to a minimum because we only change the visibility if we absolutely must. - // update the "non unified parts." - updateNonUnifiedParts = !layoutState.toolBarAreaLayout.isEmpty(); - - // We get this function before the unified toolbar does its thing. - // So, the value will be opposite of what we expect. - bool goingVisible = !macWindowToolbarIsVisible(qt_mac_window_for(layoutState.mainWindow)); - if (goingVisible) { - const int ToolBarCount = qtoolbarsInUnifiedToolbarList.size(); - bool needAllVisible = true; - for (int i = 0; i < ToolBarCount; ++i) { - if (!qtoolbarsInUnifiedToolbarList.at(i)->isHidden()) { - needAllVisible = false; - break; - } - } - if (needAllVisible) { - QBoolBlocker blocker(blockVisiblityCheck); // Disable the visibilty check because - // the toggle has already happened. - for (int i = 0; i < ToolBarCount; ++i) - qtoolbarsInUnifiedToolbarList.at(i)->setVisible(true); - } - } - if (!updateNonUnifiedParts) - layoutState.toolBarAreaLayout.visible = goingVisible; - } -#endif - if (updateNonUnifiedParts) { - layoutState.toolBarAreaLayout.visible = !layoutState.toolBarAreaLayout.visible; - if (!layoutState.mainWindow->isMaximized()) { - QPoint topLeft = parentWidget()->geometry().topLeft(); - QRect r = parentWidget()->geometry(); - r = layoutState.toolBarAreaLayout.rectHint(r); - r.moveTo(topLeft); - parentWidget()->setGeometry(r); - } else { - update(); - } + layoutState.toolBarAreaLayout.visible = !layoutState.toolBarAreaLayout.visible; + if (!layoutState.mainWindow->isMaximized()) { + QPoint topLeft = parentWidget()->geometry().topLeft(); + QRect r = parentWidget()->geometry(); + r = layoutState.toolBarAreaLayout.rectHint(r); + r.moveTo(topLeft); + parentWidget()->setGeometry(r); + } else { + update(); } } @@ -1783,39 +1835,6 @@ void QMainWindowLayout::tabMoved(int from, int to) } #endif // QT_CONFIG(tabbar) -bool QMainWindowLayout::startSeparatorMove(const QPoint &pos) -{ - movingSeparator = layoutState.dockAreaLayout.findSeparator(pos); - - if (movingSeparator.isEmpty()) - return false; - - layoutState.dockAreaLayout.fallbackToSizeHints = false; - - savedState = layoutState; - movingSeparatorPos = movingSeparatorOrigin = pos; - - return true; -} - -bool QMainWindowLayout::separatorMove(const QPoint &pos) -{ - if (movingSeparator.isEmpty()) - return false; - movingSeparatorPos = pos; - separatorMoveTimer.start(0, this); - return true; -} - -bool QMainWindowLayout::endSeparatorMove(const QPoint&) -{ - if (movingSeparator.isEmpty()) - return false; - movingSeparator.clear(); - savedState.clear(); - return true; -} - void QMainWindowLayout::raise(QDockWidget *widget) { #if QT_CONFIG(tabbar) @@ -1940,14 +1959,6 @@ QSize QMainWindowLayout::minimumSize() const const QSize sbMin = statusbar ? statusbar->minimumSize() : QSize(0, 0); minSize = QSize(qMax(sbMin.width(), minSize.width()), sbMin.height() + minSize.height()); -#if 0 // Used to be included in Qt4 for Q_WS_MAC - const QSize storedSize = minSize; - int minWidth = 0; - foreach (QToolBar *toolbar, qtoolbarsInUnifiedToolbarList) { - minWidth += toolbar->sizeHint().width() + 20; - } - minSize = QSize(qMax(minWidth, storedSize.width()), storedSize.height()); -#endif } return minSize; } @@ -1959,12 +1970,16 @@ void QMainWindowLayout::invalidate() } #if QT_CONFIG(dockwidget) -void QMainWindowLayout::setCurrentHoveredFloat(QWidget *w) +void QMainWindowLayout::setCurrentHoveredFloat(QDockWidgetGroupWindow *w) { if (currentHoveredFloat != w) { if (currentHoveredFloat) { disconnect(currentHoveredFloat.data(), &QObject::destroyed, this, &QMainWindowLayout::updateGapIndicator); + if (currentHoveredFloat) + currentHoveredFloat->restore(); + } else if (w) { + restore(true); } currentHoveredFloat = w; @@ -2035,52 +2050,30 @@ void QMainWindowLayout::revert(QLayoutItem *widgetItem) bool QMainWindowLayout::plug(QLayoutItem *widgetItem) { -#if QT_CONFIG(dockwidget) +#if QT_CONFIG(dockwidget) && QT_CONFIG(tabwidget) && QT_CONFIG(tabbar) if (currentHoveredFloat) { QWidget *widget = widgetItem->widget(); QList<int> previousPath = layoutState.indexOf(widget); if (!previousPath.isEmpty()) layoutState.remove(previousPath); + previousPath = currentHoveredFloat->layoutInfo()->indexOf(widget); // Let's remove the widget from any possible group window foreach (QDockWidgetGroupWindow *dwgw, parent()->findChildren<QDockWidgetGroupWindow*>(QString(), Qt::FindDirectChildrenOnly)) { + if (dwgw == currentHoveredFloat) + continue; QList<int> path = dwgw->layoutInfo()->indexOf(widget); if (!path.isEmpty()) dwgw->layoutInfo()->remove(path); } currentGapRect = QRect(); -#if QT_CONFIG(tabwidget) - if (QDockWidget *dropTo = qobject_cast<QDockWidget*>(currentHoveredFloat)) { - //dropping to a normal widget, we mutate it in a QDockWidgetGroupWindow with two tabs - QDockWidgetGroupWindow *floatingTabs = createTabbedDockWindow(); - floatingTabs->setGeometry(dropTo->geometry()); - QDockAreaLayoutInfo *info = floatingTabs->layoutInfo(); - *info = QDockAreaLayoutInfo(&layoutState.dockAreaLayout.sep, QInternal::LeftDock, - Qt::Horizontal, QTabBar::RoundedSouth, - static_cast<QMainWindow*>(parentWidget())); - info->tabbed = true; - QLayout *parentLayout = currentHoveredFloat->parentWidget()->layout(); - info->item_list.append(QDockAreaLayoutItem(parentLayout->takeAt(parentLayout->indexOf(currentHoveredFloat)))); - - dropTo->setParent(floatingTabs); - dropTo->show(); - dropTo->d_func()->plug(QRect()); - setCurrentHoveredFloat(floatingTabs); - } -#endif // QT_CONFIG(tabwidget) -#if QT_CONFIG(tabbar) - QDockWidgetGroupWindow *dwgw = qobject_cast<QDockWidgetGroupWindow *>(currentHoveredFloat); - Q_ASSERT(dwgw); - Q_ASSERT(dwgw->layoutInfo()->tabbed); // because floating group should always be tabbed - previousPath = dwgw->layoutInfo()->indexOf(widget); + currentHoveredFloat->apply(); if (!previousPath.isEmpty()) - dwgw->layoutInfo()->remove(previousPath); - dwgw->layoutInfo()->tab(0, widgetItem); - QRect globalRect = dwgw->layoutInfo()->tabContentRect(); - globalRect.moveTopLeft(dwgw->mapToGlobal(globalRect.topLeft())); + currentHoveredFloat->layoutInfo()->remove(previousPath); + QRect globalRect = currentHoveredFloat->currentGapRect; + globalRect.moveTopLeft(currentHoveredFloat->mapToGlobal(globalRect.topLeft())); pluggingWidget = widget; widgetAnimator.animate(widget, globalRect, dockOptions & QMainWindow::AnimatedDocks); -#endif // QT_CONFIG(tabbar) return true; } #endif @@ -2149,57 +2142,55 @@ void QMainWindowLayout::animationFinished(QWidget *widget) if (widget == pluggingWidget) { #if QT_CONFIG(dockwidget) +#if QT_CONFIG(tabbar) if (QDockWidgetGroupWindow *dwgw = qobject_cast<QDockWidgetGroupWindow *>(widget)) { // When the animated widget was a QDockWidgetGroupWindow, it means each of the // embedded QDockWidget needs to be plugged back into the QMainWindow layout. savedState.clear(); - QDockAreaLayoutInfo* info = dwgw->layoutInfo(); - QDockAreaLayoutInfo* parentInfo; - QList<int> path; - - if (QDockWidgetGroupWindow *dropTo = qobject_cast<QDockWidgetGroupWindow *>(currentHoveredFloat)) { - parentInfo = dropTo->layoutInfo(); -#if QT_CONFIG(tabbar) - Q_ASSERT(parentInfo->tabbed); -#endif - path = parentInfo->indexOf(widget); - Q_ASSERT(path.size() == 1); + QDockAreaLayoutInfo *srcInfo = dwgw->layoutInfo(); + const QDockAreaLayoutInfo *srcTabInfo = dwgw->tabLayoutInfo(); + QDockAreaLayoutInfo *dstParentInfo; + QList<int> dstPath; + + if (currentHoveredFloat) { + dstPath = currentHoveredFloat->layoutInfo()->indexOf(widget); + Q_ASSERT(dstPath.size() >= 1); + dstParentInfo = currentHoveredFloat->layoutInfo()->info(dstPath); } else { - path = layoutState.dockAreaLayout.indexOf(widget); - Q_ASSERT(path.size() >= 2); - parentInfo = layoutState.dockAreaLayout.info(path); - Q_ASSERT(parentInfo); + dstPath = layoutState.dockAreaLayout.indexOf(widget); + Q_ASSERT(dstPath.size() >= 2); + dstParentInfo = layoutState.dockAreaLayout.info(dstPath); } -#if QT_CONFIG(tabbar) - if (parentInfo->tabbed) { + Q_ASSERT(dstParentInfo); + int idx = dstPath.constLast(); + Q_ASSERT(dstParentInfo->item_list[idx].widgetItem->widget() == dwgw); + if (dstParentInfo->tabbed && srcTabInfo) { // merge the two tab widgets - int idx = path.constLast(); - Q_ASSERT(parentInfo->item_list[idx].widgetItem->widget() == dwgw); - delete parentInfo->item_list[idx].widgetItem; - parentInfo->item_list.removeAt(idx); - std::copy(info->item_list.cbegin(), info->item_list.cend(), - std::inserter(parentInfo->item_list, parentInfo->item_list.begin() + idx)); - quintptr currentId = info->currentTabId(); - *info = QDockAreaLayoutInfo(); - parentInfo->reparentWidgets(currentHoveredFloat ? currentHoveredFloat.data() : parentWidget()); - parentInfo->updateTabBar(); - parentInfo->setCurrentTabId(currentId); - } else -#endif // QT_CONFIG(tabbar) - { - QDockAreaLayoutItem &item = layoutState.dockAreaLayout.item(path); + delete dstParentInfo->item_list[idx].widgetItem; + dstParentInfo->item_list.removeAt(idx); + std::copy(srcTabInfo->item_list.cbegin(), srcTabInfo->item_list.cend(), + std::inserter(dstParentInfo->item_list, + dstParentInfo->item_list.begin() + idx)); + quintptr currentId = srcTabInfo->currentTabId(); + *srcInfo = QDockAreaLayoutInfo(); + dstParentInfo->reparentWidgets(currentHoveredFloat ? currentHoveredFloat.data() + : parentWidget()); + dstParentInfo->updateTabBar(); + dstParentInfo->setCurrentTabId(currentId); + } else { + QDockAreaLayoutItem &item = dstParentInfo->item_list[idx]; Q_ASSERT(item.widgetItem->widget() == dwgw); delete item.widgetItem; item.widgetItem = 0; - item.subinfo = new QDockAreaLayoutInfo(qMove(*info)); - *info = QDockAreaLayoutInfo(); -#if QT_CONFIG(tabbar) - item.subinfo->reparentWidgets(parentWidget()); - item.subinfo->setTabBarShape(parentInfo->tabBarShape); -#endif + item.subinfo = new QDockAreaLayoutInfo(std::move(*srcInfo)); + *srcInfo = QDockAreaLayoutInfo(); + item.subinfo->reparentWidgets(currentHoveredFloat ? currentHoveredFloat.data() + : parentWidget()); + item.subinfo->setTabBarShape(dstParentInfo->tabBarShape); } dwgw->destroyOrHideIfEmpty(); } +#endif if (QDockWidget *dw = qobject_cast<QDockWidget*>(widget)) { dw->setParent(currentHoveredFloat ? currentHoveredFloat.data() : parentWidget()); @@ -2279,9 +2270,6 @@ QMainWindowLayout::QMainWindowLayout(QMainWindow *mainwindow, QLayout *parentLay #endif // QT_CONFIG(dockwidget) , widgetAnimator(this) , pluggingWidget(0) -#if 0 // Used to be included in Qt4 for Q_WS_MAC - , blockVisiblityCheck(false) -#endif { if (parentLayout) setParent(parentLayout); @@ -2306,10 +2294,6 @@ QMainWindowLayout::~QMainWindowLayout() layoutState.deleteAllLayoutItems(); layoutState.deleteCentralWidgetItem(); -#if 0 // Used to be included in Qt4 for Q_WS_MAC - cleanUpMacToolbarItems(); -#endif - delete statusbar; } @@ -2362,6 +2346,36 @@ void QMainWindowLayout::setCentralWidget(QWidget *widget) invalidate(); } +#if QT_CONFIG(dockwidget) && QT_CONFIG(tabwidget) +/*! \internal + This helper function is called by QMainWindowLayout::unplug if QMainWindow::GroupedDragging is + set and we are dragging the title bar of a non-floating QDockWidget. + If one should unplug the whole group, do so and return true, otherwise return false. + \a item is pointing to the QLayoutItem that holds the QDockWidget, but will be updated to the + QLayoutItem that holds the new QDockWidgetGroupWindow if the group is unplugged. +*/ +static bool unplugGroup(QMainWindowLayout *layout, QLayoutItem **item, + QDockAreaLayoutItem &parentItem) +{ + if (!parentItem.subinfo || !parentItem.subinfo->tabbed) + return false; + + // The QDockWidget is part of a group of tab and we need to unplug them all. + + QDockWidgetGroupWindow *floatingTabs = layout->createTabbedDockWindow(); + QDockAreaLayoutInfo *info = floatingTabs->layoutInfo(); + *info = std::move(*parentItem.subinfo); + delete parentItem.subinfo; + parentItem.subinfo = nullptr; + floatingTabs->setGeometry(info->rect.translated(layout->parentWidget()->pos())); + floatingTabs->show(); + floatingTabs->raise(); + *item = new QDockWidgetGroupWindowItem(floatingTabs); + parentItem.widgetItem = *item; + return true; +} +#endif + /*! \internal Unplug \a widget (QDockWidget or QToolBar) from it's parent container. @@ -2375,19 +2389,31 @@ void QMainWindowLayout::setCentralWidget(QWidget *widget) QLayoutItem *QMainWindowLayout::unplug(QWidget *widget, bool group) { #if QT_CONFIG(dockwidget) && QT_CONFIG(tabbar) - if (!widget->isWindow() && qobject_cast<const QDockWidgetGroupWindow *>(widget->parentWidget())) { - if (group) { + auto *groupWindow = qobject_cast<const QDockWidgetGroupWindow *>(widget->parentWidget()); + if (!widget->isWindow() && groupWindow) { + if (group && groupWindow->tabLayoutInfo()) { // We are just dragging a floating window as it, not need to do anything, we just have to // look up the corresponding QWidgetItem* if it exists - QList<int> tabbedWindowPath = layoutState.indexOf(widget->parentWidget()); - return tabbedWindowPath.isEmpty() ? 0 : layoutState.item(tabbedWindowPath); + if (QDockAreaLayoutInfo *info = dockInfo(widget->parentWidget())) { + QList<int> groupWindowPath = info->indexOf(widget->parentWidget()); + return groupWindowPath.isEmpty() ? nullptr : info->item(groupWindowPath).widgetItem; + } + return nullptr; + } + QList<int> path = groupWindow->layoutInfo()->indexOf(widget); + QLayoutItem *item = groupWindow->layoutInfo()->item(path).widgetItem; + if (group && path.size() > 1 + && unplugGroup(this, &item, + groupWindow->layoutInfo()->item(path.mid(0, path.size() - 1)))) { + return item; } else { // We are unplugging a dock widget from a floating window. - if (QDockWidget *dw = qobject_cast<QDockWidget*>(widget)) { - dw->d_func()->unplug(widget->geometry()); - int index = widget->parentWidget()->layout()->indexOf(widget); - return widget->parentWidget()->layout()->itemAt(index); - } + QDockWidget *dw = qobject_cast<QDockWidget *>(widget); + Q_ASSERT(dw); // cannot be a QDockWidgetGroupWindow because it's not floating. + dw->d_func()->unplug(widget->geometry()); + groupWindow->layoutInfo()->fitItems(); + groupWindow->layoutInfo()->apply(dockOptions & QMainWindow::AnimatedDocks); + return item; } } #endif @@ -2405,30 +2431,15 @@ QLayoutItem *QMainWindowLayout::unplug(QWidget *widget, bool group) #if QT_CONFIG(dockwidget) if (QDockWidget *dw = qobject_cast<QDockWidget*>(widget)) { Q_ASSERT(path.constFirst() == 1); - bool actualGroup = false; #if QT_CONFIG(tabwidget) - if (group && (dockOptions & QMainWindow::GroupedDragging) && path.size() > 3) { - QDockAreaLayoutItem &parentItem = layoutState.dockAreaLayout.item(path.mid(1, path.size() - 2)); - if (parentItem.subinfo && parentItem.subinfo->tabbed) { - // The QDockWidget is part of a group of tab and we need to unplug them all. - actualGroup = true; - path.removeLast(); - - QDockWidgetGroupWindow* floatingTabs = createTabbedDockWindow(); - QDockAreaLayoutInfo* info = floatingTabs->layoutInfo(); - *info = qMove(*parentItem.subinfo); - delete parentItem.subinfo; - parentItem.subinfo = 0; - floatingTabs->setGeometry(info->rect.translated(parentWidget()->pos())); - floatingTabs->show(); - floatingTabs->raise(); - item = new QDockWidgetGroupWindowItem(floatingTabs); - parentItem.widgetItem = item; - savedState = layoutState; - } - } + if (group && (dockOptions & QMainWindow::GroupedDragging) && path.size() > 3 + && unplugGroup(this, &item, + layoutState.dockAreaLayout.item(path.mid(1, path.size() - 2)))) { + path.removeLast(); + savedState = layoutState; + } else #endif // QT_CONFIG(tabwidget) - if (!actualGroup) { + { dw->d_func()->unplug(r); } } @@ -2457,11 +2468,11 @@ QLayoutItem *QMainWindowLayout::unplug(QWidget *widget, bool group) void QMainWindowLayout::updateGapIndicator() { #if QT_CONFIG(rubberband) - if ((!widgetAnimator.animating() && !currentGapPos.isEmpty()) + if (!widgetAnimator.animating() && (!currentGapPos.isEmpty() #if QT_CONFIG(dockwidget) - || currentHoveredFloat + || currentHoveredFloat #endif - ) { + )) { QWidget *expectedParent = #if QT_CONFIG(dockwidget) currentHoveredFloat ? currentHoveredFloat.data() : @@ -2474,11 +2485,13 @@ void QMainWindowLayout::updateGapIndicator() } else if (gapIndicator->parent() != expectedParent) { gapIndicator->setParent(expectedParent); } - gapIndicator->setGeometry( + #if QT_CONFIG(dockwidget) - currentHoveredFloat ? currentHoveredFloat->rect() : + if (currentHoveredFloat) + gapIndicator->setGeometry(currentHoveredFloat->currentGapRect); + else #endif - currentGapRect); + gapIndicator->setGeometry(currentGapRect); gapIndicator->show(); gapIndicator->raise(); } else if (gapIndicator) { @@ -2527,12 +2540,36 @@ void QMainWindowLayout::hover(QLayoutItem *widgetItem, const QPoint &mousePos) if (!w->geometry().contains(mousePos)) continue; - setCurrentHoveredFloat(w); - restore(true); + if (auto dropTo = qobject_cast<QDockWidget *>(w)) { + // dropping to a normal widget, we mutate it in a QDockWidgetGroupWindow with two + // tabs + QDockWidgetGroupWindow *floatingTabs = createTabbedDockWindow(); // FIXME + floatingTabs->setGeometry(dropTo->geometry()); + QDockAreaLayoutInfo *info = floatingTabs->layoutInfo(); + *info = QDockAreaLayoutInfo(&layoutState.dockAreaLayout.sep, QInternal::LeftDock, + Qt::Horizontal, QTabBar::RoundedSouth, + static_cast<QMainWindow *>(parentWidget())); + info->tabbed = true; + QLayout *parentLayout = dropTo->parentWidget()->layout(); + info->item_list.append( + QDockAreaLayoutItem(parentLayout->takeAt(parentLayout->indexOf(dropTo)))); + + dropTo->setParent(floatingTabs); + dropTo->show(); + dropTo->d_func()->plug(QRect()); + w = floatingTabs; + } + Q_ASSERT(qobject_cast<QDockWidgetGroupWindow *>(w)); + auto group = static_cast<QDockWidgetGroupWindow *>(w); + if (group->hover(widgetItem, group->mapFromGlobal(mousePos))) { + setCurrentHoveredFloat(group); + applyState(layoutState); // update the tabbars + } return; } } setCurrentHoveredFloat(nullptr); + layoutState.dockAreaLayout.fallbackToSizeHints = false; #endif // QT_CONFIG(dockwidget) QPoint pos = parentWidget()->mapFromGlobal(mousePos); @@ -2595,7 +2632,7 @@ void QMainWindowLayout::hover(QLayoutItem *widgetItem, const QPoint &mousePos) #if QT_CONFIG(dockwidget) parentWidget()->update(layoutState.dockAreaLayout.separatorRegion()); #endif - layoutState = newState; + layoutState = std::move(newState); applyState(layoutState); updateGapIndicator(); @@ -2684,45 +2721,6 @@ bool QMainWindowLayout::restoreState(QDataStream &stream) return true; } - -// Returns if this toolbar *should* be using HIToolbar. Won't work for all in between cases -// for example, you have a toolbar in the top area and then you suddenly turn on -// HIToolbar. -bool QMainWindowLayout::usesHIToolBar(QToolBar *toolbar) const -{ -#if 1 // Used to be excluded in Qt4 for Q_WS_MAC - Q_UNUSED(toolbar); - return false; -#else - return qtoolbarsInUnifiedToolbarList.contains(toolbar) - || ((toolBarArea(toolbar) == Qt::TopToolBarArea) - && layoutState.mainWindow->unifiedTitleAndToolBarOnMac()); -#endif -} - -void QMainWindowLayout::timerEvent(QTimerEvent *e) -{ -#if QT_CONFIG(dockwidget) - if (e->timerId() == separatorMoveTimer.timerId()) { - //let's move the separators - separatorMoveTimer.stop(); - if (movingSeparator.isEmpty()) - return; - if (movingSeparatorOrigin == movingSeparatorPos) - return; - - //when moving the separator, we need to update the previous position - parentWidget()->update(layoutState.dockAreaLayout.separatorRegion()); - - layoutState = savedState; - layoutState.dockAreaLayout.separatorMove(movingSeparator, movingSeparatorOrigin, - movingSeparatorPos); - movingSeparatorPos = movingSeparatorOrigin; - } -#endif - QLayout::timerEvent(e); -} - QT_END_NAMESPACE #include "moc_qmainwindowlayout_p.cpp" |