Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qdockarealayout.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// Copyright (C) 2015 Olivier Goffart <ogoffart@woboq.com>
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4// Qt-Security score:significant reason:default
5
6#include "QtWidgets/qapplication.h"
7#include "QtWidgets/qwidget.h"
8#if QT_CONFIG(tabbar)
9#include "QtWidgets/qtabbar.h"
10#endif
11#include "QtWidgets/qstyle.h"
12#include "QtWidgets/qapplication.h"
13#include "QtCore/qvariant.h"
15#include "qdockwidget.h"
16#include "qmainwindow.h"
19#include "qmenu_p.h"
20#include "qdockwidget_p.h"
21#include <private/qlayoutengine_p.h>
22
23#if QT_CONFIG(toolbar)
24#include "qtoolbar.h"
25#include "qtoolbarlayout_p.h"
26#endif
27
28#include <qpainter.h>
29#include <qstyleoption.h>
30
32
33Q_LOGGING_CATEGORY(lcQpaDockWidgets, "qt.widgets.dockwidgets");
34
35// qmainwindow.cpp
36extern QMainWindowLayout *qt_mainwindow_layout(const QMainWindow *window);
37
39
40/******************************************************************************
41** QPlaceHolderItem
42*/
43
44QPlaceHolderItem::QPlaceHolderItem(QWidget *w)
45{
46 objectName = w->objectName();
47 hidden = w->isHidden();
48 window = w->isWindow();
49 if (window)
50 topLevelRect = w->geometry();
51}
52
53/******************************************************************************
54** QDockAreaLayoutItem
55*/
56
57QDockAreaLayoutItem::QDockAreaLayoutItem(QLayoutItem *_widgetItem)
58 : widgetItem(_widgetItem), subinfo(nullptr), placeHolderItem(nullptr), pos(0), size(-1), flags(NoFlags)
59{
60}
61
62QDockAreaLayoutItem::QDockAreaLayoutItem(QDockAreaLayoutInfo *_subinfo)
63 : widgetItem(nullptr), subinfo(_subinfo), placeHolderItem(nullptr), pos(0), size(-1), flags(NoFlags)
64{
65}
66
67QDockAreaLayoutItem::QDockAreaLayoutItem(QPlaceHolderItem *_placeHolderItem)
68 : widgetItem(nullptr), subinfo(nullptr), placeHolderItem(_placeHolderItem), pos(0), size(-1), flags(NoFlags)
69{
70}
71
72QDockAreaLayoutItem::QDockAreaLayoutItem(const QDockAreaLayoutItem &other)
73 : widgetItem(other.widgetItem), subinfo(nullptr), placeHolderItem(nullptr), pos(other.pos),
74 size(other.size), flags(other.flags)
75{
76 if (other.subinfo != nullptr)
77 subinfo = new QDockAreaLayoutInfo(*other.subinfo);
78 else if (other.placeHolderItem != nullptr)
79 placeHolderItem = new QPlaceHolderItem(*other.placeHolderItem);
80}
81
82QDockAreaLayoutItem::~QDockAreaLayoutItem()
83{
84 delete subinfo;
85 delete placeHolderItem;
86}
87
88bool QDockAreaLayoutItem::skip() const
89{
90 if (placeHolderItem != nullptr)
91 return true;
92
93 if (flags & GapItem)
94 return false;
95
96 if (widgetItem != nullptr)
97 return widgetItem->isEmpty();
98
99 if (subinfo != nullptr) {
100 for (int i = 0; i < subinfo->item_list.size(); ++i) {
101 if (!subinfo->item_list.at(i).skip())
102 return false;
103 }
104 }
105
106 return true;
107}
108
109QSize QDockAreaLayoutItem::minimumSize() const
110{
111 if (widgetItem)
112 return widgetItem->minimumSize().grownBy(widgetItem->widget()->contentsMargins());
113 if (subinfo != nullptr)
114 return subinfo->minimumSize();
115 return QSize(0, 0);
116}
117
118QSize QDockAreaLayoutItem::maximumSize() const
119{
120 if (widgetItem)
121 return widgetItem->maximumSize().grownBy(widgetItem->widget()->contentsMargins());
122 if (subinfo != nullptr)
123 return subinfo->maximumSize();
124 return QSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
125}
126
127bool QDockAreaLayoutItem::hasFixedSize(Qt::Orientation o) const
128{
129 return perp(o, minimumSize()) == perp(o, maximumSize());
130}
131
132bool QDockAreaLayoutItem::expansive(Qt::Orientation o) const
133{
134 if ((flags & GapItem) || placeHolderItem != nullptr)
135 return false;
136 if (widgetItem != nullptr)
137 return ((widgetItem->expandingDirections() & o) == o);
138 if (subinfo != nullptr)
139 return subinfo->expansive(o);
140 return false;
141}
142
143QSize QDockAreaLayoutItem::sizeHint() const
144{
145 if (placeHolderItem != nullptr)
146 return QSize(0, 0);
147 if (widgetItem)
148 return widgetItem->sizeHint().grownBy(widgetItem->widget()->contentsMargins());
149 if (subinfo != nullptr)
150 return subinfo->sizeHint();
151 return QSize(-1, -1);
152}
153
154QDockAreaLayoutItem
155 &QDockAreaLayoutItem::operator = (const QDockAreaLayoutItem &other)
156{
157 if (this == &other)
158 return *this;
159
160 widgetItem = other.widgetItem;
161 delete subinfo;
162 if (other.subinfo == nullptr)
163 subinfo = nullptr;
164 else
165 subinfo = new QDockAreaLayoutInfo(*other.subinfo);
166
167 delete placeHolderItem;
168 if (other.placeHolderItem == nullptr)
169 placeHolderItem = nullptr;
170 else
171 placeHolderItem = new QPlaceHolderItem(*other.placeHolderItem);
172
173 pos = other.pos;
174 size = other.size;
175 flags = other.flags;
176
177 return *this;
178}
179
180#ifndef QT_NO_DEBUG_STREAM
181QDebug operator<<(QDebug dbg, const QDockAreaLayoutItem *item)
182{
183 QDebugStateSaver saver(dbg);
184 dbg.nospace();
185 return item ? dbg << *item : dbg << "QDockAreaLayoutItem(0x0)";
186}
187
188QDebug operator<<(QDebug dbg, const QDockAreaLayoutItem &item)
189{
190 QDebugStateSaver saver(dbg);
191 dbg.nospace();
192 dbg << "QDockAreaLayoutItem(" << static_cast<const void *>(&item) << "->";
193 if (item.widgetItem) {
194 QWidget *widget = item.widgetItem->widget();
195 if (auto *dockWidget = qobject_cast<QDockWidget *>(widget)) {
196 dbg << "widgetItem(" << dockWidget << ")";
197 } else if (auto *groupWindow = qobject_cast<QDockWidgetGroupWindow *>(widget)) {
198 dbg << "widgetItem(" << groupWindow << "->(" << groupWindow->dockWidgets() << "))";
199 } else {
200 dbg << "widgetItem(" << widget << ")";
201 }
202 } else if (item.subinfo) {
203 dbg << "subInfo(" << item.subinfo << "->(" << item.subinfo->item_list << ")";
204 } else if (item.placeHolderItem) {
205 dbg << "placeHolderItem(" << item.placeHolderItem << ")";
206 }
207 dbg << ")";
208 return dbg;
209}
210#endif // QT_NO_DEBUG_STREAM
211
212/******************************************************************************
213** QDockAreaLayoutInfo
214*/
215
216#if QT_CONFIG(tabbar)
217static quintptr tabId(const QDockAreaLayoutItem &item)
218{
219 if (item.widgetItem == nullptr)
220 return 0;
221 return reinterpret_cast<quintptr>(item.widgetItem->widget());
222}
223#endif
224
225static const int zero = 0;
226
227QDockAreaLayoutInfo::QDockAreaLayoutInfo()
228 : sep(&zero), dockPos(QInternal::LeftDock), o(Qt::Horizontal), mainWindow(nullptr)
229#if QT_CONFIG(tabbar)
230 , tabbed(false), tabBar(nullptr), tabBarShape(QTabBar::RoundedSouth)
231#endif
232{
233}
234
235QDockAreaLayoutInfo::QDockAreaLayoutInfo(const int *_sep, QInternal::DockPosition _dockPos,
236 Qt::Orientation _o, int tbshape,
237 QMainWindow *window)
238 : sep(_sep), dockPos(_dockPos), o(_o), mainWindow(window)
239#if QT_CONFIG(tabbar)
240 , tabbed(false), tabBar(nullptr), tabBarShape(static_cast<QTabBar::Shape>(tbshape))
241#endif
242{
243#if !QT_CONFIG(tabbar)
244 Q_UNUSED(tbshape);
245#endif
246}
247
248QSize QDockAreaLayoutInfo::size() const
249{
250 return isEmpty() ? QSize(0, 0) : rect.size();
251}
252
253void QDockAreaLayoutInfo::clear()
254{
255 item_list.clear();
256 rect = QRect();
257#if QT_CONFIG(tabbar)
258 tabbed = false;
259 tabBar = nullptr;
260#endif
261}
262
263bool QDockAreaLayoutInfo::isEmpty() const
264{
265 return next(-1) == -1;
266}
267
268bool QDockAreaLayoutInfo::onlyHasPlaceholders() const
269{
270 for (const QDockAreaLayoutItem &item : item_list) {
271 if (!item.placeHolderItem)
272 return false;
273 }
274
275 return true;
276}
277
278QSize QDockAreaLayoutInfo::minimumSize() const
279{
280 if (isEmpty())
281 return QSize(0, 0);
282
283 int a = 0, b = 0;
284 bool first = true;
285 for (int i = 0; i < item_list.size(); ++i) {
286 const QDockAreaLayoutItem &item = item_list.at(i);
287 if (item.skip())
288 continue;
289
290 QSize min_size = item.minimumSize();
291#if QT_CONFIG(tabbar)
292 if (tabbed) {
293 a = qMax(a, pick(o, min_size));
294 } else
295#endif
296 {
297 if (!first)
298 a += *sep;
299 a += pick(o, min_size);
300 }
301 b = qMax(b, perp(o, min_size));
302
303 first = false;
304 }
305
306 QSize result;
307 rpick(o, result) = a;
308 rperp(o, result) = b;
309
310#if QT_CONFIG(tabbar)
311 QSize tbm = tabBarMinimumSize();
312 if (!tbm.isNull()) {
313 switch (tabBarShape) {
314 case QTabBar::RoundedNorth:
315 case QTabBar::RoundedSouth:
316 case QTabBar::TriangularNorth:
317 case QTabBar::TriangularSouth:
318 result.rheight() += tbm.height();
319 result.rwidth() = qMax(tbm.width(), result.width());
320 break;
321 case QTabBar::RoundedEast:
322 case QTabBar::RoundedWest:
323 case QTabBar::TriangularEast:
324 case QTabBar::TriangularWest:
325 result.rheight() = qMax(tbm.height(), result.height());
326 result.rwidth() += tbm.width();
327 break;
328 default:
329 break;
330 }
331 }
332#endif // QT_CONFIG(tabbar)
333
334 return result;
335}
336
337QSize QDockAreaLayoutInfo::maximumSize() const
338{
339 if (isEmpty())
340 return QSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
341
342 int a = 0, b = QWIDGETSIZE_MAX;
343#if QT_CONFIG(tabbar)
344 if (tabbed)
345 a = QWIDGETSIZE_MAX;
346#endif
347
348 int min_perp = 0;
349
350 bool first = true;
351 for (int i = 0; i < item_list.size(); ++i) {
352 const QDockAreaLayoutItem &item = item_list.at(i);
353 if (item.skip())
354 continue;
355
356 QSize max_size = item.maximumSize();
357 min_perp = qMax(min_perp, perp(o, item.minimumSize()));
358
359#if QT_CONFIG(tabbar)
360 if (tabbed) {
361 a = qMin(a, pick(o, max_size));
362 } else
363#endif
364 {
365 if (!first)
366 a += *sep;
367 a += pick(o, max_size);
368 }
369 b = qMin(b, perp(o, max_size));
370
371 a = qMin(a, int(QWIDGETSIZE_MAX));
372 b = qMin(b, int(QWIDGETSIZE_MAX));
373
374 first = false;
375 }
376
377 b = qMax(b, min_perp);
378
379 QSize result;
380 rpick(o, result) = a;
381 rperp(o, result) = b;
382
383#if QT_CONFIG(tabbar)
384 QSize tbh = tabBarSizeHint();
385 if (!tbh.isNull()) {
386 switch (tabBarShape) {
387 case QTabBar::RoundedNorth:
388 case QTabBar::RoundedSouth:
389 result.rheight() += tbh.height();
390 break;
391 case QTabBar::RoundedEast:
392 case QTabBar::RoundedWest:
393 result.rwidth() += tbh.width();
394 break;
395 default:
396 break;
397 }
398 }
399#endif // QT_CONFIG(tabbar)
400
401 return result;
402}
403
404QSize QDockAreaLayoutInfo::sizeHint() const
405{
406 if (isEmpty())
407 return QSize(0, 0);
408
409 int a = 0, b = 0;
410 int min_perp = 0;
411 int max_perp = QWIDGETSIZE_MAX;
412 const QDockAreaLayoutItem *previous = nullptr;
413 for (int i = 0; i < item_list.size(); ++i) {
414 const QDockAreaLayoutItem &item = item_list.at(i);
415 if (item.skip())
416 continue;
417
418 bool gap = item.flags & QDockAreaLayoutItem::GapItem;
419
420 QSize size_hint = item.sizeHint();
421 min_perp = qMax(min_perp, perp(o, item.minimumSize()));
422 max_perp = qMin(max_perp, perp(o, item.maximumSize()));
423
424#if QT_CONFIG(tabbar)
425 if (tabbed) {
426 a = qMax(a, gap ? item.size : pick(o, size_hint));
427 } else
428#endif
429 {
430 if (previous && !gap && !(previous->flags & QDockAreaLayoutItem::GapItem)
431 && !previous->hasFixedSize(o)) {
432 a += *sep;
433 }
434 a += gap ? item.size : pick(o, size_hint);
435 }
436 b = qMax(b, perp(o, size_hint));
437
438 previous = &item;
439 }
440
441 max_perp = qMax(max_perp, min_perp);
442 b = qMax(b, min_perp);
443 b = qMin(b, max_perp);
444
445 QSize result;
446 rpick(o, result) = a;
447 rperp(o, result) = b;
448
449#if QT_CONFIG(tabbar)
450 if (tabbed) {
451 QSize tbh = tabBarSizeHint();
452 switch (tabBarShape) {
453 case QTabBar::RoundedNorth:
454 case QTabBar::RoundedSouth:
455 case QTabBar::TriangularNorth:
456 case QTabBar::TriangularSouth:
457 result.rheight() += tbh.height();
458 result.rwidth() = qMax(tbh.width(), result.width());
459 break;
460 case QTabBar::RoundedEast:
461 case QTabBar::RoundedWest:
462 case QTabBar::TriangularEast:
463 case QTabBar::TriangularWest:
464 result.rheight() = qMax(tbh.height(), result.height());
465 result.rwidth() += tbh.width();
466 break;
467 default:
468 break;
469 }
470 }
471#endif // QT_CONFIG(tabbar)
472
473 return result;
474}
475
476bool QDockAreaLayoutInfo::expansive(Qt::Orientation o) const
477{
478 for (int i = 0; i < item_list.size(); ++i) {
479 if (item_list.at(i).expansive(o))
480 return true;
481 }
482 return false;
483}
484
485/* QDockAreaLayoutInfo::maximumSize() doesn't return the real max size. For example,
486 if the layout is empty, it returns QWIDGETSIZE_MAX. This is so that empty dock areas
487 don't constrain the size of the QMainWindow, but sometimes we really need to know the
488 maximum size. Also, these functions take into account widgets that want to keep their
489 size (f.ex. when they are hidden and then shown, they should not change size).
490*/
491
492static int realMinSize(const QDockAreaLayoutInfo &info)
493{
494 int result = 0;
495 bool first = true;
496 for (int i = 0; i < info.item_list.size(); ++i) {
497 const QDockAreaLayoutItem &item = info.item_list.at(i);
498 if (item.skip())
499 continue;
500
501 int min = 0;
502 if ((item.flags & QDockAreaLayoutItem::KeepSize) && item.size != -1)
503 min = item.size;
504 else
505 min = pick(info.o, item.minimumSize());
506
507 if (!first)
508 result += *info.sep;
509 result += min;
510
511 first = false;
512 }
513
514 return result;
515}
516
517static int realMaxSize(const QDockAreaLayoutInfo &info)
518{
519 int result = 0;
520 bool first = true;
521 for (int i = 0; i < info.item_list.size(); ++i) {
522 const QDockAreaLayoutItem &item = info.item_list.at(i);
523 if (item.skip())
524 continue;
525
526 int max = 0;
527 if ((item.flags & QDockAreaLayoutItem::KeepSize) && item.size != -1)
528 max = item.size;
529 else
530 max = pick(info.o, item.maximumSize());
531
532 if (!first)
533 result += *info.sep;
534 result += max;
535
536 if (result >= QWIDGETSIZE_MAX)
537 return QWIDGETSIZE_MAX;
538
539 first = false;
540 }
541
542 return result;
543}
544
545void QDockAreaLayoutInfo::fitItems()
546{
547#if QT_CONFIG(tabbar)
548 if (tabbed) {
549 return;
550 }
551#endif
552
553 QList<QLayoutStruct> layout_struct_list(item_list.size() * 2);
554 int j = 0;
555
556 int size = pick(o, rect.size());
557 int min_size = realMinSize(*this);
558 int max_size = realMaxSize(*this);
559 int last_index = -1;
560
561 const QDockAreaLayoutItem *previous = nullptr;
562 for (int i = 0; i < item_list.size(); ++i) {
563 QDockAreaLayoutItem &item = item_list[i];
564 if (item.skip())
565 continue;
566
567 bool gap = item.flags & QDockAreaLayoutItem::GapItem;
568 if (previous && !gap) {
569 if (!(previous->flags & QDockAreaLayoutItem::GapItem)) {
570 QLayoutStruct &ls = layout_struct_list[j++];
571 ls.init();
572 ls.minimumSize = ls.maximumSize = ls.sizeHint = previous->hasFixedSize(o) ? 0 : *sep;
573 ls.empty = false;
574 }
575 }
576
577 if (item.flags & QDockAreaLayoutItem::KeepSize) {
578 // Check if the item can keep its size, without violating size constraints
579 // of other items.
580
581 if (size < min_size) {
582 // There is too little space to keep this widget's size
583 item.flags &= ~QDockAreaLayoutItem::KeepSize;
584 min_size -= item.size;
585 min_size += pick(o, item.minimumSize());
586 min_size = qMax(0, min_size);
587 } else if (size > max_size) {
588 // There is too much space to keep this widget's size
589 item.flags &= ~QDockAreaLayoutItem::KeepSize;
590 max_size -= item.size;
591 max_size += pick(o, item.maximumSize());
592 max_size = qMin<int>(QWIDGETSIZE_MAX, max_size);
593 }
594 }
595
596 last_index = j;
597 QLayoutStruct &ls = layout_struct_list[j++];
598 ls.init();
599 ls.empty = false;
600 if (item.flags & QDockAreaLayoutItem::KeepSize) {
601 ls.minimumSize = ls.maximumSize = ls.sizeHint = item.size;
602 ls.expansive = false;
603 ls.stretch = 0;
604 } else {
605 ls.maximumSize = pick(o, item.maximumSize());
606 ls.expansive = item.expansive(o);
607 ls.minimumSize = pick(o, item.minimumSize());
608 ls.sizeHint = item.size == -1 ? pick(o, item.sizeHint()) : item.size;
609 ls.stretch = ls.expansive ? ls.sizeHint : 0;
610 }
611
612 item.flags &= ~QDockAreaLayoutItem::KeepSize;
613 previous = &item;
614 }
615 layout_struct_list.resize(j);
616
617 // If there is more space than the widgets can take (due to maximum size constraints),
618 // we detect it here and stretch the last widget to take up the rest of the space.
619 if (size > max_size && last_index != -1) {
620 layout_struct_list[last_index].maximumSize = QWIDGETSIZE_MAX;
621 layout_struct_list[last_index].expansive = true;
622 }
623
624 qGeomCalc(layout_struct_list, 0, j, pick(o, rect.topLeft()), size, 0);
625
626 j = 0;
627 bool prev_gap = false;
628 bool first = true;
629 for (int i = 0; i < item_list.size(); ++i) {
630 QDockAreaLayoutItem &item = item_list[i];
631 if (item.skip())
632 continue;
633
634 bool gap = item.flags & QDockAreaLayoutItem::GapItem;
635 if (!first && !gap && !prev_gap)
636 ++j;
637
638 const QLayoutStruct &ls = layout_struct_list.at(j++);
639 item.size = ls.size;
640 item.pos = ls.pos;
641
642 if (item.subinfo != nullptr) {
643 item.subinfo->rect = itemRect(i);
644 item.subinfo->fitItems();
645 }
646
647 prev_gap = gap;
648 first = false;
649 }
650}
651
652static QInternal::DockPosition dockPosHelper(const QRect &rect, const QPoint &_pos,
653 Qt::Orientation o,
654 bool nestingEnabled,
655 QDockAreaLayoutInfo::TabMode tabMode)
656{
657 if (tabMode == QDockAreaLayoutInfo::ForceTabs)
658 return QInternal::DockCount;
659
660 QPoint pos = _pos - rect.topLeft();
661
662 int x = pos.x();
663 int y = pos.y();
664 int w = rect.width();
665 int h = rect.height();
666
667 if (tabMode != QDockAreaLayoutInfo::NoTabs) {
668 // is it in the center?
669 if (nestingEnabled) {
670 /* 2/3
671 +--------------+
672 | |
673 | CCCCCCCC |
674 2/3 | CCCCCCCC |
675 | CCCCCCCC |
676 | |
677 +--------------+ */
678
679 QRect center(w/6, h/6, 2*w/3, 2*h/3);
680 if (center.contains(pos))
681 return QInternal::DockCount;
682 } else if (o == Qt::Horizontal) {
683 /* 2/3
684 +--------------+
685 | CCCCCCCC |
686 | CCCCCCCC |
687 | CCCCCCCC |
688 | CCCCCCCC |
689 | CCCCCCCC |
690 +--------------+ */
691
692 if (x > w/6 && x < w*5/6)
693 return QInternal::DockCount;
694 } else {
695 /*
696 +--------------+
697 | |
698 2/3 |CCCCCCCCCCCCCC|
699 |CCCCCCCCCCCCCC|
700 | |
701 +--------------+ */
702 if (y > h/6 && y < 5*h/6)
703 return QInternal::DockCount;
704 }
705 }
706
707 // not in the center. which edge?
708 if (nestingEnabled) {
709 if (o == Qt::Horizontal) {
710 /* 1/3 1/3 1/3
711 +------------+ (we've already ruled out the center)
712 |LLLLTTTTRRRR|
713 |LLLLTTTTRRRR|
714 |LLLLBBBBRRRR|
715 |LLLLBBBBRRRR|
716 +------------+ */
717
718 if (x < w/3)
719 return QInternal::LeftDock;
720 if (x > 2*w/3)
721 return QInternal::RightDock;
722 if (y < h/2)
723 return QInternal::TopDock;
724 return QInternal::BottomDock;
725 } else {
726 /* +------------+ (we've already ruled out the center)
727 1/3 |TTTTTTTTTTTT|
728 |LLLLLLRRRRRR|
729 1/3 |LLLLLLRRRRRR|
730 1/3 |BBBBBBBBBBBB|
731 +------------+ */
732
733 if (y < h/3)
734 return QInternal::TopDock;
735 if (y > 2*h/3)
736 return QInternal::BottomDock;
737 if (x < w/2)
738 return QInternal::LeftDock;
739 return QInternal::RightDock;
740 }
741 } else {
742 if (o == Qt::Horizontal) {
743 return x < w/2
744 ? QInternal::LeftDock
745 : QInternal::RightDock;
746 } else {
747 return y < h/2
748 ? QInternal::TopDock
749 : QInternal::BottomDock;
750 }
751 }
752}
753
754QList<int> QDockAreaLayoutInfo::gapIndex(const QPoint& _pos,
755 bool nestingEnabled, TabMode tabMode) const
756{
757 QList<int> result;
758 QRect item_rect;
759 int item_index = 0;
760
761#if QT_CONFIG(tabbar)
762 if (tabbed) {
763 item_rect = tabContentRect();
764 } else
765#endif
766 {
767 int pos = pick(o, _pos);
768
769 int last = -1;
770 for (int i = 0; i < item_list.size(); ++i) {
771 const QDockAreaLayoutItem &item = item_list.at(i);
772 if (item.skip())
773 continue;
774
775 last = i;
776
777 if (item.pos + item.size < pos)
778 continue;
779
780 if (item.subinfo != nullptr
781#if QT_CONFIG(tabbar)
782 && !item.subinfo->tabbed
783#endif
784 ) {
785 result = item.subinfo->gapIndex(_pos, nestingEnabled,
786 tabMode);
787 result.prepend(i);
788 return result;
789 }
790
791 item_rect = itemRect(i);
792 item_index = i;
793 break;
794 }
795
796 if (item_rect.isNull()) {
797 result.append(last + 1);
798 return result;
799 }
800 }
801
802 Q_ASSERT(!item_rect.isNull());
803
804 QInternal::DockPosition dock_pos
805 = dockPosHelper(item_rect, _pos, o, nestingEnabled, tabMode);
806
807 switch (dock_pos) {
808 case QInternal::LeftDock:
809 if (o == Qt::Horizontal)
810 result << item_index;
811 else
812 result << item_index << 0; // this subinfo doesn't exist yet, but insertGap()
813 // handles this by inserting it
814 break;
815 case QInternal::RightDock:
816 if (o == Qt::Horizontal)
817 result << item_index + 1;
818 else
819 result << item_index << 1;
820 break;
821 case QInternal::TopDock:
822 if (o == Qt::Horizontal)
823 result << item_index << 0;
824 else
825 result << item_index;
826 break;
827 case QInternal::BottomDock:
828 if (o == Qt::Horizontal)
829 result << item_index << 1;
830 else
831 result << item_index + 1;
832 break;
833 case QInternal::DockCount:
834 result << (-item_index - 1) << 0; // negative item_index means "on top of"
835 // -item_index - 1, insertGap()
836 // will insert a tabbed subinfo
837 break;
838 default:
839 break;
840 }
841
842 return result;
843}
844
845static inline int shrink(QLayoutStruct &ls, int delta)
846{
847 if (ls.empty)
848 return 0;
849 int old_size = ls.size;
850 ls.size = qMax(ls.size - delta, ls.minimumSize);
851 return old_size - ls.size;
852}
853
854static inline int grow(QLayoutStruct &ls, int delta)
855{
856 if (ls.empty)
857 return 0;
858 int old_size = ls.size;
859 ls.size = qMin(ls.size + delta, ls.maximumSize);
860 return ls.size - old_size;
861}
862
863static int separatorMoveHelper(QList<QLayoutStruct> &list, int index, int delta, int sep)
864{
865 // adjust sizes
866 int pos = -1;
867 for (int i = 0; i < list.size(); ++i) {
868 const QLayoutStruct &ls = list.at(i);
869 if (!ls.empty) {
870 pos = ls.pos;
871 break;
872 }
873 }
874 if (pos == -1)
875 return 0;
876
877 if (delta > 0) {
878 int growlimit = 0;
879 for (int i = 0; i<=index; ++i) {
880 const QLayoutStruct &ls = list.at(i);
881 if (ls.empty)
882 continue;
883 if (ls.maximumSize == QLAYOUTSIZE_MAX) {
884 growlimit = QLAYOUTSIZE_MAX;
885 break;
886 }
887 growlimit += ls.maximumSize - ls.size;
888 }
889 if (delta > growlimit)
890 delta = growlimit;
891
892 int d = 0;
893 for (int i = index + 1; d < delta && i < list.size(); ++i)
894 d += shrink(list[i], delta - d);
895 delta = d;
896 d = 0;
897 for (int i = index; d < delta && i >= 0; --i)
898 d += grow(list[i], delta - d);
899 } else if (delta < 0) {
900 int growlimit = 0;
901 for (int i = index + 1; i < list.size(); ++i) {
902 const QLayoutStruct &ls = list.at(i);
903 if (ls.empty)
904 continue;
905 if (ls.maximumSize == QLAYOUTSIZE_MAX) {
906 growlimit = QLAYOUTSIZE_MAX;
907 break;
908 }
909 growlimit += ls.maximumSize - ls.size;
910 }
911 if (-delta > growlimit)
912 delta = -growlimit;
913
914 int d = 0;
915 for (int i = index; d < -delta && i >= 0; --i)
916 d += shrink(list[i], -delta - d);
917 delta = -d;
918 d = 0;
919 for (int i = index + 1; d < -delta && i < list.size(); ++i)
920 d += grow(list[i], -delta - d);
921 }
922
923 // adjust positions
924 bool first = true;
925 for (int i = 0; i < list.size(); ++i) {
926 QLayoutStruct &ls = list[i];
927 if (ls.empty) {
928 ls.pos = pos + (first ? 0 : sep);
929 continue;
930 }
931 if (!first)
932 pos += sep;
933 ls.pos = pos;
934 pos += ls.size;
935 first = false;
936 }
937
938 return delta;
939}
940
941int QDockAreaLayoutInfo::separatorMove(int index, int delta)
942{
943#if QT_CONFIG(tabbar)
944 Q_ASSERT(!tabbed);
945#endif
946
947 QList<QLayoutStruct> list(item_list.size());
948 for (int i = 0; i < list.size(); ++i) {
949 const QDockAreaLayoutItem &item = item_list.at(i);
950 QLayoutStruct &ls = list[i];
951 Q_ASSERT(!(item.flags & QDockAreaLayoutItem::GapItem));
952 if (item.skip()) {
953 ls.empty = true;
954 } else {
955 const int separatorSpace = item.hasFixedSize(o) ? 0 : *sep;
956 ls.empty = false;
957 ls.pos = item.pos;
958 ls.size = item.size + separatorSpace;
959 ls.minimumSize = pick(o, item.minimumSize()) + separatorSpace;
960 ls.maximumSize = pick(o, item.maximumSize()) + separatorSpace;
961
962 }
963 }
964
965 //the separator space has been added to the size, so we pass 0 as a parameter
966 delta = separatorMoveHelper(list, index, delta, 0 /*separator*/);
967
968 for (int i = 0; i < list.size(); ++i) {
969 QDockAreaLayoutItem &item = item_list[i];
970 if (item.skip())
971 continue;
972 QLayoutStruct &ls = list[i];
973 const int separatorSpace = item.hasFixedSize(o) ? 0 : *sep;
974 item.size = ls.size - separatorSpace;
975 item.pos = ls.pos;
976 if (item.subinfo != nullptr) {
977 item.subinfo->rect = itemRect(i);
978 item.subinfo->fitItems();
979 }
980 }
981
982 return delta;
983}
984
985void QDockAreaLayoutInfo::unnest(int index)
986{
987 QDockAreaLayoutItem &item = item_list[index];
988 if (item.subinfo == nullptr)
989 return;
990 if (item.subinfo->item_list.size() > 1)
991 return;
992
993 if (item.subinfo->item_list.size() == 0) {
994 item_list.removeAt(index);
995 } else if (item.subinfo->item_list.size() == 1) {
996 QDockAreaLayoutItem &child = item.subinfo->item_list.first();
997 if (child.widgetItem != nullptr) {
998 item.widgetItem = child.widgetItem;
999 delete item.subinfo;
1000 item.subinfo = nullptr;
1001 } else if (child.subinfo != nullptr) {
1002 QDockAreaLayoutInfo *tmp = item.subinfo;
1003 item.subinfo = child.subinfo;
1004 child.subinfo = nullptr;
1005 tmp->item_list.clear();
1006 delete tmp;
1007 }
1008 }
1009}
1010
1011void QDockAreaLayoutInfo::remove(const QList<int> &path)
1012{
1013 Q_ASSERT(!path.isEmpty());
1014
1015 if (path.size() > 1) {
1016 const int index = path.first();
1017 QDockAreaLayoutItem &item = item_list[index];
1018 Q_ASSERT(item.subinfo != nullptr);
1019 item.subinfo->remove(path.mid(1));
1020 unnest(index);
1021 } else {
1022 int index = path.first();
1023 item_list.removeAt(index);
1024 }
1025}
1026
1027void QDockAreaLayoutInfo::remove(QWidget *widget)
1028{
1029 const QList<int> path = indexOf(widget);
1030 if (path.isEmpty())
1031 return;
1032 remove(path);
1033}
1034
1035QLayoutItem *QDockAreaLayoutInfo::plug(const QList<int> &path)
1036{
1037 Q_ASSERT(!path.isEmpty());
1038
1039 int index = path.first();
1040 if (index < 0)
1041 index = -index - 1;
1042
1043 if (path.size() > 1) {
1044 QDockAreaLayoutItem &item = item_list[index];
1045 Q_ASSERT(item.subinfo != nullptr);
1046 return item.subinfo->plug(path.mid(1));
1047 }
1048
1049 QDockAreaLayoutItem &item = item_list[index];
1050
1051 Q_ASSERT(item.widgetItem != nullptr);
1052 Q_ASSERT(item.flags & QDockAreaLayoutItem::GapItem);
1053 item.flags &= ~QDockAreaLayoutItem::GapItem;
1054 return item.widgetItem;
1055}
1056
1057QLayoutItem *QDockAreaLayoutInfo::unplug(const QList<int> &path)
1058{
1059 Q_ASSERT(!path.isEmpty());
1060
1061 const int index = path.first();
1062 if (path.size() > 1) {
1063 QDockAreaLayoutItem &item = item_list[index];
1064 Q_ASSERT(item.subinfo != nullptr);
1065 return item.subinfo->unplug(path.mid(1));
1066 }
1067
1068 QDockAreaLayoutItem &item = item_list[index];
1069 int prev = this->prev(index);
1070 int next = this->next(index);
1071
1072 Q_ASSERT(!(item.flags & QDockAreaLayoutItem::GapItem));
1073 item.flags |= QDockAreaLayoutItem::GapItem;
1074
1075#if QT_CONFIG(tabbar)
1076 if (tabbed) {
1077 } else
1078#endif
1079 {
1080 if (prev != -1 && !(item_list.at(prev).flags & QDockAreaLayoutItem::GapItem)) {
1081 item.pos -= *sep;
1082 item.size += *sep;
1083 }
1084 if (next != -1 && !(item_list.at(next).flags & QDockAreaLayoutItem::GapItem))
1085 item.size += *sep;
1086 }
1087
1088 return item.widgetItem;
1089}
1090
1091#if QT_CONFIG(tabbar)
1092
1093quintptr QDockAreaLayoutInfo::currentTabId() const
1094{
1095 if (!tabbed || tabBar == nullptr)
1096 return 0;
1097
1098 int index = tabBar->currentIndex();
1099 if (index == -1)
1100 return 0;
1101
1102 return qvariant_cast<quintptr>(tabBar->tabData(index));
1103}
1104
1105void QDockAreaLayoutInfo::setCurrentTab(QWidget *widget)
1106{
1107 setCurrentTabId(reinterpret_cast<quintptr>(widget));
1108}
1109
1110void QDockAreaLayoutInfo::setCurrentTabId(quintptr id)
1111{
1112 if (!tabbed || tabBar == nullptr)
1113 return;
1114
1115 for (int i = 0; i < tabBar->count(); ++i) {
1116 if (qvariant_cast<quintptr>(tabBar->tabData(i)) == id) {
1117 tabBar->setCurrentIndex(i);
1118 return;
1119 }
1120 }
1121}
1122
1123#endif // QT_CONFIG(tabbar)
1124
1125static QRect dockedGeometry(QWidget *widget)
1126{
1127 int titleHeight = 0;
1128
1129 QDockWidgetLayout *layout
1130 = qobject_cast<QDockWidgetLayout*>(widget->layout());
1131 if (layout && layout->nativeWindowDeco())
1132 titleHeight = layout->titleHeight();
1133
1134 QRect result = widget->geometry();
1135 result.adjust(0, -titleHeight, 0, 0);
1136 return result;
1137}
1138
1139bool QDockAreaLayoutInfo::hasGapItem(const QList<int> &path) const
1140{
1141 // empty path has no gap item
1142 if (path.isEmpty())
1143 return false;
1144
1145 // Index -1 isn't a gap
1146 // Index out of range points at a position to be created. That isn't a gap either.
1147 const int index = path.constFirst();
1148 if (index < 0 || index >= item_list.count())
1149 return false;
1150
1151 return item_list[index].flags & QDockAreaLayoutItem::GapItem;
1152}
1153
1154bool QDockAreaLayoutInfo::insertGap(const QList<int> &path, QLayoutItem *dockWidgetItem)
1155{
1156 Q_ASSERT(!path.isEmpty());
1157
1158 bool insert_tabbed = false;
1159 int index = path.first();
1160 if (index < 0) {
1161 insert_tabbed = true;
1162 index = -index - 1;
1163 }
1164
1165 if (path.size() > 1) {
1166 QDockAreaLayoutItem &item = item_list[index];
1167
1168 if (item.subinfo == nullptr
1169#if QT_CONFIG(tabbar)
1170 || (item.subinfo->tabbed && !insert_tabbed)
1171#endif
1172 ) {
1173
1174 // this is not yet a nested layout - make it
1175
1176 QDockAreaLayoutInfo *subinfo = item.subinfo;
1177 QLayoutItem *widgetItem = item.widgetItem;
1178 QPlaceHolderItem *placeHolderItem = item.placeHolderItem;
1179 QRect r = subinfo == nullptr ? widgetItem ? dockedGeometry(widgetItem->widget()) : placeHolderItem->topLevelRect : subinfo->rect;
1180
1181 Qt::Orientation opposite = o == Qt::Horizontal ? Qt::Vertical : Qt::Horizontal;
1182#if !QT_CONFIG(tabbar)
1183 const int tabBarShape = 0;
1184#endif
1185 QDockAreaLayoutInfo *new_info
1186 = new QDockAreaLayoutInfo(sep, dockPos, opposite, tabBarShape, mainWindow);
1187
1188 //item become a new top-level
1189 item.subinfo = new_info;
1190 item.widgetItem = nullptr;
1191 item.placeHolderItem = nullptr;
1192
1193 QDockAreaLayoutItem new_item
1194 = widgetItem == nullptr
1195 ? QDockAreaLayoutItem(subinfo)
1196 : widgetItem ? QDockAreaLayoutItem(widgetItem) : QDockAreaLayoutItem(placeHolderItem);
1197 new_item.size = pick(opposite, r.size());
1198 new_item.pos = pick(opposite, r.topLeft());
1199 new_info->item_list.append(new_item);
1200#if QT_CONFIG(tabbar)
1201 if (insert_tabbed) {
1202 new_info->tabbed = true;
1203 }
1204#endif
1205 }
1206
1207 return item.subinfo->insertGap(path.mid(1), dockWidgetItem);
1208 }
1209
1210 // create the gap item
1211 QDockAreaLayoutItem gap_item;
1212 gap_item.flags |= QDockAreaLayoutItem::GapItem;
1213 gap_item.widgetItem = dockWidgetItem; // so minimumSize(), maximumSize() and
1214 // sizeHint() will work
1215#if QT_CONFIG(tabbar)
1216 if (!tabbed)
1217#endif
1218 {
1219 int prev = this->prev(index);
1220 int next = this->next(index - 1);
1221 // find out how much space we have in the layout
1222 int space = 0;
1223 if (isEmpty()) {
1224 // I am an empty dock area, therefore I am a top-level dock area.
1225 switch (dockPos) {
1226 case QInternal::LeftDock:
1227 case QInternal::RightDock:
1228 if (o == Qt::Vertical) {
1229 // the "size" is the height of the dock area (remember we are empty)
1230 space = pick(Qt::Vertical, rect.size());
1231 } else {
1232 space = pick(Qt::Horizontal, dockWidgetItem->widget()->size());
1233 }
1234 break;
1235 case QInternal::TopDock:
1236 case QInternal::BottomDock:
1237 default:
1238 if (o == Qt::Horizontal) {
1239 // the "size" is width of the dock area
1240 space = pick(Qt::Horizontal, rect.size());
1241 } else {
1242 space = pick(Qt::Vertical, dockWidgetItem->widget()->size());
1243 }
1244 break;
1245 }
1246 } else {
1247 for (int i = 0; i < item_list.size(); ++i) {
1248 const QDockAreaLayoutItem &item = item_list.at(i);
1249 if (item.skip())
1250 continue;
1251 Q_ASSERT_X(!(item.flags & QDockAreaLayoutItem::GapItem),
1252 "QDockAreaLayoutInfo::insertGap", "inserting two gaps after each other");
1253 space += item.size - pick(o, item.minimumSize());
1254 qCDebug(lcQpaDockWidgets) << "Item space:" << item.flags << this;
1255 }
1256 }
1257
1258 // find the actual size of the gap
1259 int gap_size = 0;
1260 int sep_size = 0;
1261 if (isEmpty()) {
1262 gap_size = space;
1263 sep_size = 0;
1264 } else {
1265 QRect r = dockedGeometry(dockWidgetItem->widget());
1266 gap_size = pick(o, r.size());
1267 if (prev != -1 && !(item_list.at(prev).flags & QDockAreaLayoutItem::GapItem))
1268 sep_size += *sep;
1269 if (next != -1 && !(item_list.at(next).flags & QDockAreaLayoutItem::GapItem))
1270 sep_size += *sep;
1271 }
1272 if (gap_size + sep_size > space)
1273 gap_size = pick(o, gap_item.minimumSize());
1274 gap_item.size = gap_size + sep_size;
1275 }
1276
1277 // finally, insert the gap
1278 item_list.insert(index, gap_item);
1279 qCDebug(lcQpaDockWidgets) << "Insert gap after:" << index << this;
1280
1281 return true;
1282}
1283
1284QDockAreaLayoutInfo *QDockAreaLayoutInfo::info(QWidget *widget)
1285{
1286 for (int i = 0; i < item_list.size(); ++i) {
1287 const QDockAreaLayoutItem &item = item_list.at(i);
1288 if (item.skip())
1289 continue;
1290
1291#if QT_CONFIG(tabbar)
1292 if (tabbed && widget == tabBar)
1293 return this;
1294#endif
1295
1296 if (item.widgetItem != nullptr && item.widgetItem->widget() == widget)
1297 return this;
1298
1299 if (item.subinfo != nullptr) {
1300 if (QDockAreaLayoutInfo *result = item.subinfo->info(widget))
1301 return result;
1302 }
1303 }
1304
1305 return nullptr;
1306}
1307
1308QDockAreaLayoutInfo *QDockAreaLayoutInfo::info(const QList<int> &path)
1309{
1310 int index = path.first();
1311 if (index < 0)
1312 index = -index - 1;
1313 if (index >= item_list.size())
1314 return this;
1315 if (path.size() == 1 || item_list[index].subinfo == nullptr)
1316 return this;
1317 return item_list[index].subinfo->info(path.mid(1));
1318}
1319
1320QRect QDockAreaLayoutInfo::itemRect(int index, bool isGap) const
1321{
1322 const QDockAreaLayoutItem &item = item_list.at(index);
1323
1324 if (item.skip())
1325 return QRect();
1326
1327 if (isGap && !(item.flags & QDockAreaLayoutItem::GapItem))
1328 return QRect();
1329
1330 QRect result;
1331
1332#if QT_CONFIG(tabbar)
1333 if (tabbed) {
1334 if (isGap || tabId(item) == currentTabId())
1335 result = tabContentRect();
1336 } else
1337#endif
1338 {
1339 int pos = item.pos;
1340 int size = item.size;
1341
1342 if (isGap) {
1343 int prev = this->prev(index);
1344 int next = this->next(index);
1345 if (prev != -1 && !(item_list.at(prev).flags & QDockAreaLayoutItem::GapItem)) {
1346 pos += *sep;
1347 size -= *sep;
1348 }
1349 if (next != -1 && !(item_list.at(next).flags & QDockAreaLayoutItem::GapItem))
1350 size -= *sep;
1351 }
1352
1353 QPoint p;
1354 rpick(o, p) = pos;
1355 rperp(o, p) = perp(o, rect.topLeft());
1356 QSize s;
1357 rpick(o, s) = size;
1358 rperp(o, s) = perp(o, rect.size());
1359 result = QRect(p, s);
1360 }
1361
1362 return result;
1363}
1364
1365QRect QDockAreaLayoutInfo::itemRect(const QList<int> &path) const
1366{
1367 Q_ASSERT(!path.isEmpty());
1368
1369 const int index = path.first();
1370 if (path.size() > 1) {
1371 const QDockAreaLayoutItem &item = item_list.at(index);
1372 Q_ASSERT(item.subinfo != nullptr);
1373 return item.subinfo->itemRect(path.mid(1));
1374 }
1375
1376 return itemRect(index);
1377}
1378
1379QRect QDockAreaLayoutInfo::separatorRect(int index) const
1380{
1381#if QT_CONFIG(tabbar)
1382 if (tabbed)
1383 return QRect();
1384#endif
1385
1386 const QDockAreaLayoutItem &item = item_list.at(index);
1387 if (item.skip())
1388 return QRect();
1389
1390 QPoint pos = rect.topLeft();
1391 rpick(o, pos) = item.pos + item.size;
1392 QSize s = rect.size();
1393 rpick(o, s) = *sep;
1394
1395 return QRect(pos, s);
1396}
1397
1398QRect QDockAreaLayoutInfo::separatorRect(const QList<int> &path) const
1399{
1400 Q_ASSERT(!path.isEmpty());
1401
1402 const int index = path.first();
1403 if (path.size() > 1) {
1404 const QDockAreaLayoutItem &item = item_list.at(index);
1405 Q_ASSERT(item.subinfo != nullptr);
1406 return item.subinfo->separatorRect(path.mid(1));
1407 }
1408 return separatorRect(index);
1409}
1410
1411QList<int> QDockAreaLayoutInfo::findSeparator(const QPoint &_pos) const
1412{
1413#if QT_CONFIG(tabbar)
1414 if (tabbed)
1415 return QList<int>();
1416#endif
1417
1418 int pos = pick(o, _pos);
1419
1420 for (int i = 0; i < item_list.size(); ++i) {
1421 const QDockAreaLayoutItem &item = item_list.at(i);
1422 if (item.skip() || (item.flags & QDockAreaLayoutItem::GapItem))
1423 continue;
1424
1425 if (item.pos + item.size > pos) {
1426 if (item.subinfo != nullptr) {
1427 QList<int> result = item.subinfo->findSeparator(_pos);
1428 if (!result.isEmpty()) {
1429 result.prepend(i);
1430 return result;
1431 } else {
1432 return QList<int>();
1433 }
1434 }
1435 }
1436
1437 int next = this->next(i);
1438 if (next == -1 || (item_list.at(next).flags & QDockAreaLayoutItem::GapItem))
1439 continue;
1440
1441 QRect sepRect = separatorRect(i);
1442 if (!sepRect.isNull() && *sep == 1)
1443 sepRect.adjust(-2, -2, 2, 2);
1444 //we also make sure we don't find a separator that's not there
1445 if (sepRect.contains(_pos) && !item.hasFixedSize(o)) {
1446 return QList<int>() << i;
1447 }
1448
1449 }
1450
1451 return QList<int>();
1452}
1453
1454QList<int> QDockAreaLayoutInfo::indexOfPlaceHolder(const QString &objectName) const
1455{
1456 for (int i = 0; i < item_list.size(); ++i) {
1457 const QDockAreaLayoutItem &item = item_list.at(i);
1458
1459 if (item.subinfo != nullptr) {
1460 QList<int> result = item.subinfo->indexOfPlaceHolder(objectName);
1461 if (!result.isEmpty()) {
1462 result.prepend(i);
1463 return result;
1464 }
1465 continue;
1466 }
1467
1468 if (item.placeHolderItem != nullptr && item.placeHolderItem->objectName == objectName) {
1469 QList<int> result;
1470 result << i;
1471 return result;
1472 }
1473 }
1474
1475 return QList<int>();
1476}
1477
1478QList<int> QDockAreaLayoutInfo::indexOf(const QWidget *widget) const
1479{
1480 for (int i = 0; i < item_list.size(); ++i) {
1481 const QDockAreaLayoutItem &item = item_list.at(i);
1482
1483 if (item.placeHolderItem != nullptr)
1484 continue;
1485
1486 if (item.subinfo != nullptr) {
1487 QList<int> result = item.subinfo->indexOf(widget);
1488 if (!result.isEmpty()) {
1489 result.prepend(i);
1490 return result;
1491 }
1492 continue;
1493 }
1494
1495 if (!(item.flags & QDockAreaLayoutItem::GapItem) && item.widgetItem && item.widgetItem->widget() == widget) {
1496 QList<int> result;
1497 result << i;
1498 return result;
1499 }
1500 }
1501
1502 return QList<int>();
1503}
1504
1505std::unique_ptr<QLayoutItem> QDockAreaLayoutInfo::takeWidgetItem(QWidget *widget)
1506{
1507 std::unique_ptr<QLayoutItem> widgetItem;
1508 if (const auto path = indexOf(widget); !path.isEmpty())
1509 widgetItem.reset(item(path).widgetItem);
1510 return widgetItem;
1511}
1512
1513QMainWindowLayout *QDockAreaLayoutInfo::mainWindowLayout() const
1514{
1515 QMainWindowLayout *result = qt_mainwindow_layout(mainWindow);
1516 Q_ASSERT(result != nullptr);
1517 return result;
1518}
1519
1520bool QDockAreaLayoutInfo::hasFixedSize() const
1521{
1522 return perp(o, minimumSize()) == perp(o, maximumSize());
1523}
1524
1525/*! \internal
1526 Applies the layout and returns the activated QDockWidget or nullptr.
1527 */
1528QDockWidget *QDockAreaLayoutInfo::apply(bool animate)
1529{
1530 QWidgetAnimator &widgetAnimator = mainWindowLayout()->widgetAnimator;
1531
1532#if QT_CONFIG(tabbar)
1533 if (tabbed) {
1534 QRect tab_rect;
1535 QSize tbh = tabBarSizeHint();
1536
1537 if (!tbh.isNull()) {
1538 switch (tabBarShape) {
1539 case QTabBar::RoundedNorth:
1540 case QTabBar::TriangularNorth:
1541 tab_rect = QRect(rect.left(), rect.top(), rect.width(), tbh.height());
1542 break;
1543 case QTabBar::RoundedSouth:
1544 case QTabBar::TriangularSouth:
1545 tab_rect = QRect(rect.left(), rect.bottom() - tbh.height() + 1,
1546 rect.width(), tbh.height());
1547 break;
1548 case QTabBar::RoundedEast:
1549 case QTabBar::TriangularEast:
1550 tab_rect = QRect(rect.right() - tbh.width() + 1, rect.top(),
1551 tbh.width(), rect.height());
1552 break;
1553 case QTabBar::RoundedWest:
1554 case QTabBar::TriangularWest:
1555 tab_rect = QRect(rect.left(), rect.top(),
1556 tbh.width(), rect.height());
1557 break;
1558 default:
1559 break;
1560 }
1561 }
1562
1563 widgetAnimator.animate(tabBar, tab_rect, animate);
1564 }
1565#endif // QT_CONFIG(tabbar)
1566
1567 QDockWidget *activated = nullptr;
1568
1569 for (int i = 0; i < item_list.size(); ++i) {
1570 QDockAreaLayoutItem &item = item_list[i];
1571
1572 if (item.flags & QDockAreaLayoutItem::GapItem)
1573 continue;
1574
1575 if (item.subinfo != nullptr) {
1576 item.subinfo->apply(animate);
1577 continue;
1578 }
1579
1580 if (item.skip())
1581 continue;
1582
1583 Q_ASSERT(item.widgetItem);
1584 QRect r = itemRect(i);
1585 QWidget *w = item.widgetItem->widget();
1586
1587 QRect geo = w->geometry();
1588 widgetAnimator.animate(w, r, animate);
1589 if (!w->isHidden() && w->window()->isVisible()) {
1590 QDockWidget *dw = qobject_cast<QDockWidget*>(w);
1591 if (!r.isValid() && geo.right() >= 0 && geo.bottom() >= 0) {
1592 dw->lower();
1593 emit dw->visibilityChanged(false);
1594 } else if (r.isValid()
1595 && (geo.right() < 0 || geo.bottom() < 0)) {
1596 emit dw->visibilityChanged(true);
1597 activated = dw;
1598 }
1599 }
1600 }
1601#if QT_CONFIG(tabbar)
1602 if (*sep == 1)
1603 updateSeparatorWidgets();
1604#endif // QT_CONFIG(tabbar)
1605
1606 return activated;
1607}
1608
1609static void paintSep(QPainter *p, QWidget *w, const QRect &r, Qt::Orientation o, bool mouse_over)
1610{
1611 QStyleOption opt(0);
1612 opt.state = QStyle::State_None;
1613 if (w->isEnabled())
1614 opt.state |= QStyle::State_Enabled;
1615 if (o != Qt::Horizontal)
1616 opt.state |= QStyle::State_Horizontal;
1617 if (mouse_over)
1618 opt.state |= QStyle::State_MouseOver;
1619 opt.rect = r;
1620 opt.palette = w->palette();
1621
1622 w->style()->drawPrimitive(QStyle::PE_IndicatorDockWidgetResizeHandle, &opt, p, w);
1623}
1624
1625QRegion QDockAreaLayoutInfo::separatorRegion() const
1626{
1627 QRegion result;
1628
1629 if (isEmpty())
1630 return result;
1631#if QT_CONFIG(tabbar)
1632 if (tabbed)
1633 return result;
1634#endif
1635
1636 for (int i = 0; i < item_list.size(); ++i) {
1637 const QDockAreaLayoutItem &item = item_list.at(i);
1638
1639 if (item.skip())
1640 continue;
1641
1642 int next = this->next(i);
1643
1644 if (item.subinfo)
1645 result |= item.subinfo->separatorRegion();
1646
1647 if (next == -1)
1648 break;
1649 result |= separatorRect(i);
1650 }
1651
1652 return result;
1653}
1654
1655void QDockAreaLayoutInfo::paintSeparators(QPainter *p, QWidget *widget,
1656 const QRegion &clip,
1657 const QPoint &mouse) const
1658{
1659 if (isEmpty())
1660 return;
1661#if QT_CONFIG(tabbar)
1662 if (tabbed)
1663 return;
1664#endif
1665
1666 for (int i = 0; i < item_list.size(); ++i) {
1667 const QDockAreaLayoutItem &item = item_list.at(i);
1668
1669 if (item.skip())
1670 continue;
1671
1672 int next = this->next(i);
1673 if ((item.flags & QDockAreaLayoutItem::GapItem)
1674 || (next != -1 && (item_list.at(next).flags & QDockAreaLayoutItem::GapItem)))
1675 continue;
1676
1677 if (item.subinfo) {
1678 if (clip.contains(item.subinfo->rect))
1679 item.subinfo->paintSeparators(p, widget, clip, mouse);
1680 }
1681
1682 if (next == -1)
1683 break;
1684 QRect r = separatorRect(i);
1685 if (clip.contains(r) && !item.hasFixedSize(o))
1686 paintSep(p, widget, r, o, r.contains(mouse));
1687 }
1688}
1689
1690int QDockAreaLayoutInfo::next(int index) const
1691{
1692 for (int i = index + 1; i < item_list.size(); ++i) {
1693 if (!item_list.at(i).skip())
1694 return i;
1695 }
1696 return -1;
1697}
1698
1699int QDockAreaLayoutInfo::prev(int index) const
1700{
1701 for (int i = index - 1; i >= 0; --i) {
1702 if (!item_list.at(i).skip())
1703 return i;
1704 }
1705 return -1;
1706}
1707
1708#if QT_CONFIG(tabbar)
1709void QDockAreaLayoutInfo::tab(int index, QLayoutItem *dockWidgetItem)
1710{
1711 if (tabbed) {
1712 item_list.append(QDockAreaLayoutItem(dockWidgetItem));
1713 updateTabBar();
1714 setCurrentTab(dockWidgetItem->widget());
1715 } else {
1716 QDockAreaLayoutInfo *new_info
1717 = new QDockAreaLayoutInfo(sep, dockPos, o, tabBarShape, mainWindow);
1718 item_list[index].subinfo = new_info;
1719 new_info->item_list.append(QDockAreaLayoutItem(item_list.at(index).widgetItem));
1720 item_list[index].widgetItem = nullptr;
1721 new_info->item_list.append(QDockAreaLayoutItem(dockWidgetItem));
1722 new_info->tabbed = true;
1723 new_info->updateTabBar();
1724 new_info->setCurrentTab(dockWidgetItem->widget());
1725 }
1726}
1727#endif // QT_CONFIG(tabbar)
1728
1729void QDockAreaLayoutInfo::split(int index, Qt::Orientation orientation,
1730 QLayoutItem *dockWidgetItem)
1731{
1732 if (orientation == o) {
1733 item_list.insert(index + 1, QDockAreaLayoutItem(dockWidgetItem));
1734 } else {
1735#if !QT_CONFIG(tabbar)
1736 const int tabBarShape = 0;
1737#endif
1738 QDockAreaLayoutInfo *new_info
1739 = new QDockAreaLayoutInfo(sep, dockPos, orientation, tabBarShape, mainWindow);
1740 item_list[index].subinfo = new_info;
1741 new_info->item_list.append(QDockAreaLayoutItem(item_list.at(index).widgetItem));
1742 item_list[index].widgetItem = nullptr;
1743 new_info->item_list.append(QDockAreaLayoutItem(dockWidgetItem));
1744 }
1745}
1746
1747QDockAreaLayoutItem &QDockAreaLayoutInfo::item(const QList<int> &path)
1748{
1749 Q_ASSERT(!path.isEmpty());
1750 const int index = path.first();
1751 if (path.size() > 1) {
1752 const QDockAreaLayoutItem &item = item_list[index];
1753 Q_ASSERT(item.subinfo != nullptr);
1754 return item.subinfo->item(path.mid(1));
1755 }
1756 return item_list[index];
1757}
1758
1759QLayoutItem *QDockAreaLayoutInfo::itemAt(int *x, int index) const
1760{
1761 for (int i = 0; i < item_list.size(); ++i) {
1762 const QDockAreaLayoutItem &item = item_list.at(i);
1763 if (item.placeHolderItem != nullptr)
1764 continue;
1765 if (item.subinfo) {
1766 if (QLayoutItem *ret = item.subinfo->itemAt(x, index))
1767 return ret;
1768 } else if (item.widgetItem) {
1769 if ((*x)++ == index)
1770 return item.widgetItem;
1771 }
1772 }
1773 return nullptr;
1774}
1775
1776QLayoutItem *QDockAreaLayoutInfo::takeAt(int *x, int index)
1777{
1778 for (int i = 0; i < item_list.size(); ++i) {
1779 QDockAreaLayoutItem &item = item_list[i];
1780 if (item.placeHolderItem != nullptr)
1781 continue;
1782 else if (item.subinfo) {
1783 if (QLayoutItem *ret = item.subinfo->takeAt(x, index)) {
1784 unnest(i);
1785 return ret;
1786 }
1787 } else if (item.widgetItem) {
1788 if ((*x)++ == index) {
1789 QWidget *widget = item.widgetItem->widget();
1790 if (widget->isWidgetType())
1791 item.placeHolderItem = new QPlaceHolderItem(widget);
1792 else
1793 qCDebug(lcQpaDockWidgets) << widget << "is in destruction. No gap created.";
1794 QLayoutItem *ret = item.widgetItem;
1795 item.widgetItem = nullptr;
1796 if (item.size != -1)
1797 item.flags |= QDockAreaLayoutItem::KeepSize;
1798 return ret;
1799 }
1800 }
1801 }
1802 return nullptr;
1803}
1804
1805// Add a dock widget or dock widget group window to the item list
1806void QDockAreaLayoutInfo::add(QWidget *widget)
1807{
1808 // Do not add twice
1809 if (!indexOf(widget).isEmpty())
1810 return;
1811
1812 if (auto *dockWidget = qobject_cast<QDockWidget *>(widget)) {
1813 item_list.append(QDockAreaLayoutItem(new QDockWidgetItem(dockWidget)));
1814 return;
1815 }
1816
1817 if (auto *groupWindow = qobject_cast<QDockWidgetGroupWindow *>(widget)) {
1818 item_list.append(QDockAreaLayoutItem(new QDockWidgetGroupWindowItem(groupWindow)));
1819 return;
1820 }
1821
1822 qFatal("Coding error. Add supports only QDockWidget and QDockWidgetGroupWindow");
1823}
1824
1825void QDockAreaLayoutInfo::deleteAllLayoutItems()
1826{
1827 for (int i = 0; i < item_list.size(); ++i) {
1828 QDockAreaLayoutItem &item= item_list[i];
1829 if (item.subinfo) {
1830 item.subinfo->deleteAllLayoutItems();
1831 } else {
1832 delete item.widgetItem;
1833 item.widgetItem = nullptr;
1834 }
1835 }
1836}
1837
1838void QDockAreaLayoutInfo::saveState(QDataStream &stream) const
1839{
1840#if QT_CONFIG(tabbar)
1841 if (tabbed) {
1842 stream << (uchar) TabMarker;
1843
1844 // write the index in item_list of the widget that's currently on top.
1845 quintptr id = currentTabId();
1846 int index = -1;
1847 for (int i = 0; i < item_list.size(); ++i) {
1848 if (tabId(item_list.at(i)) == id) {
1849 index = i;
1850 break;
1851 }
1852 }
1853 stream << index;
1854 } else
1855#endif // QT_CONFIG(tabbar)
1856 {
1857 stream << (uchar) SequenceMarker;
1858 }
1859
1860 stream << (uchar) o << int(item_list.size());
1861
1862 for (int i = 0; i < item_list.size(); ++i) {
1863 const QDockAreaLayoutItem &item = item_list.at(i);
1864 if (item.widgetItem != nullptr) {
1865 stream << (uchar) WidgetMarker;
1866 QWidget *w = item.widgetItem->widget();
1867 QString name = w->objectName();
1868 if (Q_UNLIKELY(name.isEmpty())) {
1869 qWarning("QMainWindow::saveState(): 'objectName' not set for QDockWidget %p '%ls;",
1870 w, qUtf16Printable(w->windowTitle()));
1871 }
1872 stream << name;
1873
1874 uchar flags = 0;
1875 if (!w->isHidden())
1876 flags |= StateFlagVisible;
1877 if (w->isWindow())
1878 flags |= StateFlagFloating;
1879 stream << flags;
1880
1881 if (w->isWindow()) {
1882 const QRect geometry = w->geometry();
1883 stream << geometry.x() << geometry.y() << geometry.width() << geometry.height();
1884 } else {
1885 stream << item.pos << item.size << pick(o, item.minimumSize())
1886 << pick(o, item.maximumSize());
1887 }
1888 } else if (item.placeHolderItem != nullptr) {
1889 stream << (uchar) WidgetMarker;
1890 stream << item.placeHolderItem->objectName;
1891 uchar flags = 0;
1892 if (!item.placeHolderItem->hidden)
1893 flags |= StateFlagVisible;
1894 if (item.placeHolderItem->window)
1895 flags |= StateFlagFloating;
1896 stream << flags;
1897 if (item.placeHolderItem->window) {
1898 QRect r = item.placeHolderItem->topLevelRect;
1899 stream << r.x() << r.y() << r.width() << r.height();
1900 } else {
1901 stream << item.pos << item.size << (int)0 << (int)0;
1902 }
1903 } else if (item.subinfo != nullptr) {
1904 stream << (uchar) SequenceMarker << item.pos << item.size << pick(o, item.minimumSize()) << pick(o, item.maximumSize());
1905 item.subinfo->saveState(stream);
1906 }
1907 }
1908}
1909
1910static Qt::DockWidgetArea toDockWidgetArea(QInternal::DockPosition pos)
1911{
1912 switch (pos) {
1913 case QInternal::LeftDock: return Qt::LeftDockWidgetArea;
1914 case QInternal::RightDock: return Qt::RightDockWidgetArea;
1915 case QInternal::TopDock: return Qt::TopDockWidgetArea;
1916 case QInternal::BottomDock: return Qt::BottomDockWidgetArea;
1917 default: break;
1918 }
1919 return Qt::NoDockWidgetArea;
1920}
1921
1922bool QDockAreaLayoutInfo::restoreState(QDataStream &stream, QList<QDockWidget*> &widgets, QInternal::CallMode callMode)
1923{
1924 const bool testing = callMode == QInternal::Testing;
1925 uchar marker;
1926 stream >> marker;
1927 if (marker != TabMarker && marker != SequenceMarker)
1928 return false;
1929
1930#if QT_CONFIG(tabbar)
1931 tabbed = marker == TabMarker;
1932
1933 int index = -1;
1934 if (tabbed)
1935 stream >> index;
1936#endif
1937
1938 uchar orientation;
1939 stream >> orientation;
1940 o = static_cast<Qt::Orientation>(orientation);
1941
1942 int cnt;
1943 stream >> cnt;
1944
1945 for (int i = 0; i < cnt; ++i) {
1946 uchar nextMarker;
1947 stream >> nextMarker;
1948 if (nextMarker == WidgetMarker) {
1949 QString name;
1950 uchar flags;
1951 stream >> name >> flags;
1952 if (name.isEmpty()) {
1953 int dummy;
1954 stream >> dummy >> dummy >> dummy >> dummy;
1955 continue;
1956 }
1957
1958 QDockWidget *widget = nullptr;
1959 for (int j = 0; j < widgets.size(); ++j) {
1960 if (widgets.at(j)->objectName() == name) {
1961 widget = widgets.takeAt(j);
1962 break;
1963 }
1964 }
1965
1966 if (widget == nullptr) {
1967 QPlaceHolderItem *placeHolder = new QPlaceHolderItem;
1968 QDockAreaLayoutItem item(placeHolder);
1969
1970 placeHolder->window = flags & StateFlagFloating;
1971 placeHolder->hidden = !(flags & StateFlagVisible);
1972 if (placeHolder->window) {
1973 int x, y, w, h;
1974 stream >> x >> y >> w >> h;
1975 placeHolder->topLevelRect = QRect(x, y, w, h);
1976 } else {
1977 int dummy;
1978 stream >> item.pos >> item.size >> dummy >> dummy;
1979 }
1980 if (item.size != -1)
1981 item.flags |= QDockAreaLayoutItem::KeepSize;
1982 placeHolder->objectName = std::move(name);
1983 if (!testing)
1984 item_list.append(item);
1985 } else {
1986 QDockAreaLayoutItem item(new QDockWidgetItem(widget));
1987 if (flags & StateFlagFloating) {
1988 bool drawer = false;
1989
1990 if (!testing) {
1991 widget->hide();
1992 if (!drawer)
1993 widget->setFloating(true);
1994 }
1995
1996 int x, y, w, h;
1997 stream >> x >> y >> w >> h;
1998
1999 if (!testing)
2000 widget->setGeometry(QDockAreaLayout::constrainedRect(QRect(x, y, w, h), widget));
2001
2002 if (!testing) {
2003 widget->setVisible(flags & StateFlagVisible);
2004 item_list.append(item);
2005 }
2006 } else {
2007 int dummy;
2008 stream >> item.pos >> item.size >> dummy >> dummy;
2009 if (!testing) {
2010 item_list.append(item);
2011 widget->setFloating(false);
2012 widget->setVisible(flags & StateFlagVisible);
2013 emit widget->dockLocationChanged(toDockWidgetArea(dockPos));
2014 }
2015 }
2016 if (testing) {
2017 //was it is not really added to the layout, we need to delete the object here
2018 delete item.widgetItem;
2019 item.widgetItem = nullptr;
2020 }
2021 }
2022 } else if (nextMarker == SequenceMarker) {
2023 int dummy;
2024#if !QT_CONFIG(tabbar)
2025 const int tabBarShape = 0;
2026#endif
2027 QDockAreaLayoutItem item(new QDockAreaLayoutInfo(sep, dockPos, o,
2028 tabBarShape, mainWindow));
2029 stream >> item.pos >> item.size >> dummy >> dummy;
2030 //we need to make sure the element is in the list so the dock widget can eventually be docked correctly
2031 if (!testing)
2032 item_list.append(item);
2033
2034 //here we need to make sure we change the item in the item_list
2035 QDockAreaLayoutItem &lastItem = testing ? item : item_list.last();
2036
2037 if (!lastItem.subinfo->restoreState(stream, widgets, callMode))
2038 return false;
2039
2040 } else {
2041 return false;
2042 }
2043 }
2044
2045#if QT_CONFIG(tabbar)
2046 if (!testing && tabbed && index >= 0 && index < item_list.size()) {
2047 updateTabBar();
2048 setCurrentTabId(tabId(item_list.at(index)));
2049 }
2050 if (!testing && *sep == 1)
2051 updateSeparatorWidgets();
2052#endif
2053
2054 return true;
2055}
2056
2057#if QT_CONFIG(tabbar)
2058
2059static void raiseSeparatorWidget(QWidget *separatorWidget)
2060{
2061 Q_ASSERT(separatorWidget);
2062
2063#if QT_CONFIG(toolbar)
2064 // Raise the separator widget, but make sure it doesn't go above
2065 // an expanded toolbar, as that would break mouse event hit testing.
2066 Q_ASSERT(separatorWidget->parent());
2067 const auto toolBars = separatorWidget->parent()->findChildren<QToolBar*>(Qt::FindDirectChildrenOnly);
2068 for (auto *toolBar : toolBars) {
2069 if (auto *toolBarLayout = qobject_cast<QToolBarLayout*>(toolBar->layout())) {
2070 if (toolBarLayout->expanded) {
2071 separatorWidget->stackUnder(toolBar);
2072 return;
2073 }
2074 }
2075 }
2076#endif
2077
2078 separatorWidget->raise();
2079}
2080
2081
2082void QDockAreaLayoutInfo::updateSeparatorWidgets() const
2083{
2084 if (tabbed) {
2085 separatorWidgets.clear();
2086 return;
2087 }
2088
2089 int j = 0;
2090 for (int i = 0; i < item_list.size(); ++i) {
2091 const QDockAreaLayoutItem &item = item_list.at(i);
2092
2093 if (item.skip())
2094 continue;
2095
2096 int next = this->next(i);
2097 if ((item.flags & QDockAreaLayoutItem::GapItem)
2098 || (next != -1 && (item_list.at(next).flags & QDockAreaLayoutItem::GapItem)))
2099 continue;
2100
2101 if (item.subinfo) {
2102 item.subinfo->updateSeparatorWidgets();
2103 }
2104
2105 if (next == -1)
2106 break;
2107
2108 QWidget *sepWidget;
2109 if (j < separatorWidgets.size()) {
2110 sepWidget = separatorWidgets.at(j);
2111 if (!sepWidget) {
2112 qWarning("QDockAreaLayoutInfo::updateSeparatorWidgets: null separator widget");
2113 sepWidget = mainWindowLayout()->getSeparatorWidget();
2114 separatorWidgets[j] = sepWidget;
2115 }
2116 } else {
2117 sepWidget = mainWindowLayout()->getSeparatorWidget();
2118 separatorWidgets.append(sepWidget);
2119 }
2120 j++;
2121
2122 Q_ASSERT(sepWidget);
2123 raiseSeparatorWidget(sepWidget);
2124
2125 QRect sepRect = separatorRect(i).adjusted(-2, -2, 2, 2);
2126 sepWidget->setGeometry(sepRect);
2127 sepWidget->setMask( QRegion(separatorRect(i).translated( - sepRect.topLeft())));
2128 sepWidget->show();
2129 }
2130
2131 for (int k = j; k < separatorWidgets.size(); ++k) {
2132 separatorWidgets[k]->hide();
2133 }
2134 separatorWidgets.resize(j);
2135}
2136
2137/*! \internal
2138 reparent all the widgets contained in this layout portion to the
2139 specified parent. This is used to reparent dock widgets and tabbars
2140 to the floating window or the main window
2141 */
2142void QDockAreaLayoutInfo::reparentWidgets(QWidget *parent)
2143{
2144 if (tabBar)
2145 tabBar->setParent(parent);
2146
2147 for (int i = 0; i < item_list.size(); ++i) {
2148 const QDockAreaLayoutItem &item = item_list.at(i);
2149 if (item.flags & QDockAreaLayoutItem::GapItem)
2150 continue;
2151 if (item.subinfo)
2152 item.subinfo->reparentWidgets(parent);
2153 if (item.widgetItem) {
2154 QWidget *w = item.widgetItem->widget();
2155 if (qobject_cast<QDockWidgetGroupWindow *>(w))
2156 continue;
2157 if (w->parent() != parent) {
2158 bool hidden = w->isHidden();
2159 w->setParent(parent, w->windowFlags());
2160 if (!hidden)
2161 w->show();
2162 }
2163 }
2164 }
2165}
2166
2167//returns whether the tabbar is visible or not
2168bool QDockAreaLayoutInfo::updateTabBar() const
2169{
2170 if (!tabbed)
2171 return false;
2172
2173 QDockAreaLayoutInfo *that = const_cast<QDockAreaLayoutInfo*>(this);
2174
2175 if (that->tabBar == nullptr) {
2176 that->tabBar = mainWindowLayout()->getTabBar();
2177 that->tabBar->setShape(static_cast<QTabBar::Shape>(tabBarShape));
2178 that->tabBar->setDrawBase(true);
2179 }
2180
2181 const QSignalBlocker blocker(tabBar);
2182 bool gap = false;
2183
2184 const quintptr oldCurrentId = currentTabId();
2185
2186 int tab_idx = 0;
2187 for (int i = 0; i < item_list.size(); ++i) {
2188 const QDockAreaLayoutItem &item = item_list.at(i);
2189 if (item.skip())
2190 continue;
2191 if (item.flags & QDockAreaLayoutItem::GapItem) {
2192 gap = true;
2193 continue;
2194 }
2195 if (item.widgetItem == nullptr)
2196 continue;
2197
2198 QDockWidget *dw = qobject_cast<QDockWidget*>(item.widgetItem->widget());
2199 QString title = dw->d_func()->fixedWindowTitle;
2200 quintptr id = tabId(item);
2201 const QIcon windowIcon = dw->testAttribute(Qt::WA_SetWindowIcon) ? dw->windowIcon()
2202 : QIcon();
2203 if (tab_idx == tabBar->count()) {
2204 tabBar->insertTab(tab_idx, windowIcon, title);
2205#if QT_CONFIG(tooltip)
2206 tabBar->setTabToolTip(tab_idx, title);
2207#endif
2208 tabBar->setTabData(tab_idx, id);
2209 } else if (qvariant_cast<quintptr>(tabBar->tabData(tab_idx)) != id) {
2210 if (tab_idx + 1 < tabBar->count()
2211 && qvariant_cast<quintptr>(tabBar->tabData(tab_idx + 1)) == id)
2212 tabBar->removeTab(tab_idx);
2213 else {
2214 tabBar->insertTab(tab_idx, windowIcon, title);
2215#if QT_CONFIG(tooltip)
2216 tabBar->setTabToolTip(tab_idx, title);
2217#endif
2218 tabBar->setTabData(tab_idx, id);
2219 }
2220 }
2221
2222 if (title != tabBar->tabText(tab_idx)) {
2223 tabBar->setTabText(tab_idx, title);
2224#if QT_CONFIG(tooltip)
2225 tabBar->setTabToolTip(tab_idx, title);
2226#endif
2227 }
2228
2229 ++tab_idx;
2230 }
2231
2232 while (tab_idx < tabBar->count()) {
2233 tabBar->removeTab(tab_idx);
2234 }
2235
2236 if (oldCurrentId > 0 && currentTabId() != oldCurrentId)
2237 that->setCurrentTabId(oldCurrentId);
2238
2239 if (QDockWidgetGroupWindow *dwgw = qobject_cast<QDockWidgetGroupWindow *>(tabBar->parent()))
2240 dwgw->adjustFlags();
2241
2242 //returns if the tabbar is visible or not
2243 return ( (gap ? 1 : 0) + tabBar->count()) > 1;
2244}
2245
2246void QDockAreaLayoutInfo::setTabBarShape(int shape)
2247{
2248 if (shape == tabBarShape)
2249 return;
2250 tabBarShape = shape;
2251 if (tabBar != nullptr)
2252 tabBar->setShape(static_cast<QTabBar::Shape>(shape));
2253
2254 for (int i = 0; i < item_list.size(); ++i) {
2255 QDockAreaLayoutItem &item = item_list[i];
2256 if (item.subinfo != nullptr)
2257 item.subinfo->setTabBarShape(shape);
2258 }
2259}
2260
2261QSize QDockAreaLayoutInfo::tabBarMinimumSize() const
2262{
2263 if (!updateTabBar())
2264 return QSize(0, 0);
2265
2266 return tabBar->minimumSizeHint();
2267}
2268
2269QSize QDockAreaLayoutInfo::tabBarSizeHint() const
2270{
2271 if (!updateTabBar())
2272 return QSize(0, 0);
2273
2274 return tabBar->sizeHint();
2275}
2276
2277QSet<QTabBar*> QDockAreaLayoutInfo::usedTabBars() const
2278{
2279 QSet<QTabBar*> result;
2280
2281 if (tabbed) {
2282 updateTabBar();
2283 result.insert(tabBar);
2284 }
2285
2286 for (int i = 0; i < item_list.size(); ++i) {
2287 const QDockAreaLayoutItem &item = item_list.at(i);
2288 if (item.subinfo != nullptr)
2289 result += item.subinfo->usedTabBars();
2290 }
2291
2292 return result;
2293}
2294
2295// returns a set of all used separator widgets for this dockarelayout info
2296// and all subinfos
2297QSet<QWidget*> QDockAreaLayoutInfo::usedSeparatorWidgets() const
2298{
2299 QSet<QWidget*> result;
2300 const int numSeparatorWidgets = separatorWidgets.size();
2301 result.reserve(numSeparatorWidgets);
2302
2303 for (int i = 0; i < numSeparatorWidgets; ++i)
2304 result << separatorWidgets.at(i);
2305
2306 for (int i = 0; i < item_list.size(); ++i) {
2307 const QDockAreaLayoutItem &item = item_list.at(i);
2308 if (item.subinfo != nullptr)
2309 result += item.subinfo->usedSeparatorWidgets();
2310 }
2311
2312 return result;
2313}
2314
2315QRect QDockAreaLayoutInfo::tabContentRect() const
2316{
2317 if (!tabbed)
2318 return QRect();
2319
2320 QRect result = rect;
2321 QSize tbh = tabBarSizeHint();
2322
2323 if (!tbh.isNull()) {
2324 switch (tabBarShape) {
2325 case QTabBar::RoundedNorth:
2326 case QTabBar::TriangularNorth:
2327 result.adjust(0, tbh.height(), 0, 0);
2328 break;
2329 case QTabBar::RoundedSouth:
2330 case QTabBar::TriangularSouth:
2331 result.adjust(0, 0, 0, -tbh.height());
2332 break;
2333 case QTabBar::RoundedEast:
2334 case QTabBar::TriangularEast:
2335 result.adjust(0, 0, -tbh.width(), 0);
2336 break;
2337 case QTabBar::RoundedWest:
2338 case QTabBar::TriangularWest:
2339 result.adjust(tbh.width(), 0, 0, 0);
2340 break;
2341 default:
2342 break;
2343 }
2344 }
2345
2346 return result;
2347}
2348
2349int QDockAreaLayoutInfo::tabIndexToListIndex(int tabIndex) const
2350{
2351 Q_ASSERT(tabbed && tabBar);
2352 quintptr data = qvariant_cast<quintptr>(tabBar->tabData(tabIndex));
2353 for (int i = 0; i < item_list.size(); ++i) {
2354 if (tabId(item_list.at(i)) == data)
2355 return i;
2356 }
2357 return -1;
2358}
2359
2360void QDockAreaLayoutInfo::moveTab(int from, int to)
2361{
2362 item_list.move(tabIndexToListIndex(from), tabIndexToListIndex(to));
2363}
2364#endif // QT_CONFIG(tabbar)
2365
2366/******************************************************************************
2367** QDockAreaLayout
2368*/
2369
2370QDockAreaLayout::QDockAreaLayout(QMainWindow *win) : fallbackToSizeHints(true)
2371{
2372 mainWindow = win;
2373 sep = win->style()->pixelMetric(QStyle::PM_DockWidgetSeparatorExtent, nullptr, win);
2374#if QT_CONFIG(tabbar)
2375 const int tabShape = QTabBar::RoundedSouth;
2376#else
2377 const int tabShape = 0;
2378#endif
2379 docks[QInternal::LeftDock]
2380 = QDockAreaLayoutInfo(&sep, QInternal::LeftDock, Qt::Vertical, tabShape, win);
2381 docks[QInternal::RightDock]
2382 = QDockAreaLayoutInfo(&sep, QInternal::RightDock, Qt::Vertical, tabShape, win);
2383 docks[QInternal::TopDock]
2384 = QDockAreaLayoutInfo(&sep, QInternal::TopDock, Qt::Horizontal, tabShape, win);
2385 docks[QInternal::BottomDock]
2386 = QDockAreaLayoutInfo(&sep, QInternal::BottomDock, Qt::Horizontal, tabShape, win);
2387 centralWidgetItem = nullptr;
2388
2389
2390 corners[Qt::TopLeftCorner] = Qt::TopDockWidgetArea;
2391 corners[Qt::TopRightCorner] = Qt::TopDockWidgetArea;
2392 corners[Qt::BottomLeftCorner] = Qt::BottomDockWidgetArea;
2393 corners[Qt::BottomRightCorner] = Qt::BottomDockWidgetArea;
2394}
2395
2396bool QDockAreaLayout::isValid() const
2397{
2398 return rect.isValid();
2399}
2400
2401void QDockAreaLayout::saveState(QDataStream &stream) const
2402{
2403 stream << (uchar) DockWidgetStateMarker;
2404 int cnt = 0;
2405 for (int i = 0; i < QInternal::DockCount; ++i) {
2406 if (!docks[i].item_list.isEmpty())
2407 ++cnt;
2408 }
2409 stream << cnt;
2410 for (int i = 0; i < QInternal::DockCount; ++i) {
2411 if (docks[i].item_list.isEmpty())
2412 continue;
2413 stream << i << docks[i].rect.size();
2414 docks[i].saveState(stream);
2415 }
2416
2417 stream << centralWidgetRect.size();
2418
2419 for (int i = 0; i < 4; ++i)
2420 stream << static_cast<int>(corners[i]);
2421}
2422
2423bool QDockAreaLayout::restoreState(QDataStream &stream, const QList<QDockWidget*> &_dockwidgets, QInternal::CallMode callMode)
2424{
2425 QList<QDockWidget*> dockwidgets = _dockwidgets;
2426 const bool testing = callMode == QInternal::Testing;
2427 int cnt;
2428 stream >> cnt;
2429 for (int i = 0; i < cnt; ++i) {
2430 int pos;
2431 stream >> pos;
2432 QSize size;
2433 stream >> size;
2434 if (!testing) {
2435 docks[pos].rect = QRect(QPoint(0, 0), size);
2436 }
2437 if (!docks[pos].restoreState(stream, dockwidgets, callMode)) {
2438 stream.setStatus(QDataStream::ReadCorruptData);
2439 return false;
2440 }
2441 }
2442
2443 QSize size;
2444 stream >> size;
2445 centralWidgetRect = QRect(QPoint(0, 0), size);
2446
2447 bool ok = stream.status() == QDataStream::Ok;
2448
2449 if (ok) {
2450 int cornerData[4];
2451 for (int i = 0; i < 4; ++i)
2452 stream >> cornerData[i];
2453 if (stream.status() == QDataStream::Ok) {
2454 for (int i = 0; i < 4; ++i)
2455 corners[i] = static_cast<Qt::DockWidgetArea>(cornerData[i]);
2456 }
2457
2458 if (!testing)
2459 fallbackToSizeHints = false;
2460 }
2461
2462 return ok;
2463}
2464
2465QList<int> QDockAreaLayout::indexOfPlaceHolder(const QString &objectName) const
2466{
2467 for (int i = 0; i < QInternal::DockCount; ++i) {
2468 QList<int> result = docks[i].indexOfPlaceHolder(objectName);
2469 if (!result.isEmpty()) {
2470 result.prepend(i);
2471 return result;
2472 }
2473 }
2474 return QList<int>();
2475}
2476
2477QList<int> QDockAreaLayout::indexOf(const QWidget *dockWidget) const
2478{
2479 for (int i = 0; i < QInternal::DockCount; ++i) {
2480 QList<int> result = docks[i].indexOf(dockWidget);
2481 if (!result.isEmpty()) {
2482 result.prepend(i);
2483 return result;
2484 }
2485 }
2486 return QList<int>();
2487}
2488
2489QList<int> QDockAreaLayout::gapIndex(const QPoint &pos, bool disallowTabs) const
2490{
2491 QMainWindow::DockOptions opts = mainWindow->dockOptions();
2492 bool nestingEnabled = opts & QMainWindow::AllowNestedDocks;
2493 QDockAreaLayoutInfo::TabMode tabMode = QDockAreaLayoutInfo::NoTabs;
2494#if QT_CONFIG(tabbar)
2495 if (!disallowTabs) {
2496 if (opts & QMainWindow::AllowTabbedDocks || opts & QMainWindow::VerticalTabs)
2497 tabMode = QDockAreaLayoutInfo::AllowTabs;
2498 if (opts & QMainWindow::ForceTabbedDocks)
2499 tabMode = QDockAreaLayoutInfo::ForceTabs;
2500
2501 if (tabMode == QDockAreaLayoutInfo::ForceTabs)
2502 nestingEnabled = false;
2503 }
2504#endif
2505
2506
2507 for (int i = 0; i < QInternal::DockCount; ++i) {
2508 const QDockAreaLayoutInfo &info = docks[i];
2509
2510 if (!info.isEmpty() && info.rect.contains(pos)) {
2511 QList<int> result
2512 = docks[i].gapIndex(pos, nestingEnabled, tabMode);
2513 if (!result.isEmpty())
2514 result.prepend(i);
2515 return result;
2516 }
2517 }
2518
2519 for (int i = 0; i < QInternal::DockCount; ++i) {
2520 const QDockAreaLayoutInfo &info = docks[i];
2521
2522 if (info.isEmpty()) {
2523 const QRect r = gapRect(static_cast<QInternal::DockPosition>(i));
2524 if (r.contains(pos)) {
2525 if (opts & QMainWindow::ForceTabbedDocks && !info.item_list.isEmpty()) {
2526 //in case of ForceTabbedDocks, we pass -1 in order to force the gap to be tabbed
2527 //it mustn't be completely empty otherwise it won't work
2528 return QList<int>() << i << -1 << 0;
2529 } else {
2530 return QList<int>() << i << 0;
2531 }
2532 }
2533 }
2534 }
2535
2536 return QList<int>();
2537}
2538
2539QRect QDockAreaLayout::gapRect(QInternal::DockPosition dockPos) const
2540{
2541 Q_ASSERT_X(mainWindow, "QDockAreaLayout::gapRect", "Called without valid mainWindow pointer.");
2542
2543 // Determine gap size depending on MainWindow size (QTBUG-101657)
2544 const QSize gapSize = (mainWindow->size()/2).boundedTo(QSize(EmptyDropAreaSize, EmptyDropAreaSize));
2545
2546 // Warn if main window is too small to create proper docks.
2547 // Do not fail because this can be triggered by a user making MainWindow too small
2548 if (mainWindow->height() < (2 * sep)) {
2549 qCWarning(lcQpaDockWidgets,
2550 "QDockAreaLayout::gapRect: Main window height %i is too small. Docking will not be possible.",
2551 mainWindow->height());
2552
2553 }
2554 if (mainWindow->width() < (2 * sep)) {
2555 qCWarning(lcQpaDockWidgets,
2556 "QDockAreaLayout::gapRect: Main window width %i is too small. Docking will not be possible.",
2557 mainWindow->width());
2558 }
2559
2560 // Calculate rectangle of requested dock
2561 switch (dockPos) {
2562 case QInternal::LeftDock:
2563 return QRect(rect.left(), rect.top(), gapSize.width(), rect.height());
2564 case QInternal::RightDock:
2565 return QRect(rect.right() - gapSize.width(), rect.top(), gapSize.width(), rect.height());
2566 case QInternal::TopDock:
2567 return QRect(rect.left(), rect.top(), rect.width(), gapSize.height());
2568 case QInternal::BottomDock:
2569 return QRect(rect.left(), rect.bottom() - gapSize.height(), rect.width(), gapSize.height());
2570 case QInternal::DockCount:
2571 break;
2572 }
2573 return QRect();
2574}
2575
2576QList<int> QDockAreaLayout::findSeparator(const QPoint &pos) const
2577{
2578 QList<int> result;
2579 for (int i = 0; i < QInternal::DockCount; ++i) {
2580 const QDockAreaLayoutInfo &info = docks[i];
2581 if (info.isEmpty())
2582 continue;
2583 QRect rect = separatorRect(i);
2584 if (!rect.isNull() && sep == 1)
2585 rect.adjust(-2, -2, 2, 2);
2586 if (rect.contains(pos) && !info.hasFixedSize()) {
2587 result << i;
2588 break;
2589 } else if (info.rect.contains(pos)) {
2590 result = docks[i].findSeparator(pos);
2591 if (!result.isEmpty()) {
2592 result.prepend(i);
2593 break;
2594 }
2595 }
2596 }
2597
2598 return result;
2599}
2600
2601QDockAreaLayoutInfo *QDockAreaLayout::info(QWidget *widget)
2602{
2603 for (int i = 0; i < QInternal::DockCount; ++i) {
2604 if (QDockAreaLayoutInfo *result = docks[i].info(widget))
2605 return result;
2606 }
2607
2608 return nullptr;
2609}
2610
2611QDockAreaLayoutInfo *QDockAreaLayout::info(const QList<int> &path)
2612{
2613 Q_ASSERT(!path.isEmpty());
2614 const int index = path.first();
2615 Q_ASSERT(index >= 0 && index < QInternal::DockCount);
2616
2617 if (path.size() == 1)
2618 return &docks[index];
2619
2620 return docks[index].info(path.mid(1));
2621}
2622
2623const QDockAreaLayoutInfo *QDockAreaLayout::info(const QList<int> &path) const
2624{
2625 return const_cast<QDockAreaLayout*>(this)->info(path);
2626}
2627
2628QDockAreaLayoutItem &QDockAreaLayout::item(const QList<int> &path)
2629{
2630 Q_ASSERT(!path.isEmpty());
2631 const int index = path.first();
2632 Q_ASSERT(index >= 0 && index < QInternal::DockCount);
2633 return docks[index].item(path.mid(1));
2634}
2635
2636QRect QDockAreaLayout::itemRect(const QList<int> &path) const
2637{
2638 Q_ASSERT(!path.isEmpty());
2639 const int index = path.first();
2640 Q_ASSERT(index >= 0 && index < QInternal::DockCount);
2641 return docks[index].itemRect(path.mid(1));
2642}
2643
2644QRect QDockAreaLayout::separatorRect(int index) const
2645{
2646 const QDockAreaLayoutInfo &dock = docks[index];
2647 if (dock.isEmpty())
2648 return QRect();
2649 QRect r = dock.rect;
2650 switch (index) {
2651 case QInternal::LeftDock:
2652 return QRect(r.right() + 1, r.top(), sep, r.height());
2653 case QInternal::RightDock:
2654 return QRect(r.left() - sep, r.top(), sep, r.height());
2655 case QInternal::TopDock:
2656 return QRect(r.left(), r.bottom() + 1, r.width(), sep);
2657 case QInternal::BottomDock:
2658 return QRect(r.left(), r.top() - sep, r.width(), sep);
2659 default:
2660 break;
2661 }
2662 return QRect();
2663}
2664
2665QRect QDockAreaLayout::separatorRect(const QList<int> &path) const
2666{
2667 Q_ASSERT(!path.isEmpty());
2668
2669 const int index = path.first();
2670 Q_ASSERT(index >= 0 && index < QInternal::DockCount);
2671
2672 if (path.size() == 1)
2673 return separatorRect(index);
2674 else
2675 return docks[index].separatorRect(path.mid(1));
2676}
2677
2678bool QDockAreaLayout::insertGap(const QList<int> &path, QLayoutItem *dockWidgetItem)
2679{
2680 Q_ASSERT(!path.isEmpty());
2681 const int index = path.first();
2682 Q_ASSERT(index >= 0 && index < QInternal::DockCount);
2683 return docks[index].insertGap(path.mid(1), dockWidgetItem);
2684}
2685
2686QLayoutItem *QDockAreaLayout::plug(const QList<int> &path)
2687{
2688#if QT_CONFIG(tabbar)
2689 Q_ASSERT(!path.isEmpty());
2690 const int index = path.first();
2691 Q_ASSERT(index >= 0 && index < QInternal::DockCount);
2692 QLayoutItem *item = docks[index].plug(path.mid(1));
2693 docks[index].reparentWidgets(mainWindow);
2694 return item;
2695#else
2696 return nullptr;
2697#endif
2698}
2699
2700QLayoutItem *QDockAreaLayout::unplug(const QList<int> &path)
2701{
2702 Q_ASSERT(!path.isEmpty());
2703 const int index = path.first();
2704 Q_ASSERT(index >= 0 && index < QInternal::DockCount);
2705 return docks[index].unplug(path.mid(1));
2706}
2707
2708void QDockAreaLayout::remove(const QList<int> &path)
2709{
2710 Q_ASSERT(!path.isEmpty());
2711 const int index = path.first();
2712 Q_ASSERT(index >= 0 && index < QInternal::DockCount);
2713 docks[index].remove(path.mid(1));
2714}
2715
2716void QDockAreaLayout::removePlaceHolder(const QString &name)
2717{
2718 QList<int> index = indexOfPlaceHolder(name);
2719 if (!index.isEmpty())
2720 remove(index);
2721 const auto groups =
2722 mainWindow->findChildren<QDockWidgetGroupWindow *>(Qt::FindDirectChildrenOnly);
2723 for (QDockWidgetGroupWindow *dwgw : groups) {
2724 index = dwgw->layoutInfo()->indexOfPlaceHolder(name);
2725 if (!index.isEmpty()) {
2726 dwgw->layoutInfo()->remove(index);
2727 dwgw->destroyOrHideIfEmpty();
2728 }
2729 }
2730}
2731
2732static inline int qMax(int i1, int i2, int i3) { return qMax(i1, qMax(i2, i3)); }
2733
2734void QDockAreaLayout::getGrid(QList<QLayoutStruct> *_ver_struct_list,
2735 QList<QLayoutStruct> *_hor_struct_list)
2736{
2737 QSize center_hint(0, 0);
2738 QSize center_min(0, 0);
2739 QSize center_max(0, 0);
2740 const bool have_central = centralWidgetItem != nullptr && !centralWidgetItem->isEmpty();
2741 if (have_central) {
2742 center_hint = centralWidgetRect.size();
2743 if (!center_hint.isValid())
2744 center_hint = centralWidgetItem->sizeHint();
2745 center_min = centralWidgetItem->minimumSize();
2746 center_max = centralWidgetItem->maximumSize();
2747 }
2748
2749 QRect center_rect = rect;
2750 if (!docks[QInternal::LeftDock].isEmpty())
2751 center_rect.setLeft(rect.left() + docks[QInternal::LeftDock].rect.width() + sep);
2752 if (!docks[QInternal::TopDock].isEmpty())
2753 center_rect.setTop(rect.top() + docks[QInternal::TopDock].rect.height() + sep);
2754 if (!docks[QInternal::RightDock].isEmpty())
2755 center_rect.setRight(rect.right() - docks[QInternal::RightDock].rect.width() - sep);
2756 if (!docks[QInternal::BottomDock].isEmpty())
2757 center_rect.setBottom(rect.bottom() - docks[QInternal::BottomDock].rect.height() - sep);
2758
2759 QSize left_hint = docks[QInternal::LeftDock].size();
2760 if (left_hint.isNull() || fallbackToSizeHints)
2761 left_hint = docks[QInternal::LeftDock].sizeHint();
2762 QSize left_min = docks[QInternal::LeftDock].minimumSize();
2763 QSize left_max = docks[QInternal::LeftDock].maximumSize();
2764 left_hint = left_hint.boundedTo(left_max).expandedTo(left_min);
2765
2766 QSize right_hint = docks[QInternal::RightDock].size();
2767 if (right_hint.isNull() || fallbackToSizeHints)
2768 right_hint = docks[QInternal::RightDock].sizeHint();
2769 QSize right_min = docks[QInternal::RightDock].minimumSize();
2770 QSize right_max = docks[QInternal::RightDock].maximumSize();
2771 right_hint = right_hint.boundedTo(right_max).expandedTo(right_min);
2772
2773 QSize top_hint = docks[QInternal::TopDock].size();
2774 if (top_hint.isNull() || fallbackToSizeHints)
2775 top_hint = docks[QInternal::TopDock].sizeHint();
2776 QSize top_min = docks[QInternal::TopDock].minimumSize();
2777 QSize top_max = docks[QInternal::TopDock].maximumSize();
2778 top_hint = top_hint.boundedTo(top_max).expandedTo(top_min);
2779
2780 QSize bottom_hint = docks[QInternal::BottomDock].size();
2781 if (bottom_hint.isNull() || fallbackToSizeHints)
2782 bottom_hint = docks[QInternal::BottomDock].sizeHint();
2783 QSize bottom_min = docks[QInternal::BottomDock].minimumSize();
2784 QSize bottom_max = docks[QInternal::BottomDock].maximumSize();
2785 bottom_hint = bottom_hint.boundedTo(bottom_max).expandedTo(bottom_min);
2786
2787 if (_ver_struct_list != nullptr) {
2788 QList<QLayoutStruct> &ver_struct_list = *_ver_struct_list;
2789 ver_struct_list.resize(3);
2790
2791 // top --------------------------------------------------
2792 ver_struct_list[0].init();
2793 ver_struct_list[0].stretch = 0;
2794 ver_struct_list[0].sizeHint = top_hint.height();
2795 ver_struct_list[0].minimumSize = top_min.height();
2796 ver_struct_list[0].maximumSize = top_max.height();
2797 ver_struct_list[0].expansive = false;
2798 ver_struct_list[0].empty = docks[QInternal::TopDock].isEmpty();
2799 ver_struct_list[0].pos = docks[QInternal::TopDock].rect.top();
2800 ver_struct_list[0].size = docks[QInternal::TopDock].rect.height();
2801
2802 // center --------------------------------------------------
2803 ver_struct_list[1].init();
2804 ver_struct_list[1].stretch = center_hint.height();
2805
2806 bool tl_significant = corners[Qt::TopLeftCorner] == Qt::TopDockWidgetArea
2807 || docks[QInternal::TopDock].isEmpty();
2808 bool bl_significant = corners[Qt::BottomLeftCorner] == Qt::BottomDockWidgetArea
2809 || docks[QInternal::BottomDock].isEmpty();
2810 bool tr_significant = corners[Qt::TopRightCorner] == Qt::TopDockWidgetArea
2811 || docks[QInternal::TopDock].isEmpty();
2812 bool br_significant = corners[Qt::BottomRightCorner] == Qt::BottomDockWidgetArea
2813 || docks[QInternal::BottomDock].isEmpty();
2814
2815 int left = (tl_significant && bl_significant) ? left_hint.height() : 0;
2816 int right = (tr_significant && br_significant) ? right_hint.height() : 0;
2817 ver_struct_list[1].sizeHint = qMax(left, center_hint.height(), right);
2818
2819 left = (tl_significant && bl_significant) ? left_min.height() : 0;
2820 right = (tr_significant && br_significant) ? right_min.height() : 0;
2821 ver_struct_list[1].minimumSize = qMax(left, center_min.height(), right);
2822 ver_struct_list[1].maximumSize = center_max.height();
2823 ver_struct_list[1].expansive = have_central;
2824 ver_struct_list[1].empty = docks[QInternal::LeftDock].isEmpty()
2825 && !have_central
2826 && docks[QInternal::RightDock].isEmpty();
2827 ver_struct_list[1].pos = center_rect.top();
2828 ver_struct_list[1].size = center_rect.height();
2829
2830 // bottom --------------------------------------------------
2831 ver_struct_list[2].init();
2832 ver_struct_list[2].stretch = 0;
2833 ver_struct_list[2].sizeHint = bottom_hint.height();
2834 ver_struct_list[2].minimumSize = bottom_min.height();
2835 ver_struct_list[2].maximumSize = bottom_max.height();
2836 ver_struct_list[2].expansive = false;
2837 ver_struct_list[2].empty = docks[QInternal::BottomDock].isEmpty();
2838 ver_struct_list[2].pos = docks[QInternal::BottomDock].rect.top();
2839 ver_struct_list[2].size = docks[QInternal::BottomDock].rect.height();
2840
2841 for (int i = 0; i < 3; ++i) {
2842 ver_struct_list[i].sizeHint
2843 = qMax(ver_struct_list[i].sizeHint, ver_struct_list[i].minimumSize);
2844 }
2845 if (have_central && ver_struct_list[0].empty && ver_struct_list[2].empty)
2846 ver_struct_list[1].maximumSize = QWIDGETSIZE_MAX;
2847 }
2848
2849 if (_hor_struct_list != nullptr) {
2850 QList<QLayoutStruct> &hor_struct_list = *_hor_struct_list;
2851 hor_struct_list.resize(3);
2852
2853 // left --------------------------------------------------
2854 hor_struct_list[0].init();
2855 hor_struct_list[0].stretch = 0;
2856 hor_struct_list[0].sizeHint = left_hint.width();
2857 hor_struct_list[0].minimumSize = left_min.width();
2858 hor_struct_list[0].maximumSize = left_max.width();
2859 hor_struct_list[0].expansive = false;
2860 hor_struct_list[0].empty = docks[QInternal::LeftDock].isEmpty();
2861 hor_struct_list[0].pos = docks[QInternal::LeftDock].rect.left();
2862 hor_struct_list[0].size = docks[QInternal::LeftDock].rect.width();
2863
2864 // center --------------------------------------------------
2865 hor_struct_list[1].init();
2866 hor_struct_list[1].stretch = center_hint.width();
2867
2868 bool tl_significant = corners[Qt::TopLeftCorner] == Qt::LeftDockWidgetArea
2869 || docks[QInternal::LeftDock].isEmpty();
2870 bool tr_significant = corners[Qt::TopRightCorner] == Qt::RightDockWidgetArea
2871 || docks[QInternal::RightDock].isEmpty();
2872 bool bl_significant = corners[Qt::BottomLeftCorner] == Qt::LeftDockWidgetArea
2873 || docks[QInternal::LeftDock].isEmpty();
2874 bool br_significant = corners[Qt::BottomRightCorner] == Qt::RightDockWidgetArea
2875 || docks[QInternal::RightDock].isEmpty();
2876
2877 int top = (tl_significant && tr_significant) ? top_hint.width() : 0;
2878 int bottom = (bl_significant && br_significant) ? bottom_hint.width() : 0;
2879 hor_struct_list[1].sizeHint = qMax(top, center_hint.width(), bottom);
2880
2881 top = (tl_significant && tr_significant) ? top_min.width() : 0;
2882 bottom = (bl_significant && br_significant) ? bottom_min.width() : 0;
2883 hor_struct_list[1].minimumSize = qMax(top, center_min.width(), bottom);
2884
2885 hor_struct_list[1].maximumSize = center_max.width();
2886 hor_struct_list[1].expansive = have_central;
2887 hor_struct_list[1].empty = !have_central;
2888 hor_struct_list[1].pos = center_rect.left();
2889 hor_struct_list[1].size = center_rect.width();
2890
2891 // right --------------------------------------------------
2892 hor_struct_list[2].init();
2893 hor_struct_list[2].stretch = 0;
2894 hor_struct_list[2].sizeHint = right_hint.width();
2895 hor_struct_list[2].minimumSize = right_min.width();
2896 hor_struct_list[2].maximumSize = right_max.width();
2897 hor_struct_list[2].expansive = false;
2898 hor_struct_list[2].empty = docks[QInternal::RightDock].isEmpty();
2899 hor_struct_list[2].pos = docks[QInternal::RightDock].rect.left();
2900 hor_struct_list[2].size = docks[QInternal::RightDock].rect.width();
2901
2902 for (int i = 0; i < 3; ++i) {
2903 hor_struct_list[i].sizeHint
2904 = qMax(hor_struct_list[i].sizeHint, hor_struct_list[i].minimumSize);
2905 }
2906 if (have_central && hor_struct_list[0].empty && hor_struct_list[2].empty)
2907 hor_struct_list[1].maximumSize = QWIDGETSIZE_MAX;
2908
2909 }
2910}
2911
2912void QDockAreaLayout::setGrid(QList<QLayoutStruct> *ver_struct_list,
2913 QList<QLayoutStruct> *hor_struct_list)
2914{
2915
2916 // top ---------------------------------------------------
2917
2918 if (!docks[QInternal::TopDock].isEmpty()) {
2919 QRect r = docks[QInternal::TopDock].rect;
2920 if (hor_struct_list != nullptr) {
2921 r.setLeft(corners[Qt::TopLeftCorner] == Qt::TopDockWidgetArea
2922 || docks[QInternal::LeftDock].isEmpty()
2923 ? rect.left() : hor_struct_list->at(1).pos);
2924 r.setRight(corners[Qt::TopRightCorner] == Qt::TopDockWidgetArea
2925 || docks[QInternal::RightDock].isEmpty()
2926 ? rect.right() : hor_struct_list->at(2).pos - sep - 1);
2927 }
2928 if (ver_struct_list != nullptr) {
2929 r.setTop(rect.top());
2930 r.setBottom(ver_struct_list->at(1).pos - sep - 1);
2931 }
2932 docks[QInternal::TopDock].rect = r;
2933 docks[QInternal::TopDock].fitItems();
2934 }
2935
2936 // bottom ---------------------------------------------------
2937
2938 if (!docks[QInternal::BottomDock].isEmpty()) {
2939 QRect r = docks[QInternal::BottomDock].rect;
2940 if (hor_struct_list != nullptr) {
2941 r.setLeft(corners[Qt::BottomLeftCorner] == Qt::BottomDockWidgetArea
2942 || docks[QInternal::LeftDock].isEmpty()
2943 ? rect.left() : hor_struct_list->at(1).pos);
2944 r.setRight(corners[Qt::BottomRightCorner] == Qt::BottomDockWidgetArea
2945 || docks[QInternal::RightDock].isEmpty()
2946 ? rect.right() : hor_struct_list->at(2).pos - sep - 1);
2947 }
2948 if (ver_struct_list != nullptr) {
2949 r.setTop(ver_struct_list->at(2).pos);
2950 r.setBottom(rect.bottom());
2951 }
2952 docks[QInternal::BottomDock].rect = r;
2953 docks[QInternal::BottomDock].fitItems();
2954 }
2955
2956 // left ---------------------------------------------------
2957
2958 if (!docks[QInternal::LeftDock].isEmpty()) {
2959 QRect r = docks[QInternal::LeftDock].rect;
2960 if (hor_struct_list != nullptr) {
2961 r.setLeft(rect.left());
2962 r.setRight(hor_struct_list->at(1).pos - sep - 1);
2963 }
2964 if (ver_struct_list != nullptr) {
2965 r.setTop(corners[Qt::TopLeftCorner] == Qt::LeftDockWidgetArea
2966 || docks[QInternal::TopDock].isEmpty()
2967 ? rect.top() : ver_struct_list->at(1).pos);
2968 r.setBottom(corners[Qt::BottomLeftCorner] == Qt::LeftDockWidgetArea
2969 || docks[QInternal::BottomDock].isEmpty()
2970 ? rect.bottom() : ver_struct_list->at(2).pos - sep - 1);
2971 }
2972 docks[QInternal::LeftDock].rect = r;
2973 docks[QInternal::LeftDock].fitItems();
2974 }
2975
2976 // right ---------------------------------------------------
2977
2978 if (!docks[QInternal::RightDock].isEmpty()) {
2979 QRect r = docks[QInternal::RightDock].rect;
2980 if (hor_struct_list != nullptr) {
2981 r.setLeft(hor_struct_list->at(2).pos);
2982 r.setRight(rect.right());
2983 }
2984 if (ver_struct_list != nullptr) {
2985 r.setTop(corners[Qt::TopRightCorner] == Qt::RightDockWidgetArea
2986 || docks[QInternal::TopDock].isEmpty()
2987 ? rect.top() : ver_struct_list->at(1).pos);
2988 r.setBottom(corners[Qt::BottomRightCorner] == Qt::RightDockWidgetArea
2989 || docks[QInternal::BottomDock].isEmpty()
2990 ? rect.bottom() : ver_struct_list->at(2).pos - sep - 1);
2991 }
2992 docks[QInternal::RightDock].rect = r;
2993 docks[QInternal::RightDock].fitItems();
2994 }
2995
2996 // center ---------------------------------------------------
2997
2998 if (hor_struct_list != nullptr) {
2999 centralWidgetRect.setLeft(hor_struct_list->at(1).pos);
3000 centralWidgetRect.setWidth(hor_struct_list->at(1).size);
3001 }
3002 if (ver_struct_list != nullptr) {
3003 centralWidgetRect.setTop(ver_struct_list->at(1).pos);
3004 centralWidgetRect.setHeight(ver_struct_list->at(1).size);
3005 }
3006}
3007
3008void QDockAreaLayout::fitLayout()
3009{
3010 QList<QLayoutStruct> ver_struct_list(3);
3011 QList<QLayoutStruct> hor_struct_list(3);
3012 getGrid(&ver_struct_list, &hor_struct_list);
3013
3014 qGeomCalc(ver_struct_list, 0, 3, rect.top(), rect.height(), sep);
3015 qGeomCalc(hor_struct_list, 0, 3, rect.left(), rect.width(), sep);
3016
3017 setGrid(&ver_struct_list, &hor_struct_list);
3018}
3019
3020void QDockAreaLayout::clear()
3021{
3022 for (int i = 0; i < QInternal::DockCount; ++i)
3023 docks[i].clear();
3024
3025 rect = QRect();
3026 centralWidgetRect = QRect();
3027}
3028
3029template<typename SizePMF, typename CenterPMF>
3030QSize QDockAreaLayout::size_helper(SizePMF sizeFn, CenterPMF centerFn) const
3031{
3032 int left_sep = 0;
3033 int right_sep = 0;
3034 int top_sep = 0;
3035 int bottom_sep = 0;
3036
3037 if (centralWidgetItem != nullptr) {
3038 left_sep = docks[QInternal::LeftDock].isEmpty() ? 0 : sep;
3039 right_sep = docks[QInternal::RightDock].isEmpty() ? 0 : sep;
3040 top_sep = docks[QInternal::TopDock].isEmpty() ? 0 : sep;
3041 bottom_sep = docks[QInternal::BottomDock].isEmpty() ? 0 : sep;
3042 }
3043
3044 const QSize left = (docks[QInternal::LeftDock].*sizeFn)() + QSize(left_sep, 0);
3045 const QSize right = (docks[QInternal::RightDock].*sizeFn)() + QSize(right_sep, 0);
3046 const QSize top = (docks[QInternal::TopDock].*sizeFn)() + QSize(0, top_sep);
3047 const QSize bottom = (docks[QInternal::BottomDock].*sizeFn)() + QSize(0, bottom_sep);
3048 const QSize center = centralWidgetItem == nullptr
3049 ? QSize(0, 0) : (centralWidgetItem->*centerFn)();
3050
3051 int row1 = top.width();
3052 int row2 = left.width() + center.width() + right.width();
3053 int row3 = bottom.width();
3054 int col1 = left.height();
3055 int col2 = top.height() + center.height() + bottom.height();
3056 int col3 = right.height();
3057
3058 if (corners[Qt::TopLeftCorner] == Qt::LeftDockWidgetArea)
3059 row1 += left.width();
3060 else
3061 col1 += top.height();
3062
3063 if (corners[Qt::TopRightCorner] == Qt::RightDockWidgetArea)
3064 row1 += right.width();
3065 else
3066 col3 += top.height();
3067
3068 if (corners[Qt::BottomLeftCorner] == Qt::LeftDockWidgetArea)
3069 row3 += left.width();
3070 else
3071 col1 += bottom.height();
3072
3073 if (corners[Qt::BottomRightCorner] == Qt::RightDockWidgetArea)
3074 row3 += right.width();
3075 else
3076 col3 += bottom.height();
3077
3078 return QSize(qMax(row1, row2, row3), qMax(col1, col2, col3));
3079}
3080
3081QSize QDockAreaLayout::sizeHint() const
3082{
3083 return size_helper(&QDockAreaLayoutInfo::sizeHint, &QLayoutItem::sizeHint);
3084}
3085
3086QSize QDockAreaLayout::minimumSize() const
3087{
3088 return size_helper(&QDockAreaLayoutInfo::minimumSize, &QLayoutItem::minimumSize);
3089}
3090
3091/*!
3092 \internal
3093
3094 Returns the smallest size that doesn't change the size of any of the dock areas.
3095*/
3096QSize QDockAreaLayout::minimumStableSize() const
3097{
3098 return size_helper(&QDockAreaLayoutInfo::size, &QLayoutItem::minimumSize);
3099}
3100
3101/*! \internal
3102 Try to fit the given rectangle \a rect on the screen which contains
3103 the window \a widget.
3104 Used to compute the geometry of a dragged a dock widget that should
3105 be shown with \a rect, but needs to be visible on the screen
3106 */
3107QRect QDockAreaLayout::constrainedRect(QRect rect, QWidget* widget)
3108{
3109 QScreen *screen = nullptr;
3110 if (QGuiApplication::primaryScreen()->virtualSiblings().size() > 1)
3111 screen = QGuiApplication::screenAt(rect.topLeft());
3112 if (!screen)
3113 screen = widget->screen();
3114
3115 const QRect screenRect = screen->geometry();
3116 if (screenRect.isValid()) {
3117 rect.setWidth(qMin(rect.width(), screenRect.width()));
3118 rect.setHeight(qMin(rect.height(), screenRect.height()));
3119 rect.moveLeft(qMax(rect.left(), screenRect.left()));
3120 rect.moveTop(qMax(rect.top(), screenRect.top()));
3121 rect.moveRight(qMin(rect.right(), screenRect.right()));
3122 rect.moveBottom(qMin(rect.bottom(), screenRect.bottom()));
3123 }
3124
3125 return rect;
3126}
3127
3128bool QDockAreaLayout::restoreDockWidget(QDockWidget *dockWidget)
3129{
3130 QDockAreaLayoutItem *item = nullptr;
3131 const auto groups =
3132 mainWindow->findChildren<QDockWidgetGroupWindow *>(Qt::FindDirectChildrenOnly);
3133 for (QDockWidgetGroupWindow *dwgw : groups) {
3134 QList<int> index = dwgw->layoutInfo()->indexOfPlaceHolder(dockWidget->objectName());
3135 if (!index.isEmpty()) {
3136 dockWidget->setParent(dwgw);
3137 item = const_cast<QDockAreaLayoutItem *>(&dwgw->layoutInfo()->item(index));
3138 break;
3139 }
3140 }
3141 if (!item) {
3142 QList<int> index = indexOfPlaceHolder(dockWidget->objectName());
3143 if (index.isEmpty())
3144 return false;
3145 item = const_cast<QDockAreaLayoutItem *>(&this->item(index));
3146 }
3147
3148 QPlaceHolderItem *placeHolder = item->placeHolderItem;
3149 Q_ASSERT(placeHolder != nullptr);
3150
3151 item->widgetItem = new QDockWidgetItem(dockWidget);
3152
3153 if (placeHolder->window) {
3154 const QRect r = constrainedRect(placeHolder->topLevelRect, dockWidget);
3155 dockWidget->d_func()->setWindowState(QDockWidgetPrivate::WindowStates(
3156 {QDockWidgetPrivate::WindowState::Floating,
3157 QDockWidgetPrivate::WindowState::Unplug}), r);
3158 }
3159 dockWidget->setVisible(!placeHolder->hidden);
3160
3161 item->placeHolderItem = nullptr;
3162 delete placeHolder;
3163
3164 return true;
3165}
3166
3167void QDockAreaLayout::addDockWidget(QInternal::DockPosition pos, QDockWidget *dockWidget,
3168 Qt::Orientation orientation)
3169{
3170 QLayoutItem *dockWidgetItem = new QDockWidgetItem(dockWidget);
3171 QDockAreaLayoutInfo &info = docks[pos];
3172 if (orientation == info.o || info.item_list.size() <= 1) {
3173 // empty dock areas, or dock areas containing exactly one widget can have their orientation
3174 // switched.
3175 info.o = orientation;
3176
3177 QDockAreaLayoutItem new_item(dockWidgetItem);
3178 info.item_list.append(new_item);
3179#if QT_CONFIG(tabbar)
3180 if (info.tabbed && !new_item.skip()) {
3181 info.updateTabBar();
3182 info.setCurrentTabId(tabId(new_item));
3183 }
3184#endif
3185 } else {
3186#if QT_CONFIG(tabbar)
3187 int tbshape = info.tabBarShape;
3188#else
3189 int tbshape = 0;
3190#endif
3191 QDockAreaLayoutInfo new_info(&sep, pos, orientation, tbshape, mainWindow);
3192 new_info.item_list.append(QDockAreaLayoutItem(new QDockAreaLayoutInfo(info)));
3193 new_info.item_list.append(QDockAreaLayoutItem(dockWidgetItem));
3194 info = new_info;
3195 }
3196
3197 removePlaceHolder(dockWidget->objectName());
3198}
3199
3200#if QT_CONFIG(tabbar)
3201void QDockAreaLayout::tabifyDockWidget(QDockWidget *first, QDockWidget *second)
3202{
3203 const QList<int> path = indexOf(first);
3204 if (path.isEmpty())
3205 return;
3206
3207 QDockAreaLayoutInfo *info = this->info(path);
3208 Q_ASSERT(info != nullptr);
3209 info->tab(path.last(), new QDockWidgetItem(second));
3210
3211 removePlaceHolder(second->objectName());
3212}
3213#endif // QT_CONFIG(tabbar)
3214
3215void QDockAreaLayout::resizeDocks(const QList<QDockWidget *> &docks,
3216 const QList<int> &sizes, Qt::Orientation o)
3217{
3218 if (Q_UNLIKELY(docks.size() != sizes.size())) {
3219 qWarning("QMainWidget::resizeDocks: size of the lists are not the same");
3220 return;
3221 }
3222 int count = docks.size();
3223 fallbackToSizeHints = false;
3224 for (int i = 0; i < count; ++i) {
3225 QList<int> path = indexOf(docks[i]);
3226 if (Q_UNLIKELY(path.isEmpty())) {
3227 qWarning("QMainWidget::resizeDocks: one QDockWidget is not part of the layout");
3228 continue;
3229 }
3230 int size = sizes[i];
3231 if (Q_UNLIKELY(size <= 0)) {
3232 qWarning("QMainWidget::resizeDocks: all sizes need to be larger than 0");
3233 size = 1;
3234 }
3235
3236 while (path.size() > 1) {
3237 QDockAreaLayoutInfo *info = this->info(path);
3238#if QT_CONFIG(tabbar)
3239 if (!info->tabbed && info->o == o) {
3240 info->item_list[path.constLast()].size = size;
3241 int totalSize = 0;
3242 for (const QDockAreaLayoutItem &item : std::as_const(info->item_list)) {
3243 if (!item.skip()) {
3244 if (totalSize != 0)
3245 totalSize += sep;
3246 totalSize += item.size == -1 ? pick(o, item.sizeHint()) : item.size;
3247 }
3248 }
3249 size = totalSize;
3250 }
3251#endif // QT_CONFIG(tabbar)
3252 path.removeLast();
3253 }
3254
3255 const int dockNum = path.constFirst();
3256 Q_ASSERT(dockNum < QInternal::DockCount);
3257 QRect &r = this->docks[dockNum].rect;
3258 QSize s = r.size();
3259 rpick(o, s) = size;
3260 r.setSize(s);
3261 }
3262}
3263
3264void QDockAreaLayout::splitDockWidget(QDockWidget *after,
3265 QDockWidget *dockWidget,
3266 Qt::Orientation orientation)
3267{
3268 const QList<int> path = indexOf(after);
3269 if (path.isEmpty())
3270 return;
3271
3272 QDockAreaLayoutInfo *info = this->info(path);
3273 Q_ASSERT(info != nullptr);
3274 info->split(path.last(), orientation, new QDockWidgetItem(dockWidget));
3275
3276 removePlaceHolder(dockWidget->objectName());
3277}
3278
3279void QDockAreaLayout::apply(bool animate)
3280{
3281 QWidgetAnimator &widgetAnimator = qt_mainwindow_layout(mainWindow)->widgetAnimator;
3282
3283 for (int i = 0; i < QInternal::DockCount; ++i)
3284 docks[i].apply(animate);
3285 if (centralWidgetItem != nullptr && !centralWidgetItem->isEmpty()) {
3286 widgetAnimator.animate(centralWidgetItem->widget(), centralWidgetRect,
3287 animate);
3288 }
3289#if QT_CONFIG(tabbar)
3290 if (sep == 1)
3291 updateSeparatorWidgets();
3292#endif // QT_CONFIG(tabbar)
3293}
3294
3295void QDockAreaLayout::paintSeparators(QPainter *p, QWidget *widget,
3296 const QRegion &clip,
3297 const QPoint &mouse) const
3298{
3299 for (int i = 0; i < QInternal::DockCount; ++i) {
3300 const QDockAreaLayoutInfo &dock = docks[i];
3301 if (dock.isEmpty())
3302 continue;
3303 QRect r = separatorRect(i);
3304 if (clip.contains(r) && !dock.hasFixedSize()) {
3305 Qt::Orientation opposite = dock.o == Qt::Horizontal
3306 ? Qt::Vertical : Qt::Horizontal;
3307 paintSep(p, widget, r, opposite, r.contains(mouse));
3308 }
3309 if (clip.contains(dock.rect))
3310 dock.paintSeparators(p, widget, clip, mouse);
3311 }
3312}
3313
3314QRegion QDockAreaLayout::separatorRegion() const
3315{
3316 QRegion result;
3317
3318 for (int i = 0; i < QInternal::DockCount; ++i) {
3319 const QDockAreaLayoutInfo &dock = docks[i];
3320 if (dock.isEmpty())
3321 continue;
3322 result |= separatorRect(i);
3323 result |= dock.separatorRegion();
3324 }
3325
3326 return result;
3327}
3328
3329int QDockAreaLayout::separatorMove(const QList<int> &separator, const QPoint &origin,
3330 const QPoint &dest)
3331{
3332 int delta = 0;
3333 const auto dockPosition = static_cast<QInternal::DockPosition>(separator.last());
3334 const bool isHorizontal =
3335 dockPosition == QInternal::LeftDock || dockPosition == QInternal::RightDock;
3336 const bool isLeftOrTop = dockPosition == QInternal::LeftDock || dockPosition == QInternal::TopDock;
3337 const bool separatorIsWithinDock = separator.size() > 1;
3338
3339 if (separatorIsWithinDock) {
3340 // The dock area contains more than one dock widget and therefore an internal separator,
3341 // which is being moved. The move changes the sizes of the dock widgets docked in the dock area.
3342 // The dock area's geometry remains unchanged.
3343 QDockAreaLayoutInfo *info = this->info(separator);
3344 delta = pick(info->o, dest - origin);
3345 if (delta != 0)
3346 delta = info->separatorMove(dockPosition, delta);
3347 info->apply(/* animate = */ false);
3348 return delta;
3349 }
3350
3351 // The dock area's external separator is moved. The move changes the size of the contained
3352 // dock widgets, as well as the size of neighbouring dock areas and their docked dock widgets.
3353 // If the move shrinks the contained dock widgets to their minimum size,
3354 // the entire dock area will attempted to be moved, retaining the minumum size.
3355 // If the contained dock widgets have to be shrunk below minimum size, they will be collapsed.
3356
3357 QList<QLayoutStruct> list;
3358
3359 if (isHorizontal)
3360 getGrid(nullptr, &list);
3361 else
3362 getGrid(&list, nullptr);
3363
3364 const int sep_index = isLeftOrTop ? 0 : 1;
3365 const Qt::Orientation o = isHorizontal ? Qt::Horizontal : Qt::Vertical;
3366
3367 delta = pick(o, dest - origin);
3368 delta = separatorMoveHelper(list, sep_index, delta, sep);
3369
3370 fallbackToSizeHints = false;
3371
3372 if (isHorizontal)
3373 setGrid(nullptr, &list);
3374 else
3375 setGrid(&list, nullptr);
3376
3377 apply(/* animate = */ false);
3378
3379 return delta;
3380}
3381
3382int QDockAreaLayoutInfo::separatorMove(const QList<int> &separator, const QPoint &origin,
3383 const QPoint &dest)
3384{
3385 int delta = 0;
3386 int index = separator.last();
3387 QDockAreaLayoutInfo *info = this->info(separator);
3388 delta = pick(info->o, dest - origin);
3389 if (delta != 0)
3390 delta = info->separatorMove(index, delta);
3391 info->apply(/* animate = */ false);
3392 return delta;
3393}
3394
3395#if QT_CONFIG(tabbar)
3396// Sets the correct positions for the separator widgets
3397// Allocates new separator widgets with getSeparatorWidget
3398void QDockAreaLayout::updateSeparatorWidgets() const
3399{
3400 int j = 0;
3401
3402 for (int i = 0; i < QInternal::DockCount; ++i) {
3403 const QDockAreaLayoutInfo &dock = docks[i];
3404 if (dock.isEmpty())
3405 continue;
3406
3407 QWidget *sepWidget;
3408 if (j < separatorWidgets.size()) {
3409 sepWidget = separatorWidgets.at(j);
3410 if (!sepWidget) {
3411 qWarning("QDockAreaLayout::updateSeparatorWidgets: null separator widget");
3412 sepWidget = qt_mainwindow_layout(mainWindow)->getSeparatorWidget();
3413 separatorWidgets[j] = sepWidget;
3414 }
3415 } else {
3416 sepWidget = qt_mainwindow_layout(mainWindow)->getSeparatorWidget();
3417 separatorWidgets.append(sepWidget);
3418 }
3419 j++;
3420
3421 Q_ASSERT(sepWidget);
3422 raiseSeparatorWidget(sepWidget);
3423
3424 QRect sepRect = separatorRect(i).adjusted(-2, -2, 2, 2);
3425 sepWidget->setGeometry(sepRect);
3426 sepWidget->setMask( QRegion(separatorRect(i).translated( - sepRect.topLeft())));
3427 sepWidget->show();
3428 }
3429 for (int i = j; i < separatorWidgets.size(); ++i)
3430 separatorWidgets.at(i)->hide();
3431
3432 separatorWidgets.resize(j);
3433}
3434#endif // QT_CONFIG(tabbar)
3435
3436QLayoutItem *QDockAreaLayout::itemAt(int *x, int index) const
3437{
3438 Q_ASSERT(x != nullptr);
3439
3440 for (int i = 0; i < QInternal::DockCount; ++i) {
3441 const QDockAreaLayoutInfo &dock = docks[i];
3442 if (QLayoutItem *ret = dock.itemAt(x, index))
3443 return ret;
3444 }
3445
3446 if (centralWidgetItem && (*x)++ == index)
3447 return centralWidgetItem;
3448
3449 return nullptr;
3450}
3451
3452QLayoutItem *QDockAreaLayout::takeAt(int *x, int index)
3453{
3454 Q_ASSERT(x != nullptr);
3455
3456 for (int i = 0; i < QInternal::DockCount; ++i) {
3457 QDockAreaLayoutInfo &dock = docks[i];
3458 if (QLayoutItem *ret = dock.takeAt(x, index))
3459 return ret;
3460 }
3461
3462 if (centralWidgetItem && (*x)++ == index) {
3463 QLayoutItem *ret = centralWidgetItem;
3464 centralWidgetItem = nullptr;
3465 return ret;
3466 }
3467
3468 return nullptr;
3469}
3470
3471void QDockAreaLayout::deleteAllLayoutItems()
3472{
3473 for (int i = 0; i < QInternal::DockCount; ++i)
3474 docks[i].deleteAllLayoutItems();
3475}
3476
3477#if QT_CONFIG(tabbar)
3478QSet<QTabBar*> QDockAreaLayout::usedTabBars() const
3479{
3480 QSet<QTabBar*> result;
3481 for (int i = 0; i < QInternal::DockCount; ++i) {
3482 const QDockAreaLayoutInfo &dock = docks[i];
3483 result += dock.usedTabBars();
3484 }
3485 return result;
3486}
3487
3488// Returns the set of all used separator widgets
3489QSet<QWidget*> QDockAreaLayout::usedSeparatorWidgets() const
3490{
3491 QSet<QWidget*> result;
3492 const int numSeparators = separatorWidgets.size();
3493 result.reserve(numSeparators);
3494 for (int i = 0; i < numSeparators; ++i)
3495 result << separatorWidgets.at(i);
3496 for (int i = 0; i < QInternal::DockCount; ++i) {
3497 const QDockAreaLayoutInfo &dock = docks[i];
3498 result += dock.usedSeparatorWidgets();
3499 }
3500 return result;
3501}
3502#endif
3503
3504QRect QDockAreaLayout::gapRect(const QList<int> &path) const
3505{
3506 const QDockAreaLayoutInfo *info = this->info(path);
3507 if (info == nullptr)
3508 return QRect();
3509 int index = path.last();
3510 if (index < 0 || index >= info->item_list.size())
3511 return QRect();
3512 return info->itemRect(index, true);
3513}
3514
3515void QDockAreaLayout::keepSize(QDockWidget *w)
3516{
3517 QList<int> path = indexOf(w);
3518 if (path.isEmpty())
3519 return;
3520 QDockAreaLayoutItem &item = this->item(path);
3521 if (item.size != -1)
3522 item.flags |= QDockAreaLayoutItem::KeepSize;
3523}
3524
3525void QDockAreaLayout::styleChangedEvent()
3526{
3527 sep = mainWindow->style()->pixelMetric(QStyle::PM_DockWidgetSeparatorExtent, nullptr, mainWindow);
3528 if (isValid())
3529 fitLayout();
3530}
3531
3532QT_END_NAMESPACE
friend class QWidget
Definition qpainter.h:432
Combined button and popup list for selecting options.
static int grow(QLayoutStruct &ls, int delta)
static QRect dockedGeometry(QWidget *widget)
static Qt::DockWidgetArea toDockWidgetArea(QInternal::DockPosition pos)
static int realMinSize(const QDockAreaLayoutInfo &info)
static int qMax(int i1, int i2, int i3)
static int shrink(QLayoutStruct &ls, int delta)
static const int zero
static int separatorMoveHelper(QList< QLayoutStruct > &list, int index, int delta, int sep)
static QInternal::DockPosition dockPosHelper(const QRect &rect, const QPoint &_pos, Qt::Orientation o, bool nestingEnabled, QDockAreaLayoutInfo::TabMode tabMode)
static void paintSep(QPainter *p, QWidget *w, const QRect &r, Qt::Orientation o, bool mouse_over)
static int realMaxSize(const QDockAreaLayoutInfo &info)
QMainWindowLayout * qt_mainwindow_layout(const QMainWindow *window)
@ StateFlagFloating
@ StateFlagVisible