8#if QT_CONFIG(dockwidget)
9#include "qdockarealayout_p.h"
10#include "qdockwidget.h"
11#include "qdockwidget_p.h"
14#include "qtoolbar_p.h"
16#include "qtoolbarlayout_p.h"
20#if QT_CONFIG(rubberband)
21#include "qrubberband.h"
27#include <qapplication.h>
28#if QT_CONFIG(draganddrop)
32#if QT_CONFIG(statusbar)
33#include <qstatusbar.h>
37#include <qstylepainter.h>
38#include <qvarlengtharray.h>
43#ifndef QT_NO_DEBUG_STREAM
45# include <qtextstream.h>
48#include <private/qmenu_p.h>
49#include <private/qapplication_p.h>
50#include <private/qlayoutengine_p.h>
51#include <private/qwidgetresizehandler_p.h>
53#include <QScopedValueRollback>
57using namespace Qt::StringLiterals;
62
63
65#if QT_CONFIG(dockwidget) && !defined(QT_NO_DEBUG_STREAM)
67static void dumpLayout(QTextStream &qout,
const QDockAreaLayoutInfo &layout, QString indent);
69static void dumpLayout(QTextStream &qout,
const QDockAreaLayoutItem &item, QString indent)
71 qout << indent <<
"QDockAreaLayoutItem: "
72 <<
"pos: " << item.pos <<
" size:" << item.size
73 <<
" gap:" << (item.flags & QDockAreaLayoutItem::GapItem)
74 <<
" keepSize:" << (item.flags & QDockAreaLayoutItem::KeepSize) <<
'\n';
76 if (item.widgetItem !=
nullptr) {
77 qout << indent <<
"widget: "
78 << item.widgetItem->widget()->metaObject()->className()
79 <<
" \"" << item.widgetItem->widget()->windowTitle() <<
"\"\n";
80 }
else if (item.subinfo !=
nullptr) {
81 qout << indent <<
"subinfo:\n";
82 dumpLayout(qout, *item.subinfo, indent +
" "_L1);
83 }
else if (item.placeHolderItem !=
nullptr) {
84 QRect r = item.placeHolderItem->topLevelRect;
85 qout << indent <<
"placeHolder: "
86 <<
"pos: " << item.pos <<
" size:" << item.size
87 <<
" gap:" << (item.flags & QDockAreaLayoutItem::GapItem)
88 <<
" keepSize:" << (item.flags & QDockAreaLayoutItem::KeepSize)
89 <<
" objectName:" << item.placeHolderItem->objectName
90 <<
" hidden:" << item.placeHolderItem->hidden
91 <<
" window:" << item.placeHolderItem->window
92 <<
" rect:" << r.x() <<
',' << r.y() <<
' '
93 << r.width() <<
'x' << r.height() <<
'\n';
97static void dumpLayout(QTextStream &qout,
const QDockAreaLayoutInfo &layout, QString indent)
99 const QSize minSize = layout.minimumSize();
100 qout << indent <<
"QDockAreaLayoutInfo: "
101 << layout.rect.left() <<
','
102 << layout.rect.top() <<
' '
103 << layout.rect.width() <<
'x'
104 << layout.rect.height()
105 <<
" min size: " << minSize.width() <<
',' << minSize.height()
106 <<
" orient:" << layout.o
108 <<
" tabbed:" << layout.tabbed
109 <<
" tbshape:" << layout.tabBarShape
115 for (
int i = 0; i < layout.item_list.size(); ++i) {
116 qout << indent <<
"Item: " << i <<
'\n';
117 dumpLayout(qout, layout.item_list.at(i), indent +
" "_L1);
121static void dumpLayout(QTextStream &qout,
const QDockAreaLayout &layout)
123 qout <<
"QDockAreaLayout: "
124 << layout.rect.left() <<
','
125 << layout.rect.top() <<
' '
126 << layout.rect.width() <<
'x'
127 << layout.rect.height() <<
'\n';
129 qout <<
"TopDockArea:\n";
130 dumpLayout(qout, layout.docks[QInternal::TopDock],
" "_L1);
131 qout <<
"LeftDockArea:\n";
132 dumpLayout(qout, layout.docks[QInternal::LeftDock],
" "_L1);
133 qout <<
"RightDockArea:\n";
134 dumpLayout(qout, layout.docks[QInternal::RightDock],
" "_L1);
135 qout <<
"BottomDockArea:\n";
136 dumpLayout(qout, layout.docks[QInternal::BottomDock],
" "_L1);
139QDebug operator<<(QDebug debug,
const QDockAreaLayout &layout)
143 dumpLayout(str, layout);
148QDebug operator<<(QDebug debug,
const QMainWindowLayout *layout)
151 return std::move(debug) << layout->layoutState.dockAreaLayout;
152 return debug <<
"QMainWindowLayout(0x0)";
158static void dumpItemLists(
const QMainWindowLayout *layout,
const char *function,
const char *comment)
160 for (
int i = 0; i < QInternal::DockCount; ++i) {
161 const auto &list = layout->layoutState.dockAreaLayout.docks[i].item_list;
164 qDebug() << function << comment <<
"Dock" << i << list;
167#define DUMP(comment) dumpItemLists(this, __FUNCTION__, comment)
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
206#if QT_CONFIG(dockwidget)
207class QDockWidgetGroupLayout :
public QLayout,
208 public QMainWindowLayoutSeparatorHelper<QDockWidgetGroupLayout>
210 QWidgetResizeHandler *resizer;
212 QDockWidgetGroupLayout(QDockWidgetGroupWindow* parent) : QLayout(parent) {
213 setSizeConstraint(QLayout::SetMinAndMaxSize);
214 resizer =
new QWidgetResizeHandler(parent);
216 ~QDockWidgetGroupLayout() {
217 layoutState.deleteAllLayoutItems();
220 void addItem(QLayoutItem*) override { Q_UNREACHABLE(); }
221 int count()
const override {
return 0; }
222 QLayoutItem* itemAt(
int index)
const override
225 return layoutState.itemAt(&x, index);
227 QLayoutItem* takeAt(
int index) override
230 QLayoutItem *ret = layoutState.takeAt(&x, index);
231 if (savedState.rect.isValid() && ret->widget()) {
233 QList<
int> path = savedState.indexOf(ret->widget());
235 savedState.remove(path);
237 path = layoutState.indexOf(ret->widget());
239 layoutState.remove(path);
243 QSize sizeHint()
const override
245 int fw = frameWidth();
246 return layoutState.sizeHint() + QSize(fw, fw);
248 QSize minimumSize()
const override
250 int fw = frameWidth();
251 return layoutState.minimumSize() + QSize(fw, fw);
253 QSize maximumSize()
const override
255 int fw = frameWidth();
256 return layoutState.maximumSize() + QSize(fw, fw);
258 void setGeometry(
const QRect&r) override
260 groupWindow()->destroyOrHideIfEmpty();
261 QDockAreaLayoutInfo *li = dockAreaLayoutInfo();
264 int fw = frameWidth();
266 li->reparentWidgets(parentWidget());
268 li->rect = r.adjusted(fw, fw, -fw, -fw);
271 if (savedState.rect.isValid())
272 savedState.rect = li->rect;
273 resizer->setEnabled(!nativeWindowDeco());
276 QDockAreaLayoutInfo *dockAreaLayoutInfo() {
return &layoutState; }
278#if QT_CONFIG(toolbar)
279 QToolBarAreaLayout *toolBarAreaLayout()
285 bool nativeWindowDeco()
const
287 return groupWindow()->hasNativeDecos();
290 int frameWidth()
const
292 return nativeWindowDeco() ? 0 :
293 parentWidget()->style()->pixelMetric(QStyle::PM_DockWidgetFrameWidth,
nullptr, parentWidget());
296 QDockWidgetGroupWindow *groupWindow()
const
298 return static_cast<QDockWidgetGroupWindow *>(parent());
301 QDockAreaLayoutInfo layoutState;
302 QDockAreaLayoutInfo savedState;
305bool QDockWidgetGroupWindow::event(QEvent *e)
307 auto lay =
static_cast<QDockWidgetGroupLayout *>(layout());
308 if (lay && lay->windowEvent(e))
315 if (QDockWidget *dw = activeTabbedDockWidget()) {
324 if (QDockWidget *dw = activeTabbedDockWidget())
325 static_cast<QDockWidgetPrivate *>(QObjectPrivate::get(dw))->moveEvent(
static_cast<QMoveEvent*>(e));
328 case QEvent::NonClientAreaMouseMove:
329 case QEvent::NonClientAreaMouseButtonPress:
330 case QEvent::NonClientAreaMouseButtonRelease:
331 case QEvent::NonClientAreaMouseButtonDblClick:
334 if (QDockWidget *dw = activeTabbedDockWidget())
335 static_cast<QDockWidgetPrivate *>(QObjectPrivate::get(dw))->nonClientAreaMouseEvent(
static_cast<QMouseEvent*>(e));
338 case QEvent::ChildAdded:
339 if (qobject_cast<QDockWidget *>(
static_cast<QChildEvent*>(e)->child()))
342 case QEvent::LayoutRequest:
344 destroyOrHideIfEmpty();
347 updateCurrentGapRect();
353 return QWidget::event(e);
356void QDockWidgetGroupWindow::paintEvent(QPaintEvent *)
358 QDockWidgetGroupLayout *lay =
static_cast<QDockWidgetGroupLayout *>(layout());
359 bool nativeDeco = lay->nativeWindowDeco();
362 QStyleOptionFrame framOpt;
363 framOpt.initFrom(
this);
364 QStylePainter p(
this);
365 p.drawPrimitive(QStyle::PE_FrameDockWidget, framOpt);
369QDockAreaLayoutInfo *QDockWidgetGroupWindow::layoutInfo()
const
371 return static_cast<QDockWidgetGroupLayout *>(layout())->dockAreaLayoutInfo();
376
377
378
379
380const QDockAreaLayoutInfo *QDockWidgetGroupWindow::tabLayoutInfo()
const
382 const QDockAreaLayoutInfo *info = layoutInfo();
383 while (info && !info->tabbed) {
386 const QDockAreaLayoutInfo *next =
nullptr;
387 bool isSingle =
false;
388 for (
const auto &item : info->item_list) {
389 if (item.skip() || (item.flags & QDockAreaLayoutItem::GapItem))
391 if (next || isSingle)
395 else if (item.widgetItem)
406
407
408QDockWidget *QDockWidgetGroupWindow::activeTabbedDockWidget()
const
410 QDockWidget *dw =
nullptr;
411 const QDockAreaLayoutInfo *info = tabLayoutInfo();
414 if (info->tabBar && info->tabBar->currentIndex() >= 0) {
415 int i = info->tabIndexToListIndex(info->tabBar->currentIndex());
417 const QDockAreaLayoutItem &item = info->item_list.at(i);
419 dw = qobject_cast<QDockWidget *>(item.widgetItem->widget());
423 for (
int i = 0; !dw && i < info->item_list.size(); ++i) {
424 const QDockAreaLayoutItem &item = info->item_list.at(i);
427 if (!item.widgetItem)
429 dw = qobject_cast<QDockWidget *>(item.widgetItem->widget());
437
438
439
440void QDockWidgetGroupWindow::destroyOrHideIfEmpty()
442 const QDockAreaLayoutInfo *info = layoutInfo();
443 if (!info->isEmpty()) {
448 if (!info->item_list.isEmpty()) {
454 const auto dockWidgetsList = dockWidgets();
455 for (QDockWidget *dw : dockWidgetsList) {
456 const bool wasFloating = dw->isFloating();
457 const bool wasHidden = dw->isHidden();
458 dw->setParent(parentWidget());
459 qCDebug(lcQpaDockWidgets) <<
"Reparented:" << dw <<
"to" << parentWidget() <<
"by" <<
this;
461 dw->setFloating(
true);
464 QMainWindowLayout *ml =
465 qt_mainwindow_layout(
static_cast<QMainWindow *>(parentWidget()));
466 Qt::DockWidgetArea area = ml->dockWidgetArea(
this);
467 if (area == Qt::NoDockWidgetArea)
468 area = Qt::LeftDockWidgetArea;
469 static_cast<QMainWindow *>(parentWidget())->addDockWidget(area, dw);
470 qCDebug(lcQpaDockWidgets) <<
"Redocked to Mainwindow:" << area << dw <<
"by" <<
this;
475 Q_ASSERT(qobject_cast<QMainWindow *>(parentWidget()));
476 auto *mainWindow =
static_cast<QMainWindow *>(parentWidget());
477 QMainWindowLayout *mwLayout = qt_mainwindow_layout(mainWindow);
478 QDockAreaLayoutInfo &parentInfo = mwLayout->layoutState.dockAreaLayout.docks[layoutInfo()->dockPos];
479 std::unique_ptr<QLayoutItem> cleanup = parentInfo.takeWidgetItem(
this);
480 parentInfo.remove(
this);
485
486
487
488
489bool QDockWidgetGroupWindow::hasVisibleDockWidgets()
const
491 const auto &children = findChildren<QDockWidget *>(Qt::FindChildrenRecursively);
492 for (
auto child : children) {
497 if (!child->testAttribute(Qt::WA_WState_Hidden))
504
505
506void QDockWidgetGroupWindow::adjustFlags()
508 Qt::WindowFlags oldFlags = windowFlags();
509 Qt::WindowFlags flags = oldFlags;
512 QDockWidget *top = activeTabbedDockWidget();
514 QDockWidget *top =
nullptr;
518 ((oldFlags & ~Qt::FramelessWindowHint) | Qt::CustomizeWindowHint | Qt::WindowTitleHint);
519 }
else if (
static_cast<QDockWidgetGroupLayout *>(layout())->nativeWindowDeco()) {
520 flags |= Qt::CustomizeWindowHint | Qt::WindowTitleHint;
521 flags.setFlag(Qt::WindowCloseButtonHint, top->features() & QDockWidget::DockWidgetClosable);
522 flags &= ~Qt::FramelessWindowHint;
524 flags &= ~(Qt::WindowCloseButtonHint | Qt::CustomizeWindowHint | Qt::WindowTitleHint);
525 flags |= Qt::FramelessWindowHint;
528 if (oldFlags != flags) {
531 setWindowFlags(flags);
532 const bool gainedNativeDecos = (oldFlags & Qt::FramelessWindowHint) && !(flags & Qt::FramelessWindowHint);
533 const bool lostNativeDecos = !(oldFlags & Qt::FramelessWindowHint) && (flags & Qt::FramelessWindowHint);
537 if (lostNativeDecos) {
538 QRect newGeometry = geometry();
539 newGeometry.setTop(frameGeometry().top());
540 const int bottomFrame = geometry().top() - frameGeometry().top();
541 m_removedFrameSize = QSize((frameSize() - size()).width(), bottomFrame);
542 setGeometry(newGeometry);
543 }
else if (gainedNativeDecos && m_removedFrameSize.isValid()) {
544 QRect r = geometry();
545 r.adjust(-m_removedFrameSize.width() / 2, 0,
546 -m_removedFrameSize.width() / 2, -m_removedFrameSize.height());
548 m_removedFrameSize = QSize();
551 setVisible(hasVisibleDockWidgets());
554 QWidget *titleBarOf = top ? top : parentWidget();
555 setWindowTitle(titleBarOf->windowTitle());
556 setWindowIcon(titleBarOf->windowIcon());
559bool QDockWidgetGroupWindow::hasNativeDecos()
const
562 QDockWidget *dw = activeTabbedDockWidget();
566 if (!QDockWidgetLayout::wmSupportsNativeWindowDeco())
569 return dw->titleBarWidget() ==
nullptr;
576QT_WARNING_DISABLE_GCC(
"-Waggressive-loop-optimizations")
578
579
580
581
582
583
584bool QDockWidgetGroupWindow::hover(QLayoutItem *widgetItem,
const QPoint &mousePos)
586 QDockAreaLayoutInfo &savedState =
static_cast<QDockWidgetGroupLayout *>(layout())->savedState;
587 if (savedState.isEmpty())
588 savedState = *layoutInfo();
590 QMainWindow::DockOptions opts =
static_cast<QMainWindow *>(parentWidget())->dockOptions();
591 QDockAreaLayoutInfo newState = savedState;
592 bool nestingEnabled =
593 (opts & QMainWindow::AllowNestedDocks) && !(opts & QMainWindow::ForceTabbedDocks);
594 QDockAreaLayoutInfo::TabMode tabMode =
595#if !QT_CONFIG(tabbar)
596 QDockAreaLayoutInfo::NoTabs;
598 nestingEnabled ? QDockAreaLayoutInfo::AllowTabs : QDockAreaLayoutInfo::ForceTabs;
599 if (
auto group = qobject_cast<QDockWidgetGroupWindow *>(widgetItem->widget())) {
600 if (!group->tabLayoutInfo())
601 tabMode = QDockAreaLayoutInfo::NoTabs;
603 if (newState.tabbed) {
605 newState.item_list = { QDockAreaLayoutItem(
new QDockAreaLayoutInfo(newState)) };
606 newState.item_list.first().size = pick(savedState.o, savedState.rect.size());
607 newState.tabbed =
false;
608 newState.tabBar =
nullptr;
612 auto newGapPos = newState.gapIndex(mousePos, nestingEnabled, tabMode);
613 Q_ASSERT(!newGapPos.isEmpty());
617 if (newGapPos == currentGapPos || newState.hasGapItem(newGapPos))
620 currentGapPos = newGapPos;
621 newState.insertGap(currentGapPos, widgetItem);
623 *layoutInfo() = std::move(newState);
624 updateCurrentGapRect();
625 layoutInfo()->apply(opts & QMainWindow::AnimatedDocks);
630void QDockWidgetGroupWindow::updateCurrentGapRect()
632 if (!currentGapPos.isEmpty())
633 currentGapRect = layoutInfo()->info(currentGapPos)->itemRect(currentGapPos.last(),
true);
637
638
639void QDockWidgetGroupWindow::restore()
641 QDockAreaLayoutInfo &savedState =
static_cast<QDockWidgetGroupLayout *>(layout())->savedState;
642 if (!savedState.isEmpty()) {
643 *layoutInfo() = savedState;
644 savedState = QDockAreaLayoutInfo();
646 currentGapRect = QRect();
647 currentGapPos.clear();
649 layoutInfo()->fitItems();
650 layoutInfo()->apply(
static_cast<QMainWindow *>(parentWidget())->dockOptions()
651 & QMainWindow::AnimatedDocks);
655
656
657void QDockWidgetGroupWindow::apply()
659 static_cast<QDockWidgetGroupLayout *>(layout())->savedState.clear();
660 currentGapRect = QRect();
661 layoutInfo()->plug(currentGapPos);
662 currentGapPos.clear();
664 layoutInfo()->apply(
false);
667void QDockWidgetGroupWindow::childEvent(QChildEvent *event)
669 switch (event->type()) {
670 case QEvent::ChildRemoved:
671 if (
auto *dockWidget = qobject_cast<QDockWidget *>(event->child()))
672 dockWidget->removeEventFilter(
this);
673 destroyIfSingleItemLeft();
675 case QEvent::ChildAdded:
676 if (
auto *dockWidget = qobject_cast<QDockWidget *>(event->child()))
677 dockWidget->installEventFilter(
this);
684bool QDockWidgetGroupWindow::eventFilter(QObject *obj, QEvent *event)
686 auto *dockWidget = qobject_cast<QDockWidget *>(obj);
688 return QWidget::eventFilter(obj, event);
690 switch (event->type()) {
694 reparentToMainWindow(dockWidget);
695 dockWidget->setFloating(
false);
701 if (dockWidget->isVisible())
708 return QWidget::eventFilter(obj, event);
711void QDockWidgetGroupWindow::destroyIfSingleItemLeft()
713 const auto &dockWidgets =
this->dockWidgets();
716 if (dockWidgets.count() != 1)
719 auto *lastDockWidget = dockWidgets.at(0);
724 if (layoutInfo()->indexOf(lastDockWidget).isEmpty())
727 Q_ASSERT(qobject_cast<QMainWindow *>(parentWidget()));
728 auto *mainWindow =
static_cast<QMainWindow *>(parentWidget());
729 QMainWindowLayout *mwLayout = qt_mainwindow_layout(mainWindow);
732 mwLayout->unplug(lastDockWidget, QDockWidgetPrivate::DragScope::Widget);
733 const QPoint position = pos();
737 reparentToMainWindow(lastDockWidget);
741 if (lastDockWidget->isFloating())
742 lastDockWidget->move(position);
745 layoutInfo()->item_list.clear();
747 destroyOrHideIfEmpty();
750void QDockWidgetGroupWindow::reparentToMainWindow(QDockWidget *dockWidget)
756 Q_ASSERT(qobject_cast<QMainWindow *>(parentWidget()));
757 auto *mainWindow =
static_cast<QMainWindow *>(parentWidget());
758 QMainWindowLayout *mwLayout = qt_mainwindow_layout(mainWindow);
760 mwLayout->widgetAnimator.abort(dockWidget);
761 QDockAreaLayoutInfo &parentInfo = mwLayout->layoutState.dockAreaLayout.docks[layoutInfo()->dockPos];
762 dockWidget->removeEventFilter(
this);
763 parentInfo.add(dockWidget);
764 layoutInfo()->remove(dockWidget);
765 const bool wasFloating = dockWidget->isFloating();
766 const bool wasVisible = dockWidget->isVisible();
767 dockWidget->setParent(mainWindow);
768 dockWidget->setFloating(wasFloating);
769 dockWidget->setVisible(wasVisible);
774
775
779QMainWindowLayoutState::QMainWindowLayoutState(QMainWindow *win)
781#if QT_CONFIG(toolbar)
782 toolBarAreaLayout(win),
784#if QT_CONFIG(dockwidget)
794QSize QMainWindowLayoutState::sizeHint()
const
799#if QT_CONFIG(dockwidget)
800 result = dockAreaLayout.sizeHint();
802 if (centralWidgetItem)
803 result = centralWidgetItem->sizeHint();
806#if QT_CONFIG(toolbar)
807 result = toolBarAreaLayout.sizeHint(result);
813QSize QMainWindowLayoutState::minimumSize()
const
817#if QT_CONFIG(dockwidget)
818 result = dockAreaLayout.minimumSize();
820 if (centralWidgetItem)
821 result = centralWidgetItem->minimumSize();
824#if QT_CONFIG(toolbar)
825 result = toolBarAreaLayout.minimumSize(result);
832
833
834
835
836bool QMainWindowLayoutState::fits()
const
838 Q_ASSERT(mainWindow);
842#if QT_CONFIG(dockwidget)
843 size = dockAreaLayout.minimumStableSize();
846#if QT_CONFIG(toolbar)
847 size.rwidth() += toolBarAreaLayout.docks[QInternal::LeftDock].rect.width();
848 size.rwidth() += toolBarAreaLayout.docks[QInternal::RightDock].rect.width();
849 size.rheight() += toolBarAreaLayout.docks[QInternal::TopDock].rect.height();
850 size.rheight() += toolBarAreaLayout.docks[QInternal::BottomDock].rect.height();
853 return size.width() <= mainWindow->width() && size.height() <= mainWindow->height();
856void QMainWindowLayoutState::apply(
bool animated)
858#if QT_CONFIG(toolbar)
859 toolBarAreaLayout.apply(animated);
862#if QT_CONFIG(dockwidget)
864 dockAreaLayout.apply(animated);
866 if (centralWidgetItem) {
867 QMainWindowLayout *layout = qt_mainwindow_layout(mainWindow);
869 layout->widgetAnimator.animate(centralWidgetItem->widget(), centralWidgetRect, animated);
874void QMainWindowLayoutState::fitLayout()
877#if !QT_CONFIG(toolbar)
880 toolBarAreaLayout.rect = rect;
881 r = toolBarAreaLayout.fitLayout();
884#if QT_CONFIG(dockwidget)
885 dockAreaLayout.rect = r;
886 dockAreaLayout.fitLayout();
888 centralWidgetRect = r;
892void QMainWindowLayoutState::deleteAllLayoutItems()
894#if QT_CONFIG(toolbar)
895 toolBarAreaLayout.deleteAllLayoutItems();
898#if QT_CONFIG(dockwidget)
899 dockAreaLayout.deleteAllLayoutItems();
903void QMainWindowLayoutState::deleteCentralWidgetItem()
905#if QT_CONFIG(dockwidget)
906 delete dockAreaLayout.centralWidgetItem;
907 dockAreaLayout.centralWidgetItem =
nullptr;
909 delete centralWidgetItem;
910 centralWidgetItem = 0;
914QLayoutItem *QMainWindowLayoutState::itemAt(
int index,
int *x)
const
916#if QT_CONFIG(toolbar)
917 if (QLayoutItem *ret = toolBarAreaLayout.itemAt(x, index))
921#if QT_CONFIG(dockwidget)
922 if (QLayoutItem *ret = dockAreaLayout.itemAt(x, index))
925 if (centralWidgetItem && (*x)++ == index)
926 return centralWidgetItem;
932QLayoutItem *QMainWindowLayoutState::takeAt(
int index,
int *x)
934#if QT_CONFIG(toolbar)
935 if (QLayoutItem *ret = toolBarAreaLayout.takeAt(x, index))
939#if QT_CONFIG(dockwidget)
940 if (QLayoutItem *ret = dockAreaLayout.takeAt(x, index))
943 if (centralWidgetItem && (*x)++ == index) {
944 QLayoutItem *ret = centralWidgetItem;
945 centralWidgetItem =
nullptr;
953QList<
int> QMainWindowLayoutState::indexOf(QWidget *widget)
const
957#if QT_CONFIG(toolbar)
959 if (QToolBar *toolBar = qobject_cast<QToolBar*>(widget)) {
960 result = toolBarAreaLayout.indexOf(toolBar);
961 if (!result.isEmpty())
967#if QT_CONFIG(dockwidget)
969 if (qobject_cast<QDockWidget *>(widget) || qobject_cast<QDockWidgetGroupWindow *>(widget)) {
970 result = dockAreaLayout.indexOf(widget);
971 if (!result.isEmpty())
980bool QMainWindowLayoutState::contains(QWidget *widget)
const
982#if QT_CONFIG(dockwidget)
983 if (dockAreaLayout.centralWidgetItem !=
nullptr && dockAreaLayout.centralWidgetItem->widget() == widget)
985 if (!dockAreaLayout.indexOf(widget).isEmpty())
988 if (centralWidgetItem && centralWidgetItem->widget() == widget)
992#if QT_CONFIG(toolbar)
993 if (!toolBarAreaLayout.indexOf(widget).isEmpty())
999void QMainWindowLayoutState::setCentralWidget(QWidget *widget)
1001 QLayoutItem *item =
nullptr;
1003 deleteCentralWidgetItem();
1005 if (widget !=
nullptr)
1006 item =
new QWidgetItemV2(widget);
1008#if QT_CONFIG(dockwidget)
1009 dockAreaLayout.centralWidgetItem = item;
1011 centralWidgetItem = item;
1015QWidget *QMainWindowLayoutState::centralWidget()
const
1017 QLayoutItem *item =
nullptr;
1019#if QT_CONFIG(dockwidget)
1020 item = dockAreaLayout.centralWidgetItem;
1022 item = centralWidgetItem;
1025 if (item !=
nullptr)
1026 return item->widget();
1030QList<
int> QMainWindowLayoutState::gapIndex(QWidget *widget,
1031 const QPoint &pos)
const
1035#if QT_CONFIG(toolbar)
1037 if (qobject_cast<QToolBar*>(widget) !=
nullptr) {
1038 result = toolBarAreaLayout.gapIndex(pos);
1039 if (!result.isEmpty())
1045#if QT_CONFIG(dockwidget)
1047 if (qobject_cast<QDockWidget *>(widget) !=
nullptr
1048 || qobject_cast<QDockWidgetGroupWindow *>(widget)) {
1049 bool disallowTabs =
false;
1050#if QT_CONFIG(tabbar)
1051 if (
auto *group = qobject_cast<QDockWidgetGroupWindow *>(widget)) {
1052 if (!group->tabLayoutInfo())
1053 disallowTabs =
true;
1056 result = dockAreaLayout.gapIndex(pos, disallowTabs);
1057 if (!result.isEmpty())
1066bool QMainWindowLayoutState::insertGap(
const QList<
int> &path, QLayoutItem *item)
1071 int i = path.first();
1073#if QT_CONFIG(toolbar)
1075 Q_ASSERT(qobject_cast<QToolBar*>(item->widget()) !=
nullptr);
1076 return toolBarAreaLayout.insertGap(path.mid(1), item);
1080#if QT_CONFIG(dockwidget)
1082 Q_ASSERT(qobject_cast<QDockWidget*>(item->widget()) || qobject_cast<QDockWidgetGroupWindow*>(item->widget()));
1083 return dockAreaLayout.insertGap(path.mid(1), item);
1090void QMainWindowLayoutState::remove(
const QList<
int> &path)
1092 int i = path.first();
1094#if QT_CONFIG(toolbar)
1096 toolBarAreaLayout.remove(path.mid(1));
1099#if QT_CONFIG(dockwidget)
1101 dockAreaLayout.remove(path.mid(1));
1105void QMainWindowLayoutState::remove(QLayoutItem *item)
1107#if QT_CONFIG(toolbar)
1108 toolBarAreaLayout.remove(item);
1111#if QT_CONFIG(dockwidget)
1113 if (QDockWidget *dockWidget = qobject_cast<QDockWidget *>(item->widget())) {
1114 QList<
int> path = dockAreaLayout.indexOf(dockWidget);
1115 if (!path.isEmpty())
1116 dockAreaLayout.remove(path);
1121void QMainWindowLayoutState::clear()
1123#if QT_CONFIG(toolbar)
1124 toolBarAreaLayout.clear();
1127#if QT_CONFIG(dockwidget)
1128 dockAreaLayout.clear();
1130 centralWidgetRect = QRect();
1136bool QMainWindowLayoutState::isValid()
const
1138 return rect.isValid();
1141QLayoutItem *QMainWindowLayoutState::item(
const QList<
int> &path)
1143 int i = path.first();
1145#if QT_CONFIG(toolbar)
1147 const QToolBarAreaLayoutItem *tbItem = toolBarAreaLayout.item(path.mid(1));
1149 return tbItem->widgetItem;
1153#if QT_CONFIG(dockwidget)
1155 return dockAreaLayout.item(path.mid(1)).widgetItem;
1161QRect QMainWindowLayoutState::itemRect(
const QList<
int> &path)
const
1163 int i = path.first();
1165#if QT_CONFIG(toolbar)
1167 return toolBarAreaLayout.itemRect(path.mid(1));
1170#if QT_CONFIG(dockwidget)
1172 return dockAreaLayout.itemRect(path.mid(1));
1178QRect QMainWindowLayoutState::gapRect(
const QList<
int> &path)
const
1180 int i = path.first();
1182#if QT_CONFIG(toolbar)
1184 return toolBarAreaLayout.itemRect(path.mid(1));
1187#if QT_CONFIG(dockwidget)
1189 return dockAreaLayout.gapRect(path.mid(1));
1195QLayoutItem *QMainWindowLayoutState::plug(
const QList<
int> &path)
1197 int i = path.first();
1199#if QT_CONFIG(toolbar)
1201 return toolBarAreaLayout.plug(path.mid(1));
1204#if QT_CONFIG(dockwidget)
1206 return dockAreaLayout.plug(path.mid(1));
1212QLayoutItem *QMainWindowLayoutState::unplug(
const QList<
int> &path, QMainWindowLayoutState *other)
1214 int i = path.first();
1216#if !QT_CONFIG(toolbar)
1220 return toolBarAreaLayout.unplug(path.mid(1), other ? &other->toolBarAreaLayout :
nullptr);
1223#if QT_CONFIG(dockwidget)
1225 return dockAreaLayout.unplug(path.mid(1));
1231void QMainWindowLayoutState::saveState(QDataStream &stream)
const
1233#if QT_CONFIG(dockwidget)
1234 dockAreaLayout.saveState(stream);
1235#if QT_CONFIG(tabbar)
1236 const QList<QDockWidgetGroupWindow *> floatingTabs =
1237 mainWindow->findChildren<QDockWidgetGroupWindow *>(Qt::FindDirectChildrenOnly);
1239 for (QDockWidgetGroupWindow *floating : floatingTabs) {
1240 if (floating->layoutInfo()->isEmpty())
1242 stream << uchar(QDockAreaLayout::FloatingDockWidgetTabMarker) << floating->geometry();
1243 floating->layoutInfo()->saveState(stream);
1247#if QT_CONFIG(toolbar)
1248 toolBarAreaLayout.saveState(stream);
1252#if QT_CONFIG(dockwidget)
1253static QList<QDockWidget*> allMyDockWidgets(
const QWidget *mainWindow)
1255 QList<QDockWidget*> result;
1256 for (QObject *c : mainWindow->children()) {
1257 if (
auto *dw = qobject_cast<QDockWidget*>(c)) {
1259 }
else if (
auto *gw = qobject_cast<QDockWidgetGroupWindow*>(c)) {
1260 for (QObject *c : gw->children()) {
1261 if (
auto *dw = qobject_cast<QDockWidget*>(c))
1272bool QMainWindowLayoutState::checkFormat(QDataStream &stream)
1274 while (!stream.atEnd()) {
1279#if QT_CONFIG(toolbar)
1280 case QToolBarAreaLayout::ToolBarStateMarker:
1281 case QToolBarAreaLayout::ToolBarStateMarkerEx:
1283 const auto toolBars = mainWindow->findChildren<QToolBar*>();
1284 if (!toolBarAreaLayout.restoreState(stream, toolBars, marker, QInternal::Testing))
1290#if QT_CONFIG(dockwidget)
1291 case QDockAreaLayout::DockWidgetStateMarker:
1293 const auto dockWidgets = allMyDockWidgets(mainWindow);
1294 if (!dockAreaLayout.restoreState(stream, dockWidgets, QInternal::Testing))
1298#if QT_CONFIG(tabbar)
1299 case QDockAreaLayout::FloatingDockWidgetTabMarker:
1303 QDockAreaLayoutInfo info;
1304 auto dockWidgets = allMyDockWidgets(mainWindow);
1305 if (!info.restoreState(stream, dockWidgets, QInternal::Testing))
1321bool QMainWindowLayoutState::restoreState(QDataStream &_stream,
1322 const QMainWindowLayoutState &oldState)
1326 while(!_stream.atEnd()) {
1328 QByteArray ba(length,
'\0');
1329 length = _stream.readRawData(ba.data(), ba.size());
1334 QDataStream ds(copy);
1335 ds.setVersion(_stream.version());
1336 if (!checkFormat(ds))
1339 QDataStream stream(copy);
1340 stream.setVersion(_stream.version());
1342 while (!stream.atEnd()) {
1347#if QT_CONFIG(dockwidget)
1348 case QDockAreaLayout::DockWidgetStateMarker:
1350 const auto dockWidgets = allMyDockWidgets(mainWindow);
1351 if (!dockAreaLayout.restoreState(stream, dockWidgets, QInternal::Live))
1354 for (
auto *w : dockWidgets) {
1355 const QList<
int> path = dockAreaLayout.indexOf(w);
1356 if (path.isEmpty()) {
1357 QList<
int> oldPath = oldState.dockAreaLayout.indexOf(w);
1358 if (oldPath.isEmpty())
1360 QDockAreaLayoutInfo *info = dockAreaLayout.info(oldPath);
1361 if (info ==
nullptr) {
1369#if QT_CONFIG(tabwidget)
1370 case QDockAreaLayout::FloatingDockWidgetTabMarker:
1372 auto dockWidgets = allMyDockWidgets(mainWindow);
1373 QDockWidgetGroupWindow* floatingTab = qt_mainwindow_layout(mainWindow)->createTabbedDockWindow();
1374 *floatingTab->layoutInfo() = QDockAreaLayoutInfo(
1375 &dockAreaLayout.sep, QInternal::LeftDock,
1376 Qt::Horizontal, QTabBar::RoundedSouth, mainWindow);
1379 QDockAreaLayoutInfo *info = floatingTab->layoutInfo();
1380 if (!info->restoreState(stream, dockWidgets, QInternal::Live))
1382 geometry = QDockAreaLayout::constrainedRect(geometry, floatingTab);
1383 floatingTab->move(geometry.topLeft());
1384 floatingTab->resize(geometry.size());
1388 if (info->onlyHasPlaceholders())
1389 info->reparentWidgets(floatingTab);
1391 floatingTab->show();
1397#if QT_CONFIG(toolbar)
1398 case QToolBarAreaLayout::ToolBarStateMarker:
1399 case QToolBarAreaLayout::ToolBarStateMarkerEx:
1401 const auto toolBars = mainWindow->findChildren<QToolBar*>();
1402 if (!toolBarAreaLayout.restoreState(stream, toolBars, marker, QInternal::Live))
1405 for (
auto *bar : toolBars) {
1406 const QList<
int> path = toolBarAreaLayout.indexOf(bar);
1407 if (path.isEmpty()) {
1408 const QList<
int> oldPath = oldState.toolBarAreaLayout.indexOf(bar);
1409 if (oldPath.isEmpty())
1411 toolBarAreaLayout.docks[oldPath.at(0)].insertToolBar(
nullptr, bar);
1427
1428
1430#if QT_CONFIG(toolbar)
1432static constexpr Qt::ToolBarArea validateToolBarArea(Qt::ToolBarArea area)
1435 case Qt::LeftToolBarArea:
1436 case Qt::RightToolBarArea:
1437 case Qt::TopToolBarArea:
1438 case Qt::BottomToolBarArea:
1443 return Qt::TopToolBarArea;
1446static QInternal::DockPosition toDockPos(Qt::ToolBarArea area)
1449 case Qt::LeftToolBarArea:
return QInternal::LeftDock;
1450 case Qt::RightToolBarArea:
return QInternal::RightDock;
1451 case Qt::TopToolBarArea:
return QInternal::TopDock;
1452 case Qt::BottomToolBarArea:
return QInternal::BottomDock;
1457 return QInternal::DockCount;
1460static Qt::ToolBarArea toToolBarArea(QInternal::DockPosition pos)
1463 case QInternal::LeftDock:
return Qt::LeftToolBarArea;
1464 case QInternal::RightDock:
return Qt::RightToolBarArea;
1465 case QInternal::TopDock:
return Qt::TopToolBarArea;
1466 case QInternal::BottomDock:
return Qt::BottomToolBarArea;
1469 return Qt::NoToolBarArea;
1472static inline Qt::ToolBarArea toToolBarArea(
int pos)
1474 return toToolBarArea(
static_cast<QInternal::DockPosition>(pos));
1477void QMainWindowLayout::addToolBarBreak(Qt::ToolBarArea area)
1479 area = validateToolBarArea(area);
1481 layoutState.toolBarAreaLayout.addToolBarBreak(toDockPos(area));
1482 if (savedState.isValid())
1483 savedState.toolBarAreaLayout.addToolBarBreak(toDockPos(area));
1488void QMainWindowLayout::insertToolBarBreak(QToolBar *before)
1490 layoutState.toolBarAreaLayout.insertToolBarBreak(before);
1491 if (savedState.isValid())
1492 savedState.toolBarAreaLayout.insertToolBarBreak(before);
1496void QMainWindowLayout::removeToolBarBreak(QToolBar *before)
1498 layoutState.toolBarAreaLayout.removeToolBarBreak(before);
1499 if (savedState.isValid())
1500 savedState.toolBarAreaLayout.removeToolBarBreak(before);
1504void QMainWindowLayout::moveToolBar(QToolBar *toolbar,
int pos)
1506 layoutState.toolBarAreaLayout.moveToolBar(toolbar, pos);
1507 if (savedState.isValid())
1508 savedState.toolBarAreaLayout.moveToolBar(toolbar, pos);
1513
1514void QMainWindowLayout::removeToolBar(QToolBar *toolbar)
1517 QObject::disconnect(parentWidget(), SIGNAL(iconSizeChanged(QSize)),
1518 toolbar, SLOT(_q_updateIconSize(QSize)));
1519 QObject::disconnect(parentWidget(), SIGNAL(toolButtonStyleChanged(Qt::ToolButtonStyle)),
1520 toolbar, SLOT(_q_updateToolButtonStyle(Qt::ToolButtonStyle)));
1522 removeWidget(toolbar);
1527
1528
1529
1530
1533
1534
1535void QMainWindowLayout::addToolBar(Qt::ToolBarArea area,
1539 area = validateToolBarArea(area);
1541 addChildWidget(toolbar);
1542 QLayoutItem *item = layoutState.toolBarAreaLayout.addToolBar(toDockPos(area), toolbar);
1543 if (savedState.isValid() && item) {
1545 savedState.toolBarAreaLayout.insertItem(toDockPos(area), item);
1550 toolbar->d_func()->updateWindowFlags(
false );
1554
1555
1556void QMainWindowLayout::insertToolBar(QToolBar *before, QToolBar *toolbar)
1558 addChildWidget(toolbar);
1559 QLayoutItem *item = layoutState.toolBarAreaLayout.insertToolBar(before, toolbar);
1560 if (savedState.isValid() && item) {
1562 savedState.toolBarAreaLayout.insertItem(before, item);
1564 if (!currentGapPos.isEmpty() && currentGapPos.constFirst() == 0) {
1565 currentGapPos = layoutState.toolBarAreaLayout.currentGapIndex();
1566 if (!currentGapPos.isEmpty()) {
1567 currentGapPos.prepend(0);
1568 currentGapRect = layoutState.itemRect(currentGapPos);
1574Qt::ToolBarArea QMainWindowLayout::toolBarArea(
const QToolBar *toolbar)
const
1576 QInternal::DockPosition pos = layoutState.toolBarAreaLayout.findToolBar(toolbar);
1578 case QInternal::LeftDock:
return Qt::LeftToolBarArea;
1579 case QInternal::RightDock:
return Qt::RightToolBarArea;
1580 case QInternal::TopDock:
return Qt::TopToolBarArea;
1581 case QInternal::BottomDock:
return Qt::BottomToolBarArea;
1584 return Qt::NoToolBarArea;
1587bool QMainWindowLayout::toolBarBreak(QToolBar *toolBar)
const
1589 return layoutState.toolBarAreaLayout.toolBarBreak(toolBar);
1592void QMainWindowLayout::getStyleOptionInfo(QStyleOptionToolBar *option, QToolBar *toolBar)
const
1594 option->toolBarArea = toolBarArea(toolBar);
1595 layoutState.toolBarAreaLayout.getStyleOptionInfo(option, toolBar);
1598void QMainWindowLayout::toggleToolBarsVisible()
1600 layoutState.toolBarAreaLayout.visible = !layoutState.toolBarAreaLayout.visible;
1601 if (!layoutState.mainWindow->isMaximized()) {
1602 QPoint topLeft = parentWidget()->geometry().topLeft();
1603 QRect r = parentWidget()->geometry();
1604 r = layoutState.toolBarAreaLayout.rectHint(r);
1606 parentWidget()->setGeometry(r);
1615
1616
1618#if QT_CONFIG(dockwidget)
1620static QInternal::DockPosition toDockPos(Qt::DockWidgetArea area)
1623 case Qt::LeftDockWidgetArea:
return QInternal::LeftDock;
1624 case Qt::RightDockWidgetArea:
return QInternal::RightDock;
1625 case Qt::TopDockWidgetArea:
return QInternal::TopDock;
1626 case Qt::BottomDockWidgetArea:
return QInternal::BottomDock;
1631 return QInternal::DockCount;
1634inline static Qt::DockWidgetArea toDockWidgetArea(
int pos)
1636 return QDockWidgetPrivate::toDockWidgetArea(
static_cast<QInternal::DockPosition>(pos));
1641static bool isAreaAllowed(QWidget *widget,
const QList<
int> &path)
1643 Q_ASSERT_X((path.size() > 1),
"isAreaAllowed",
"invalid path size");
1644 const Qt::DockWidgetArea area = toDockWidgetArea(path[1]);
1647 if (QDockWidget *dw = qobject_cast<QDockWidget *>(widget)) {
1648 const bool allowed = dw->isAreaAllowed(area);
1650 qCDebug(lcQpaDockWidgets) <<
"No permission for single DockWidget" << widget <<
"to dock on" << area;
1655 if (QDockWidgetGroupWindow *dwgw = qobject_cast<QDockWidgetGroupWindow *>(widget)) {
1656 const QList<QDockWidget *> children = dwgw->findChildren<QDockWidget *>(QString(), Qt::FindDirectChildrenOnly);
1658 if (children.size() == 1) {
1660 const bool allowed = children.at(0)->isAreaAllowed(area);
1662 qCDebug(lcQpaDockWidgets) <<
"No permission for DockWidgetGroupWindow" << widget <<
"to dock on" << area;
1666 qCDebug(lcQpaDockWidgets) <<
"DockWidgetGroupWindow" << widget <<
"has" << children.size() <<
"children:";
1667 qCDebug(lcQpaDockWidgets) << children;
1668 qCDebug(lcQpaDockWidgets) <<
"DockWidgetGroupWindow" << widget <<
"can dock at" << area <<
"and anywhere else.";
1672 qCDebug(lcQpaDockWidgets) <<
"Docking requested for invalid widget type (coding error)." << widget << area;
1676void QMainWindowLayout::setCorner(Qt::Corner corner, Qt::DockWidgetArea area)
1678 if (layoutState.dockAreaLayout.corners[corner] == area)
1680 layoutState.dockAreaLayout.corners[corner] = area;
1681 if (savedState.isValid())
1682 savedState.dockAreaLayout.corners[corner] = area;
1686Qt::DockWidgetArea QMainWindowLayout::corner(Qt::Corner corner)
const
1688 return layoutState.dockAreaLayout.corners[corner];
1694QRect QMainWindowLayout::dockWidgetAreaRect(
const Qt::DockWidgetArea area, DockWidgetAreaSize size)
const
1696 const QInternal::DockPosition dockPosition = toDockPos(area);
1699 if (dockPosition == QInternal::DockCount) {
1700 qCDebug(lcQpaDockWidgets) <<
"QMainWindowLayout::dockWidgetAreaRect called with" << area;
1704 const QDockAreaLayout dl = layoutState.dockAreaLayout;
1707 return (size == Maximum) ? dl.gapRect(dockPosition) : dl.docks[dockPosition].rect;
1711
1712
1713
1714void QMainWindowLayout::addDockWidget(Qt::DockWidgetArea area,
1715 QDockWidget *dockwidget,
1716 Qt::Orientation orientation)
1718 addChildWidget(dockwidget);
1722 if (!movingSeparator.isEmpty())
1723 endSeparatorMove(movingSeparatorPos);
1725 layoutState.dockAreaLayout.addDockWidget(toDockPos(area), dockwidget, orientation);
1729bool QMainWindowLayout::restoreDockWidget(QDockWidget *dockwidget)
1731 addChildWidget(dockwidget);
1732 if (!layoutState.dockAreaLayout.restoreDockWidget(dockwidget))
1734 emit dockwidget->dockLocationChanged(dockWidgetArea(dockwidget));
1739#if QT_CONFIG(dockwidget) && QT_CONFIG(tabwidget)
1740static QTabBar::Shape tabwidgetPositionToTabBarShape(QDockWidget *w)
1742 switch (
static_cast<QDockWidgetPrivate *>(qt_widget_private(w))->tabPosition) {
1743 case QTabWidget::North:
1744 return QTabBar::RoundedNorth;
1745 case QTabWidget::South:
1746 return QTabBar::RoundedSouth;
1747 case QTabWidget::West:
1748 return QTabBar::RoundedWest;
1749 case QTabWidget::East:
1750 return QTabBar::RoundedEast;
1752 Q_UNREACHABLE_RETURN(QTabBar::RoundedSouth);
1756#if QT_CONFIG(tabbar)
1757void QMainWindowLayout::tabifyDockWidget(QDockWidget *first, QDockWidget *second)
1763 if (layoutState.dockAreaLayout.indexOf(first).isEmpty()) {
1764 qCritical() <<
"Coding error: QDockWidget" << first
1765 <<
"tabbed before QMainWindow::addDockWidget()."
1766 <<
"Ignoring" <<
__FUNCTION__;
1771 if (layoutState.dockAreaLayout.indexOf(second).isEmpty()) {
1772 layoutState.mainWindow->addDockWidget(first->dockLocation(), second);
1773 qCDebug(lcQpaDockWidgets) <<
"QDockWidget" << second <<
"has been added to"
1774 << parent() <<
"at" << first->dockLocation();
1778 if (isDockWidgetTabbed(second)) {
1779 qCDebug(lcQpaDockWidgets) <<
"QDockWidget" << second
1780 <<
"is already tabbed. Ignoring" <<
__FUNCTION__;
1784 const auto oldLocationFirst = dockWidgetArea(first);
1785 if (first->isFloating()) {
1786 tabifyWhileFloating(first, second);
1788 applyRestoredState();
1789 addChildWidget(second);
1790 layoutState.dockAreaLayout.tabifyDockWidget(first, second);
1792 const auto newLocationFirst = dockWidgetArea(first);
1793 if (newLocationFirst != oldLocationFirst)
1794 emit second->dockLocationChanged(newLocationFirst);
1798void QMainWindowLayout::tabifyWhileFloating(QDockWidget *first, QDockWidget *second)
1800 Q_ASSERT(first->isFloating());
1801 Q_ASSERT(!isDockWidgetTabbed(first));
1802 Q_ASSERT(!isDockWidgetTabbed(second));
1805 second->setFloating(
true);
1807 QDockWidgetGroupWindow *floatingTabs = createTabbedDockWindow();
1808 floatingTabs->setGeometry(first->geometry());
1809 QDockAreaLayoutInfo *info = floatingTabs->layoutInfo();
1810 const QTabBar::Shape shape = tabwidgetPositionToTabBarShape(first);
1812 const QInternal::DockPosition dockPosition = toDockPos(dockWidgetArea(first));
1813 Q_ASSERT(dockPosition != QInternal::DockPosition::DockCount);
1814 *info = QDockAreaLayoutInfo(&layoutState.dockAreaLayout.sep, dockPosition,
1815 Qt::Horizontal, shape,
1816 static_cast<QMainWindow *>(parentWidget()));
1817 info->tabBar = getTabBar();
1818 info->tabbed =
true;
1821 second->d_func()->plug(first->geometry());
1822 QDockAreaLayoutInfo &parentInfo = layoutState.dockAreaLayout.docks[dockPosition];
1823 parentInfo.add(floatingTabs);
1824 first->setParent(floatingTabs);
1825 second->setParent(floatingTabs);
1826 floatingTabs->show();
1827 floatingTabs->raise();
1830bool QMainWindowLayout::documentMode()
const
1832 return _documentMode;
1835void QMainWindowLayout::setDocumentMode(
bool enabled)
1837 if (_documentMode == enabled)
1840 _documentMode = enabled;
1843 for (QTabBar *bar : std::as_const(usedTabBars))
1844 bar->setDocumentMode(_documentMode);
1847void QMainWindowLayout::setVerticalTabsEnabled(
bool enabled)
1849 if (verticalTabsEnabled == enabled)
1852 verticalTabsEnabled = enabled;
1854 updateTabBarShapes();
1857#if QT_CONFIG(tabwidget)
1858QTabWidget::TabShape QMainWindowLayout::tabShape()
const
1863void QMainWindowLayout::setTabShape(QTabWidget::TabShape tabShape)
1865 if (_tabShape == tabShape)
1868 _tabShape = tabShape;
1870 updateTabBarShapes();
1873QTabWidget::TabPosition QMainWindowLayout::tabPosition(Qt::DockWidgetArea area)
const
1875 const QInternal::DockPosition dockPos = toDockPos(area);
1876 if (dockPos < QInternal::DockCount)
1877 return tabPositions[dockPos];
1878 qWarning(
"QMainWindowLayout::tabPosition called with out-of-bounds value '%d'",
int(area));
1879 return QTabWidget::North;
1882void QMainWindowLayout::setTabPosition(Qt::DockWidgetAreas areas, QTabWidget::TabPosition tabPosition)
1884 static constexpr Qt::DockWidgetArea dockWidgetAreas[] = {
1885 Qt::TopDockWidgetArea,
1886 Qt::LeftDockWidgetArea,
1887 Qt::BottomDockWidgetArea,
1888 Qt::RightDockWidgetArea
1890 static constexpr QInternal::DockPosition dockPositions[] = {
1892 QInternal::LeftDock,
1893 QInternal::BottomDock,
1894 QInternal::RightDock
1897 for (
int i = 0; i < QInternal::DockCount; ++i)
1898 if (areas & dockWidgetAreas[i])
1899 tabPositions[dockPositions[i]] = tabPosition;
1901 updateTabBarShapes();
1904QTabBar::Shape _q_tb_tabBarShapeFrom(QTabWidget::TabShape shape, QTabWidget::TabPosition position);
1907void QMainWindowLayout::showTabBars()
1909 const auto usedTabBarsCopy = usedTabBars;
1910 for (QTabBar *tab_bar : usedTabBarsCopy) {
1911 if (usedTabBars.contains(tab_bar))
1916void QMainWindowLayout::updateTabBarShapes()
1918#if QT_CONFIG(tabwidget)
1919 static constexpr QTabWidget::TabPosition vertical[] = {
1926 static constexpr QTabBar::Shape vertical[] = {
1927 QTabBar::RoundedWest,
1928 QTabBar::RoundedEast,
1929 QTabBar::RoundedNorth,
1930 QTabBar::RoundedSouth
1934 QDockAreaLayout &layout = layoutState.dockAreaLayout;
1936 for (
int i = 0; i < QInternal::DockCount; ++i) {
1937#if QT_CONFIG(tabwidget)
1938 QTabWidget::TabPosition pos = verticalTabsEnabled ? vertical[i] : tabPositions[i];
1939 QTabBar::Shape shape = _q_tb_tabBarShapeFrom(_tabShape, pos);
1941 QTabBar::Shape shape = verticalTabsEnabled ? vertical[i] : QTabBar::RoundedSouth;
1943 layout.docks[i].setTabBarShape(shape);
1948void QMainWindowLayout::splitDockWidget(QDockWidget *after,
1949 QDockWidget *dockwidget,
1950 Qt::Orientation orientation)
1952 applyRestoredState();
1953 addChildWidget(dockwidget);
1954 layoutState.dockAreaLayout.splitDockWidget(after, dockwidget, orientation);
1955 emit dockwidget->dockLocationChanged(dockWidgetArea(after));
1959Qt::DockWidgetArea QMainWindowLayout::dockWidgetArea(
const QWidget *widget)
const
1961 const QList<
int> pathToWidget = layoutState.dockAreaLayout.indexOf(widget);
1962 if (pathToWidget.isEmpty())
1963 return Qt::NoDockWidgetArea;
1964 return toDockWidgetArea(pathToWidget.first());
1967void QMainWindowLayout::keepSize(QDockWidget *w)
1969 layoutState.dockAreaLayout.keepSize(w);
1972#if QT_CONFIG(tabbar)
1975class QMainWindowTabBar :
public QTabBar
1978 QPointer<QMainWindow> mainWindow;
1979 QPointer<QDockWidget> draggingDock;
1981 QMainWindowTabBar(QMainWindow *parent);
1982 ~QMainWindowTabBar();
1983 QDockWidget *dockAt(
int index)
const;
1984 QList<QDockWidget *> dockWidgets()
const;
1985 bool contains(
const QDockWidget *dockWidget)
const;
1987 bool event(QEvent *e) override;
1988 void mouseReleaseEvent(QMouseEvent*) override;
1989 void mouseMoveEvent(QMouseEvent*) override;
1993QDebug operator<<(QDebug debug,
const QMainWindowTabBar *bar)
1996 return debug <<
"QMainWindowTabBar(0x0)";
1997 QDebugStateSaver saver(debug);
1998 debug.nospace().noquote() <<
"QMainWindowTabBar(" <<
static_cast<
const void *>(bar) <<
", ";
1999 debug.nospace().noquote() <<
"ParentWidget=(" << bar->parentWidget() <<
"), ";
2000 const auto dockWidgets = bar->dockWidgets();
2001 if (dockWidgets.isEmpty())
2002 debug.nospace().noquote() <<
"No QDockWidgets";
2004 debug.nospace().noquote() <<
"DockWidgets(" << dockWidgets <<
")";
2005 debug.nospace().noquote() <<
")";
2009QMainWindowTabBar *QMainWindowLayout::findTabBar(
const QDockWidget *dockWidget)
const
2011 for (
auto *bar : usedTabBars) {
2012 Q_ASSERT(qobject_cast<QMainWindowTabBar *>(bar));
2013 auto *tabBar =
static_cast<QMainWindowTabBar *>(bar);
2014 if (tabBar->contains(dockWidget))
2020QMainWindowTabBar::QMainWindowTabBar(QMainWindow *parent)
2021 : QTabBar(parent), mainWindow(parent)
2023 setExpanding(
false);
2026QList<QDockWidget *> QMainWindowTabBar::dockWidgets()
const
2028 QList<QDockWidget *> docks;
2029 for (
int i = 0; i < count(); ++i) {
2030 if (QDockWidget *dock = dockAt(i))
2036bool QMainWindowTabBar::contains(
const QDockWidget *dockWidget)
const
2038 for (
int i = 0; i < count(); ++i) {
2039 if (dockAt(i) == dockWidget)
2050QDockWidget *QMainWindowTabBar::dockAt(
int index)
const
2052 QMainWindowTabBar *that =
const_cast<QMainWindowTabBar *>(
this);
2053 QMainWindowLayout* mlayout = qt_mainwindow_layout(mainWindow);
2054 QDockAreaLayoutInfo *info = mlayout ? mlayout->dockInfo(that) :
nullptr;
2058 const int itemIndex = info->tabIndexToListIndex(index);
2059 if (itemIndex >= 0) {
2060 Q_ASSERT(itemIndex < info->item_list.count());
2061 const QDockAreaLayoutItem &item = info->item_list.at(itemIndex);
2062 return item.widgetItem ? qobject_cast<QDockWidget *>(item.widgetItem->widget()) :
nullptr;
2069
2070
2071
2072
2073
2074
2075
2076
2077static void moveToUnplugPosition(QPoint mouse, QDockWidget *dockWidget)
2079 Q_ASSERT(dockWidget);
2081 if (
auto *tbWidget = dockWidget->titleBarWidget()) {
2082 dockWidget->move(mouse - tbWidget->rect().center());
2086 const bool vertical = dockWidget->features().testFlag(QDockWidget::DockWidgetVerticalTitleBar);
2087 const int deltaX = vertical ? QApplication::startDragDistance() : dockWidget->width() / 2;
2088 const int deltaY = vertical ? dockWidget->height() / 2 : QApplication::startDragDistance();
2089 dockWidget->move(mouse - QPoint(deltaX, deltaY));
2092void QMainWindowTabBar::mouseMoveEvent(QMouseEvent *e)
2099 QTabBarPrivate *d =
static_cast<QTabBarPrivate*>(d_ptr.data());
2100 if (!draggingDock && (mainWindow->dockOptions() & QMainWindow::GroupedDragging)) {
2101 int offset = QApplication::startDragDistance() + 1;
2103 QRect r = rect().adjusted(-offset, -offset, offset, offset);
2104 if (d->dragInProgress && !r.contains(e->position().toPoint()) && d->validIndex(d->pressedIndex)) {
2105 draggingDock = dockAt(d->pressedIndex);
2109 d->moveTabFinished(d->pressedIndex);
2110 d->pressedIndex = -1;
2112 d->movingTab->setVisible(
false);
2113 d->dragStartPosition = QPoint();
2116 QDockWidgetPrivate *dockPriv =
static_cast<QDockWidgetPrivate *>(QObjectPrivate::get(draggingDock));
2117 QDockWidgetLayout *dwlayout =
static_cast<QDockWidgetLayout *>(draggingDock->layout());
2118 dockPriv->initDrag(dwlayout->titleArea().center(),
true);
2119 dockPriv->startDrag(QDockWidgetPrivate::DragScope::Widget);
2120 if (dockPriv->state)
2121 dockPriv->state->ctrlDrag = e->modifiers() & Qt::ControlModifier;
2127 QDockWidgetPrivate *dockPriv =
static_cast<QDockWidgetPrivate *>(QObjectPrivate::get(draggingDock));
2128 if (dockPriv->state && dockPriv->state->dragging) {
2130 moveToUnplugPosition(e->globalPosition().toPoint(), draggingDock);
2133 QTabBar::mouseMoveEvent(e);
2136QMainWindowTabBar::~QMainWindowTabBar()
2140 if (!qobject_cast<QMainWindow *>(mainWindow) || mainWindow == parentWidget())
2145 auto *mwLayout = qt_mainwindow_layout(mainWindow);
2148 mwLayout->usedTabBars.remove(
this);
2151void QMainWindowTabBar::mouseReleaseEvent(QMouseEvent *e)
2153 if (draggingDock && e->button() == Qt::LeftButton) {
2154 QDockWidgetPrivate *dockPriv =
static_cast<QDockWidgetPrivate *>(QObjectPrivate::get(draggingDock));
2155 if (dockPriv->state && dockPriv->state->dragging)
2156 dockPriv->endDrag(QDockWidgetPrivate::EndDragMode::LocationChange);
2158 draggingDock =
nullptr;
2160 QTabBar::mouseReleaseEvent(e);
2163bool QMainWindowTabBar::event(QEvent *e)
2167 if (e->type() != QEvent::ToolTip)
2168 return QTabBar::event(e);
2169 QSize size =
this->size();
2170 QSize hint = sizeHint();
2171 if (shape() == QTabBar::RoundedWest || shape() == QTabBar::RoundedEast) {
2172 size = size.transposed();
2173 hint = hint.transposed();
2175 if (size.width() < hint.width())
2176 return QTabBar::event(e);
2181QList<QDockWidget *> QMainWindowLayout::tabifiedDockWidgets(
const QDockWidget *dockWidget)
const
2183 const auto *bar = findTabBar(dockWidget);
2187 QList<QDockWidget *> buddies = bar->dockWidgets();
2190 buddies.removeOne(dockWidget);
2194bool QMainWindowLayout::isDockWidgetTabbed(
const QDockWidget *dockWidget)
const
2199 const auto *bar = findTabBar(dockWidget);
2200 return bar && bar->count() > 1;
2203void QMainWindowLayout::unuseTabBar(QTabBar *bar)
2205 Q_ASSERT(qobject_cast<QMainWindowTabBar *>(bar));
2209QTabBar *QMainWindowLayout::getTabBar()
2211 if (!usedTabBars.isEmpty() && !isInRestoreState) {
2213
2214
2215
2216
2220 QTabBar *bar =
new QMainWindowTabBar(
static_cast<QMainWindow *>(parentWidget()));
2221 bar->setDrawBase(
true);
2222 bar->setElideMode(Qt::ElideRight);
2223 bar->setDocumentMode(_documentMode);
2224 bar->setMovable(
true);
2225 connect(bar, SIGNAL(currentChanged(
int)),
this, SLOT(tabChanged()));
2226 connect(bar, &QTabBar::tabMoved,
this, &QMainWindowLayout::tabMoved);
2228 usedTabBars.insert(bar);
2232QWidget *QMainWindowLayout::getSeparatorWidget()
2234 auto *separator =
new QWidget(parentWidget());
2235 separator->setAttribute(Qt::WA_MouseNoMask,
true);
2236 separator->setAutoFillBackground(
false);
2237 separator->setObjectName(
"qt_qmainwindow_extended_splitter"_L1);
2238 usedSeparatorWidgets.insert(separator);
2243
2244
2245
2246QDockAreaLayoutInfo *QMainWindowLayout::dockInfo(QWidget *widget)
2248 QDockAreaLayoutInfo *info = layoutState.dockAreaLayout.info(widget);
2252 parent()->findChildren<QDockWidgetGroupWindow*>(Qt::FindDirectChildrenOnly);
2253 for (QDockWidgetGroupWindow *dwgw : groups) {
2254 info = dwgw->layoutInfo()->info(widget);
2261void QMainWindowLayout::tabChanged()
2263 QTabBar *tb = qobject_cast<QTabBar*>(sender());
2266 QDockAreaLayoutInfo *info = dockInfo(tb);
2267 if (info ==
nullptr)
2270 QDockWidget *activated = info->apply(
false);
2273 emit
static_cast<QMainWindow *>(parentWidget())->tabifiedDockWidgetActivated(activated);
2275 if (
auto dwgw = qobject_cast<QDockWidgetGroupWindow*>(tb->parentWidget()))
2276 dwgw->adjustFlags();
2278 if (QWidget *w = centralWidget())
2282void QMainWindowLayout::tabMoved(
int from,
int to)
2284 QTabBar *tb = qobject_cast<QTabBar*>(sender());
2286 QDockAreaLayoutInfo *info = dockInfo(tb);
2289 info->moveTab(from, to);
2292void QMainWindowLayout::raise(QDockWidget *widget)
2294 QDockAreaLayoutInfo *info = dockInfo(widget);
2295 if (info ==
nullptr)
2299 info->setCurrentTab(widget);
2307
2308
2310int QMainWindowLayout::count()
const
2313 while (itemAt(result))
2318QLayoutItem *QMainWindowLayout::itemAt(
int index)
const
2322 if (QLayoutItem *ret = layoutState.itemAt(index, &x))
2325 if (statusbar && x++ == index)
2331QLayoutItem *QMainWindowLayout::takeAt(
int index)
2335 if (QLayoutItem *ret = layoutState.takeAt(index, &x)) {
2337 if (QWidget *w = ret->widget()) {
2338 widgetAnimator.abort(w);
2339 if (w == pluggingWidget)
2340 pluggingWidget =
nullptr;
2343 if (savedState.isValid() ) {
2345 savedState.remove(ret);
2347 layoutState.remove(ret);
2350#if QT_CONFIG(toolbar)
2351 if (!currentGapPos.isEmpty() && currentGapPos.constFirst() == 0) {
2352 currentGapPos = layoutState.toolBarAreaLayout.currentGapIndex();
2353 if (!currentGapPos.isEmpty()) {
2354 currentGapPos.prepend(0);
2355 currentGapRect = layoutState.itemRect(currentGapPos);
2363 if (statusbar && x++ == index) {
2364 QLayoutItem *ret = statusbar;
2365 statusbar =
nullptr;
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389void QMainWindowLayout::applyRestoredState()
2391 if (restoredState) {
2392 layoutState = *restoredState;
2393 restoredState.reset();
2394 discardRestoredStateTimer.stop();
2398void QMainWindowLayout::setGeometry(
const QRect &_r)
2402 if (savedState.isValid() || (restoredState && isInApplyState))
2407 QLayout::setGeometry(r);
2410 QRect sbr(QPoint(r.left(), 0),
2411 QSize(r.width(), statusbar->heightForWidth(r.width()))
2412 .expandedTo(statusbar->minimumSize()));
2413 sbr.moveBottom(r.bottom());
2414 QRect vr = QStyle::visualRect(parentWidget()->layoutDirection(), _r, sbr);
2415 statusbar->setGeometry(vr);
2416 r.setBottom(sbr.top() - 1);
2419 if (restoredState) {
2421
2422
2423
2424
2425
2426
2427 layoutState = *restoredState;
2428 if (restoredState->fits()) {
2429 restoredState.reset();
2430 discardRestoredStateTimer.stop();
2433
2434
2435
2436
2437
2438
2439
2440
2441 discardRestoredStateTimer.start(150,
this);
2445 layoutState.rect = r;
2447 layoutState.fitLayout();
2448 applyState(layoutState,
false);
2451void QMainWindowLayout::timerEvent(QTimerEvent *e)
2453 if (e->timerId() == discardRestoredStateTimer.timerId()) {
2454 discardRestoredStateTimer.stop();
2455 restoredState.reset();
2457 QLayout::timerEvent(e);
2460void QMainWindowLayout::addItem(QLayoutItem *)
2461{ qWarning(
"QMainWindowLayout::addItem: Please use the public QMainWindow API instead"); }
2463QSize QMainWindowLayout::sizeHint()
const
2465 if (!szHint.isValid()) {
2466 szHint = layoutState.sizeHint();
2467 const QSize sbHint = statusbar ? statusbar->sizeHint() : QSize(0, 0);
2468 szHint = QSize(qMax(sbHint.width(), szHint.width()),
2469 sbHint.height() + szHint.height());
2474QSize QMainWindowLayout::minimumSize()
const
2476 if (!minSize.isValid()) {
2477 minSize = layoutState.minimumSize();
2478 const QSize sbMin = statusbar ? statusbar->minimumSize() : QSize(0, 0);
2479 minSize = QSize(qMax(sbMin.width(), minSize.width()),
2480 sbMin.height() + minSize.height());
2485void QMainWindowLayout::invalidate()
2487 QLayout::invalidate();
2488 minSize = szHint = QSize();
2491#if QT_CONFIG(dockwidget)
2492void QMainWindowLayout::setCurrentHoveredFloat(QDockWidgetGroupWindow *w)
2494 if (currentHoveredFloat != w) {
2495 if (currentHoveredFloat) {
2496 disconnect(currentHoveredFloat.data(), &QObject::destroyed,
2497 this, &QMainWindowLayout::updateGapIndicator);
2498 disconnect(currentHoveredFloat.data(), &QDockWidgetGroupWindow::resized,
2499 this, &QMainWindowLayout::updateGapIndicator);
2500 if (currentHoveredFloat)
2501 currentHoveredFloat->restore();
2503 restore(QInternal::KeepSavedState);
2506 currentHoveredFloat = w;
2509 connect(w, &QObject::destroyed,
2510 this, &QMainWindowLayout::updateGapIndicator, Qt::UniqueConnection);
2511 connect(w, &QDockWidgetGroupWindow::resized,
2512 this, &QMainWindowLayout::updateGapIndicator, Qt::UniqueConnection);
2515 updateGapIndicator();
2521
2522
2526#if QT_CONFIG(toolbar)
2527 QToolBar *toolBar = qobject_cast<QToolBar*>(item->widget());
2528 if (toolBar ==
nullptr)
2531 QRect oldGeo = toolBar->geometry();
2533 QInternal::DockPosition pos
2534 =
static_cast<QInternal::DockPosition>(dockPos);
2535 Qt::Orientation o = pos == QInternal::TopDock || pos == QInternal::BottomDock
2536 ? Qt::Horizontal : Qt::Vertical;
2537 if (o != toolBar->orientation())
2538 toolBar->setOrientation(o);
2540 QSize hint = toolBar->sizeHint().boundedTo(toolBar->maximumSize())
2541 .expandedTo(toolBar->minimumSize());
2543 if (toolBar->size() != hint) {
2544 QRect newGeo(oldGeo.topLeft(), hint);
2545 if (toolBar->layoutDirection() == Qt::RightToLeft)
2546 newGeo.moveRight(oldGeo.right());
2547 toolBar->setGeometry(newGeo);
2556void QMainWindowLayout::revert(QLayoutItem *widgetItem)
2558 if (!savedState.isValid())
2561 QWidget *widget = widgetItem->widget();
2562 layoutState = savedState;
2563 currentGapPos = layoutState.indexOf(widget);
2564 if (currentGapPos.isEmpty())
2566 fixToolBarOrientation(widgetItem, currentGapPos.at(1));
2567 layoutState.unplug(currentGapPos);
2568 layoutState.fitLayout();
2569 currentGapRect = layoutState.itemRect(currentGapPos);
2574bool QMainWindowLayout::plug(QLayoutItem *widgetItem)
2576#if QT_CONFIG(dockwidget) && QT_CONFIG(tabwidget) && QT_CONFIG(tabbar)
2577 if (currentHoveredFloat) {
2578 QWidget *widget = widgetItem->widget();
2579 QList<
int> previousPath = layoutState.indexOf(widget);
2580 if (!previousPath.isEmpty())
2581 layoutState.remove(previousPath);
2582 previousPath = currentHoveredFloat->layoutInfo()->indexOf(widget);
2585 parent()->findChildren<QDockWidgetGroupWindow*>(Qt::FindDirectChildrenOnly);
2586 for (QDockWidgetGroupWindow *dwgw : groups) {
2587 if (dwgw == currentHoveredFloat)
2589 QList<
int> path = dwgw->layoutInfo()->indexOf(widget);
2590 if (!path.isEmpty())
2591 dwgw->layoutInfo()->remove(path);
2593 currentGapRect = QRect();
2594 currentHoveredFloat->apply();
2595 if (!previousPath.isEmpty())
2596 currentHoveredFloat->layoutInfo()->remove(previousPath);
2597 QRect globalRect = currentHoveredFloat->currentGapRect;
2598 globalRect.moveTopLeft(currentHoveredFloat->mapToGlobal(globalRect.topLeft()));
2599 pluggingWidget = widget;
2600 widgetAnimator.animate(widget, globalRect, dockOptions & QMainWindow::AnimatedDocks);
2605 if (!parentWidget()->isVisible() || parentWidget()->isMinimized() || currentGapPos.isEmpty())
2608 fixToolBarOrientation(widgetItem, currentGapPos.at(1));
2610 QWidget *widget = widgetItem->widget();
2612#if QT_CONFIG(dockwidget)
2615 parent()->findChildren<QDockWidgetGroupWindow*>(Qt::FindDirectChildrenOnly);
2616 for (QDockWidgetGroupWindow *dwgw : groups) {
2617 QList<
int> path = dwgw->layoutInfo()->indexOf(widget);
2618 if (!path.isEmpty())
2619 dwgw->layoutInfo()->remove(path);
2623 QList<
int> previousPath = layoutState.indexOf(widget);
2625 const QLayoutItem *it = layoutState.plug(currentGapPos);
2628 Q_ASSERT(it == widgetItem);
2629 if (!previousPath.isEmpty())
2630 layoutState.remove(previousPath);
2632 pluggingWidget = widget;
2633 QRect globalRect = currentGapRect;
2634 globalRect.moveTopLeft(parentWidget()->mapToGlobal(globalRect.topLeft()));
2635#if QT_CONFIG(dockwidget)
2636 if (qobject_cast<QDockWidget*>(widget) !=
nullptr) {
2637 QDockWidgetLayout *layout = qobject_cast<QDockWidgetLayout*>(widget->layout());
2638 if (layout->nativeWindowDeco()) {
2639 globalRect.adjust(0, layout->titleHeight(), 0, 0);
2641 int fw = widget->style()->pixelMetric(QStyle::PM_DockWidgetFrameWidth,
nullptr, widget);
2642 globalRect.adjust(-fw, -fw, fw, fw);
2646 widgetAnimator.animate(widget, globalRect, dockOptions & QMainWindow::AnimatedDocks);
2651void QMainWindowLayout::animationFinished(QWidget *widget)
2655#if QT_CONFIG(toolbar)
2656 if (QToolBar *tb = qobject_cast<QToolBar*>(widget)) {
2657 QToolBarLayout *tbl = qobject_cast<QToolBarLayout*>(tb->layout());
2658 if (tbl->animating) {
2659 tbl->animating =
false;
2661 tbl->layoutActions(tb->size());
2667 if (widget == pluggingWidget) {
2669#if QT_CONFIG(dockwidget)
2670#if QT_CONFIG(tabbar)
2671 if (QDockWidgetGroupWindow *dwgw = qobject_cast<QDockWidgetGroupWindow *>(widget)) {
2675 QDockAreaLayoutInfo *srcInfo = dwgw->layoutInfo();
2676 const QDockAreaLayoutInfo *srcTabInfo = dwgw->tabLayoutInfo();
2677 QDockAreaLayoutInfo *dstParentInfo;
2680 if (currentHoveredFloat) {
2681 dstPath = currentHoveredFloat->layoutInfo()->indexOf(widget);
2682 Q_ASSERT(dstPath.size() >= 1);
2683 dstParentInfo = currentHoveredFloat->layoutInfo()->info(dstPath);
2685 dstPath = layoutState.dockAreaLayout.indexOf(widget);
2686 Q_ASSERT(dstPath.size() >= 2);
2687 dstParentInfo = layoutState.dockAreaLayout.info(dstPath);
2689 Q_ASSERT(dstParentInfo);
2690 int idx = dstPath.constLast();
2691 Q_ASSERT(dstParentInfo->item_list[idx].widgetItem->widget() == dwgw);
2692 if (dstParentInfo->tabbed && srcTabInfo) {
2694 delete dstParentInfo->item_list[idx].widgetItem;
2695 dstParentInfo->item_list.removeAt(idx);
2696 std::copy(srcTabInfo->item_list.cbegin(), srcTabInfo->item_list.cend(),
2697 std::inserter(dstParentInfo->item_list,
2698 dstParentInfo->item_list.begin() + idx));
2699 quintptr currentId = srcTabInfo->currentTabId();
2700 *srcInfo = QDockAreaLayoutInfo();
2701 dstParentInfo->reparentWidgets(currentHoveredFloat ? currentHoveredFloat.data()
2703 dstParentInfo->updateTabBar();
2704 dstParentInfo->setCurrentTabId(currentId);
2706 QDockAreaLayoutItem &item = dstParentInfo->item_list[idx];
2707 Q_ASSERT(item.widgetItem->widget() == dwgw);
2708 delete item.widgetItem;
2709 item.widgetItem =
nullptr;
2710 item.subinfo =
new QDockAreaLayoutInfo(std::move(*srcInfo));
2711 *srcInfo = QDockAreaLayoutInfo();
2712 item.subinfo->reparentWidgets(currentHoveredFloat ? currentHoveredFloat.data()
2714 item.subinfo->setTabBarShape(dstParentInfo->tabBarShape);
2716 dwgw->destroyOrHideIfEmpty();
2720 if (QDockWidget *dw = qobject_cast<QDockWidget*>(widget)) {
2721 dw->setParent(currentHoveredFloat ? currentHoveredFloat.data() : parentWidget());
2723 dw->d_func()->plug(currentGapRect);
2726#if QT_CONFIG(toolbar)
2727 if (QToolBar *tb = qobject_cast<QToolBar*>(widget))
2728 tb->d_func()->plug(currentGapRect);
2732 currentGapPos.clear();
2733 pluggingWidget =
nullptr;
2734#if QT_CONFIG(dockwidget)
2735 setCurrentHoveredFloat(
nullptr);
2739 layoutState.apply(
false);
2741#if QT_CONFIG(dockwidget)
2742#if QT_CONFIG(tabbar)
2743 if (qobject_cast<QDockWidget*>(widget) !=
nullptr) {
2746 if (QDockAreaLayoutInfo *info = dockInfo(widget))
2747 info->setCurrentTab(widget);
2753 if (!widgetAnimator.animating()) {
2755#if QT_CONFIG(dockwidget)
2756 parentWidget()->update(layoutState.dockAreaLayout.separatorRegion());
2757#if QT_CONFIG(tabbar)
2763 updateGapIndicator();
2766void QMainWindowLayout::restore(QInternal::SaveStateRule rule)
2768 if (!savedState.isValid())
2771 layoutState = savedState;
2772 applyState(layoutState);
2773 if (rule == QInternal::ClearSavedState)
2775 currentGapPos.clear();
2776 pluggingWidget =
nullptr;
2777 updateGapIndicator();
2780QMainWindowLayout::QMainWindowLayout(QMainWindow *mainwindow, QLayout *parentLayout)
2781 : QLayout(parentLayout ?
static_cast<QWidget *>(
nullptr) : mainwindow)
2782 , layoutState(mainwindow)
2783 , savedState(mainwindow)
2784 , dockOptions(QMainWindow::AnimatedDocks | QMainWindow::AllowTabbedDocks)
2785 , statusbar(
nullptr)
2786#if QT_CONFIG(dockwidget)
2787#if QT_CONFIG(tabbar)
2788 , _documentMode(
false)
2789 , verticalTabsEnabled(
false)
2790#if QT_CONFIG(tabwidget)
2791 , _tabShape(QTabWidget::Rounded)
2795 , widgetAnimator(
this)
2796 , pluggingWidget(
nullptr)
2799 setParent(parentLayout);
2801#if QT_CONFIG(dockwidget)
2802#if QT_CONFIG(tabbar)
2803 sep = mainwindow->style()->pixelMetric(QStyle::PM_DockWidgetSeparatorExtent,
nullptr, mainwindow);
2806#if QT_CONFIG(tabwidget)
2807 for (
int i = 0; i < QInternal::DockCount; ++i)
2808 tabPositions[i] = QTabWidget::South;
2811 pluggingWidget =
nullptr;
2813 setObjectName(mainwindow->objectName() +
"_layout"_L1);
2816QMainWindowLayout::~QMainWindowLayout()
2818 layoutState.deleteAllLayoutItems();
2819 layoutState.deleteCentralWidgetItem();
2824void QMainWindowLayout::setDockOptions(QMainWindow::DockOptions opts)
2826 if (opts == dockOptions)
2831#if QT_CONFIG(dockwidget) && QT_CONFIG(tabbar)
2832 setVerticalTabsEnabled(opts & QMainWindow::VerticalTabs);
2838#if QT_CONFIG(statusbar)
2839QStatusBar *QMainWindowLayout::statusBar()
const
2840{
return statusbar ? qobject_cast<QStatusBar *>(statusbar->widget()) : 0; }
2842void QMainWindowLayout::setStatusBar(QStatusBar *sb)
2847 statusbar = sb ?
new QWidgetItemV2(sb) :
nullptr;
2852QWidget *QMainWindowLayout::centralWidget()
const
2854 return layoutState.centralWidget();
2857void QMainWindowLayout::setCentralWidget(QWidget *widget)
2859 if (widget !=
nullptr)
2860 addChildWidget(widget);
2861 layoutState.setCentralWidget(widget);
2862 if (savedState.isValid()) {
2863#if QT_CONFIG(dockwidget)
2864 savedState.dockAreaLayout.centralWidgetItem = layoutState.dockAreaLayout.centralWidgetItem;
2865 savedState.dockAreaLayout.fallbackToSizeHints =
true;
2867 savedState.centralWidgetItem = layoutState.centralWidgetItem;
2873#if QT_CONFIG(dockwidget) && QT_CONFIG(tabwidget)
2875
2876
2877
2878
2879
2880
2881static bool unplugGroup(QMainWindowLayout *layout, QLayoutItem **item,
2882 QDockAreaLayoutItem &parentItem)
2884 if (!parentItem.subinfo || !parentItem.subinfo->tabbed)
2888 QDockWidgetGroupWindow *floatingTabs = layout->createTabbedDockWindow();
2889 QDockAreaLayoutInfo *info = floatingTabs->layoutInfo();
2890 *info = std::move(*parentItem.subinfo);
2891 delete parentItem.subinfo;
2892 parentItem.subinfo =
nullptr;
2893 floatingTabs->setGeometry(info->rect.translated(layout->parentWidget()->pos()));
2894 floatingTabs->show();
2895 floatingTabs->raise();
2896 *item =
new QDockWidgetGroupWindowItem(floatingTabs);
2897 parentItem.widgetItem = *item;
2903
2904
2905
2906
2907
2908
2909
2910
2911
2912QLayoutItem *QMainWindowLayout::unplug(QWidget *widget, QDockWidgetPrivate::DragScope scope)
2914#if QT_CONFIG(dockwidget) && QT_CONFIG(tabwidget)
2915 auto *groupWindow = qobject_cast<
const QDockWidgetGroupWindow *>(widget->parentWidget());
2916 if (!widget->isWindow() && groupWindow) {
2917 if (scope == QDockWidgetPrivate::DragScope::Group && groupWindow->tabLayoutInfo()) {
2920 if (QDockAreaLayoutInfo *info = dockInfo(widget->parentWidget())) {
2921 QList<
int> groupWindowPath = info->indexOf(widget->parentWidget());
2922 return groupWindowPath.isEmpty() ?
nullptr : info->item(groupWindowPath).widgetItem;
2924 qCDebug(lcQpaDockWidgets) <<
"Drag only:" << widget <<
"Group:" << (scope == QDockWidgetPrivate::DragScope::Group);
2927 QList<
int> path = groupWindow->layoutInfo()->indexOf(widget);
2928 QDockAreaLayoutItem parentItem = groupWindow->layoutInfo()->item(path);
2929 QLayoutItem *item = parentItem.widgetItem;
2930 if (scope == QDockWidgetPrivate::DragScope::Group && path.size() > 1
2931 && unplugGroup(
this, &item, parentItem)) {
2932 qCDebug(lcQpaDockWidgets) <<
"Unplugging:" << widget <<
"from" << item;
2936 QDockWidget *dockWidget = qobject_cast<QDockWidget *>(widget);
2937 Q_ASSERT(dockWidget);
2938 dockWidget->d_func()->unplug(widget->geometry());
2940 qCDebug(lcQpaDockWidgets) <<
"Unplugged from floating dock:" << widget <<
"from" << parentItem.widgetItem;
2945 QList<
int> path = layoutState.indexOf(widget);
2949 QLayoutItem *item = layoutState.item(path);
2950 if (widget->isWindow())
2953 QRect r = layoutState.itemRect(path);
2954 savedState = layoutState;
2956#if QT_CONFIG(dockwidget)
2957 if (QDockWidget *dw = qobject_cast<QDockWidget*>(widget)) {
2958 Q_ASSERT(path.constFirst() == 1);
2959#if QT_CONFIG(tabwidget)
2960 if (scope == QDockWidgetPrivate::DragScope::Group && (dockOptions & QMainWindow::GroupedDragging) && path.size() > 3
2961 && unplugGroup(
this, &item,
2962 layoutState.dockAreaLayout.item(path.mid(1, path.size() - 2)))) {
2964 savedState = layoutState;
2970 switch (dockWidgetArea(dw)) {
2971 case Qt::LeftDockWidgetArea:
2972 case Qt::RightDockWidgetArea:
2973 r.setHeight(r.height() - sep);
2975 case Qt::TopDockWidgetArea:
2976 case Qt::BottomDockWidgetArea:
2977 r.setWidth(r.width() - sep);
2979 case Qt::NoDockWidgetArea:
2980 case Qt::DockWidgetArea_Mask:
2988 const auto *layout = qobject_cast<QDockWidgetLayout *>(dw->layout());
2989 const bool verticalTitleBar = layout ? layout->verticalTitleBar :
false;
2990 const int tbHeight = QApplication::style()
2991 ? QApplication::style()->pixelMetric(QStyle::PixelMetric::PM_TitleBarHeight,
nullptr, dw)
2993 const int minHeight = verticalTitleBar ? 2 * tbHeight : tbHeight;
2994 const int minWidth = verticalTitleBar ? tbHeight : 2 * tbHeight;
2995 r.setSize(r.size().expandedTo(QSize(minWidth, minHeight)));
2996 qCDebug(lcQpaDockWidgets) << dw <<
"will be unplugged with size" << r.size();
2998 dw->d_func()->unplug(r);
3002#if QT_CONFIG(toolbar)
3003 if (QToolBar *tb = qobject_cast<QToolBar*>(widget)) {
3004 tb->d_func()->unplug(r);
3008#if !QT_CONFIG(dockwidget) || !QT_CONFIG(tabbar)
3012 layoutState.unplug(path ,&savedState);
3013 savedState.fitLayout();
3014 currentGapPos = path;
3016 updateGapIndicator();
3018 fixToolBarOrientation(item, currentGapPos.at(1));
3023void QMainWindowLayout::updateGapIndicator()
3025#if QT_CONFIG(rubberband)
3026 if (!widgetAnimator.animating() && (!currentGapPos.isEmpty()
3027#if QT_CONFIG(dockwidget)
3028 || currentHoveredFloat
3031 QWidget *expectedParent =
3032#if QT_CONFIG(dockwidget)
3033 currentHoveredFloat ? currentHoveredFloat.data() :
3036 if (!gapIndicator) {
3037 gapIndicator =
new QRubberBand(QRubberBand::Rectangle, expectedParent);
3039 gapIndicator->setObjectName(
"qt_rubberband"_L1);
3040 }
else if (gapIndicator->parent() != expectedParent) {
3041 gapIndicator->setParent(expectedParent);
3045 const bool sigBlockState = gapIndicator->signalsBlocked();
3046 auto resetSignals = qScopeGuard([
this, sigBlockState](){ gapIndicator->blockSignals(sigBlockState); });
3047 gapIndicator->blockSignals(
true);
3049#if QT_CONFIG(dockwidget)
3050 if (currentHoveredFloat)
3051 gapIndicator->setGeometry(currentHoveredFloat->currentGapRect);
3054 gapIndicator->setGeometry(currentGapRect);
3056 gapIndicator->show();
3057 gapIndicator->raise();
3061 }
else if (gapIndicator) {
3062 gapIndicator->hide();
3068void QMainWindowLayout::hover(QLayoutItem *hoverTarget,
3069 const QPoint &mousePos) {
3070 if (!parentWidget()->isVisible() || parentWidget()->isMinimized() ||
3071 pluggingWidget !=
nullptr || hoverTarget ==
nullptr)
3074 QWidget *widget = hoverTarget->widget();
3076#if QT_CONFIG(dockwidget)
3078 if ((dockOptions & QMainWindow::GroupedDragging) && (qobject_cast<QDockWidget*>(widget)
3079 || qobject_cast<QDockWidgetGroupWindow *>(widget))) {
3082 QVarLengthArray<QWidget *, 10> candidates;
3083 const auto siblings = parentWidget()->children();
3084 for (QObject *c : siblings) {
3085 QWidget *w = qobject_cast<QWidget*>(c);
3090 if (!qobject_cast<QDockWidget*>(w) && !qobject_cast<QDockWidgetGroupWindow *>(w))
3096 if (w != widget && w->isWindow() && w->isVisible() && !w->isMinimized())
3099 if (QDockWidgetGroupWindow *group = qobject_cast<QDockWidgetGroupWindow *>(w)) {
3102 const auto groupChildren = group->children();
3103 for (QObject *c : groupChildren) {
3104 if (QDockWidget *dw = qobject_cast<QDockWidget*>(c)) {
3105 if (dw != widget && dw->isFloating() && dw->isVisible() && !dw->isMinimized())
3112 for (QWidget *w : candidates) {
3113 const QScreen *screen1 = qt_widget_private(widget)->associatedScreen();
3114 const QScreen *screen2 = qt_widget_private(w)->associatedScreen();
3115 if (screen1 && screen2 && screen1 != screen2)
3117 if (!w->geometry().contains(mousePos))
3120#if QT_CONFIG(tabwidget)
3121 if (
auto dropTo = qobject_cast<QDockWidget *>(w)) {
3124 w = dropTo->widget();
3127 if (!qobject_cast<QDockWidgetGroupWindow *>(w)) {
3128 QDockWidgetGroupWindow *floatingTabs = createTabbedDockWindow();
3129 floatingTabs->setGeometry(dropTo->geometry());
3130 QDockAreaLayoutInfo *info = floatingTabs->layoutInfo();
3131 const QTabBar::Shape shape = tabwidgetPositionToTabBarShape(dropTo);
3138 QInternal::DockPosition dockPosition = toDockPos(dockWidgetArea(dropTo));
3139 if (dockPosition == QInternal::DockPosition::DockCount)
3140 dockPosition = toDockPos(dockWidgetArea(widget));
3141 if (dockPosition == QInternal::DockPosition::DockCount)
3142 dockPosition = QInternal::DockPosition::RightDock;
3144 *info = QDockAreaLayoutInfo(&layoutState.dockAreaLayout.sep, dockPosition,
3145 Qt::Horizontal, shape,
3146 static_cast<QMainWindow *>(parentWidget()));
3147 info->tabBar = getTabBar();
3148 info->tabbed =
true;
3150 QDockAreaLayoutInfo &parentInfo = layoutState.dockAreaLayout.docks[dockPosition];
3151 parentInfo.add(floatingTabs);
3152 dropTo->setParent(floatingTabs);
3153 qCDebug(lcQpaDockWidgets) <<
"Wrapping" << widget <<
"into floating tabs" << floatingTabs;
3159 qCDebug(lcQpaDockWidgets) <<
"Showing" << dropTo;
3161 qCDebug(lcQpaDockWidgets) <<
"Raising" << widget;
3164 auto *groupWindow = qobject_cast<QDockWidgetGroupWindow *>(w);
3165 Q_ASSERT(groupWindow);
3166 if (groupWindow->hover(hoverTarget, groupWindow->mapFromGlobal(mousePos))) {
3167 setCurrentHoveredFloat(groupWindow);
3168 applyState(layoutState);
3176 if (currentHoveredFloat)
3177 currentHoveredFloat->destroyIfSingleItemLeft();
3179 setCurrentHoveredFloat(
nullptr);
3180 layoutState.dockAreaLayout.fallbackToSizeHints =
false;
3183 QPoint pos = parentWidget()->mapFromGlobal(mousePos);
3185 if (!savedState.isValid())
3186 savedState = layoutState;
3188 QList<
int> path = savedState.gapIndex(widget, pos);
3190 if (!path.isEmpty()) {
3191 bool allowed =
false;
3193#if QT_CONFIG(dockwidget)
3194 allowed = isAreaAllowed(widget, path);
3196#if QT_CONFIG(toolbar)
3197 if (QToolBar *tb = qobject_cast<QToolBar*>(widget))
3198 allowed = tb->isAreaAllowed(toToolBarArea(path.at(1)));
3205 if (path == currentGapPos)
3208 currentGapPos = path;
3209 if (path.isEmpty()) {
3210 fixToolBarOrientation(hoverTarget, 2);
3211 restore(QInternal::KeepSavedState);
3215 fixToolBarOrientation(hoverTarget, currentGapPos.at(1));
3217 QMainWindowLayoutState newState = savedState;
3219 if (!newState.insertGap(path, hoverTarget)) {
3220 restore(QInternal::KeepSavedState);
3224 QSize min = newState.minimumSize();
3225 QSize size = newState.rect.size();
3227 if (min.width() > size.width() || min.height() > size.height()) {
3228 restore(QInternal::KeepSavedState);
3232 newState.fitLayout();
3234 currentGapRect = newState.gapRect(currentGapPos);
3236#if QT_CONFIG(dockwidget)
3237 parentWidget()->update(layoutState.dockAreaLayout.separatorRegion());
3239 layoutState = std::move(newState);
3240 applyState(layoutState);
3242 updateGapIndicator();
3245#if QT_CONFIG(dockwidget) && QT_CONFIG(tabwidget)
3246QDockWidgetGroupWindow *QMainWindowLayout::createTabbedDockWindow()
3248 QDockWidgetGroupWindow* f =
new QDockWidgetGroupWindow(parentWidget(), Qt::Tool);
3249 new QDockWidgetGroupLayout(f);
3254void QMainWindowLayout::applyState(QMainWindowLayoutState &newState,
bool animate)
3261 isInApplyState =
true;
3262#if QT_CONFIG(dockwidget) && QT_CONFIG(tabwidget)
3263 QSet<QTabBar*> used = newState.dockAreaLayout.usedTabBars();
3265 parent()->findChildren<QDockWidgetGroupWindow*>(Qt::FindDirectChildrenOnly);
3266 for (QDockWidgetGroupWindow *dwgw : groups)
3267 used += dwgw->layoutInfo()->usedTabBars();
3269 const QSet<QTabBar*> retired = usedTabBars - used;
3271 for (QTabBar *tab_bar : retired) {
3272 unuseTabBar(tab_bar);
3276 const QSet<QWidget*> usedSeps = newState.dockAreaLayout.usedSeparatorWidgets();
3277 const QSet<QWidget*> retiredSeps = usedSeparatorWidgets - usedSeps;
3278 usedSeparatorWidgets = usedSeps;
3279 for (QWidget *sepWidget : retiredSeps)
3283 for (
int i = 0; i < QInternal::DockCount; ++i)
3284 newState.dockAreaLayout.docks[i].reparentWidgets(parentWidget());
3287 newState.apply(dockOptions & QMainWindow::AnimatedDocks && animate);
3288 isInApplyState =
false;
3291void QMainWindowLayout::saveState(QDataStream &stream)
const
3293 layoutState.saveState(stream);
3296bool QMainWindowLayout::restoreState(QDataStream &stream)
3298 QScopedValueRollback<
bool> guard(isInRestoreState,
true);
3299 savedState = layoutState;
3300 layoutState.clear();
3301 layoutState.rect = savedState.rect;
3303 if (!layoutState.restoreState(stream, savedState)) {
3304 layoutState.deleteAllLayoutItems();
3305 layoutState = savedState;
3306 if (parentWidget()->isVisible())
3307 applyState(layoutState,
false);
3311 if (parentWidget()->isVisible()) {
3312 layoutState.fitLayout();
3313 applyState(layoutState,
false);
3316
3317
3318
3319
3320
3321
3322 if ((parentWidget()->windowState() & (Qt::WindowFullScreen | Qt::WindowMaximized))
3323 && !layoutState.fits()) {
3324 restoredState.reset(
new QMainWindowLayoutState(layoutState));
3328 savedState.deleteAllLayoutItems();
3331#if QT_CONFIG(dockwidget) && QT_CONFIG(tabbar)
3332 if (parentWidget()->isVisible())
3339#if QT_CONFIG(draganddrop)
3340bool QMainWindowLayout::needsPlatformDrag()
3342 static const bool wayland =
3343 QGuiApplication::platformName().startsWith(
"wayland"_L1, Qt::CaseInsensitive);
3347Qt::DropAction QMainWindowLayout::performPlatformWidgetDrag(QLayoutItem *widgetItem,
3348 const QPoint &pressPosition)
3350 draggingWidget = widgetItem;
3351 QWidget *widget = widgetItem->widget();
3352 auto drag = QDrag(widget);
3353 auto mimeData =
new QMimeData();
3354 auto window = widgetItem->widget()->windowHandle();
3356 auto serialize = [](
const auto &object) {
3358 QDataStream dataStream(&data, QIODevice::WriteOnly);
3359 dataStream << object;
3362 mimeData->setData(
"application/x-qt-mainwindowdrag-window"_L1,
3363 serialize(
reinterpret_cast<qintptr>(window)));
3364 mimeData->setData(
"application/x-qt-mainwindowdrag-position"_L1, serialize(pressPosition));
3365 drag.setMimeData(mimeData);
3367 auto result = drag.exec();
3369 draggingWidget =
nullptr;
3376#include "qmainwindowlayout.moc"
3377#include "moc_qmainwindowlayout_p.cpp"
Combined button and popup list for selecting options.
QMainWindowLayout * qt_mainwindow_layout(const QMainWindow *mainWindow)
static void fixToolBarOrientation(QLayoutItem *item, int dockPos)