1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Copyright (C) 2015 Olivier Goffart <ogoffart@woboq.com>
5** Contact: https://www.qt.io/licensing/
6**
7** This file is part of the QtWidgets module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial License Usage
11** Licensees holding valid commercial Qt licenses may use this file in
12** accordance with the commercial license agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and The Qt Company. For licensing terms
15** and conditions see https://www.qt.io/terms-conditions. For further
16** information use the contact form at https://www.qt.io/contact-us.
17**
18** GNU Lesser General Public License Usage
19** Alternatively, this file may be used under the terms of the GNU Lesser
20** General Public License version 3 as published by the Free Software
21** Foundation and appearing in the file LICENSE.LGPL3 included in the
22** packaging of this file. Please review the following information to
23** ensure the GNU Lesser General Public License version 3 requirements
24** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
25**
26** GNU General Public License Usage
27** Alternatively, this file may be used under the terms of the GNU
28** General Public License version 2.0 or (at your option) the GNU General
29** Public license version 3 or any later version approved by the KDE Free
30** Qt Foundation. The licenses are as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
32** included in the packaging of this file. Please review the following
33** information to ensure the GNU General Public License requirements will
34** be met: https://www.gnu.org/licenses/gpl-2.0.html and
35** https://www.gnu.org/licenses/gpl-3.0.html.
36**
37** $QT_END_LICENSE$
38**
39****************************************************************************/
40
41#include "qmainwindowlayout_p.h"
42
43#if QT_CONFIG(dockwidget)
44#include "qdockarealayout_p.h"
45#include "qdockwidget.h"
46#include "qdockwidget_p.h"
47#endif
48#if QT_CONFIG(toolbar)
49#include "qtoolbar_p.h"
50#include "qtoolbar.h"
51#include "qtoolbarlayout_p.h"
52#endif
53#include "qmainwindow.h"
54#include "qwidgetanimator_p.h"
55#if QT_CONFIG(rubberband)
56#include "qrubberband.h"
57#endif
58#if QT_CONFIG(tabbar)
59#include "qtabbar_p.h"
60#endif
61
62#include <qapplication.h>
63#if QT_CONFIG(statusbar)
64#include <qstatusbar.h>
65#endif
66#include <qstring.h>
67#include <qstyle.h>
68#include <qstylepainter.h>
69#include <qvarlengtharray.h>
70#include <qstack.h>
71#include <qmap.h>
72#include <qtimer.h>
73#include <qpointer.h>
74
75#ifndef QT_NO_DEBUG_STREAM
76# include <qdebug.h>
77# include <qtextstream.h>
78#endif
79
80#include <private/qmenu_p.h>
81#include <private/qapplication_p.h>
82#include <private/qlayoutengine_p.h>
83#include <private/qwidgetresizehandler_p.h>
84
85QT_BEGIN_NAMESPACE
86
87extern QMainWindowLayout *qt_mainwindow_layout(const QMainWindow *window);
88
89/******************************************************************************
90** debug
91*/
92
93#if QT_CONFIG(dockwidget) && !defined(QT_NO_DEBUG_STREAM)
94
95static void dumpLayout(QTextStream &qout, const QDockAreaLayoutInfo &layout, QString indent);
96
97static void dumpLayout(QTextStream &qout, const QDockAreaLayoutItem &item, QString indent)
98{
99 qout << indent << "QDockAreaLayoutItem: "
100 << "pos: " << item.pos << " size:" << item.size
101 << " gap:" << (item.flags & QDockAreaLayoutItem::GapItem)
102 << " keepSize:" << (item.flags & QDockAreaLayoutItem::KeepSize) << '\n';
103 indent += QLatin1String(" ");
104 if (item.widgetItem != nullptr) {
105 qout << indent << "widget: "
106 << item.widgetItem->widget()->metaObject()->className()
107 << " \"" << item.widgetItem->widget()->windowTitle() << "\"\n";
108 } else if (item.subinfo != nullptr) {
109 qout << indent << "subinfo:\n";
110 dumpLayout(qout, layout: *item.subinfo, indent: indent + QLatin1String(" "));
111 } else if (item.placeHolderItem != nullptr) {
112 QRect r = item.placeHolderItem->topLevelRect;
113 qout << indent << "placeHolder: "
114 << "pos: " << item.pos << " size:" << item.size
115 << " gap:" << (item.flags & QDockAreaLayoutItem::GapItem)
116 << " keepSize:" << (item.flags & QDockAreaLayoutItem::KeepSize)
117 << " objectName:" << item.placeHolderItem->objectName
118 << " hidden:" << item.placeHolderItem->hidden
119 << " window:" << item.placeHolderItem->window
120 << " rect:" << r.x() << ',' << r.y() << ' '
121 << r.width() << 'x' << r.height() << '\n';
122 }
123}
124
125static void dumpLayout(QTextStream &qout, const QDockAreaLayoutInfo &layout, QString indent)
126{
127 const QSize minSize = layout.minimumSize();
128 qout << indent << "QDockAreaLayoutInfo: "
129 << layout.rect.left() << ','
130 << layout.rect.top() << ' '
131 << layout.rect.width() << 'x'
132 << layout.rect.height()
133 << " min size: " << minSize.width() << ',' << minSize.height()
134 << " orient:" << layout.o
135#if QT_CONFIG(tabbar)
136 << " tabbed:" << layout.tabbed
137 << " tbshape:" << layout.tabBarShape
138#endif
139 << '\n';
140
141 indent += QLatin1String(" ");
142
143 for (int i = 0; i < layout.item_list.count(); ++i) {
144 qout << indent << "Item: " << i << '\n';
145 dumpLayout(qout, item: layout.item_list.at(i), indent: indent + QLatin1String(" "));
146 }
147}
148
149static void dumpLayout(QTextStream &qout, const QDockAreaLayout &layout)
150{
151 qout << "QDockAreaLayout: "
152 << layout.rect.left() << ','
153 << layout.rect.top() << ' '
154 << layout.rect.width() << 'x'
155 << layout.rect.height() << '\n';
156
157 qout << "TopDockArea:\n";
158 dumpLayout(qout, layout: layout.docks[QInternal::TopDock], indent: QLatin1String(" "));
159 qout << "LeftDockArea:\n";
160 dumpLayout(qout, layout: layout.docks[QInternal::LeftDock], indent: QLatin1String(" "));
161 qout << "RightDockArea:\n";
162 dumpLayout(qout, layout: layout.docks[QInternal::RightDock], indent: QLatin1String(" "));
163 qout << "BottomDockArea:\n";
164 dumpLayout(qout, layout: layout.docks[QInternal::BottomDock], indent: QLatin1String(" "));
165}
166
167QDebug operator<<(QDebug debug, const QDockAreaLayout &layout)
168{
169 QString s;
170 QTextStream str(&s);
171 dumpLayout(qout&: str, layout);
172 debug << s;
173 return debug;
174}
175
176QDebug operator<<(QDebug debug, const QMainWindowLayout *layout)
177{
178 debug << layout->layoutState.dockAreaLayout;
179 return debug;
180}
181
182#endif // QT_CONFIG(dockwidget) && !defined(QT_NO_DEBUG)
183
184/******************************************************************************
185 ** QDockWidgetGroupWindow
186 */
187// QDockWidgetGroupWindow is the floating window containing several QDockWidgets floating together.
188// (QMainWindow::GroupedDragging feature)
189// QDockWidgetGroupLayout is the layout of that window and use a QDockAreaLayoutInfo to layout
190// the QDockWidgets inside it.
191// If there is only one QDockWidgets, or all QDockWidgets are tabbed together, it is equivalent
192// of a floating QDockWidget (the title of the QDockWidget is the title of the window). But if there
193// are nested QDockWidget, an additional title bar is there.
194#if QT_CONFIG(dockwidget)
195class QDockWidgetGroupLayout : public QLayout,
196 public QMainWindowLayoutSeparatorHelper<QDockWidgetGroupLayout>
197{
198 QWidgetResizeHandler *resizer;
199public:
200 QDockWidgetGroupLayout(QDockWidgetGroupWindow* parent) : QLayout(parent) {
201 setSizeConstraint(QLayout::SetMinAndMaxSize);
202 resizer = new QWidgetResizeHandler(parent);
203 resizer->setMovingEnabled(false);
204 }
205 ~QDockWidgetGroupLayout() {
206 layoutState.deleteAllLayoutItems();
207 }
208
209 void addItem(QLayoutItem*) override { Q_UNREACHABLE(); }
210 int count() const override { return 0; }
211 QLayoutItem* itemAt(int index) const override
212 {
213 int x = 0;
214 return layoutState.itemAt(x: &x, index);
215 }
216 QLayoutItem* takeAt(int index) override
217 {
218 int x = 0;
219 QLayoutItem *ret = layoutState.takeAt(x: &x, index);
220 if (savedState.rect.isValid() && ret->widget()) {
221 // we need to remove the item also from the saved state to prevent crash
222 QList<int> path = savedState.indexOf(widget: ret->widget());
223 if (!path.isEmpty())
224 savedState.remove(path);
225 // Also, the item may be contained several times as a gap item.
226 path = layoutState.indexOf(widget: ret->widget());
227 if (!path.isEmpty())
228 layoutState.remove(path);
229 }
230 return ret;
231 }
232 QSize sizeHint() const override
233 {
234 int fw = frameWidth();
235 return layoutState.sizeHint() + QSize(fw, fw);
236 }
237 QSize minimumSize() const override
238 {
239 int fw = frameWidth();
240 return layoutState.minimumSize() + QSize(fw, fw);
241 }
242 QSize maximumSize() const override
243 {
244 int fw = frameWidth();
245 return layoutState.maximumSize() + QSize(fw, fw);
246 }
247 void setGeometry(const QRect&r) override
248 {
249 groupWindow()->destroyOrHideIfEmpty();
250 QDockAreaLayoutInfo *li = dockAreaLayoutInfo();
251 if (li->isEmpty())
252 return;
253 int fw = frameWidth();
254#if QT_CONFIG(tabbar)
255 li->reparentWidgets(p: parentWidget());
256#endif
257 li->rect = r.adjusted(xp1: fw, yp1: fw, xp2: -fw, yp2: -fw);
258 li->fitItems();
259 li->apply(animate: false);
260 if (savedState.rect.isValid())
261 savedState.rect = li->rect;
262 resizer->setActive(ac: QWidgetResizeHandler::Resize, b: !nativeWindowDeco());
263 }
264
265 QDockAreaLayoutInfo *dockAreaLayoutInfo() { return &layoutState; }
266
267 bool nativeWindowDeco() const
268 {
269 return groupWindow()->hasNativeDecos();
270 }
271
272 int frameWidth() const
273 {
274 return nativeWindowDeco() ? 0 :
275 parentWidget()->style()->pixelMetric(metric: QStyle::PM_DockWidgetFrameWidth, option: nullptr, widget: parentWidget());
276 }
277
278 QDockWidgetGroupWindow *groupWindow() const
279 {
280 return static_cast<QDockWidgetGroupWindow *>(parent());
281 }
282
283 QDockAreaLayoutInfo layoutState;
284 QDockAreaLayoutInfo savedState;
285};
286
287bool QDockWidgetGroupWindow::event(QEvent *e)
288{
289 auto lay = static_cast<QDockWidgetGroupLayout *>(layout());
290 if (lay && lay->windowEvent(e))
291 return true;
292
293 switch (e->type()) {
294 case QEvent::Close:
295#if QT_CONFIG(tabbar)
296 // Forward the close to the QDockWidget just as if its close button was pressed
297 if (QDockWidget *dw = activeTabbedDockWidget()) {
298 e->ignore();
299 dw->close();
300 adjustFlags();
301 }
302#endif
303 return true;
304 case QEvent::Move:
305#if QT_CONFIG(tabbar)
306 // Let QDockWidgetPrivate::moseEvent handle the dragging
307 if (QDockWidget *dw = activeTabbedDockWidget())
308 static_cast<QDockWidgetPrivate *>(QObjectPrivate::get(o: dw))->moveEvent(event: static_cast<QMoveEvent*>(e));
309#endif
310 return true;
311 case QEvent::NonClientAreaMouseMove:
312 case QEvent::NonClientAreaMouseButtonPress:
313 case QEvent::NonClientAreaMouseButtonRelease:
314 case QEvent::NonClientAreaMouseButtonDblClick:
315#if QT_CONFIG(tabbar)
316 // Let the QDockWidgetPrivate of the currently visible dock widget handle the drag and drop
317 if (QDockWidget *dw = activeTabbedDockWidget())
318 static_cast<QDockWidgetPrivate *>(QObjectPrivate::get(o: dw))->nonClientAreaMouseEvent(event: static_cast<QMouseEvent*>(e));
319#endif
320 return true;
321 case QEvent::ChildAdded:
322 if (qobject_cast<QDockWidget *>(object: static_cast<QChildEvent*>(e)->child()))
323 adjustFlags();
324 break;
325 case QEvent::LayoutRequest:
326 // We might need to show the widget again
327 destroyOrHideIfEmpty();
328 break;
329 case QEvent::Resize:
330 updateCurrentGapRect();
331 emit resized();
332 default:
333 break;
334 }
335 return QWidget::event(event: e);
336}
337
338void QDockWidgetGroupWindow::paintEvent(QPaintEvent *)
339{
340 QDockWidgetGroupLayout *lay = static_cast<QDockWidgetGroupLayout *>(layout());
341 bool nativeDeco = lay->nativeWindowDeco();
342
343 if (!nativeDeco) {
344 QStyleOptionFrame framOpt;
345 framOpt.init(w: this);
346 QStylePainter p(this);
347 p.drawPrimitive(pe: QStyle::PE_FrameDockWidget, opt: framOpt);
348 }
349}
350
351QDockAreaLayoutInfo *QDockWidgetGroupWindow::layoutInfo() const
352{
353 return static_cast<QDockWidgetGroupLayout *>(layout())->dockAreaLayoutInfo();
354}
355
356#if QT_CONFIG(tabbar)
357/*! \internal
358 If this is a floating tab bar returns the currently the QDockWidgetGroupWindow that contains
359 tab, otherwise, return nullptr;
360 \note: if there is only one QDockWidget, it's still considered as a floating tab
361 */
362const QDockAreaLayoutInfo *QDockWidgetGroupWindow::tabLayoutInfo() const
363{
364 const QDockAreaLayoutInfo *info = layoutInfo();
365 while (info && !info->tabbed) {
366 // There should be only one tabbed subinfo otherwise we are not a floating tab but a real
367 // window
368 const QDockAreaLayoutInfo *next = nullptr;
369 bool isSingle = false;
370 for (const auto &item : info->item_list) {
371 if (item.skip() || (item.flags & QDockAreaLayoutItem::GapItem))
372 continue;
373 if (next || isSingle) // Two visible things
374 return nullptr;
375 if (item.subinfo)
376 next = item.subinfo;
377 else if (item.widgetItem)
378 isSingle = true;
379 }
380 if (isSingle)
381 return info;
382 info = next;
383 }
384 return info;
385}
386
387/*! \internal
388 If this is a floating tab bar returns the currently active QDockWidget, otherwise nullptr
389 */
390QDockWidget *QDockWidgetGroupWindow::activeTabbedDockWidget() const
391{
392 QDockWidget *dw = nullptr;
393 const QDockAreaLayoutInfo *info = tabLayoutInfo();
394 if (!info)
395 return nullptr;
396 if (info->tabBar && info->tabBar->currentIndex() >= 0) {
397 int i = info->tabIndexToListIndex(info->tabBar->currentIndex());
398 if (i >= 0) {
399 const QDockAreaLayoutItem &item = info->item_list.at(i);
400 if (item.widgetItem)
401 dw = qobject_cast<QDockWidget *>(object: item.widgetItem->widget());
402 }
403 }
404 if (!dw) {
405 for (int i = 0; !dw && i < info->item_list.count(); ++i) {
406 const QDockAreaLayoutItem &item = info->item_list.at(i);
407 if (item.skip())
408 continue;
409 if (!item.widgetItem)
410 continue;
411 dw = qobject_cast<QDockWidget *>(object: item.widgetItem->widget());
412 }
413 }
414 return dw;
415}
416#endif // QT_CONFIG(tabbar)
417
418/*! \internal
419 Destroy or hide this window if there is no more QDockWidget in it.
420 Otherwise make sure it is shown.
421 */
422void QDockWidgetGroupWindow::destroyOrHideIfEmpty()
423{
424 if (!layoutInfo()->isEmpty()) {
425 show(); // It might have been hidden,
426 return;
427 }
428 // There might still be placeholders
429 if (!layoutInfo()->item_list.isEmpty()) {
430 hide();
431 return;
432 }
433
434 // Make sure to reparent the possibly floating or hidden QDockWidgets to the parent
435 const auto dockWidgets = findChildren<QDockWidget *>(aName: QString(), options: Qt::FindDirectChildrenOnly);
436 for (QDockWidget *dw : dockWidgets) {
437 bool wasFloating = dw->isFloating();
438 bool wasHidden = dw->isHidden();
439 dw->setParent(parentWidget());
440 if (wasFloating) {
441 dw->setFloating(true);
442 } else {
443 // maybe it was hidden, we still have to put it back in the main layout.
444 QMainWindowLayout *ml =
445 qt_mainwindow_layout(window: static_cast<QMainWindow *>(parentWidget()));
446 Qt::DockWidgetArea area = ml->dockWidgetArea(widget: this);
447 if (area == Qt::NoDockWidgetArea)
448 area = Qt::LeftDockWidgetArea;
449 static_cast<QMainWindow *>(parentWidget())->addDockWidget(area, dockwidget: dw);
450 }
451 if (!wasHidden)
452 dw->show();
453 }
454#if QT_CONFIG(tabbar)
455 const auto tabBars = findChildren<QTabBar *>(aName: QString(), options: Qt::FindDirectChildrenOnly);
456 for (QTabBar *tb : tabBars)
457 tb->setParent(parentWidget());
458#endif
459 deleteLater();
460}
461
462/*! \internal
463 Sets the flags of this window in accordance to the capabilities of the dock widgets
464 */
465void QDockWidgetGroupWindow::adjustFlags()
466{
467 Qt::WindowFlags oldFlags = windowFlags();
468 Qt::WindowFlags flags = oldFlags;
469
470#if QT_CONFIG(tabbar)
471 QDockWidget *top = activeTabbedDockWidget();
472#else
473 QDockWidget *top = nullptr;
474#endif
475 if (!top) { // nested tabs, show window decoration
476 flags =
477 ((oldFlags & ~Qt::FramelessWindowHint) | Qt::CustomizeWindowHint | Qt::WindowTitleHint);
478 } else if (static_cast<QDockWidgetGroupLayout *>(layout())->nativeWindowDeco()) {
479 flags |= Qt::CustomizeWindowHint | Qt::WindowTitleHint;
480 flags.setFlag(flag: Qt::WindowCloseButtonHint, on: top->features() & QDockWidget::DockWidgetClosable);
481 flags &= ~Qt::FramelessWindowHint;
482 } else {
483 flags &= ~(Qt::WindowCloseButtonHint | Qt::CustomizeWindowHint | Qt::WindowTitleHint);
484 flags |= Qt::FramelessWindowHint;
485 }
486
487 if (oldFlags != flags) {
488 if (!windowHandle())
489 create(); // The desired geometry is forgotten if we call setWindowFlags before having a window
490 setWindowFlags(flags);
491 const bool gainedNativeDecos = (oldFlags & Qt::FramelessWindowHint) && !(flags & Qt::FramelessWindowHint);
492 const bool lostNativeDecos = !(oldFlags & Qt::FramelessWindowHint) && (flags & Qt::FramelessWindowHint);
493
494 // Adjust the geometry after gaining/losing decos, so that the client area appears always
495 // at the same place when tabbing
496 if (lostNativeDecos) {
497 QRect newGeometry = geometry();
498 newGeometry.setTop(frameGeometry().top());
499 const int bottomFrame = geometry().top() - frameGeometry().top();
500 m_removedFrameSize = QSize((frameSize() - size()).width(), bottomFrame);
501 setGeometry(newGeometry);
502 } else if (gainedNativeDecos && m_removedFrameSize.isValid()) {
503 QRect r = geometry();
504 r.adjust(dx1: -m_removedFrameSize.width() / 2, dy1: 0,
505 dx2: -m_removedFrameSize.width() / 2, dy2: -m_removedFrameSize.height());
506 setGeometry(r);
507 m_removedFrameSize = QSize();
508 }
509
510 show(); // setWindowFlags hides the window
511 }
512
513 QWidget *titleBarOf = top ? top : parentWidget();
514 setWindowTitle(titleBarOf->windowTitle());
515 setWindowIcon(titleBarOf->windowIcon());
516}
517
518bool QDockWidgetGroupWindow::hasNativeDecos() const
519{
520#if QT_CONFIG(tabbar)
521 QDockWidget *dw = activeTabbedDockWidget();
522 if (!dw) // We have a group of nested QDockWidgets (not just floating tabs)
523 return true;
524
525 if (!QDockWidgetLayout::wmSupportsNativeWindowDeco())
526 return false;
527
528 return dw->titleBarWidget() == nullptr;
529#else
530 return true;
531#endif
532}
533
534/*
535 The given widget is hovered over this floating group.
536 This function will save the state and create a gap in the actual state.
537 currentGapRect and currentGapPos will be set.
538 One must call restore() or apply() after this function.
539 Returns true if there was any change in the currentGapPos
540 */
541bool QDockWidgetGroupWindow::hover(QLayoutItem *widgetItem, const QPoint &mousePos)
542{
543 QDockAreaLayoutInfo &savedState = static_cast<QDockWidgetGroupLayout *>(layout())->savedState;
544 if (savedState.isEmpty())
545 savedState = *layoutInfo();
546
547 QMainWindow::DockOptions opts = static_cast<QMainWindow *>(parentWidget())->dockOptions();
548 QDockAreaLayoutInfo newState = savedState;
549 bool nestingEnabled =
550 (opts & QMainWindow::AllowNestedDocks) && !(opts & QMainWindow::ForceTabbedDocks);
551 QDockAreaLayoutInfo::TabMode tabMode =
552#if !QT_CONFIG(tabbar)
553 QDockAreaLayoutInfo::NoTabs;
554#else
555 nestingEnabled ? QDockAreaLayoutInfo::AllowTabs : QDockAreaLayoutInfo::ForceTabs;
556 if (auto group = qobject_cast<QDockWidgetGroupWindow *>(object: widgetItem->widget())) {
557 if (!group->tabLayoutInfo())
558 tabMode = QDockAreaLayoutInfo::NoTabs;
559 }
560 if (newState.tabbed) {
561 // insertion into a top-level tab
562 newState.item_list = { QDockAreaLayoutItem(new QDockAreaLayoutInfo(newState)) };
563 newState.item_list.first().size = pick(o: savedState.o, size: savedState.rect.size());
564 newState.tabbed = false;
565 newState.tabBar = nullptr;
566 }
567#endif
568
569 auto newGapPos = newState.gapIndex(pos: mousePos, nestingEnabled, tabMode);
570 Q_ASSERT(!newGapPos.isEmpty());
571 if (newGapPos == currentGapPos)
572 return false; // gap is already there
573 currentGapPos = newGapPos;
574 newState.insertGap(path: currentGapPos, dockWidgetItem: widgetItem);
575 newState.fitItems();
576 *layoutInfo() = std::move(newState);
577 updateCurrentGapRect();
578 layoutInfo()->apply(animate: opts & QMainWindow::AnimatedDocks);
579 return true;
580}
581
582void QDockWidgetGroupWindow::updateCurrentGapRect()
583{
584 if (!currentGapPos.isEmpty())
585 currentGapRect = layoutInfo()->info(path: currentGapPos)->itemRect(index: currentGapPos.last(), isGap: true);
586}
587
588/*
589 Remove the gap that was created by hover()
590 */
591void QDockWidgetGroupWindow::restore()
592{
593 QDockAreaLayoutInfo &savedState = static_cast<QDockWidgetGroupLayout *>(layout())->savedState;
594 if (!savedState.isEmpty()) {
595 *layoutInfo() = savedState;
596 savedState = QDockAreaLayoutInfo();
597 }
598 currentGapRect = QRect();
599 currentGapPos.clear();
600 adjustFlags();
601 layoutInfo()->fitItems();
602 layoutInfo()->apply(animate: static_cast<QMainWindow *>(parentWidget())->dockOptions()
603 & QMainWindow::AnimatedDocks);
604}
605
606/*
607 Apply the state that was created by hover
608 */
609void QDockWidgetGroupWindow::apply()
610{
611 static_cast<QDockWidgetGroupLayout *>(layout())->savedState.clear();
612 currentGapRect = QRect();
613 layoutInfo()->plug(path: currentGapPos);
614 currentGapPos.clear();
615 adjustFlags();
616 layoutInfo()->apply(animate: false);
617}
618
619#endif
620
621/******************************************************************************
622** QMainWindowLayoutState
623*/
624
625// we deal with all the #ifndefferry here so QMainWindowLayout code is clean
626
627QMainWindowLayoutState::QMainWindowLayoutState(QMainWindow *win)
628 :
629#if QT_CONFIG(toolbar)
630 toolBarAreaLayout(win),
631#endif
632#if QT_CONFIG(dockwidget)
633 dockAreaLayout(win)
634#else
635 centralWidgetItem(0)
636#endif
637
638{
639 mainWindow = win;
640}
641
642QSize QMainWindowLayoutState::sizeHint() const
643{
644
645 QSize result(0, 0);
646
647#if QT_CONFIG(dockwidget)
648 result = dockAreaLayout.sizeHint();
649#else
650 if (centralWidgetItem != 0)
651 result = centralWidgetItem->sizeHint();
652#endif
653
654#if QT_CONFIG(toolbar)
655 result = toolBarAreaLayout.sizeHint(center: result);
656#endif // QT_CONFIG(toolbar)
657
658 return result;
659}
660
661QSize QMainWindowLayoutState::minimumSize() const
662{
663 QSize result(0, 0);
664
665#if QT_CONFIG(dockwidget)
666 result = dockAreaLayout.minimumSize();
667#else
668 if (centralWidgetItem != 0)
669 result = centralWidgetItem->minimumSize();
670#endif
671
672#if QT_CONFIG(toolbar)
673 result = toolBarAreaLayout.minimumSize(centerMin: result);
674#endif // QT_CONFIG(toolbar)
675
676 return result;
677}
678
679void QMainWindowLayoutState::apply(bool animated)
680{
681#if QT_CONFIG(toolbar)
682 toolBarAreaLayout.apply(animate: animated);
683#endif
684
685#if QT_CONFIG(dockwidget)
686// dumpLayout(dockAreaLayout, QString());
687 dockAreaLayout.apply(animate: animated);
688#else
689 if (centralWidgetItem != 0) {
690 QMainWindowLayout *layout = qt_mainwindow_layout(mainWindow);
691 Q_ASSERT(layout != 0);
692 layout->widgetAnimator.animate(centralWidgetItem->widget(), centralWidgetRect, animated);
693 }
694#endif
695}
696
697void QMainWindowLayoutState::fitLayout()
698{
699 QRect r;
700#if !QT_CONFIG(toolbar)
701 r = rect;
702#else
703 toolBarAreaLayout.rect = rect;
704 r = toolBarAreaLayout.fitLayout();
705#endif // QT_CONFIG(toolbar)
706
707#if QT_CONFIG(dockwidget)
708 dockAreaLayout.rect = r;
709 dockAreaLayout.fitLayout();
710#else
711 centralWidgetRect = r;
712#endif
713}
714
715void QMainWindowLayoutState::deleteAllLayoutItems()
716{
717#if QT_CONFIG(toolbar)
718 toolBarAreaLayout.deleteAllLayoutItems();
719#endif
720
721#if QT_CONFIG(dockwidget)
722 dockAreaLayout.deleteAllLayoutItems();
723#endif
724}
725
726void QMainWindowLayoutState::deleteCentralWidgetItem()
727{
728#if QT_CONFIG(dockwidget)
729 delete dockAreaLayout.centralWidgetItem;
730 dockAreaLayout.centralWidgetItem = nullptr;
731#else
732 delete centralWidgetItem;
733 centralWidgetItem = 0;
734#endif
735}
736
737QLayoutItem *QMainWindowLayoutState::itemAt(int index, int *x) const
738{
739#if QT_CONFIG(toolbar)
740 if (QLayoutItem *ret = toolBarAreaLayout.itemAt(x, index))
741 return ret;
742#endif
743
744#if QT_CONFIG(dockwidget)
745 if (QLayoutItem *ret = dockAreaLayout.itemAt(x, index))
746 return ret;
747#else
748 if (centralWidgetItem != 0 && (*x)++ == index)
749 return centralWidgetItem;
750#endif
751
752 return nullptr;
753}
754
755QLayoutItem *QMainWindowLayoutState::takeAt(int index, int *x)
756{
757#if QT_CONFIG(toolbar)
758 if (QLayoutItem *ret = toolBarAreaLayout.takeAt(x, index))
759 return ret;
760#endif
761
762#if QT_CONFIG(dockwidget)
763 if (QLayoutItem *ret = dockAreaLayout.takeAt(x, index))
764 return ret;
765#else
766 if (centralWidgetItem != 0 && (*x)++ == index) {
767 QLayoutItem *ret = centralWidgetItem;
768 centralWidgetItem = 0;
769 return ret;
770 }
771#endif
772
773 return nullptr;
774}
775
776QList<int> QMainWindowLayoutState::indexOf(QWidget *widget) const
777{
778 QList<int> result;
779
780#if QT_CONFIG(toolbar)
781 // is it a toolbar?
782 if (QToolBar *toolBar = qobject_cast<QToolBar*>(object: widget)) {
783 result = toolBarAreaLayout.indexOf(toolBar);
784 if (!result.isEmpty())
785 result.prepend(t: 0);
786 return result;
787 }
788#endif
789
790#if QT_CONFIG(dockwidget)
791 // is it a dock widget?
792 if (qobject_cast<QDockWidget *>(object: widget) || qobject_cast<QDockWidgetGroupWindow *>(object: widget)) {
793 result = dockAreaLayout.indexOf(dockWidget: widget);
794 if (!result.isEmpty())
795 result.prepend(t: 1);
796 return result;
797 }
798#endif // QT_CONFIG(dockwidget)
799
800 return result;
801}
802
803bool QMainWindowLayoutState::contains(QWidget *widget) const
804{
805#if QT_CONFIG(dockwidget)
806 if (dockAreaLayout.centralWidgetItem != nullptr && dockAreaLayout.centralWidgetItem->widget() == widget)
807 return true;
808 if (!dockAreaLayout.indexOf(dockWidget: widget).isEmpty())
809 return true;
810#else
811 if (centralWidgetItem != 0 && centralWidgetItem->widget() == widget)
812 return true;
813#endif
814
815#if QT_CONFIG(toolbar)
816 if (!toolBarAreaLayout.indexOf(toolBar: widget).isEmpty())
817 return true;
818#endif
819 return false;
820}
821
822void QMainWindowLayoutState::setCentralWidget(QWidget *widget)
823{
824 QLayoutItem *item = nullptr;
825 //make sure we remove the widget
826 deleteCentralWidgetItem();
827
828 if (widget != nullptr)
829 item = new QWidgetItemV2(widget);
830
831#if QT_CONFIG(dockwidget)
832 dockAreaLayout.centralWidgetItem = item;
833#else
834 centralWidgetItem = item;
835#endif
836}
837
838QWidget *QMainWindowLayoutState::centralWidget() const
839{
840 QLayoutItem *item = nullptr;
841
842#if QT_CONFIG(dockwidget)
843 item = dockAreaLayout.centralWidgetItem;
844#else
845 item = centralWidgetItem;
846#endif
847
848 if (item != nullptr)
849 return item->widget();
850 return nullptr;
851}
852
853QList<int> QMainWindowLayoutState::gapIndex(QWidget *widget,
854 const QPoint &pos) const
855{
856 QList<int> result;
857
858#if QT_CONFIG(toolbar)
859 // is it a toolbar?
860 if (qobject_cast<QToolBar*>(object: widget) != 0) {
861 result = toolBarAreaLayout.gapIndex(pos);
862 if (!result.isEmpty())
863 result.prepend(t: 0);
864 return result;
865 }
866#endif
867
868#if QT_CONFIG(dockwidget)
869 // is it a dock widget?
870 if (qobject_cast<QDockWidget *>(object: widget) != 0
871 || qobject_cast<QDockWidgetGroupWindow *>(object: widget)) {
872 bool disallowTabs = false;
873#if QT_CONFIG(tabbar)
874 if (auto *group = qobject_cast<QDockWidgetGroupWindow *>(object: widget)) {
875 if (!group->tabLayoutInfo()) // Disallow to drop nested docks as a tab
876 disallowTabs = true;
877 }
878#endif
879 result = dockAreaLayout.gapIndex(pos, disallowTabs);
880 if (!result.isEmpty())
881 result.prepend(t: 1);
882 return result;
883 }
884#endif // QT_CONFIG(dockwidget)
885
886 return result;
887}
888
889bool QMainWindowLayoutState::insertGap(const QList<int> &path, QLayoutItem *item)
890{
891 if (path.isEmpty())
892 return false;
893
894 int i = path.first();
895
896#if QT_CONFIG(toolbar)
897 if (i == 0) {
898 Q_ASSERT(qobject_cast<QToolBar*>(item->widget()) != 0);
899 return toolBarAreaLayout.insertGap(path: path.mid(pos: 1), item);
900 }
901#endif
902
903#if QT_CONFIG(dockwidget)
904 if (i == 1) {
905 Q_ASSERT(qobject_cast<QDockWidget*>(item->widget()) || qobject_cast<QDockWidgetGroupWindow*>(item->widget()));
906 return dockAreaLayout.insertGap(path: path.mid(pos: 1), dockWidgetItem: item);
907 }
908#endif // QT_CONFIG(dockwidget)
909
910 return false;
911}
912
913void QMainWindowLayoutState::remove(const QList<int> &path)
914{
915 int i = path.first();
916
917#if QT_CONFIG(toolbar)
918 if (i == 0)
919 toolBarAreaLayout.remove(path: path.mid(pos: 1));
920#endif
921
922#if QT_CONFIG(dockwidget)
923 if (i == 1)
924 dockAreaLayout.remove(path: path.mid(pos: 1));
925#endif // QT_CONFIG(dockwidget)
926}
927
928void QMainWindowLayoutState::remove(QLayoutItem *item)
929{
930#if QT_CONFIG(toolbar)
931 toolBarAreaLayout.remove(item);
932#endif
933
934#if QT_CONFIG(dockwidget)
935 // is it a dock widget?
936 if (QDockWidget *dockWidget = qobject_cast<QDockWidget *>(object: item->widget())) {
937 QList<int> path = dockAreaLayout.indexOf(dockWidget);
938 if (!path.isEmpty())
939 dockAreaLayout.remove(path);
940 }
941#endif // QT_CONFIG(dockwidget)
942}
943
944void QMainWindowLayoutState::clear()
945{
946#if QT_CONFIG(toolbar)
947 toolBarAreaLayout.clear();
948#endif
949
950#if QT_CONFIG(dockwidget)
951 dockAreaLayout.clear();
952#else
953 centralWidgetRect = QRect();
954#endif
955
956 rect = QRect();
957}
958
959bool QMainWindowLayoutState::isValid() const
960{
961 return rect.isValid();
962}
963
964QLayoutItem *QMainWindowLayoutState::item(const QList<int> &path)
965{
966 int i = path.first();
967
968#if QT_CONFIG(toolbar)
969 if (i == 0) {
970 const QToolBarAreaLayoutItem *tbItem = toolBarAreaLayout.item(path: path.mid(pos: 1));
971 Q_ASSERT(tbItem);
972 return tbItem->widgetItem;
973 }
974#endif
975
976#if QT_CONFIG(dockwidget)
977 if (i == 1)
978 return dockAreaLayout.item(path: path.mid(pos: 1)).widgetItem;
979#endif // QT_CONFIG(dockwidget)
980
981 return nullptr;
982}
983
984QRect QMainWindowLayoutState::itemRect(const QList<int> &path) const
985{
986 int i = path.first();
987
988#if QT_CONFIG(toolbar)
989 if (i == 0)
990 return toolBarAreaLayout.itemRect(path: path.mid(pos: 1));
991#endif
992
993#if QT_CONFIG(dockwidget)
994 if (i == 1)
995 return dockAreaLayout.itemRect(path: path.mid(pos: 1));
996#endif // QT_CONFIG(dockwidget)
997
998 return QRect();
999}
1000
1001QRect QMainWindowLayoutState::gapRect(const QList<int> &path) const
1002{
1003 int i = path.first();
1004
1005#if QT_CONFIG(toolbar)
1006 if (i == 0)
1007 return toolBarAreaLayout.itemRect(path: path.mid(pos: 1));
1008#endif
1009
1010#if QT_CONFIG(dockwidget)
1011 if (i == 1)
1012 return dockAreaLayout.gapRect(path: path.mid(pos: 1));
1013#endif // QT_CONFIG(dockwidget)
1014
1015 return QRect();
1016}
1017
1018QLayoutItem *QMainWindowLayoutState::plug(const QList<int> &path)
1019{
1020 int i = path.first();
1021
1022#if QT_CONFIG(toolbar)
1023 if (i == 0)
1024 return toolBarAreaLayout.plug(path: path.mid(pos: 1));
1025#endif
1026
1027#if QT_CONFIG(dockwidget)
1028 if (i == 1)
1029 return dockAreaLayout.plug(path: path.mid(pos: 1));
1030#endif // QT_CONFIG(dockwidget)
1031
1032 return nullptr;
1033}
1034
1035QLayoutItem *QMainWindowLayoutState::unplug(const QList<int> &path, QMainWindowLayoutState *other)
1036{
1037 int i = path.first();
1038
1039#if !QT_CONFIG(toolbar)
1040 Q_UNUSED(other);
1041#else
1042 if (i == 0)
1043 return toolBarAreaLayout.unplug(path: path.mid(pos: 1), other: other ? &other->toolBarAreaLayout : nullptr);
1044#endif
1045
1046#if QT_CONFIG(dockwidget)
1047 if (i == 1)
1048 return dockAreaLayout.unplug(path: path.mid(pos: 1));
1049#endif // QT_CONFIG(dockwidget)
1050
1051 return nullptr;
1052}
1053
1054void QMainWindowLayoutState::saveState(QDataStream &stream) const
1055{
1056#if QT_CONFIG(dockwidget)
1057 dockAreaLayout.saveState(stream);
1058#if QT_CONFIG(tabbar)
1059 const QList<QDockWidgetGroupWindow *> floatingTabs =
1060 mainWindow->findChildren<QDockWidgetGroupWindow *>(aName: QString(), options: Qt::FindDirectChildrenOnly);
1061
1062 for (QDockWidgetGroupWindow *floating : floatingTabs) {
1063 if (floating->layoutInfo()->isEmpty())
1064 continue;
1065 stream << uchar(QDockAreaLayout::FloatingDockWidgetTabMarker) << floating->geometry();
1066 floating->layoutInfo()->saveState(stream);
1067 }
1068#endif
1069#endif
1070#if QT_CONFIG(toolbar)
1071 toolBarAreaLayout.saveState(stream);
1072#endif
1073}
1074
1075template <typename T>
1076static QList<T> findChildrenHelper(const QObject *o)
1077{
1078 const QObjectList &list = o->children();
1079 QList<T> result;
1080
1081 for (int i=0; i < list.size(); ++i) {
1082 if (T t = qobject_cast<T>(list[i])) {
1083 result.append(t);
1084 }
1085 }
1086
1087 return result;
1088}
1089
1090#if QT_CONFIG(dockwidget)
1091static QList<QDockWidget*> allMyDockWidgets(const QWidget *mainWindow)
1092{
1093 QList<QDockWidget*> result;
1094 for (QObject *c : mainWindow->children()) {
1095 if (auto *dw = qobject_cast<QDockWidget*>(object: c)) {
1096 result.append(t: dw);
1097 } else if (auto *gw = qobject_cast<QDockWidgetGroupWindow*>(object: c)) {
1098 for (QObject *c : gw->children()) {
1099 if (auto *dw = qobject_cast<QDockWidget*>(object: c))
1100 result.append(t: dw);
1101 }
1102 }
1103 }
1104
1105 return result;
1106}
1107#endif // QT_CONFIG(dockwidget)
1108
1109//pre4.3 tests the format that was used before 4.3
1110bool QMainWindowLayoutState::checkFormat(QDataStream &stream)
1111{
1112 while (!stream.atEnd()) {
1113 uchar marker;
1114 stream >> marker;
1115 switch(marker)
1116 {
1117#if QT_CONFIG(toolbar)
1118 case QToolBarAreaLayout::ToolBarStateMarker:
1119 case QToolBarAreaLayout::ToolBarStateMarkerEx:
1120 {
1121 QList<QToolBar *> toolBars = findChildrenHelper<QToolBar*>(o: mainWindow);
1122 if (!toolBarAreaLayout.restoreState(stream, toolBars, tmarker: marker, testing: true /*testing*/)) {
1123 return false;
1124 }
1125 }
1126 break;
1127#endif // QT_CONFIG(toolbar)
1128
1129#if QT_CONFIG(dockwidget)
1130 case QDockAreaLayout::DockWidgetStateMarker:
1131 {
1132 const auto dockWidgets = allMyDockWidgets(mainWindow);
1133 if (!dockAreaLayout.restoreState(stream, widgets: dockWidgets, testing: true /*testing*/)) {
1134 return false;
1135 }
1136 }
1137 break;
1138#if QT_CONFIG(tabbar)
1139 case QDockAreaLayout::FloatingDockWidgetTabMarker:
1140 {
1141 QRect geom;
1142 stream >> geom;
1143 QDockAreaLayoutInfo info;
1144 auto dockWidgets = allMyDockWidgets(mainWindow);
1145 if (!info.restoreState(stream, widgets&: dockWidgets, testing: true /* testing*/))
1146 return false;
1147 }
1148 break;
1149#endif // QT_CONFIG(tabbar)
1150#endif // QT_CONFIG(dockwidget)
1151 default:
1152 //there was an error during the parsing
1153 return false;
1154 }// switch
1155 } //while
1156
1157 //everything went fine: it must be a pre-4.3 saved state
1158 return true;
1159}
1160
1161bool QMainWindowLayoutState::restoreState(QDataStream &_stream,
1162 const QMainWindowLayoutState &oldState)
1163{
1164 //make a copy of the data so that we can read it more than once
1165 QByteArray copy;
1166 while(!_stream.atEnd()) {
1167 int length = 1024;
1168 QByteArray ba(length, '\0');
1169 length = _stream.readRawData(ba.data(), len: ba.size());
1170 ba.resize(size: length);
1171 copy += ba;
1172 }
1173
1174 QDataStream ds(copy);
1175 if (!checkFormat(stream&: ds))
1176 return false;
1177
1178 QDataStream stream(copy);
1179
1180 while (!stream.atEnd()) {
1181 uchar marker;
1182 stream >> marker;
1183 switch(marker)
1184 {
1185#if QT_CONFIG(dockwidget)
1186 case QDockAreaLayout::DockWidgetStateMarker:
1187 {
1188 const auto dockWidgets = allMyDockWidgets(mainWindow);
1189 if (!dockAreaLayout.restoreState(stream, widgets: dockWidgets))
1190 return false;
1191
1192 for (int i = 0; i < dockWidgets.size(); ++i) {
1193 QDockWidget *w = dockWidgets.at(i);
1194 QList<int> path = dockAreaLayout.indexOf(dockWidget: w);
1195 if (path.isEmpty()) {
1196 QList<int> oldPath = oldState.dockAreaLayout.indexOf(dockWidget: w);
1197 if (oldPath.isEmpty()) {
1198 continue;
1199 }
1200 QDockAreaLayoutInfo *info = dockAreaLayout.info(path: oldPath);
1201 if (info == nullptr) {
1202 continue;
1203 }
1204 info->item_list.append(t: QDockAreaLayoutItem(new QDockWidgetItem(w)));
1205 }
1206 }
1207 }
1208 break;
1209#if QT_CONFIG(tabwidget)
1210 case QDockAreaLayout::FloatingDockWidgetTabMarker:
1211 {
1212 auto dockWidgets = allMyDockWidgets(mainWindow);
1213 QDockWidgetGroupWindow* floatingTab = qt_mainwindow_layout(window: mainWindow)->createTabbedDockWindow();
1214 *floatingTab->layoutInfo() = QDockAreaLayoutInfo(&dockAreaLayout.sep, QInternal::LeftDock,
1215 Qt::Horizontal, QTabBar::RoundedSouth, mainWindow);
1216 QRect geometry;
1217 stream >> geometry;
1218 QDockAreaLayoutInfo *info = floatingTab->layoutInfo();
1219 if (!info->restoreState(stream, widgets&: dockWidgets, testing: false))
1220 return false;
1221 geometry = QDockAreaLayout::constrainedRect(rect: geometry, widget: floatingTab);
1222 floatingTab->move(geometry.topLeft());
1223 floatingTab->resize(geometry.size());
1224
1225 // Don't show an empty QDockWidgetGroupWindow if no dock widget is available yet.
1226 // reparentWidgets() would be triggered by show(), so do it explicitly here.
1227 if (info->onlyHasPlaceholders())
1228 info->reparentWidgets(p: floatingTab);
1229 else
1230 floatingTab->show();
1231 }
1232 break;
1233#endif // QT_CONFIG(tabwidget)
1234#endif // QT_CONFIG(dockwidget)
1235
1236#if QT_CONFIG(toolbar)
1237 case QToolBarAreaLayout::ToolBarStateMarker:
1238 case QToolBarAreaLayout::ToolBarStateMarkerEx:
1239 {
1240 QList<QToolBar *> toolBars = findChildrenHelper<QToolBar*>(o: mainWindow);
1241 if (!toolBarAreaLayout.restoreState(stream, toolBars, tmarker: marker))
1242 return false;
1243
1244 for (int i = 0; i < toolBars.size(); ++i) {
1245 QToolBar *w = toolBars.at(i);
1246 QList<int> path = toolBarAreaLayout.indexOf(toolBar: w);
1247 if (path.isEmpty()) {
1248 QList<int> oldPath = oldState.toolBarAreaLayout.indexOf(toolBar: w);
1249 if (oldPath.isEmpty()) {
1250 continue;
1251 }
1252 toolBarAreaLayout.docks[oldPath.at(i: 0)].insertToolBar(before: nullptr, toolBar: w);
1253 }
1254 }
1255 }
1256 break;
1257#endif // QT_CONFIG(toolbar)
1258 default:
1259 return false;
1260 }// switch
1261 } //while
1262
1263
1264 return true;
1265}
1266
1267/******************************************************************************
1268** QMainWindowLayoutState - toolbars
1269*/
1270
1271#if QT_CONFIG(toolbar)
1272
1273static inline void validateToolBarArea(Qt::ToolBarArea &area)
1274{
1275 switch (area) {
1276 case Qt::LeftToolBarArea:
1277 case Qt::RightToolBarArea:
1278 case Qt::TopToolBarArea:
1279 case Qt::BottomToolBarArea:
1280 break;
1281 default:
1282 area = Qt::TopToolBarArea;
1283 }
1284}
1285
1286static QInternal::DockPosition toDockPos(Qt::ToolBarArea area)
1287{
1288 switch (area) {
1289 case Qt::LeftToolBarArea: return QInternal::LeftDock;
1290 case Qt::RightToolBarArea: return QInternal::RightDock;
1291 case Qt::TopToolBarArea: return QInternal::TopDock;
1292 case Qt::BottomToolBarArea: return QInternal::BottomDock;
1293 default:
1294 break;
1295 }
1296
1297 return QInternal::DockCount;
1298}
1299
1300static Qt::ToolBarArea toToolBarArea(QInternal::DockPosition pos)
1301{
1302 switch (pos) {
1303 case QInternal::LeftDock: return Qt::LeftToolBarArea;
1304 case QInternal::RightDock: return Qt::RightToolBarArea;
1305 case QInternal::TopDock: return Qt::TopToolBarArea;
1306 case QInternal::BottomDock: return Qt::BottomToolBarArea;
1307 default: break;
1308 }
1309 return Qt::NoToolBarArea;
1310}
1311
1312static inline Qt::ToolBarArea toToolBarArea(int pos)
1313{
1314 return toToolBarArea(pos: static_cast<QInternal::DockPosition>(pos));
1315}
1316
1317void QMainWindowLayout::addToolBarBreak(Qt::ToolBarArea area)
1318{
1319 validateToolBarArea(area);
1320
1321 layoutState.toolBarAreaLayout.addToolBarBreak(pos: toDockPos(area));
1322 if (savedState.isValid())
1323 savedState.toolBarAreaLayout.addToolBarBreak(pos: toDockPos(area));
1324
1325 invalidate();
1326}
1327
1328void QMainWindowLayout::insertToolBarBreak(QToolBar *before)
1329{
1330 layoutState.toolBarAreaLayout.insertToolBarBreak(before);
1331 if (savedState.isValid())
1332 savedState.toolBarAreaLayout.insertToolBarBreak(before);
1333 invalidate();
1334}
1335
1336void QMainWindowLayout::removeToolBarBreak(QToolBar *before)
1337{
1338 layoutState.toolBarAreaLayout.removeToolBarBreak(before);
1339 if (savedState.isValid())
1340 savedState.toolBarAreaLayout.removeToolBarBreak(before);
1341 invalidate();
1342}
1343
1344void QMainWindowLayout::moveToolBar(QToolBar *toolbar, int pos)
1345{
1346 layoutState.toolBarAreaLayout.moveToolBar(toolbar, pos);
1347 if (savedState.isValid())
1348 savedState.toolBarAreaLayout.moveToolBar(toolbar, pos);
1349 invalidate();
1350}
1351
1352/* Removes the toolbar from the mainwindow so that it can be added again. Does not
1353 explicitly hide the toolbar. */
1354void QMainWindowLayout::removeToolBar(QToolBar *toolbar)
1355{
1356 if (toolbar) {
1357 QObject::disconnect(sender: parentWidget(), SIGNAL(iconSizeChanged(QSize)),
1358 receiver: toolbar, SLOT(_q_updateIconSize(QSize)));
1359 QObject::disconnect(sender: parentWidget(), SIGNAL(toolButtonStyleChanged(Qt::ToolButtonStyle)),
1360 receiver: toolbar, SLOT(_q_updateToolButtonStyle(Qt::ToolButtonStyle)));
1361
1362 removeWidget(w: toolbar);
1363 }
1364}
1365
1366/*!
1367 Adds \a toolbar to \a area, continuing the current line.
1368*/
1369void QMainWindowLayout::addToolBar(Qt::ToolBarArea area,
1370 QToolBar *toolbar,
1371 bool)
1372{
1373 validateToolBarArea(area);
1374 // let's add the toolbar to the layout
1375 addChildWidget(w: toolbar);
1376 QLayoutItem *item = layoutState.toolBarAreaLayout.addToolBar(pos: toDockPos(area), toolBar: toolbar);
1377 if (savedState.isValid() && item) {
1378 // copy the toolbar also in the saved state
1379 savedState.toolBarAreaLayout.insertItem(pos: toDockPos(area), item);
1380 }
1381 invalidate();
1382
1383 // this ensures that the toolbar has the right window flags (not floating any more)
1384 toolbar->d_func()->updateWindowFlags(floating: false /*floating*/);
1385}
1386
1387/*!
1388 Adds \a toolbar before \a before
1389*/
1390void QMainWindowLayout::insertToolBar(QToolBar *before, QToolBar *toolbar)
1391{
1392 addChildWidget(w: toolbar);
1393 QLayoutItem *item = layoutState.toolBarAreaLayout.insertToolBar(before, toolBar: toolbar);
1394 if (savedState.isValid() && item) {
1395 // copy the toolbar also in the saved state
1396 savedState.toolBarAreaLayout.insertItem(before, item);
1397 }
1398 if (!currentGapPos.isEmpty() && currentGapPos.constFirst() == 0) {
1399 currentGapPos = layoutState.toolBarAreaLayout.currentGapIndex();
1400 if (!currentGapPos.isEmpty()) {
1401 currentGapPos.prepend(t: 0);
1402 currentGapRect = layoutState.itemRect(path: currentGapPos);
1403 }
1404 }
1405 invalidate();
1406}
1407
1408Qt::ToolBarArea QMainWindowLayout::toolBarArea(const QToolBar *toolbar) const
1409{
1410 QInternal::DockPosition pos = layoutState.toolBarAreaLayout.findToolBar(toolBar: toolbar);
1411 switch (pos) {
1412 case QInternal::LeftDock: return Qt::LeftToolBarArea;
1413 case QInternal::RightDock: return Qt::RightToolBarArea;
1414 case QInternal::TopDock: return Qt::TopToolBarArea;
1415 case QInternal::BottomDock: return Qt::BottomToolBarArea;
1416 default: break;
1417 }
1418 return Qt::NoToolBarArea;
1419}
1420
1421bool QMainWindowLayout::toolBarBreak(QToolBar *toolBar) const
1422{
1423 return layoutState.toolBarAreaLayout.toolBarBreak(toolBar);
1424}
1425
1426void QMainWindowLayout::getStyleOptionInfo(QStyleOptionToolBar *option, QToolBar *toolBar) const
1427{
1428 option->toolBarArea = toolBarArea(toolbar: toolBar);
1429 layoutState.toolBarAreaLayout.getStyleOptionInfo(option, toolBar);
1430}
1431
1432void QMainWindowLayout::toggleToolBarsVisible()
1433{
1434 layoutState.toolBarAreaLayout.visible = !layoutState.toolBarAreaLayout.visible;
1435 if (!layoutState.mainWindow->isMaximized()) {
1436 QPoint topLeft = parentWidget()->geometry().topLeft();
1437 QRect r = parentWidget()->geometry();
1438 r = layoutState.toolBarAreaLayout.rectHint(r);
1439 r.moveTo(p: topLeft);
1440 parentWidget()->setGeometry(r);
1441 } else {
1442 update();
1443 }
1444}
1445
1446#endif // QT_CONFIG(toolbar)
1447
1448/******************************************************************************
1449** QMainWindowLayoutState - dock areas
1450*/
1451
1452#if QT_CONFIG(dockwidget)
1453
1454static QInternal::DockPosition toDockPos(Qt::DockWidgetArea area)
1455{
1456 switch (area) {
1457 case Qt::LeftDockWidgetArea: return QInternal::LeftDock;
1458 case Qt::RightDockWidgetArea: return QInternal::RightDock;
1459 case Qt::TopDockWidgetArea: return QInternal::TopDock;
1460 case Qt::BottomDockWidgetArea: return QInternal::BottomDock;
1461 default:
1462 break;
1463 }
1464
1465 return QInternal::DockCount;
1466}
1467
1468static Qt::DockWidgetArea toDockWidgetArea(QInternal::DockPosition pos)
1469{
1470 switch (pos) {
1471 case QInternal::LeftDock : return Qt::LeftDockWidgetArea;
1472 case QInternal::RightDock : return Qt::RightDockWidgetArea;
1473 case QInternal::TopDock : return Qt::TopDockWidgetArea;
1474 case QInternal::BottomDock : return Qt::BottomDockWidgetArea;
1475 default:
1476 break;
1477 }
1478
1479 return Qt::NoDockWidgetArea;
1480}
1481
1482inline static Qt::DockWidgetArea toDockWidgetArea(int pos)
1483{
1484 return toDockWidgetArea(pos: static_cast<QInternal::DockPosition>(pos));
1485}
1486
1487void QMainWindowLayout::setCorner(Qt::Corner corner, Qt::DockWidgetArea area)
1488{
1489 if (layoutState.dockAreaLayout.corners[corner] == area)
1490 return;
1491 layoutState.dockAreaLayout.corners[corner] = area;
1492 if (savedState.isValid())
1493 savedState.dockAreaLayout.corners[corner] = area;
1494 invalidate();
1495}
1496
1497Qt::DockWidgetArea QMainWindowLayout::corner(Qt::Corner corner) const
1498{
1499 return layoutState.dockAreaLayout.corners[corner];
1500}
1501
1502void QMainWindowLayout::addDockWidget(Qt::DockWidgetArea area,
1503 QDockWidget *dockwidget,
1504 Qt::Orientation orientation)
1505{
1506 addChildWidget(w: dockwidget);
1507
1508 // If we are currently moving a separator, then we need to abort the move, since each
1509 // time we move the mouse layoutState is replaced by savedState modified by the move.
1510 if (!movingSeparator.isEmpty())
1511 endSeparatorMove(pos: movingSeparatorPos);
1512
1513 layoutState.dockAreaLayout.addDockWidget(pos: toDockPos(area), dockWidget: dockwidget, orientation);
1514 emit dockwidget->dockLocationChanged(area);
1515 invalidate();
1516}
1517
1518bool QMainWindowLayout::restoreDockWidget(QDockWidget *dockwidget)
1519{
1520 addChildWidget(w: dockwidget);
1521 if (!layoutState.dockAreaLayout.restoreDockWidget(dockWidget: dockwidget))
1522 return false;
1523 emit dockwidget->dockLocationChanged(area: dockWidgetArea(widget: dockwidget));
1524 invalidate();
1525 return true;
1526}
1527
1528#if QT_CONFIG(tabbar)
1529void QMainWindowLayout::tabifyDockWidget(QDockWidget *first, QDockWidget *second)
1530{
1531 addChildWidget(w: second);
1532 layoutState.dockAreaLayout.tabifyDockWidget(first, second);
1533 emit second->dockLocationChanged(area: dockWidgetArea(widget: first));
1534 invalidate();
1535}
1536
1537bool QMainWindowLayout::documentMode() const
1538{
1539 return _documentMode;
1540}
1541
1542void QMainWindowLayout::setDocumentMode(bool enabled)
1543{
1544 if (_documentMode == enabled)
1545 return;
1546
1547 _documentMode = enabled;
1548
1549 // Update the document mode for all tab bars
1550 for (QTabBar *bar : qAsConst(t&: usedTabBars))
1551 bar->setDocumentMode(_documentMode);
1552 for (QTabBar *bar : qAsConst(t&: unusedTabBars))
1553 bar->setDocumentMode(_documentMode);
1554}
1555
1556void QMainWindowLayout::setVerticalTabsEnabled(bool enabled)
1557{
1558 if (verticalTabsEnabled == enabled)
1559 return;
1560
1561 verticalTabsEnabled = enabled;
1562
1563 updateTabBarShapes();
1564}
1565
1566#if QT_CONFIG(tabwidget)
1567QTabWidget::TabShape QMainWindowLayout::tabShape() const
1568{
1569 return _tabShape;
1570}
1571
1572void QMainWindowLayout::setTabShape(QTabWidget::TabShape tabShape)
1573{
1574 if (_tabShape == tabShape)
1575 return;
1576
1577 _tabShape = tabShape;
1578
1579 updateTabBarShapes();
1580}
1581
1582QTabWidget::TabPosition QMainWindowLayout::tabPosition(Qt::DockWidgetArea area) const
1583{
1584 const auto dockPos = toDockPos(area);
1585 if (dockPos < QInternal::DockCount)
1586 return tabPositions[dockPos];
1587 qWarning(msg: "QMainWindowLayout::tabPosition called with out-of-bounds value '%d'", int(area));
1588 return QTabWidget::North;
1589}
1590
1591void QMainWindowLayout::setTabPosition(Qt::DockWidgetAreas areas, QTabWidget::TabPosition tabPosition)
1592{
1593 const Qt::DockWidgetArea dockWidgetAreas[] = {
1594 Qt::TopDockWidgetArea,
1595 Qt::LeftDockWidgetArea,
1596 Qt::BottomDockWidgetArea,
1597 Qt::RightDockWidgetArea
1598 };
1599 const QInternal::DockPosition dockPositions[] = {
1600 QInternal::TopDock,
1601 QInternal::LeftDock,
1602 QInternal::BottomDock,
1603 QInternal::RightDock
1604 };
1605
1606 for (int i = 0; i < QInternal::DockCount; ++i)
1607 if (areas & dockWidgetAreas[i])
1608 tabPositions[dockPositions[i]] = tabPosition;
1609
1610 updateTabBarShapes();
1611}
1612
1613static inline QTabBar::Shape tabBarShapeFrom(QTabWidget::TabShape shape, QTabWidget::TabPosition position)
1614{
1615 const bool rounded = (shape == QTabWidget::Rounded);
1616 if (position == QTabWidget::North)
1617 return rounded ? QTabBar::RoundedNorth : QTabBar::TriangularNorth;
1618 if (position == QTabWidget::South)
1619 return rounded ? QTabBar::RoundedSouth : QTabBar::TriangularSouth;
1620 if (position == QTabWidget::East)
1621 return rounded ? QTabBar::RoundedEast : QTabBar::TriangularEast;
1622 if (position == QTabWidget::West)
1623 return rounded ? QTabBar::RoundedWest : QTabBar::TriangularWest;
1624 return QTabBar::RoundedNorth;
1625}
1626#endif // QT_CONFIG(tabwidget)
1627
1628void QMainWindowLayout::updateTabBarShapes()
1629{
1630#if QT_CONFIG(tabwidget)
1631 const QTabWidget::TabPosition vertical[] = {
1632 QTabWidget::West,
1633 QTabWidget::East,
1634 QTabWidget::North,
1635 QTabWidget::South
1636 };
1637#else
1638 const QTabBar::Shape vertical[] = {
1639 QTabBar::RoundedWest,
1640 QTabBar::RoundedEast,
1641 QTabBar::RoundedNorth,
1642 QTabBar::RoundedSouth
1643 };
1644#endif
1645
1646 QDockAreaLayout &layout = layoutState.dockAreaLayout;
1647
1648 for (int i = 0; i < QInternal::DockCount; ++i) {
1649#if QT_CONFIG(tabwidget)
1650 QTabWidget::TabPosition pos = verticalTabsEnabled ? vertical[i] : tabPositions[i];
1651 QTabBar::Shape shape = tabBarShapeFrom(shape: _tabShape, position: pos);
1652#else
1653 QTabBar::Shape shape = verticalTabsEnabled ? vertical[i] : QTabBar::RoundedSouth;
1654#endif
1655 layout.docks[i].setTabBarShape(shape);
1656 }
1657}
1658#endif // QT_CONFIG(tabbar)
1659
1660void QMainWindowLayout::splitDockWidget(QDockWidget *after,
1661 QDockWidget *dockwidget,
1662 Qt::Orientation orientation)
1663{
1664 addChildWidget(w: dockwidget);
1665 layoutState.dockAreaLayout.splitDockWidget(after, dockWidget: dockwidget, orientation);
1666 emit dockwidget->dockLocationChanged(area: dockWidgetArea(widget: after));
1667 invalidate();
1668}
1669
1670Qt::DockWidgetArea QMainWindowLayout::dockWidgetArea(QWidget *widget) const
1671{
1672 const QList<int> pathToWidget = layoutState.dockAreaLayout.indexOf(dockWidget: widget);
1673 if (pathToWidget.isEmpty())
1674 return Qt::NoDockWidgetArea;
1675 return toDockWidgetArea(pos: pathToWidget.first());
1676}
1677
1678void QMainWindowLayout::keepSize(QDockWidget *w)
1679{
1680 layoutState.dockAreaLayout.keepSize(w);
1681}
1682
1683#if QT_CONFIG(tabbar)
1684
1685// Handle custom tooltip, and allow to drag tabs away.
1686class QMainWindowTabBar : public QTabBar
1687{
1688 QMainWindow *mainWindow;
1689 QPointer<QDockWidget> draggingDock; // Currently dragging (detached) dock widget
1690public:
1691 QMainWindowTabBar(QMainWindow *parent);
1692protected:
1693 bool event(QEvent *e) override;
1694 void mouseReleaseEvent(QMouseEvent*) override;
1695 void mouseMoveEvent(QMouseEvent*) override;
1696
1697};
1698
1699QMainWindowTabBar::QMainWindowTabBar(QMainWindow *parent)
1700 : QTabBar(parent), mainWindow(parent)
1701{
1702 setExpanding(false);
1703}
1704
1705void QMainWindowTabBar::mouseMoveEvent(QMouseEvent *e)
1706{
1707 // The QTabBar handles the moving (reordering) of tabs.
1708 // When QTabBarPrivate::dragInProgress is true, and that the mouse is outside of a region
1709 // around the QTabBar, we will consider the user wants to drag that QDockWidget away from this
1710 // tab area.
1711
1712 QTabBarPrivate *d = static_cast<QTabBarPrivate*>(d_ptr.data());
1713 if (!draggingDock && (mainWindow->dockOptions() & QMainWindow::GroupedDragging)) {
1714 int offset = QApplication::startDragDistance() + 1;
1715 offset *= 3;
1716 QRect r = rect().adjusted(xp1: -offset, yp1: -offset, xp2: offset, yp2: offset);
1717 if (d->dragInProgress && !r.contains(p: e->pos()) && d->validIndex(index: d->pressedIndex)) {
1718 QMainWindowLayout* mlayout = qt_mainwindow_layout(window: mainWindow);
1719 QDockAreaLayoutInfo *info = mlayout->dockInfo(w: this);
1720 Q_ASSERT(info);
1721 int idx = info->tabIndexToListIndex(d->pressedIndex);
1722 const QDockAreaLayoutItem &item = info->item_list.at(i: idx);
1723 if (item.widgetItem
1724 && (draggingDock = qobject_cast<QDockWidget *>(object: item.widgetItem->widget()))) {
1725 // We should drag this QDockWidget away by unpluging it.
1726 // First cancel the QTabBar's internal move
1727 d->moveTabFinished(index: d->pressedIndex);
1728 d->pressedIndex = -1;
1729 if (d->movingTab)
1730 d->movingTab->setVisible(false);
1731 d->dragStartPosition = QPoint();
1732
1733 // Then starts the drag using QDockWidgetPrivate's API
1734 QDockWidgetPrivate *dockPriv = static_cast<QDockWidgetPrivate *>(QObjectPrivate::get(o: draggingDock));
1735 QDockWidgetLayout *dwlayout = static_cast<QDockWidgetLayout *>(draggingDock->layout());
1736 dockPriv->initDrag(pos: dwlayout->titleArea().center(), nca: true);
1737 dockPriv->startDrag(group: false);
1738 if (dockPriv->state)
1739 dockPriv->state->ctrlDrag = e->modifiers() & Qt::ControlModifier;
1740 }
1741 }
1742 }
1743
1744 if (draggingDock) {
1745 QDockWidgetPrivate *dockPriv = static_cast<QDockWidgetPrivate *>(QObjectPrivate::get(o: draggingDock));
1746 if (dockPriv->state && dockPriv->state->dragging) {
1747 QPoint pos = e->globalPos() - dockPriv->state->pressPos;
1748 draggingDock->move(pos);
1749 // move will call QMainWindowLayout::hover
1750 }
1751 }
1752 QTabBar::mouseMoveEvent(e);
1753}
1754
1755void QMainWindowTabBar::mouseReleaseEvent(QMouseEvent *e)
1756{
1757 if (draggingDock && e->button() == Qt::LeftButton) {
1758 QDockWidgetPrivate *dockPriv = static_cast<QDockWidgetPrivate *>(QObjectPrivate::get(o: draggingDock));
1759 if (dockPriv->state && dockPriv->state->dragging) {
1760 dockPriv->endDrag();
1761 }
1762 draggingDock = nullptr;
1763 }
1764 QTabBar::mouseReleaseEvent(e);
1765}
1766
1767bool QMainWindowTabBar::event(QEvent *e)
1768{
1769 // show the tooltip if tab is too small to fit label
1770
1771 if (e->type() != QEvent::ToolTip)
1772 return QTabBar::event(e);
1773 QSize size = this->size();
1774 QSize hint = sizeHint();
1775 if (shape() == QTabBar::RoundedWest || shape() == QTabBar::RoundedEast) {
1776 size = size.transposed();
1777 hint = hint.transposed();
1778 }
1779 if (size.width() < hint.width())
1780 return QTabBar::event(e);
1781 e->accept();
1782 return true;
1783}
1784
1785QTabBar *QMainWindowLayout::getTabBar()
1786{
1787 if (!usedTabBars.isEmpty()) {
1788 /*
1789 If dock widgets have been removed and added while the main window was
1790 hidden, then the layout hasn't been activated yet, and tab bars from empty
1791 docking areas haven't been put in the cache yet.
1792 */
1793 activate();
1794 }
1795
1796 QTabBar *result = nullptr;
1797 if (!unusedTabBars.isEmpty()) {
1798 result = unusedTabBars.takeLast();
1799 } else {
1800 result = new QMainWindowTabBar(static_cast<QMainWindow *>(parentWidget()));
1801 result->setDrawBase(true);
1802 result->setElideMode(Qt::ElideRight);
1803 result->setDocumentMode(_documentMode);
1804 result->setMovable(true);
1805 connect(sender: result, SIGNAL(currentChanged(int)), receiver: this, SLOT(tabChanged()));
1806 connect(sender: result, signal: &QTabBar::tabMoved, receiver: this, slot: &QMainWindowLayout::tabMoved);
1807 }
1808
1809 usedTabBars.insert(value: result);
1810 return result;
1811}
1812
1813// Allocates a new separator widget if needed
1814QWidget *QMainWindowLayout::getSeparatorWidget()
1815{
1816 QWidget *result = nullptr;
1817 if (!unusedSeparatorWidgets.isEmpty()) {
1818 result = unusedSeparatorWidgets.takeLast();
1819 } else {
1820 result = new QWidget(parentWidget());
1821 result->setAttribute(Qt::WA_MouseNoMask, on: true);
1822 result->setAutoFillBackground(false);
1823 result->setObjectName(QLatin1String("qt_qmainwindow_extended_splitter"));
1824 }
1825 usedSeparatorWidgets.insert(value: result);
1826 return result;
1827}
1828
1829/*! \internal
1830 Returns a pointer QDockAreaLayoutInfo which contains this \a widget directly
1831 (in its internal list)
1832 */
1833QDockAreaLayoutInfo *QMainWindowLayout::dockInfo(QWidget *widget)
1834{
1835 QDockAreaLayoutInfo *info = layoutState.dockAreaLayout.info(widget);
1836 if (info)
1837 return info;
1838 const auto groups =
1839 parent()->findChildren<QDockWidgetGroupWindow*>(aName: QString(), options: Qt::FindDirectChildrenOnly);
1840 for (QDockWidgetGroupWindow *dwgw : groups) {
1841 info = dwgw->layoutInfo()->info(widget);
1842 if (info)
1843 return info;
1844 }
1845 return nullptr;
1846}
1847
1848void QMainWindowLayout::tabChanged()
1849{
1850 QTabBar *tb = qobject_cast<QTabBar*>(object: sender());
1851 if (tb == nullptr)
1852 return;
1853 QDockAreaLayoutInfo *info = dockInfo(widget: tb);
1854 if (info == nullptr)
1855 return;
1856
1857 QDockWidget *activated = info->apply(animate: false);
1858
1859 if (activated)
1860 emit static_cast<QMainWindow *>(parentWidget())->tabifiedDockWidgetActivated(dockWidget: activated);
1861
1862 if (auto dwgw = qobject_cast<QDockWidgetGroupWindow*>(object: tb->parentWidget()))
1863 dwgw->adjustFlags();
1864
1865 if (QWidget *w = centralWidget())
1866 w->raise();
1867}
1868
1869void QMainWindowLayout::tabMoved(int from, int to)
1870{
1871 QTabBar *tb = qobject_cast<QTabBar*>(object: sender());
1872 Q_ASSERT(tb);
1873 QDockAreaLayoutInfo *info = dockInfo(widget: tb);
1874 Q_ASSERT(info);
1875
1876 info->moveTab(from, to);
1877}
1878
1879void QMainWindowLayout::raise(QDockWidget *widget)
1880{
1881 QDockAreaLayoutInfo *info = dockInfo(widget);
1882 if (info == nullptr)
1883 return;
1884 if (!info->tabbed)
1885 return;
1886 info->setCurrentTab(widget);
1887}
1888#endif // QT_CONFIG(tabbar)
1889
1890#endif // QT_CONFIG(dockwidget)
1891
1892
1893/******************************************************************************
1894** QMainWindowLayoutState - layout interface
1895*/
1896
1897int QMainWindowLayout::count() const
1898{
1899 qWarning(msg: "QMainWindowLayout::count: ?");
1900 return 0; //#################################################
1901}
1902
1903QLayoutItem *QMainWindowLayout::itemAt(int index) const
1904{
1905 int x = 0;
1906
1907 if (QLayoutItem *ret = layoutState.itemAt(index, x: &x))
1908 return ret;
1909
1910 if (statusbar && x++ == index)
1911 return statusbar;
1912
1913 return nullptr;
1914}
1915
1916QLayoutItem *QMainWindowLayout::takeAt(int index)
1917{
1918 int x = 0;
1919
1920 if (QLayoutItem *ret = layoutState.takeAt(index, x: &x)) {
1921 // the widget might in fact have been destroyed by now
1922 if (QWidget *w = ret->widget()) {
1923 widgetAnimator.abort(widget: w);
1924 if (w == pluggingWidget)
1925 pluggingWidget = nullptr;
1926 }
1927
1928 if (savedState.isValid() ) {
1929 //we need to remove the item also from the saved state to prevent crash
1930 savedState.remove(item: ret);
1931 //Also, the item may be contained several times as a gap item.
1932 layoutState.remove(item: ret);
1933 }
1934
1935#if QT_CONFIG(toolbar)
1936 if (!currentGapPos.isEmpty() && currentGapPos.constFirst() == 0) {
1937 currentGapPos = layoutState.toolBarAreaLayout.currentGapIndex();
1938 if (!currentGapPos.isEmpty()) {
1939 currentGapPos.prepend(t: 0);
1940 currentGapRect = layoutState.itemRect(path: currentGapPos);
1941 }
1942 }
1943#endif
1944
1945 return ret;
1946 }
1947
1948 if (statusbar && x++ == index) {
1949 QLayoutItem *ret = statusbar;
1950 statusbar = nullptr;
1951 return ret;
1952 }
1953
1954 return nullptr;
1955}
1956
1957void QMainWindowLayout::setGeometry(const QRect &_r)
1958{
1959 if (savedState.isValid())
1960 return;
1961
1962 QRect r = _r;
1963
1964 QLayout::setGeometry(r);
1965
1966 if (statusbar) {
1967 QRect sbr(QPoint(r.left(), 0),
1968 QSize(r.width(), statusbar->heightForWidth(r.width()))
1969 .expandedTo(otherSize: statusbar->minimumSize()));
1970 sbr.moveBottom(pos: r.bottom());
1971 QRect vr = QStyle::visualRect(direction: parentWidget()->layoutDirection(), boundingRect: _r, logicalRect: sbr);
1972 statusbar->setGeometry(vr);
1973 r.setBottom(sbr.top() - 1);
1974 }
1975
1976 layoutState.rect = r;
1977 layoutState.fitLayout();
1978 applyState(newState&: layoutState, animate: false);
1979}
1980
1981void QMainWindowLayout::addItem(QLayoutItem *)
1982{ qWarning(msg: "QMainWindowLayout::addItem: Please use the public QMainWindow API instead"); }
1983
1984QSize QMainWindowLayout::sizeHint() const
1985{
1986 if (!szHint.isValid()) {
1987 szHint = layoutState.sizeHint();
1988 const QSize sbHint = statusbar ? statusbar->sizeHint() : QSize(0, 0);
1989 szHint = QSize(qMax(a: sbHint.width(), b: szHint.width()),
1990 sbHint.height() + szHint.height());
1991 }
1992 return szHint;
1993}
1994
1995QSize QMainWindowLayout::minimumSize() const
1996{
1997 if (!minSize.isValid()) {
1998 minSize = layoutState.minimumSize();
1999 const QSize sbMin = statusbar ? statusbar->minimumSize() : QSize(0, 0);
2000 minSize = QSize(qMax(a: sbMin.width(), b: minSize.width()),
2001 sbMin.height() + minSize.height());
2002 }
2003 return minSize;
2004}
2005
2006void QMainWindowLayout::invalidate()
2007{
2008 QLayout::invalidate();
2009 minSize = szHint = QSize();
2010}
2011
2012#if QT_CONFIG(dockwidget)
2013void QMainWindowLayout::setCurrentHoveredFloat(QDockWidgetGroupWindow *w)
2014{
2015 if (currentHoveredFloat != w) {
2016 if (currentHoveredFloat) {
2017 disconnect(sender: currentHoveredFloat.data(), signal: &QObject::destroyed,
2018 receiver: this, slot: &QMainWindowLayout::updateGapIndicator);
2019 disconnect(sender: currentHoveredFloat.data(), signal: &QDockWidgetGroupWindow::resized,
2020 receiver: this, slot: &QMainWindowLayout::updateGapIndicator);
2021 if (currentHoveredFloat)
2022 currentHoveredFloat->restore();
2023 } else if (w) {
2024 restore(keepSavedState: true);
2025 }
2026
2027 currentHoveredFloat = w;
2028
2029 if (w) {
2030 connect(sender: w, signal: &QObject::destroyed,
2031 receiver: this, slot: &QMainWindowLayout::updateGapIndicator, type: Qt::UniqueConnection);
2032 connect(sender: w, signal: &QDockWidgetGroupWindow::resized,
2033 receiver: this, slot: &QMainWindowLayout::updateGapIndicator, type: Qt::UniqueConnection);
2034 }
2035
2036 updateGapIndicator();
2037 }
2038}
2039#endif // QT_CONFIG(dockwidget)
2040
2041/******************************************************************************
2042** QMainWindowLayout - remaining stuff
2043*/
2044
2045static void fixToolBarOrientation(QLayoutItem *item, int dockPos)
2046{
2047#if QT_CONFIG(toolbar)
2048 QToolBar *toolBar = qobject_cast<QToolBar*>(object: item->widget());
2049 if (toolBar == nullptr)
2050 return;
2051
2052 QRect oldGeo = toolBar->geometry();
2053
2054 QInternal::DockPosition pos
2055 = static_cast<QInternal::DockPosition>(dockPos);
2056 Qt::Orientation o = pos == QInternal::TopDock || pos == QInternal::BottomDock
2057 ? Qt::Horizontal : Qt::Vertical;
2058 if (o != toolBar->orientation())
2059 toolBar->setOrientation(o);
2060
2061 QSize hint = toolBar->sizeHint().boundedTo(otherSize: toolBar->maximumSize())
2062 .expandedTo(otherSize: toolBar->minimumSize());
2063
2064 if (toolBar->size() != hint) {
2065 QRect newGeo(oldGeo.topLeft(), hint);
2066 if (toolBar->layoutDirection() == Qt::RightToLeft)
2067 newGeo.moveRight(pos: oldGeo.right());
2068 toolBar->setGeometry(newGeo);
2069 }
2070
2071#else
2072 Q_UNUSED(item);
2073 Q_UNUSED(dockPos);
2074#endif
2075}
2076
2077void QMainWindowLayout::revert(QLayoutItem *widgetItem)
2078{
2079 if (!savedState.isValid())
2080 return;
2081
2082 QWidget *widget = widgetItem->widget();
2083 layoutState = savedState;
2084 currentGapPos = layoutState.indexOf(widget);
2085 if (currentGapPos.isEmpty())
2086 return;
2087 fixToolBarOrientation(item: widgetItem, dockPos: currentGapPos.at(i: 1));
2088 layoutState.unplug(path: currentGapPos);
2089 layoutState.fitLayout();
2090 currentGapRect = layoutState.itemRect(path: currentGapPos);
2091
2092 plug(widgetItem);
2093}
2094
2095bool QMainWindowLayout::plug(QLayoutItem *widgetItem)
2096{
2097#if QT_CONFIG(dockwidget) && QT_CONFIG(tabwidget) && QT_CONFIG(tabbar)
2098 if (currentHoveredFloat) {
2099 QWidget *widget = widgetItem->widget();
2100 QList<int> previousPath = layoutState.indexOf(widget);
2101 if (!previousPath.isEmpty())
2102 layoutState.remove(path: previousPath);
2103 previousPath = currentHoveredFloat->layoutInfo()->indexOf(widget);
2104 // Let's remove the widget from any possible group window
2105 const auto groups =
2106 parent()->findChildren<QDockWidgetGroupWindow*>(aName: QString(), options: Qt::FindDirectChildrenOnly);
2107 for (QDockWidgetGroupWindow *dwgw : groups) {
2108 if (dwgw == currentHoveredFloat)
2109 continue;
2110 QList<int> path = dwgw->layoutInfo()->indexOf(widget);
2111 if (!path.isEmpty())
2112 dwgw->layoutInfo()->remove(path);
2113 }
2114 currentGapRect = QRect();
2115 currentHoveredFloat->apply();
2116 if (!previousPath.isEmpty())
2117 currentHoveredFloat->layoutInfo()->remove(path: previousPath);
2118 QRect globalRect = currentHoveredFloat->currentGapRect;
2119 globalRect.moveTopLeft(p: currentHoveredFloat->mapToGlobal(globalRect.topLeft()));
2120 pluggingWidget = widget;
2121 widgetAnimator.animate(widget, final_geometry: globalRect, animate: dockOptions & QMainWindow::AnimatedDocks);
2122 return true;
2123 }
2124#endif
2125
2126 if (!parentWidget()->isVisible() || parentWidget()->isMinimized() || currentGapPos.isEmpty())
2127 return false;
2128
2129 fixToolBarOrientation(item: widgetItem, dockPos: currentGapPos.at(i: 1));
2130
2131 QWidget *widget = widgetItem->widget();
2132
2133#if QT_CONFIG(dockwidget)
2134 // Let's remove the widget from any possible group window
2135 const auto groups =
2136 parent()->findChildren<QDockWidgetGroupWindow*>(aName: QString(), options: Qt::FindDirectChildrenOnly);
2137 for (QDockWidgetGroupWindow *dwgw : groups) {
2138 QList<int> path = dwgw->layoutInfo()->indexOf(widget);
2139 if (!path.isEmpty())
2140 dwgw->layoutInfo()->remove(path);
2141 }
2142#endif
2143
2144 QList<int> previousPath = layoutState.indexOf(widget);
2145
2146 const QLayoutItem *it = layoutState.plug(path: currentGapPos);
2147 if (!it)
2148 return false;
2149 Q_ASSERT(it == widgetItem);
2150 if (!previousPath.isEmpty())
2151 layoutState.remove(path: previousPath);
2152
2153 pluggingWidget = widget;
2154 QRect globalRect = currentGapRect;
2155 globalRect.moveTopLeft(p: parentWidget()->mapToGlobal(globalRect.topLeft()));
2156#if QT_CONFIG(dockwidget)
2157 if (qobject_cast<QDockWidget*>(object: widget) != 0) {
2158 QDockWidgetLayout *layout = qobject_cast<QDockWidgetLayout*>(object: widget->layout());
2159 if (layout->nativeWindowDeco()) {
2160 globalRect.adjust(dx1: 0, dy1: layout->titleHeight(), dx2: 0, dy2: 0);
2161 } else {
2162 int fw = widget->style()->pixelMetric(metric: QStyle::PM_DockWidgetFrameWidth, option: nullptr, widget);
2163 globalRect.adjust(dx1: -fw, dy1: -fw, dx2: fw, dy2: fw);
2164 }
2165 }
2166#endif
2167 widgetAnimator.animate(widget, final_geometry: globalRect, animate: dockOptions & QMainWindow::AnimatedDocks);
2168
2169 return true;
2170}
2171
2172void QMainWindowLayout::animationFinished(QWidget *widget)
2173{
2174 //this function is called from within the Widget Animator whenever an animation is finished
2175 //on a certain widget
2176#if QT_CONFIG(toolbar)
2177 if (QToolBar *tb = qobject_cast<QToolBar*>(object: widget)) {
2178 QToolBarLayout *tbl = qobject_cast<QToolBarLayout*>(object: tb->layout());
2179 if (tbl->animating) {
2180 tbl->animating = false;
2181 if (tbl->expanded)
2182 tbl->layoutActions(size: tb->size());
2183 tb->update();
2184 }
2185 }
2186#endif
2187
2188 if (widget == pluggingWidget) {
2189
2190#if QT_CONFIG(dockwidget)
2191#if QT_CONFIG(tabbar)
2192 if (QDockWidgetGroupWindow *dwgw = qobject_cast<QDockWidgetGroupWindow *>(object: widget)) {
2193 // When the animated widget was a QDockWidgetGroupWindow, it means each of the
2194 // embedded QDockWidget needs to be plugged back into the QMainWindow layout.
2195 savedState.clear();
2196 QDockAreaLayoutInfo *srcInfo = dwgw->layoutInfo();
2197 const QDockAreaLayoutInfo *srcTabInfo = dwgw->tabLayoutInfo();
2198 QDockAreaLayoutInfo *dstParentInfo;
2199 QList<int> dstPath;
2200
2201 if (currentHoveredFloat) {
2202 dstPath = currentHoveredFloat->layoutInfo()->indexOf(widget);
2203 Q_ASSERT(dstPath.size() >= 1);
2204 dstParentInfo = currentHoveredFloat->layoutInfo()->info(path: dstPath);
2205 } else {
2206 dstPath = layoutState.dockAreaLayout.indexOf(dockWidget: widget);
2207 Q_ASSERT(dstPath.size() >= 2);
2208 dstParentInfo = layoutState.dockAreaLayout.info(path: dstPath);
2209 }
2210 Q_ASSERT(dstParentInfo);
2211 int idx = dstPath.constLast();
2212 Q_ASSERT(dstParentInfo->item_list[idx].widgetItem->widget() == dwgw);
2213 if (dstParentInfo->tabbed && srcTabInfo) {
2214 // merge the two tab widgets
2215 delete dstParentInfo->item_list[idx].widgetItem;
2216 dstParentInfo->item_list.removeAt(i: idx);
2217 std::copy(srcTabInfo->item_list.cbegin(), srcTabInfo->item_list.cend(),
2218 std::inserter(x&: dstParentInfo->item_list,
2219 i: dstParentInfo->item_list.begin() + idx));
2220 quintptr currentId = srcTabInfo->currentTabId();
2221 *srcInfo = QDockAreaLayoutInfo();
2222 dstParentInfo->reparentWidgets(p: currentHoveredFloat ? currentHoveredFloat.data()
2223 : parentWidget());
2224 dstParentInfo->updateTabBar();
2225 dstParentInfo->setCurrentTabId(currentId);
2226 } else {
2227 QDockAreaLayoutItem &item = dstParentInfo->item_list[idx];
2228 Q_ASSERT(item.widgetItem->widget() == dwgw);
2229 delete item.widgetItem;
2230 item.widgetItem = nullptr;
2231 item.subinfo = new QDockAreaLayoutInfo(std::move(*srcInfo));
2232 *srcInfo = QDockAreaLayoutInfo();
2233 item.subinfo->reparentWidgets(p: currentHoveredFloat ? currentHoveredFloat.data()
2234 : parentWidget());
2235 item.subinfo->setTabBarShape(dstParentInfo->tabBarShape);
2236 }
2237 dwgw->destroyOrHideIfEmpty();
2238 }
2239#endif
2240
2241 if (QDockWidget *dw = qobject_cast<QDockWidget*>(object: widget)) {
2242 dw->setParent(currentHoveredFloat ? currentHoveredFloat.data() : parentWidget());
2243 dw->show();
2244 dw->d_func()->plug(rect: currentGapRect);
2245 }
2246#endif
2247#if QT_CONFIG(toolbar)
2248 if (QToolBar *tb = qobject_cast<QToolBar*>(object: widget))
2249 tb->d_func()->plug(r: currentGapRect);
2250#endif
2251
2252 savedState.clear();
2253 currentGapPos.clear();
2254 pluggingWidget = nullptr;
2255#if QT_CONFIG(dockwidget)
2256 setCurrentHoveredFloat(nullptr);
2257#endif
2258 //applying the state will make sure that the currentGap is updated correctly
2259 //and all the geometries (especially the one from the central widget) is correct
2260 layoutState.apply(animated: false);
2261
2262#if QT_CONFIG(dockwidget)
2263#if QT_CONFIG(tabbar)
2264 if (qobject_cast<QDockWidget*>(object: widget) != 0) {
2265 // info() might return null if the widget is destroyed while
2266 // animating but before the animationFinished signal is received.
2267 if (QDockAreaLayoutInfo *info = dockInfo(widget))
2268 info->setCurrentTab(widget);
2269 }
2270#endif
2271#endif
2272 }
2273
2274 if (!widgetAnimator.animating()) {
2275 //all animations are finished
2276#if QT_CONFIG(dockwidget)
2277 parentWidget()->update(layoutState.dockAreaLayout.separatorRegion());
2278#if QT_CONFIG(tabbar)
2279 const auto usedTabBarsCopy = usedTabBars; // list potentially modified by animations
2280 for (QTabBar *tab_bar : usedTabBarsCopy)
2281 tab_bar->show();
2282#endif // QT_CONFIG(tabbar)
2283#endif // QT_CONFIG(dockwidget)
2284 }
2285
2286 updateGapIndicator();
2287}
2288
2289void QMainWindowLayout::restore(bool keepSavedState)
2290{
2291 if (!savedState.isValid())
2292 return;
2293
2294 layoutState = savedState;
2295 applyState(newState&: layoutState);
2296 if (!keepSavedState)
2297 savedState.clear();
2298 currentGapPos.clear();
2299 pluggingWidget = nullptr;
2300 updateGapIndicator();
2301}
2302
2303QMainWindowLayout::QMainWindowLayout(QMainWindow *mainwindow, QLayout *parentLayout)
2304 : QLayout(parentLayout ? static_cast<QWidget *>(nullptr) : mainwindow)
2305 , layoutState(mainwindow)
2306 , savedState(mainwindow)
2307 , dockOptions(QMainWindow::AnimatedDocks | QMainWindow::AllowTabbedDocks)
2308 , statusbar(nullptr)
2309#if QT_CONFIG(dockwidget)
2310#if QT_CONFIG(tabbar)
2311 , _documentMode(false)
2312 , verticalTabsEnabled(false)
2313#if QT_CONFIG(tabwidget)
2314 , _tabShape(QTabWidget::Rounded)
2315#endif
2316#endif
2317#endif // QT_CONFIG(dockwidget)
2318 , widgetAnimator(this)
2319 , pluggingWidget(nullptr)
2320{
2321 if (parentLayout)
2322 setParent(parentLayout);
2323
2324#if QT_CONFIG(dockwidget)
2325#if QT_CONFIG(tabbar)
2326 sep = mainwindow->style()->pixelMetric(metric: QStyle::PM_DockWidgetSeparatorExtent, option: nullptr, widget: mainwindow);
2327#endif
2328
2329#if QT_CONFIG(tabwidget)
2330 for (int i = 0; i < QInternal::DockCount; ++i)
2331 tabPositions[i] = QTabWidget::South;
2332#endif
2333#endif // QT_CONFIG(dockwidget)
2334 pluggingWidget = nullptr;
2335
2336 setObjectName(mainwindow->objectName() + QLatin1String("_layout"));
2337}
2338
2339QMainWindowLayout::~QMainWindowLayout()
2340{
2341 layoutState.deleteAllLayoutItems();
2342 layoutState.deleteCentralWidgetItem();
2343
2344 delete statusbar;
2345}
2346
2347void QMainWindowLayout::setDockOptions(QMainWindow::DockOptions opts)
2348{
2349 if (opts == dockOptions)
2350 return;
2351
2352 dockOptions = opts;
2353
2354#if QT_CONFIG(dockwidget) && QT_CONFIG(tabbar)
2355 setVerticalTabsEnabled(opts & QMainWindow::VerticalTabs);
2356#endif
2357
2358 invalidate();
2359}
2360
2361#if QT_CONFIG(statusbar)
2362QStatusBar *QMainWindowLayout::statusBar() const
2363{ return statusbar ? qobject_cast<QStatusBar *>(object: statusbar->widget()) : 0; }
2364
2365void QMainWindowLayout::setStatusBar(QStatusBar *sb)
2366{
2367 if (sb)
2368 addChildWidget(w: sb);
2369 delete statusbar;
2370 statusbar = sb ? new QWidgetItemV2(sb) : nullptr;
2371 invalidate();
2372}
2373#endif // QT_CONFIG(statusbar)
2374
2375QWidget *QMainWindowLayout::centralWidget() const
2376{
2377 return layoutState.centralWidget();
2378}
2379
2380void QMainWindowLayout::setCentralWidget(QWidget *widget)
2381{
2382 if (widget != nullptr)
2383 addChildWidget(w: widget);
2384 layoutState.setCentralWidget(widget);
2385 if (savedState.isValid()) {
2386#if QT_CONFIG(dockwidget)
2387 savedState.dockAreaLayout.centralWidgetItem = layoutState.dockAreaLayout.centralWidgetItem;
2388 savedState.dockAreaLayout.fallbackToSizeHints = true;
2389#else
2390 savedState.centralWidgetItem = layoutState.centralWidgetItem;
2391#endif
2392 }
2393 invalidate();
2394}
2395
2396#if QT_CONFIG(dockwidget) && QT_CONFIG(tabwidget)
2397/*! \internal
2398 This helper function is called by QMainWindowLayout::unplug if QMainWindow::GroupedDragging is
2399 set and we are dragging the title bar of a non-floating QDockWidget.
2400 If one should unplug the whole group, do so and return true, otherwise return false.
2401 \a item is pointing to the QLayoutItem that holds the QDockWidget, but will be updated to the
2402 QLayoutItem that holds the new QDockWidgetGroupWindow if the group is unplugged.
2403*/
2404static bool unplugGroup(QMainWindowLayout *layout, QLayoutItem **item,
2405 QDockAreaLayoutItem &parentItem)
2406{
2407 if (!parentItem.subinfo || !parentItem.subinfo->tabbed)
2408 return false;
2409
2410 // The QDockWidget is part of a group of tab and we need to unplug them all.
2411
2412 QDockWidgetGroupWindow *floatingTabs = layout->createTabbedDockWindow();
2413 QDockAreaLayoutInfo *info = floatingTabs->layoutInfo();
2414 *info = std::move(*parentItem.subinfo);
2415 delete parentItem.subinfo;
2416 parentItem.subinfo = nullptr;
2417 floatingTabs->setGeometry(info->rect.translated(p: layout->parentWidget()->pos()));
2418 floatingTabs->show();
2419 floatingTabs->raise();
2420 *item = new QDockWidgetGroupWindowItem(floatingTabs);
2421 parentItem.widgetItem = *item;
2422 return true;
2423}
2424#endif
2425
2426/*! \internal
2427 Unplug \a widget (QDockWidget or QToolBar) from it's parent container.
2428
2429 If \a group is true we might actually unplug the group of tabs this
2430 widget is part if QMainWindow::GroupedDragging is set. When \a group
2431 is false, the widget itself is always unplugged alone
2432
2433 Returns the QLayoutItem of the dragged element.
2434 The layout item is kept in the layout but set as a gap item.
2435 */
2436QLayoutItem *QMainWindowLayout::unplug(QWidget *widget, bool group)
2437{
2438#if QT_CONFIG(dockwidget) && QT_CONFIG(tabwidget)
2439 auto *groupWindow = qobject_cast<const QDockWidgetGroupWindow *>(object: widget->parentWidget());
2440 if (!widget->isWindow() && groupWindow) {
2441 if (group && groupWindow->tabLayoutInfo()) {
2442 // We are just dragging a floating window as it, not need to do anything, we just have to
2443 // look up the corresponding QWidgetItem* if it exists
2444 if (QDockAreaLayoutInfo *info = dockInfo(widget: widget->parentWidget())) {
2445 QList<int> groupWindowPath = info->indexOf(widget: widget->parentWidget());
2446 return groupWindowPath.isEmpty() ? nullptr : info->item(path: groupWindowPath).widgetItem;
2447 }
2448 return nullptr;
2449 }
2450 QList<int> path = groupWindow->layoutInfo()->indexOf(widget);
2451 QLayoutItem *item = groupWindow->layoutInfo()->item(path).widgetItem;
2452 if (group && path.size() > 1
2453 && unplugGroup(layout: this, item: &item,
2454 parentItem&: groupWindow->layoutInfo()->item(path: path.mid(pos: 0, alength: path.size() - 1)))) {
2455 return item;
2456 } else {
2457 // We are unplugging a dock widget from a floating window.
2458 QDockWidget *dw = qobject_cast<QDockWidget *>(object: widget);
2459 Q_ASSERT(dw); // cannot be a QDockWidgetGroupWindow because it's not floating.
2460 dw->d_func()->unplug(rect: widget->geometry());
2461 groupWindow->layoutInfo()->fitItems();
2462 groupWindow->layoutInfo()->apply(animate: dockOptions & QMainWindow::AnimatedDocks);
2463 return item;
2464 }
2465 }
2466#endif
2467 QList<int> path = layoutState.indexOf(widget);
2468 if (path.isEmpty())
2469 return nullptr;
2470
2471 QLayoutItem *item = layoutState.item(path);
2472 if (widget->isWindow())
2473 return item;
2474
2475 QRect r = layoutState.itemRect(path);
2476 savedState = layoutState;
2477
2478#if QT_CONFIG(dockwidget)
2479 if (QDockWidget *dw = qobject_cast<QDockWidget*>(object: widget)) {
2480 Q_ASSERT(path.constFirst() == 1);
2481#if QT_CONFIG(tabwidget)
2482 if (group && (dockOptions & QMainWindow::GroupedDragging) && path.size() > 3
2483 && unplugGroup(layout: this, item: &item,
2484 parentItem&: layoutState.dockAreaLayout.item(path: path.mid(pos: 1, alength: path.size() - 2)))) {
2485 path.removeLast();
2486 savedState = layoutState;
2487 } else
2488#endif // QT_CONFIG(tabwidget)
2489 {
2490 dw->d_func()->unplug(rect: r);
2491 }
2492 }
2493#endif // QT_CONFIG(dockwidget)
2494#if QT_CONFIG(toolbar)
2495 if (QToolBar *tb = qobject_cast<QToolBar*>(object: widget)) {
2496 tb->d_func()->unplug(r);
2497 }
2498#endif
2499
2500#if !QT_CONFIG(dockwidget) || !QT_CONFIG(tabbar)
2501 Q_UNUSED(group);
2502#endif
2503
2504 layoutState.unplug(path ,other: &savedState);
2505 savedState.fitLayout();
2506 currentGapPos = path;
2507 currentGapRect = r;
2508 updateGapIndicator();
2509
2510 fixToolBarOrientation(item, dockPos: currentGapPos.at(i: 1));
2511
2512 return item;
2513}
2514
2515void QMainWindowLayout::updateGapIndicator()
2516{
2517#if QT_CONFIG(rubberband)
2518 if (!widgetAnimator.animating() && (!currentGapPos.isEmpty()
2519#if QT_CONFIG(dockwidget)
2520 || currentHoveredFloat
2521#endif
2522 )) {
2523 QWidget *expectedParent =
2524#if QT_CONFIG(dockwidget)
2525 currentHoveredFloat ? currentHoveredFloat.data() :
2526#endif
2527 parentWidget();
2528 if (!gapIndicator) {
2529 gapIndicator = new QRubberBand(QRubberBand::Rectangle, expectedParent);
2530 // For accessibility to identify this special widget.
2531 gapIndicator->setObjectName(QLatin1String("qt_rubberband"));
2532 } else if (gapIndicator->parent() != expectedParent) {
2533 gapIndicator->setParent(expectedParent);
2534 }
2535
2536#if QT_CONFIG(dockwidget)
2537 if (currentHoveredFloat)
2538 gapIndicator->setGeometry(currentHoveredFloat->currentGapRect);
2539 else
2540#endif
2541 gapIndicator->setGeometry(currentGapRect);
2542 gapIndicator->show();
2543 gapIndicator->raise();
2544 } else if (gapIndicator) {
2545 gapIndicator->hide();
2546 }
2547#endif // QT_CONFIG(rubberband)
2548}
2549
2550#if QT_CONFIG(dockwidget) && QT_CONFIG(tabwidget)
2551static QTabBar::Shape tabwidgetPositionToTabBarShape(QWidget *w)
2552{
2553 QTabBar::Shape result = QTabBar::RoundedSouth;
2554 if (qobject_cast<QDockWidget *>(object: w)) {
2555 switch (static_cast<QDockWidgetPrivate *>(qt_widget_private(widget: w))->tabPosition) {
2556 case QTabWidget::North:
2557 result = QTabBar::RoundedNorth;
2558 break;
2559 case QTabWidget::South:
2560 result = QTabBar::RoundedSouth;
2561 break;
2562 case QTabWidget::West:
2563 result = QTabBar::RoundedWest;
2564 break;
2565 case QTabWidget::East:
2566 result = QTabBar::RoundedEast;
2567 break;
2568 }
2569 }
2570 return result;
2571}
2572#endif // QT_CONFIG(dockwidget) && QT_CONFIG(tabwidget)
2573
2574void QMainWindowLayout::hover(QLayoutItem *widgetItem, const QPoint &mousePos)
2575{
2576 if (!parentWidget()->isVisible() || parentWidget()->isMinimized()
2577 || pluggingWidget != nullptr || widgetItem == nullptr)
2578 return;
2579
2580 QWidget *widget = widgetItem->widget();
2581
2582#if QT_CONFIG(dockwidget)
2583 if ((dockOptions & QMainWindow::GroupedDragging) && (qobject_cast<QDockWidget*>(object: widget)
2584 || qobject_cast<QDockWidgetGroupWindow *>(object: widget))) {
2585
2586 // Check if we are over another floating dock widget
2587 QVarLengthArray<QWidget *, 10> candidates;
2588 const auto siblings = parentWidget()->children();
2589 for (QObject *c : siblings) {
2590 QWidget *w = qobject_cast<QWidget*>(o: c);
2591 if (!w)
2592 continue;
2593 if (!qobject_cast<QDockWidget*>(object: w) && !qobject_cast<QDockWidgetGroupWindow *>(object: w))
2594 continue;
2595 if (w != widget && w->isTopLevel() && w->isVisible() && !w->isMinimized())
2596 candidates << w;
2597 if (QDockWidgetGroupWindow *group = qobject_cast<QDockWidgetGroupWindow *>(object: w)) {
2598 // Sometimes, there are floating QDockWidget that have a QDockWidgetGroupWindow as a parent.
2599 const auto groupChildren = group->children();
2600 for (QObject *c : groupChildren) {
2601 if (QDockWidget *dw = qobject_cast<QDockWidget*>(object: c)) {
2602 if (dw != widget && dw->isFloating() && dw->isVisible() && !dw->isMinimized())
2603 candidates << dw;
2604 }
2605 }
2606 }
2607 }
2608 for (QWidget *w : candidates) {
2609 const QScreen *screen1 = qt_widget_private(widget)->associatedScreen();
2610 const QScreen *screen2 = qt_widget_private(widget: w)->associatedScreen();
2611 if (screen1 && screen2 && screen1 != screen2)
2612 continue;
2613 if (!w->geometry().contains(p: mousePos))
2614 continue;
2615
2616#if QT_CONFIG(tabwidget)
2617 if (auto dropTo = qobject_cast<QDockWidget *>(object: w)) {
2618 // dropping to a normal widget, we mutate it in a QDockWidgetGroupWindow with two
2619 // tabs
2620 QDockWidgetGroupWindow *floatingTabs = createTabbedDockWindow(); // FIXME
2621 floatingTabs->setGeometry(dropTo->geometry());
2622 QDockAreaLayoutInfo *info = floatingTabs->layoutInfo();
2623 const QTabBar::Shape shape = tabwidgetPositionToTabBarShape(w: dropTo);
2624 *info = QDockAreaLayoutInfo(&layoutState.dockAreaLayout.sep, QInternal::LeftDock,
2625 Qt::Horizontal, shape,
2626 static_cast<QMainWindow *>(parentWidget()));
2627 info->tabbed = true;
2628 QLayout *parentLayout = dropTo->parentWidget()->layout();
2629 info->item_list.append(
2630 t: QDockAreaLayoutItem(parentLayout->takeAt(index: parentLayout->indexOf(dropTo))));
2631
2632 dropTo->setParent(floatingTabs);
2633 dropTo->show();
2634 dropTo->d_func()->plug(rect: QRect());
2635 w = floatingTabs;
2636 widget->raise(); // raise, as our newly created drop target is now on top
2637 }
2638#endif
2639 Q_ASSERT(qobject_cast<QDockWidgetGroupWindow *>(w));
2640 auto group = static_cast<QDockWidgetGroupWindow *>(w);
2641 if (group->hover(widgetItem, mousePos: group->mapFromGlobal(mousePos))) {
2642 setCurrentHoveredFloat(group);
2643 applyState(newState&: layoutState); // update the tabbars
2644 }
2645 return;
2646 }
2647 }
2648 setCurrentHoveredFloat(nullptr);
2649 layoutState.dockAreaLayout.fallbackToSizeHints = false;
2650#endif // QT_CONFIG(dockwidget)
2651
2652 QPoint pos = parentWidget()->mapFromGlobal(mousePos);
2653
2654 if (!savedState.isValid())
2655 savedState = layoutState;
2656
2657 QList<int> path = savedState.gapIndex(widget, pos);
2658
2659 if (!path.isEmpty()) {
2660 bool allowed = false;
2661
2662#if QT_CONFIG(dockwidget)
2663 if (QDockWidget *dw = qobject_cast<QDockWidget*>(object: widget))
2664 allowed = dw->isAreaAllowed(area: toDockWidgetArea(pos: path.at(i: 1)));
2665
2666 // Read permissions from a DockWidgetGroupWindow depending on its DockWidget children
2667 if (QDockWidgetGroupWindow* dwgw = qobject_cast<QDockWidgetGroupWindow *>(object: widget)) {
2668 const QList<QDockWidget*> children = dwgw->findChildren<QDockWidget*>(aName: QString(), options: Qt::FindDirectChildrenOnly);
2669
2670 if (children.count() == 1) {
2671 // Group window has a single child => read its permissions
2672 allowed = children.at(i: 0)->isAreaAllowed(area: toDockWidgetArea(pos: path.at(i: 1)));
2673 } else {
2674 // Group window has more than one or no children => dock it anywhere
2675 allowed = true;
2676 }
2677 }
2678#endif
2679#if QT_CONFIG(toolbar)
2680 if (QToolBar *tb = qobject_cast<QToolBar*>(object: widget))
2681 allowed = tb->isAreaAllowed(area: toToolBarArea(pos: path.at(i: 1)));
2682#endif
2683
2684 if (!allowed)
2685 path.clear();
2686 }
2687
2688 if (path == currentGapPos)
2689 return; // the gap is already there
2690
2691 currentGapPos = path;
2692 if (path.isEmpty()) {
2693 fixToolBarOrientation(item: widgetItem, dockPos: 2); // 2 = top dock, ie. horizontal
2694 restore(keepSavedState: true);
2695 return;
2696 }
2697
2698 fixToolBarOrientation(item: widgetItem, dockPos: currentGapPos.at(i: 1));
2699
2700 QMainWindowLayoutState newState = savedState;
2701
2702 if (!newState.insertGap(path, item: widgetItem)) {
2703 restore(keepSavedState: true); // not enough space
2704 return;
2705 }
2706
2707 QSize min = newState.minimumSize();
2708 QSize size = newState.rect.size();
2709
2710 if (min.width() > size.width() || min.height() > size.height()) {
2711 restore(keepSavedState: true);
2712 return;
2713 }
2714
2715 newState.fitLayout();
2716
2717 currentGapRect = newState.gapRect(path: currentGapPos);
2718
2719#if QT_CONFIG(dockwidget)
2720 parentWidget()->update(layoutState.dockAreaLayout.separatorRegion());
2721#endif
2722 layoutState = std::move(newState);
2723 applyState(newState&: layoutState);
2724
2725 updateGapIndicator();
2726}
2727
2728#if QT_CONFIG(dockwidget) && QT_CONFIG(tabwidget)
2729QDockWidgetGroupWindow *QMainWindowLayout::createTabbedDockWindow()
2730{
2731 QDockWidgetGroupWindow* f = new QDockWidgetGroupWindow(parentWidget(), Qt::Tool);
2732 new QDockWidgetGroupLayout(f);
2733 return f;
2734}
2735#endif
2736
2737void QMainWindowLayout::applyState(QMainWindowLayoutState &newState, bool animate)
2738{
2739#if QT_CONFIG(dockwidget) && QT_CONFIG(tabwidget)
2740 QSet<QTabBar*> used = newState.dockAreaLayout.usedTabBars();
2741 const auto groups =
2742 parent()->findChildren<QDockWidgetGroupWindow*>(aName: QString(), options: Qt::FindDirectChildrenOnly);
2743 for (QDockWidgetGroupWindow *dwgw : groups)
2744 used += dwgw->layoutInfo()->usedTabBars();
2745
2746 const QSet<QTabBar*> retired = usedTabBars - used;
2747 usedTabBars = used;
2748 for (QTabBar *tab_bar : retired) {
2749 tab_bar->hide();
2750 while (tab_bar->count() > 0)
2751 tab_bar->removeTab(index: 0);
2752 unusedTabBars.append(t: tab_bar);
2753 }
2754
2755 if (sep == 1) {
2756 const QSet<QWidget*> usedSeps = newState.dockAreaLayout.usedSeparatorWidgets();
2757 const QSet<QWidget*> retiredSeps = usedSeparatorWidgets - usedSeps;
2758 usedSeparatorWidgets = usedSeps;
2759 for (QWidget *sepWidget : retiredSeps) {
2760 unusedSeparatorWidgets.append(t: sepWidget);
2761 }
2762 }
2763
2764 for (int i = 0; i < QInternal::DockCount; ++i)
2765 newState.dockAreaLayout.docks[i].reparentWidgets(p: parentWidget());
2766
2767#endif // QT_CONFIG(dockwidget) && QT_CONFIG(tabwidget)
2768 newState.apply(animated: dockOptions & QMainWindow::AnimatedDocks && animate);
2769}
2770
2771void QMainWindowLayout::saveState(QDataStream &stream) const
2772{
2773 layoutState.saveState(stream);
2774}
2775
2776bool QMainWindowLayout::restoreState(QDataStream &stream)
2777{
2778 savedState = layoutState;
2779 layoutState.clear();
2780 layoutState.rect = savedState.rect;
2781
2782 if (!layoutState.restoreState(stream&: stream, oldState: savedState)) {
2783 layoutState.deleteAllLayoutItems();
2784 layoutState = savedState;
2785 if (parentWidget()->isVisible())
2786 applyState(newState&: layoutState, animate: false); // hides tabBars allocated by newState
2787 return false;
2788 }
2789
2790 if (parentWidget()->isVisible()) {
2791 layoutState.fitLayout();
2792 applyState(newState&: layoutState, animate: false);
2793 }
2794
2795 savedState.deleteAllLayoutItems();
2796 savedState.clear();
2797
2798#if QT_CONFIG(dockwidget)
2799 if (parentWidget()->isVisible()) {
2800#if QT_CONFIG(tabbar)
2801 for (QTabBar *tab_bar : qAsConst(t&: usedTabBars))
2802 tab_bar->show();
2803
2804#endif
2805 }
2806#endif // QT_CONFIG(dockwidget)
2807
2808 return true;
2809}
2810
2811QT_END_NAMESPACE
2812
2813#include "moc_qmainwindowlayout_p.cpp"
2814

source code of qtbase/src/widgets/widgets/qmainwindowlayout.cpp