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, bool testing)
1923{
1924 uchar marker;
1925 stream >> marker;
1926 if (marker != TabMarker && marker != SequenceMarker)
1927 return false;
1928
1929#if QT_CONFIG(tabbar)
1930 tabbed = marker == TabMarker;
1931
1932 int index = -1;
1933 if (tabbed)
1934 stream >> index;
1935#endif
1936
1937 uchar orientation;
1938 stream >> orientation;
1939 o = static_cast<Qt::Orientation>(orientation);
1940
1941 int cnt;
1942 stream >> cnt;
1943
1944 for (int i = 0; i < cnt; ++i) {
1945 uchar nextMarker;
1946 stream >> nextMarker;
1947 if (nextMarker == WidgetMarker) {
1948 QString name;
1949 uchar flags;
1950 stream >> name >> flags;
1951 if (name.isEmpty()) {
1952 int dummy;
1953 stream >> dummy >> dummy >> dummy >> dummy;
1954 continue;
1955 }
1956
1957 QDockWidget *widget = nullptr;
1958 for (int j = 0; j < widgets.size(); ++j) {
1959 if (widgets.at(j)->objectName() == name) {
1960 widget = widgets.takeAt(j);
1961 break;
1962 }
1963 }
1964
1965 if (widget == nullptr) {
1966 QPlaceHolderItem *placeHolder = new QPlaceHolderItem;
1967 QDockAreaLayoutItem item(placeHolder);
1968
1969 placeHolder->objectName = name;
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 if (!testing)
1983 item_list.append(item);
1984 } else {
1985 QDockAreaLayoutItem item(new QDockWidgetItem(widget));
1986 if (flags & StateFlagFloating) {
1987 bool drawer = false;
1988
1989 if (!testing) {
1990 widget->hide();
1991 if (!drawer)
1992 widget->setFloating(true);
1993 }
1994
1995 int x, y, w, h;
1996 stream >> x >> y >> w >> h;
1997
1998 if (!testing)
1999 widget->setGeometry(QDockAreaLayout::constrainedRect(QRect(x, y, w, h), widget));
2000
2001 if (!testing) {
2002 widget->setVisible(flags & StateFlagVisible);
2003 item_list.append(item);
2004 }
2005 } else {
2006 int dummy;
2007 stream >> item.pos >> item.size >> dummy >> dummy;
2008 if (!testing) {
2009 item_list.append(item);
2010 widget->setFloating(false);
2011 widget->setVisible(flags & StateFlagVisible);
2012 emit widget->dockLocationChanged(toDockWidgetArea(dockPos));
2013 }
2014 }
2015 if (testing) {
2016 //was it is not really added to the layout, we need to delete the object here
2017 delete item.widgetItem;
2018 item.widgetItem = nullptr;
2019 }
2020 }
2021 } else if (nextMarker == SequenceMarker) {
2022 int dummy;
2023#if !QT_CONFIG(tabbar)
2024 const int tabBarShape = 0;
2025#endif
2026 QDockAreaLayoutItem item(new QDockAreaLayoutInfo(sep, dockPos, o,
2027 tabBarShape, mainWindow));
2028 stream >> item.pos >> item.size >> dummy >> dummy;
2029 //we need to make sure the element is in the list so the dock widget can eventually be docked correctly
2030 if (!testing)
2031 item_list.append(item);
2032
2033 //here we need to make sure we change the item in the item_list
2034 QDockAreaLayoutItem &lastItem = testing ? item : item_list.last();
2035
2036 if (!lastItem.subinfo->restoreState(stream, widgets, testing))
2037 return false;
2038
2039 } else {
2040 return false;
2041 }
2042 }
2043
2044#if QT_CONFIG(tabbar)
2045 if (!testing && tabbed && index >= 0 && index < item_list.size()) {
2046 updateTabBar();
2047 setCurrentTabId(tabId(item_list.at(index)));
2048 }
2049 if (!testing && *sep == 1)
2050 updateSeparatorWidgets();
2051#endif
2052
2053 return true;
2054}
2055
2056#if QT_CONFIG(tabbar)
2057
2058static void raiseSeparatorWidget(QWidget *separatorWidget)
2059{
2060 Q_ASSERT(separatorWidget);
2061
2062#if QT_CONFIG(toolbar)
2063 // Raise the separator widget, but make sure it doesn't go above
2064 // an expanded toolbar, as that would break mouse event hit testing.
2065 Q_ASSERT(separatorWidget->parent());
2066 const auto toolBars = separatorWidget->parent()->findChildren<QToolBar*>(Qt::FindDirectChildrenOnly);
2067 for (auto *toolBar : toolBars) {
2068 if (auto *toolBarLayout = qobject_cast<QToolBarLayout*>(toolBar->layout())) {
2069 if (toolBarLayout->expanded) {
2070 separatorWidget->stackUnder(toolBar);
2071 return;
2072 }
2073 }
2074 }
2075#endif
2076
2077 separatorWidget->raise();
2078}
2079
2080
2081void QDockAreaLayoutInfo::updateSeparatorWidgets() const
2082{
2083 if (tabbed) {
2084 separatorWidgets.clear();
2085 return;
2086 }
2087
2088 int j = 0;
2089 for (int i = 0; i < item_list.size(); ++i) {
2090 const QDockAreaLayoutItem &item = item_list.at(i);
2091
2092 if (item.skip())
2093 continue;
2094
2095 int next = this->next(i);
2096 if ((item.flags & QDockAreaLayoutItem::GapItem)
2097 || (next != -1 && (item_list.at(next).flags & QDockAreaLayoutItem::GapItem)))
2098 continue;
2099
2100 if (item.subinfo) {
2101 item.subinfo->updateSeparatorWidgets();
2102 }
2103
2104 if (next == -1)
2105 break;
2106
2107 QWidget *sepWidget;
2108 if (j < separatorWidgets.size()) {
2109 sepWidget = separatorWidgets.at(j);
2110 if (!sepWidget) {
2111 qWarning("QDockAreaLayoutInfo::updateSeparatorWidgets: null separator widget");
2112 sepWidget = mainWindowLayout()->getSeparatorWidget();
2113 separatorWidgets[j] = sepWidget;
2114 }
2115 } else {
2116 sepWidget = mainWindowLayout()->getSeparatorWidget();
2117 separatorWidgets.append(sepWidget);
2118 }
2119 j++;
2120
2121 Q_ASSERT(sepWidget);
2122 raiseSeparatorWidget(sepWidget);
2123
2124 QRect sepRect = separatorRect(i).adjusted(-2, -2, 2, 2);
2125 sepWidget->setGeometry(sepRect);
2126 sepWidget->setMask( QRegion(separatorRect(i).translated( - sepRect.topLeft())));
2127 sepWidget->show();
2128 }
2129
2130 for (int k = j; k < separatorWidgets.size(); ++k) {
2131 separatorWidgets[k]->hide();
2132 }
2133 separatorWidgets.resize(j);
2134}
2135
2136/*! \internal
2137 reparent all the widgets contained in this layout portion to the
2138 specified parent. This is used to reparent dock widgets and tabbars
2139 to the floating window or the main window
2140 */
2141void QDockAreaLayoutInfo::reparentWidgets(QWidget *parent)
2142{
2143 if (tabBar)
2144 tabBar->setParent(parent);
2145
2146 for (int i = 0; i < item_list.size(); ++i) {
2147 const QDockAreaLayoutItem &item = item_list.at(i);
2148 if (item.flags & QDockAreaLayoutItem::GapItem)
2149 continue;
2150 if (item.subinfo)
2151 item.subinfo->reparentWidgets(parent);
2152 if (item.widgetItem) {
2153 QWidget *w = item.widgetItem->widget();
2154 if (qobject_cast<QDockWidgetGroupWindow *>(w))
2155 continue;
2156 if (w->parent() != parent) {
2157 bool hidden = w->isHidden();
2158 w->setParent(parent, w->windowFlags());
2159 if (!hidden)
2160 w->show();
2161 }
2162 }
2163 }
2164}
2165
2166//returns whether the tabbar is visible or not
2167bool QDockAreaLayoutInfo::updateTabBar() const
2168{
2169 if (!tabbed)
2170 return false;
2171
2172 QDockAreaLayoutInfo *that = const_cast<QDockAreaLayoutInfo*>(this);
2173
2174 if (that->tabBar == nullptr) {
2175 that->tabBar = mainWindowLayout()->getTabBar();
2176 that->tabBar->setShape(static_cast<QTabBar::Shape>(tabBarShape));
2177 that->tabBar->setDrawBase(true);
2178 }
2179
2180 const QSignalBlocker blocker(tabBar);
2181 bool gap = false;
2182
2183 const quintptr oldCurrentId = currentTabId();
2184
2185 int tab_idx = 0;
2186 for (int i = 0; i < item_list.size(); ++i) {
2187 const QDockAreaLayoutItem &item = item_list.at(i);
2188 if (item.skip())
2189 continue;
2190 if (item.flags & QDockAreaLayoutItem::GapItem) {
2191 gap = true;
2192 continue;
2193 }
2194 if (item.widgetItem == nullptr)
2195 continue;
2196
2197 QDockWidget *dw = qobject_cast<QDockWidget*>(item.widgetItem->widget());
2198 QString title = dw->d_func()->fixedWindowTitle;
2199 quintptr id = tabId(item);
2200 const QIcon windowIcon = dw->testAttribute(Qt::WA_SetWindowIcon) ? dw->windowIcon()
2201 : QIcon();
2202 if (tab_idx == tabBar->count()) {
2203 tabBar->insertTab(tab_idx, windowIcon, title);
2204#if QT_CONFIG(tooltip)
2205 tabBar->setTabToolTip(tab_idx, title);
2206#endif
2207 tabBar->setTabData(tab_idx, id);
2208 } else if (qvariant_cast<quintptr>(tabBar->tabData(tab_idx)) != id) {
2209 if (tab_idx + 1 < tabBar->count()
2210 && qvariant_cast<quintptr>(tabBar->tabData(tab_idx + 1)) == id)
2211 tabBar->removeTab(tab_idx);
2212 else {
2213 tabBar->insertTab(tab_idx, windowIcon, title);
2214#if QT_CONFIG(tooltip)
2215 tabBar->setTabToolTip(tab_idx, title);
2216#endif
2217 tabBar->setTabData(tab_idx, id);
2218 }
2219 }
2220
2221 if (title != tabBar->tabText(tab_idx)) {
2222 tabBar->setTabText(tab_idx, title);
2223#if QT_CONFIG(tooltip)
2224 tabBar->setTabToolTip(tab_idx, title);
2225#endif
2226 }
2227
2228 ++tab_idx;
2229 }
2230
2231 while (tab_idx < tabBar->count()) {
2232 tabBar->removeTab(tab_idx);
2233 }
2234
2235 if (oldCurrentId > 0 && currentTabId() != oldCurrentId)
2236 that->setCurrentTabId(oldCurrentId);
2237
2238 if (QDockWidgetGroupWindow *dwgw = qobject_cast<QDockWidgetGroupWindow *>(tabBar->parent()))
2239 dwgw->adjustFlags();
2240
2241 //returns if the tabbar is visible or not
2242 return ( (gap ? 1 : 0) + tabBar->count()) > 1;
2243}
2244
2245void QDockAreaLayoutInfo::setTabBarShape(int shape)
2246{
2247 if (shape == tabBarShape)
2248 return;
2249 tabBarShape = shape;
2250 if (tabBar != nullptr)
2251 tabBar->setShape(static_cast<QTabBar::Shape>(shape));
2252
2253 for (int i = 0; i < item_list.size(); ++i) {
2254 QDockAreaLayoutItem &item = item_list[i];
2255 if (item.subinfo != nullptr)
2256 item.subinfo->setTabBarShape(shape);
2257 }
2258}
2259
2260QSize QDockAreaLayoutInfo::tabBarMinimumSize() const
2261{
2262 if (!updateTabBar())
2263 return QSize(0, 0);
2264
2265 return tabBar->minimumSizeHint();
2266}
2267
2268QSize QDockAreaLayoutInfo::tabBarSizeHint() const
2269{
2270 if (!updateTabBar())
2271 return QSize(0, 0);
2272
2273 return tabBar->sizeHint();
2274}
2275
2276QSet<QTabBar*> QDockAreaLayoutInfo::usedTabBars() const
2277{
2278 QSet<QTabBar*> result;
2279
2280 if (tabbed) {
2281 updateTabBar();
2282 result.insert(tabBar);
2283 }
2284
2285 for (int i = 0; i < item_list.size(); ++i) {
2286 const QDockAreaLayoutItem &item = item_list.at(i);
2287 if (item.subinfo != nullptr)
2288 result += item.subinfo->usedTabBars();
2289 }
2290
2291 return result;
2292}
2293
2294// returns a set of all used separator widgets for this dockarelayout info
2295// and all subinfos
2296QSet<QWidget*> QDockAreaLayoutInfo::usedSeparatorWidgets() const
2297{
2298 QSet<QWidget*> result;
2299 const int numSeparatorWidgets = separatorWidgets.size();
2300 result.reserve(numSeparatorWidgets);
2301
2302 for (int i = 0; i < numSeparatorWidgets; ++i)
2303 result << separatorWidgets.at(i);
2304
2305 for (int i = 0; i < item_list.size(); ++i) {
2306 const QDockAreaLayoutItem &item = item_list.at(i);
2307 if (item.subinfo != nullptr)
2308 result += item.subinfo->usedSeparatorWidgets();
2309 }
2310
2311 return result;
2312}
2313
2314QRect QDockAreaLayoutInfo::tabContentRect() const
2315{
2316 if (!tabbed)
2317 return QRect();
2318
2319 QRect result = rect;
2320 QSize tbh = tabBarSizeHint();
2321
2322 if (!tbh.isNull()) {
2323 switch (tabBarShape) {
2324 case QTabBar::RoundedNorth:
2325 case QTabBar::TriangularNorth:
2326 result.adjust(0, tbh.height(), 0, 0);
2327 break;
2328 case QTabBar::RoundedSouth:
2329 case QTabBar::TriangularSouth:
2330 result.adjust(0, 0, 0, -tbh.height());
2331 break;
2332 case QTabBar::RoundedEast:
2333 case QTabBar::TriangularEast:
2334 result.adjust(0, 0, -tbh.width(), 0);
2335 break;
2336 case QTabBar::RoundedWest:
2337 case QTabBar::TriangularWest:
2338 result.adjust(tbh.width(), 0, 0, 0);
2339 break;
2340 default:
2341 break;
2342 }
2343 }
2344
2345 return result;
2346}
2347
2348int QDockAreaLayoutInfo::tabIndexToListIndex(int tabIndex) const
2349{
2350 Q_ASSERT(tabbed && tabBar);
2351 quintptr data = qvariant_cast<quintptr>(tabBar->tabData(tabIndex));
2352 for (int i = 0; i < item_list.size(); ++i) {
2353 if (tabId(item_list.at(i)) == data)
2354 return i;
2355 }
2356 return -1;
2357}
2358
2359void QDockAreaLayoutInfo::moveTab(int from, int to)
2360{
2361 item_list.move(tabIndexToListIndex(from), tabIndexToListIndex(to));
2362}
2363#endif // QT_CONFIG(tabbar)
2364
2365/******************************************************************************
2366** QDockAreaLayout
2367*/
2368
2369QDockAreaLayout::QDockAreaLayout(QMainWindow *win) : fallbackToSizeHints(true)
2370{
2371 mainWindow = win;
2372 sep = win->style()->pixelMetric(QStyle::PM_DockWidgetSeparatorExtent, nullptr, win);
2373#if QT_CONFIG(tabbar)
2374 const int tabShape = QTabBar::RoundedSouth;
2375#else
2376 const int tabShape = 0;
2377#endif
2378 docks[QInternal::LeftDock]
2379 = QDockAreaLayoutInfo(&sep, QInternal::LeftDock, Qt::Vertical, tabShape, win);
2380 docks[QInternal::RightDock]
2381 = QDockAreaLayoutInfo(&sep, QInternal::RightDock, Qt::Vertical, tabShape, win);
2382 docks[QInternal::TopDock]
2383 = QDockAreaLayoutInfo(&sep, QInternal::TopDock, Qt::Horizontal, tabShape, win);
2384 docks[QInternal::BottomDock]
2385 = QDockAreaLayoutInfo(&sep, QInternal::BottomDock, Qt::Horizontal, tabShape, win);
2386 centralWidgetItem = nullptr;
2387
2388
2389 corners[Qt::TopLeftCorner] = Qt::TopDockWidgetArea;
2390 corners[Qt::TopRightCorner] = Qt::TopDockWidgetArea;
2391 corners[Qt::BottomLeftCorner] = Qt::BottomDockWidgetArea;
2392 corners[Qt::BottomRightCorner] = Qt::BottomDockWidgetArea;
2393}
2394
2395bool QDockAreaLayout::isValid() const
2396{
2397 return rect.isValid();
2398}
2399
2400void QDockAreaLayout::saveState(QDataStream &stream) const
2401{
2402 stream << (uchar) DockWidgetStateMarker;
2403 int cnt = 0;
2404 for (int i = 0; i < QInternal::DockCount; ++i) {
2405 if (!docks[i].item_list.isEmpty())
2406 ++cnt;
2407 }
2408 stream << cnt;
2409 for (int i = 0; i < QInternal::DockCount; ++i) {
2410 if (docks[i].item_list.isEmpty())
2411 continue;
2412 stream << i << docks[i].rect.size();
2413 docks[i].saveState(stream);
2414 }
2415
2416 stream << centralWidgetRect.size();
2417
2418 for (int i = 0; i < 4; ++i)
2419 stream << static_cast<int>(corners[i]);
2420}
2421
2422bool QDockAreaLayout::restoreState(QDataStream &stream, const QList<QDockWidget*> &_dockwidgets, bool testing)
2423{
2424 QList<QDockWidget*> dockwidgets = _dockwidgets;
2425
2426 int cnt;
2427 stream >> cnt;
2428 for (int i = 0; i < cnt; ++i) {
2429 int pos;
2430 stream >> pos;
2431 QSize size;
2432 stream >> size;
2433 if (!testing) {
2434 docks[pos].rect = QRect(QPoint(0, 0), size);
2435 }
2436 if (!docks[pos].restoreState(stream, dockwidgets, testing)) {
2437 stream.setStatus(QDataStream::ReadCorruptData);
2438 return false;
2439 }
2440 }
2441
2442 QSize size;
2443 stream >> size;
2444 centralWidgetRect = QRect(QPoint(0, 0), size);
2445
2446 bool ok = stream.status() == QDataStream::Ok;
2447
2448 if (ok) {
2449 int cornerData[4];
2450 for (int i = 0; i < 4; ++i)
2451 stream >> cornerData[i];
2452 if (stream.status() == QDataStream::Ok) {
2453 for (int i = 0; i < 4; ++i)
2454 corners[i] = static_cast<Qt::DockWidgetArea>(cornerData[i]);
2455 }
2456
2457 if (!testing)
2458 fallbackToSizeHints = false;
2459 }
2460
2461 return ok;
2462}
2463
2464QList<int> QDockAreaLayout::indexOfPlaceHolder(const QString &objectName) const
2465{
2466 for (int i = 0; i < QInternal::DockCount; ++i) {
2467 QList<int> result = docks[i].indexOfPlaceHolder(objectName);
2468 if (!result.isEmpty()) {
2469 result.prepend(i);
2470 return result;
2471 }
2472 }
2473 return QList<int>();
2474}
2475
2476QList<int> QDockAreaLayout::indexOf(const QWidget *dockWidget) const
2477{
2478 for (int i = 0; i < QInternal::DockCount; ++i) {
2479 QList<int> result = docks[i].indexOf(dockWidget);
2480 if (!result.isEmpty()) {
2481 result.prepend(i);
2482 return result;
2483 }
2484 }
2485 return QList<int>();
2486}
2487
2488QList<int> QDockAreaLayout::gapIndex(const QPoint &pos, bool disallowTabs) const
2489{
2490 QMainWindow::DockOptions opts = mainWindow->dockOptions();
2491 bool nestingEnabled = opts & QMainWindow::AllowNestedDocks;
2492 QDockAreaLayoutInfo::TabMode tabMode = QDockAreaLayoutInfo::NoTabs;
2493#if QT_CONFIG(tabbar)
2494 if (!disallowTabs) {
2495 if (opts & QMainWindow::AllowTabbedDocks || opts & QMainWindow::VerticalTabs)
2496 tabMode = QDockAreaLayoutInfo::AllowTabs;
2497 if (opts & QMainWindow::ForceTabbedDocks)
2498 tabMode = QDockAreaLayoutInfo::ForceTabs;
2499
2500 if (tabMode == QDockAreaLayoutInfo::ForceTabs)
2501 nestingEnabled = false;
2502 }
2503#endif
2504
2505
2506 for (int i = 0; i < QInternal::DockCount; ++i) {
2507 const QDockAreaLayoutInfo &info = docks[i];
2508
2509 if (!info.isEmpty() && info.rect.contains(pos)) {
2510 QList<int> result
2511 = docks[i].gapIndex(pos, nestingEnabled, tabMode);
2512 if (!result.isEmpty())
2513 result.prepend(i);
2514 return result;
2515 }
2516 }
2517
2518 for (int i = 0; i < QInternal::DockCount; ++i) {
2519 const QDockAreaLayoutInfo &info = docks[i];
2520
2521 if (info.isEmpty()) {
2522 const QRect r = gapRect(static_cast<QInternal::DockPosition>(i));
2523 if (r.contains(pos)) {
2524 if (opts & QMainWindow::ForceTabbedDocks && !info.item_list.isEmpty()) {
2525 //in case of ForceTabbedDocks, we pass -1 in order to force the gap to be tabbed
2526 //it mustn't be completely empty otherwise it won't work
2527 return QList<int>() << i << -1 << 0;
2528 } else {
2529 return QList<int>() << i << 0;
2530 }
2531 }
2532 }
2533 }
2534
2535 return QList<int>();
2536}
2537
2538QRect QDockAreaLayout::gapRect(QInternal::DockPosition dockPos) const
2539{
2540 Q_ASSERT_X(mainWindow, "QDockAreaLayout::gapRect", "Called without valid mainWindow pointer.");
2541
2542 // Determine gap size depending on MainWindow size (QTBUG-101657)
2543 const QSize gapSize = (mainWindow->size()/2).boundedTo(QSize(EmptyDropAreaSize, EmptyDropAreaSize));
2544
2545 // Warn if main window is too small to create proper docks.
2546 // Do not fail because this can be triggered by a user making MainWindow too small
2547 if (mainWindow->height() < (2 * sep)) {
2548 qCWarning(lcQpaDockWidgets,
2549 "QDockAreaLayout::gapRect: Main window height %i is too small. Docking will not be possible.",
2550 mainWindow->height());
2551
2552 }
2553 if (mainWindow->width() < (2 * sep)) {
2554 qCWarning(lcQpaDockWidgets,
2555 "QDockAreaLayout::gapRect: Main window width %i is too small. Docking will not be possible.",
2556 mainWindow->width());
2557 }
2558
2559 // Calculate rectangle of requested dock
2560 switch (dockPos) {
2561 case QInternal::LeftDock:
2562 return QRect(rect.left(), rect.top(), gapSize.width(), rect.height());
2563 case QInternal::RightDock:
2564 return QRect(rect.right() - gapSize.width(), rect.top(), gapSize.width(), rect.height());
2565 case QInternal::TopDock:
2566 return QRect(rect.left(), rect.top(), rect.width(), gapSize.height());
2567 case QInternal::BottomDock:
2568 return QRect(rect.left(), rect.bottom() - gapSize.height(), rect.width(), gapSize.height());
2569 case QInternal::DockCount:
2570 break;
2571 }
2572 return QRect();
2573}
2574
2575QList<int> QDockAreaLayout::findSeparator(const QPoint &pos) const
2576{
2577 QList<int> result;
2578 for (int i = 0; i < QInternal::DockCount; ++i) {
2579 const QDockAreaLayoutInfo &info = docks[i];
2580 if (info.isEmpty())
2581 continue;
2582 QRect rect = separatorRect(i);
2583 if (!rect.isNull() && sep == 1)
2584 rect.adjust(-2, -2, 2, 2);
2585 if (rect.contains(pos) && !info.hasFixedSize()) {
2586 result << i;
2587 break;
2588 } else if (info.rect.contains(pos)) {
2589 result = docks[i].findSeparator(pos);
2590 if (!result.isEmpty()) {
2591 result.prepend(i);
2592 break;
2593 }
2594 }
2595 }
2596
2597 return result;
2598}
2599
2600QDockAreaLayoutInfo *QDockAreaLayout::info(QWidget *widget)
2601{
2602 for (int i = 0; i < QInternal::DockCount; ++i) {
2603 if (QDockAreaLayoutInfo *result = docks[i].info(widget))
2604 return result;
2605 }
2606
2607 return nullptr;
2608}
2609
2610QDockAreaLayoutInfo *QDockAreaLayout::info(const QList<int> &path)
2611{
2612 Q_ASSERT(!path.isEmpty());
2613 const int index = path.first();
2614 Q_ASSERT(index >= 0 && index < QInternal::DockCount);
2615
2616 if (path.size() == 1)
2617 return &docks[index];
2618
2619 return docks[index].info(path.mid(1));
2620}
2621
2622const QDockAreaLayoutInfo *QDockAreaLayout::info(const QList<int> &path) const
2623{
2624 return const_cast<QDockAreaLayout*>(this)->info(path);
2625}
2626
2627QDockAreaLayoutItem &QDockAreaLayout::item(const QList<int> &path)
2628{
2629 Q_ASSERT(!path.isEmpty());
2630 const int index = path.first();
2631 Q_ASSERT(index >= 0 && index < QInternal::DockCount);
2632 return docks[index].item(path.mid(1));
2633}
2634
2635QRect QDockAreaLayout::itemRect(const QList<int> &path) const
2636{
2637 Q_ASSERT(!path.isEmpty());
2638 const int index = path.first();
2639 Q_ASSERT(index >= 0 && index < QInternal::DockCount);
2640 return docks[index].itemRect(path.mid(1));
2641}
2642
2643QRect QDockAreaLayout::separatorRect(int index) const
2644{
2645 const QDockAreaLayoutInfo &dock = docks[index];
2646 if (dock.isEmpty())
2647 return QRect();
2648 QRect r = dock.rect;
2649 switch (index) {
2650 case QInternal::LeftDock:
2651 return QRect(r.right() + 1, r.top(), sep, r.height());
2652 case QInternal::RightDock:
2653 return QRect(r.left() - sep, r.top(), sep, r.height());
2654 case QInternal::TopDock:
2655 return QRect(r.left(), r.bottom() + 1, r.width(), sep);
2656 case QInternal::BottomDock:
2657 return QRect(r.left(), r.top() - sep, r.width(), sep);
2658 default:
2659 break;
2660 }
2661 return QRect();
2662}
2663
2664QRect QDockAreaLayout::separatorRect(const QList<int> &path) const
2665{
2666 Q_ASSERT(!path.isEmpty());
2667
2668 const int index = path.first();
2669 Q_ASSERT(index >= 0 && index < QInternal::DockCount);
2670
2671 if (path.size() == 1)
2672 return separatorRect(index);
2673 else
2674 return docks[index].separatorRect(path.mid(1));
2675}
2676
2677bool QDockAreaLayout::insertGap(const QList<int> &path, QLayoutItem *dockWidgetItem)
2678{
2679 Q_ASSERT(!path.isEmpty());
2680 const int index = path.first();
2681 Q_ASSERT(index >= 0 && index < QInternal::DockCount);
2682 return docks[index].insertGap(path.mid(1), dockWidgetItem);
2683}
2684
2685QLayoutItem *QDockAreaLayout::plug(const QList<int> &path)
2686{
2687#if QT_CONFIG(tabbar)
2688 Q_ASSERT(!path.isEmpty());
2689 const int index = path.first();
2690 Q_ASSERT(index >= 0 && index < QInternal::DockCount);
2691 QLayoutItem *item = docks[index].plug(path.mid(1));
2692 docks[index].reparentWidgets(mainWindow);
2693 return item;
2694#else
2695 return nullptr;
2696#endif
2697}
2698
2699QLayoutItem *QDockAreaLayout::unplug(const QList<int> &path)
2700{
2701 Q_ASSERT(!path.isEmpty());
2702 const int index = path.first();
2703 Q_ASSERT(index >= 0 && index < QInternal::DockCount);
2704 return docks[index].unplug(path.mid(1));
2705}
2706
2707void QDockAreaLayout::remove(const QList<int> &path)
2708{
2709 Q_ASSERT(!path.isEmpty());
2710 const int index = path.first();
2711 Q_ASSERT(index >= 0 && index < QInternal::DockCount);
2712 docks[index].remove(path.mid(1));
2713}
2714
2715void QDockAreaLayout::removePlaceHolder(const QString &name)
2716{
2717 QList<int> index = indexOfPlaceHolder(name);
2718 if (!index.isEmpty())
2719 remove(index);
2720 const auto groups =
2721 mainWindow->findChildren<QDockWidgetGroupWindow *>(Qt::FindDirectChildrenOnly);
2722 for (QDockWidgetGroupWindow *dwgw : groups) {
2723 index = dwgw->layoutInfo()->indexOfPlaceHolder(name);
2724 if (!index.isEmpty()) {
2725 dwgw->layoutInfo()->remove(index);
2726 dwgw->destroyOrHideIfEmpty();
2727 }
2728 }
2729}
2730
2731static inline int qMax(int i1, int i2, int i3) { return qMax(i1, qMax(i2, i3)); }
2732
2733void QDockAreaLayout::getGrid(QList<QLayoutStruct> *_ver_struct_list,
2734 QList<QLayoutStruct> *_hor_struct_list)
2735{
2736 QSize center_hint(0, 0);
2737 QSize center_min(0, 0);
2738 QSize center_max(0, 0);
2739 const bool have_central = centralWidgetItem != nullptr && !centralWidgetItem->isEmpty();
2740 if (have_central) {
2741 center_hint = centralWidgetRect.size();
2742 if (!center_hint.isValid())
2743 center_hint = centralWidgetItem->sizeHint();
2744 center_min = centralWidgetItem->minimumSize();
2745 center_max = centralWidgetItem->maximumSize();
2746 }
2747
2748 QRect center_rect = rect;
2749 if (!docks[QInternal::LeftDock].isEmpty())
2750 center_rect.setLeft(rect.left() + docks[QInternal::LeftDock].rect.width() + sep);
2751 if (!docks[QInternal::TopDock].isEmpty())
2752 center_rect.setTop(rect.top() + docks[QInternal::TopDock].rect.height() + sep);
2753 if (!docks[QInternal::RightDock].isEmpty())
2754 center_rect.setRight(rect.right() - docks[QInternal::RightDock].rect.width() - sep);
2755 if (!docks[QInternal::BottomDock].isEmpty())
2756 center_rect.setBottom(rect.bottom() - docks[QInternal::BottomDock].rect.height() - sep);
2757
2758 QSize left_hint = docks[QInternal::LeftDock].size();
2759 if (left_hint.isNull() || fallbackToSizeHints)
2760 left_hint = docks[QInternal::LeftDock].sizeHint();
2761 QSize left_min = docks[QInternal::LeftDock].minimumSize();
2762 QSize left_max = docks[QInternal::LeftDock].maximumSize();
2763 left_hint = left_hint.boundedTo(left_max).expandedTo(left_min);
2764
2765 QSize right_hint = docks[QInternal::RightDock].size();
2766 if (right_hint.isNull() || fallbackToSizeHints)
2767 right_hint = docks[QInternal::RightDock].sizeHint();
2768 QSize right_min = docks[QInternal::RightDock].minimumSize();
2769 QSize right_max = docks[QInternal::RightDock].maximumSize();
2770 right_hint = right_hint.boundedTo(right_max).expandedTo(right_min);
2771
2772 QSize top_hint = docks[QInternal::TopDock].size();
2773 if (top_hint.isNull() || fallbackToSizeHints)
2774 top_hint = docks[QInternal::TopDock].sizeHint();
2775 QSize top_min = docks[QInternal::TopDock].minimumSize();
2776 QSize top_max = docks[QInternal::TopDock].maximumSize();
2777 top_hint = top_hint.boundedTo(top_max).expandedTo(top_min);
2778
2779 QSize bottom_hint = docks[QInternal::BottomDock].size();
2780 if (bottom_hint.isNull() || fallbackToSizeHints)
2781 bottom_hint = docks[QInternal::BottomDock].sizeHint();
2782 QSize bottom_min = docks[QInternal::BottomDock].minimumSize();
2783 QSize bottom_max = docks[QInternal::BottomDock].maximumSize();
2784 bottom_hint = bottom_hint.boundedTo(bottom_max).expandedTo(bottom_min);
2785
2786 if (_ver_struct_list != nullptr) {
2787 QList<QLayoutStruct> &ver_struct_list = *_ver_struct_list;
2788 ver_struct_list.resize(3);
2789
2790 // top --------------------------------------------------
2791 ver_struct_list[0].init();
2792 ver_struct_list[0].stretch = 0;
2793 ver_struct_list[0].sizeHint = top_hint.height();
2794 ver_struct_list[0].minimumSize = top_min.height();
2795 ver_struct_list[0].maximumSize = top_max.height();
2796 ver_struct_list[0].expansive = false;
2797 ver_struct_list[0].empty = docks[QInternal::TopDock].isEmpty();
2798 ver_struct_list[0].pos = docks[QInternal::TopDock].rect.top();
2799 ver_struct_list[0].size = docks[QInternal::TopDock].rect.height();
2800
2801 // center --------------------------------------------------
2802 ver_struct_list[1].init();
2803 ver_struct_list[1].stretch = center_hint.height();
2804
2805 bool tl_significant = corners[Qt::TopLeftCorner] == Qt::TopDockWidgetArea
2806 || docks[QInternal::TopDock].isEmpty();
2807 bool bl_significant = corners[Qt::BottomLeftCorner] == Qt::BottomDockWidgetArea
2808 || docks[QInternal::BottomDock].isEmpty();
2809 bool tr_significant = corners[Qt::TopRightCorner] == Qt::TopDockWidgetArea
2810 || docks[QInternal::TopDock].isEmpty();
2811 bool br_significant = corners[Qt::BottomRightCorner] == Qt::BottomDockWidgetArea
2812 || docks[QInternal::BottomDock].isEmpty();
2813
2814 int left = (tl_significant && bl_significant) ? left_hint.height() : 0;
2815 int right = (tr_significant && br_significant) ? right_hint.height() : 0;
2816 ver_struct_list[1].sizeHint = qMax(left, center_hint.height(), right);
2817
2818 left = (tl_significant && bl_significant) ? left_min.height() : 0;
2819 right = (tr_significant && br_significant) ? right_min.height() : 0;
2820 ver_struct_list[1].minimumSize = qMax(left, center_min.height(), right);
2821 ver_struct_list[1].maximumSize = center_max.height();
2822 ver_struct_list[1].expansive = have_central;
2823 ver_struct_list[1].empty = docks[QInternal::LeftDock].isEmpty()
2824 && !have_central
2825 && docks[QInternal::RightDock].isEmpty();
2826 ver_struct_list[1].pos = center_rect.top();
2827 ver_struct_list[1].size = center_rect.height();
2828
2829 // bottom --------------------------------------------------
2830 ver_struct_list[2].init();
2831 ver_struct_list[2].stretch = 0;
2832 ver_struct_list[2].sizeHint = bottom_hint.height();
2833 ver_struct_list[2].minimumSize = bottom_min.height();
2834 ver_struct_list[2].maximumSize = bottom_max.height();
2835 ver_struct_list[2].expansive = false;
2836 ver_struct_list[2].empty = docks[QInternal::BottomDock].isEmpty();
2837 ver_struct_list[2].pos = docks[QInternal::BottomDock].rect.top();
2838 ver_struct_list[2].size = docks[QInternal::BottomDock].rect.height();
2839
2840 for (int i = 0; i < 3; ++i) {
2841 ver_struct_list[i].sizeHint
2842 = qMax(ver_struct_list[i].sizeHint, ver_struct_list[i].minimumSize);
2843 }
2844 if (have_central && ver_struct_list[0].empty && ver_struct_list[2].empty)
2845 ver_struct_list[1].maximumSize = QWIDGETSIZE_MAX;
2846 }
2847
2848 if (_hor_struct_list != nullptr) {
2849 QList<QLayoutStruct> &hor_struct_list = *_hor_struct_list;
2850 hor_struct_list.resize(3);
2851
2852 // left --------------------------------------------------
2853 hor_struct_list[0].init();
2854 hor_struct_list[0].stretch = 0;
2855 hor_struct_list[0].sizeHint = left_hint.width();
2856 hor_struct_list[0].minimumSize = left_min.width();
2857 hor_struct_list[0].maximumSize = left_max.width();
2858 hor_struct_list[0].expansive = false;
2859 hor_struct_list[0].empty = docks[QInternal::LeftDock].isEmpty();
2860 hor_struct_list[0].pos = docks[QInternal::LeftDock].rect.left();
2861 hor_struct_list[0].size = docks[QInternal::LeftDock].rect.width();
2862
2863 // center --------------------------------------------------
2864 hor_struct_list[1].init();
2865 hor_struct_list[1].stretch = center_hint.width();
2866
2867 bool tl_significant = corners[Qt::TopLeftCorner] == Qt::LeftDockWidgetArea
2868 || docks[QInternal::LeftDock].isEmpty();
2869 bool tr_significant = corners[Qt::TopRightCorner] == Qt::RightDockWidgetArea
2870 || docks[QInternal::RightDock].isEmpty();
2871 bool bl_significant = corners[Qt::BottomLeftCorner] == Qt::LeftDockWidgetArea
2872 || docks[QInternal::LeftDock].isEmpty();
2873 bool br_significant = corners[Qt::BottomRightCorner] == Qt::RightDockWidgetArea
2874 || docks[QInternal::RightDock].isEmpty();
2875
2876 int top = (tl_significant && tr_significant) ? top_hint.width() : 0;
2877 int bottom = (bl_significant && br_significant) ? bottom_hint.width() : 0;
2878 hor_struct_list[1].sizeHint = qMax(top, center_hint.width(), bottom);
2879
2880 top = (tl_significant && tr_significant) ? top_min.width() : 0;
2881 bottom = (bl_significant && br_significant) ? bottom_min.width() : 0;
2882 hor_struct_list[1].minimumSize = qMax(top, center_min.width(), bottom);
2883
2884 hor_struct_list[1].maximumSize = center_max.width();
2885 hor_struct_list[1].expansive = have_central;
2886 hor_struct_list[1].empty = !have_central;
2887 hor_struct_list[1].pos = center_rect.left();
2888 hor_struct_list[1].size = center_rect.width();
2889
2890 // right --------------------------------------------------
2891 hor_struct_list[2].init();
2892 hor_struct_list[2].stretch = 0;
2893 hor_struct_list[2].sizeHint = right_hint.width();
2894 hor_struct_list[2].minimumSize = right_min.width();
2895 hor_struct_list[2].maximumSize = right_max.width();
2896 hor_struct_list[2].expansive = false;
2897 hor_struct_list[2].empty = docks[QInternal::RightDock].isEmpty();
2898 hor_struct_list[2].pos = docks[QInternal::RightDock].rect.left();
2899 hor_struct_list[2].size = docks[QInternal::RightDock].rect.width();
2900
2901 for (int i = 0; i < 3; ++i) {
2902 hor_struct_list[i].sizeHint
2903 = qMax(hor_struct_list[i].sizeHint, hor_struct_list[i].minimumSize);
2904 }
2905 if (have_central && hor_struct_list[0].empty && hor_struct_list[2].empty)
2906 hor_struct_list[1].maximumSize = QWIDGETSIZE_MAX;
2907
2908 }
2909}
2910
2911void QDockAreaLayout::setGrid(QList<QLayoutStruct> *ver_struct_list,
2912 QList<QLayoutStruct> *hor_struct_list)
2913{
2914
2915 // top ---------------------------------------------------
2916
2917 if (!docks[QInternal::TopDock].isEmpty()) {
2918 QRect r = docks[QInternal::TopDock].rect;
2919 if (hor_struct_list != nullptr) {
2920 r.setLeft(corners[Qt::TopLeftCorner] == Qt::TopDockWidgetArea
2921 || docks[QInternal::LeftDock].isEmpty()
2922 ? rect.left() : hor_struct_list->at(1).pos);
2923 r.setRight(corners[Qt::TopRightCorner] == Qt::TopDockWidgetArea
2924 || docks[QInternal::RightDock].isEmpty()
2925 ? rect.right() : hor_struct_list->at(2).pos - sep - 1);
2926 }
2927 if (ver_struct_list != nullptr) {
2928 r.setTop(rect.top());
2929 r.setBottom(ver_struct_list->at(1).pos - sep - 1);
2930 }
2931 docks[QInternal::TopDock].rect = r;
2932 docks[QInternal::TopDock].fitItems();
2933 }
2934
2935 // bottom ---------------------------------------------------
2936
2937 if (!docks[QInternal::BottomDock].isEmpty()) {
2938 QRect r = docks[QInternal::BottomDock].rect;
2939 if (hor_struct_list != nullptr) {
2940 r.setLeft(corners[Qt::BottomLeftCorner] == Qt::BottomDockWidgetArea
2941 || docks[QInternal::LeftDock].isEmpty()
2942 ? rect.left() : hor_struct_list->at(1).pos);
2943 r.setRight(corners[Qt::BottomRightCorner] == Qt::BottomDockWidgetArea
2944 || docks[QInternal::RightDock].isEmpty()
2945 ? rect.right() : hor_struct_list->at(2).pos - sep - 1);
2946 }
2947 if (ver_struct_list != nullptr) {
2948 r.setTop(ver_struct_list->at(2).pos);
2949 r.setBottom(rect.bottom());
2950 }
2951 docks[QInternal::BottomDock].rect = r;
2952 docks[QInternal::BottomDock].fitItems();
2953 }
2954
2955 // left ---------------------------------------------------
2956
2957 if (!docks[QInternal::LeftDock].isEmpty()) {
2958 QRect r = docks[QInternal::LeftDock].rect;
2959 if (hor_struct_list != nullptr) {
2960 r.setLeft(rect.left());
2961 r.setRight(hor_struct_list->at(1).pos - sep - 1);
2962 }
2963 if (ver_struct_list != nullptr) {
2964 r.setTop(corners[Qt::TopLeftCorner] == Qt::LeftDockWidgetArea
2965 || docks[QInternal::TopDock].isEmpty()
2966 ? rect.top() : ver_struct_list->at(1).pos);
2967 r.setBottom(corners[Qt::BottomLeftCorner] == Qt::LeftDockWidgetArea
2968 || docks[QInternal::BottomDock].isEmpty()
2969 ? rect.bottom() : ver_struct_list->at(2).pos - sep - 1);
2970 }
2971 docks[QInternal::LeftDock].rect = r;
2972 docks[QInternal::LeftDock].fitItems();
2973 }
2974
2975 // right ---------------------------------------------------
2976
2977 if (!docks[QInternal::RightDock].isEmpty()) {
2978 QRect r = docks[QInternal::RightDock].rect;
2979 if (hor_struct_list != nullptr) {
2980 r.setLeft(hor_struct_list->at(2).pos);
2981 r.setRight(rect.right());
2982 }
2983 if (ver_struct_list != nullptr) {
2984 r.setTop(corners[Qt::TopRightCorner] == Qt::RightDockWidgetArea
2985 || docks[QInternal::TopDock].isEmpty()
2986 ? rect.top() : ver_struct_list->at(1).pos);
2987 r.setBottom(corners[Qt::BottomRightCorner] == Qt::RightDockWidgetArea
2988 || docks[QInternal::BottomDock].isEmpty()
2989 ? rect.bottom() : ver_struct_list->at(2).pos - sep - 1);
2990 }
2991 docks[QInternal::RightDock].rect = r;
2992 docks[QInternal::RightDock].fitItems();
2993 }
2994
2995 // center ---------------------------------------------------
2996
2997 if (hor_struct_list != nullptr) {
2998 centralWidgetRect.setLeft(hor_struct_list->at(1).pos);
2999 centralWidgetRect.setWidth(hor_struct_list->at(1).size);
3000 }
3001 if (ver_struct_list != nullptr) {
3002 centralWidgetRect.setTop(ver_struct_list->at(1).pos);
3003 centralWidgetRect.setHeight(ver_struct_list->at(1).size);
3004 }
3005}
3006
3007void QDockAreaLayout::fitLayout()
3008{
3009 QList<QLayoutStruct> ver_struct_list(3);
3010 QList<QLayoutStruct> hor_struct_list(3);
3011 getGrid(&ver_struct_list, &hor_struct_list);
3012
3013 qGeomCalc(ver_struct_list, 0, 3, rect.top(), rect.height(), sep);
3014 qGeomCalc(hor_struct_list, 0, 3, rect.left(), rect.width(), sep);
3015
3016 setGrid(&ver_struct_list, &hor_struct_list);
3017}
3018
3019void QDockAreaLayout::clear()
3020{
3021 for (int i = 0; i < QInternal::DockCount; ++i)
3022 docks[i].clear();
3023
3024 rect = QRect();
3025 centralWidgetRect = QRect();
3026}
3027
3028template<typename SizePMF, typename CenterPMF>
3029QSize QDockAreaLayout::size_helper(SizePMF sizeFn, CenterPMF centerFn) const
3030{
3031 int left_sep = 0;
3032 int right_sep = 0;
3033 int top_sep = 0;
3034 int bottom_sep = 0;
3035
3036 if (centralWidgetItem != nullptr) {
3037 left_sep = docks[QInternal::LeftDock].isEmpty() ? 0 : sep;
3038 right_sep = docks[QInternal::RightDock].isEmpty() ? 0 : sep;
3039 top_sep = docks[QInternal::TopDock].isEmpty() ? 0 : sep;
3040 bottom_sep = docks[QInternal::BottomDock].isEmpty() ? 0 : sep;
3041 }
3042
3043 const QSize left = (docks[QInternal::LeftDock].*sizeFn)() + QSize(left_sep, 0);
3044 const QSize right = (docks[QInternal::RightDock].*sizeFn)() + QSize(right_sep, 0);
3045 const QSize top = (docks[QInternal::TopDock].*sizeFn)() + QSize(0, top_sep);
3046 const QSize bottom = (docks[QInternal::BottomDock].*sizeFn)() + QSize(0, bottom_sep);
3047 const QSize center = centralWidgetItem == nullptr
3048 ? QSize(0, 0) : (centralWidgetItem->*centerFn)();
3049
3050 int row1 = top.width();
3051 int row2 = left.width() + center.width() + right.width();
3052 int row3 = bottom.width();
3053 int col1 = left.height();
3054 int col2 = top.height() + center.height() + bottom.height();
3055 int col3 = right.height();
3056
3057 if (corners[Qt::TopLeftCorner] == Qt::LeftDockWidgetArea)
3058 row1 += left.width();
3059 else
3060 col1 += top.height();
3061
3062 if (corners[Qt::TopRightCorner] == Qt::RightDockWidgetArea)
3063 row1 += right.width();
3064 else
3065 col3 += top.height();
3066
3067 if (corners[Qt::BottomLeftCorner] == Qt::LeftDockWidgetArea)
3068 row3 += left.width();
3069 else
3070 col1 += bottom.height();
3071
3072 if (corners[Qt::BottomRightCorner] == Qt::RightDockWidgetArea)
3073 row3 += right.width();
3074 else
3075 col3 += bottom.height();
3076
3077 return QSize(qMax(row1, row2, row3), qMax(col1, col2, col3));
3078}
3079
3080QSize QDockAreaLayout::sizeHint() const
3081{
3082 return size_helper(&QDockAreaLayoutInfo::sizeHint, &QLayoutItem::sizeHint);
3083}
3084
3085QSize QDockAreaLayout::minimumSize() const
3086{
3087 return size_helper(&QDockAreaLayoutInfo::minimumSize, &QLayoutItem::minimumSize);
3088}
3089
3090/*!
3091 \internal
3092
3093 Returns the smallest size that doesn't change the size of any of the dock areas.
3094*/
3095QSize QDockAreaLayout::minimumStableSize() const
3096{
3097 return size_helper(&QDockAreaLayoutInfo::size, &QLayoutItem::minimumSize);
3098}
3099
3100/*! \internal
3101 Try to fit the given rectangle \a rect on the screen which contains
3102 the window \a widget.
3103 Used to compute the geometry of a dragged a dock widget that should
3104 be shown with \a rect, but needs to be visible on the screen
3105 */
3106QRect QDockAreaLayout::constrainedRect(QRect rect, QWidget* widget)
3107{
3108 QScreen *screen = nullptr;
3109 if (QGuiApplication::primaryScreen()->virtualSiblings().size() > 1)
3110 screen = QGuiApplication::screenAt(rect.topLeft());
3111 if (!screen)
3112 screen = widget->screen();
3113
3114 const QRect screenRect = screen->geometry();
3115 if (screenRect.isValid()) {
3116 rect.setWidth(qMin(rect.width(), screenRect.width()));
3117 rect.setHeight(qMin(rect.height(), screenRect.height()));
3118 rect.moveLeft(qMax(rect.left(), screenRect.left()));
3119 rect.moveTop(qMax(rect.top(), screenRect.top()));
3120 rect.moveRight(qMin(rect.right(), screenRect.right()));
3121 rect.moveBottom(qMin(rect.bottom(), screenRect.bottom()));
3122 }
3123
3124 return rect;
3125}
3126
3127bool QDockAreaLayout::restoreDockWidget(QDockWidget *dockWidget)
3128{
3129 QDockAreaLayoutItem *item = nullptr;
3130 const auto groups =
3131 mainWindow->findChildren<QDockWidgetGroupWindow *>(Qt::FindDirectChildrenOnly);
3132 for (QDockWidgetGroupWindow *dwgw : groups) {
3133 QList<int> index = dwgw->layoutInfo()->indexOfPlaceHolder(dockWidget->objectName());
3134 if (!index.isEmpty()) {
3135 dockWidget->setParent(dwgw);
3136 item = const_cast<QDockAreaLayoutItem *>(&dwgw->layoutInfo()->item(index));
3137 break;
3138 }
3139 }
3140 if (!item) {
3141 QList<int> index = indexOfPlaceHolder(dockWidget->objectName());
3142 if (index.isEmpty())
3143 return false;
3144 item = const_cast<QDockAreaLayoutItem *>(&this->item(index));
3145 }
3146
3147 QPlaceHolderItem *placeHolder = item->placeHolderItem;
3148 Q_ASSERT(placeHolder != nullptr);
3149
3150 item->widgetItem = new QDockWidgetItem(dockWidget);
3151
3152 if (placeHolder->window) {
3153 const QRect r = constrainedRect(placeHolder->topLevelRect, dockWidget);
3154 dockWidget->d_func()->setWindowState(QDockWidgetPrivate::WindowStates(
3155 {QDockWidgetPrivate::WindowState::Floating,
3156 QDockWidgetPrivate::WindowState::Unplug}), r);
3157 }
3158 dockWidget->setVisible(!placeHolder->hidden);
3159
3160 item->placeHolderItem = nullptr;
3161 delete placeHolder;
3162
3163 return true;
3164}
3165
3166void QDockAreaLayout::addDockWidget(QInternal::DockPosition pos, QDockWidget *dockWidget,
3167 Qt::Orientation orientation)
3168{
3169 QLayoutItem *dockWidgetItem = new QDockWidgetItem(dockWidget);
3170 QDockAreaLayoutInfo &info = docks[pos];
3171 if (orientation == info.o || info.item_list.size() <= 1) {
3172 // empty dock areas, or dock areas containing exactly one widget can have their orientation
3173 // switched.
3174 info.o = orientation;
3175
3176 QDockAreaLayoutItem new_item(dockWidgetItem);
3177 info.item_list.append(new_item);
3178#if QT_CONFIG(tabbar)
3179 if (info.tabbed && !new_item.skip()) {
3180 info.updateTabBar();
3181 info.setCurrentTabId(tabId(new_item));
3182 }
3183#endif
3184 } else {
3185#if QT_CONFIG(tabbar)
3186 int tbshape = info.tabBarShape;
3187#else
3188 int tbshape = 0;
3189#endif
3190 QDockAreaLayoutInfo new_info(&sep, pos, orientation, tbshape, mainWindow);
3191 new_info.item_list.append(QDockAreaLayoutItem(new QDockAreaLayoutInfo(info)));
3192 new_info.item_list.append(QDockAreaLayoutItem(dockWidgetItem));
3193 info = new_info;
3194 }
3195
3196 removePlaceHolder(dockWidget->objectName());
3197}
3198
3199#if QT_CONFIG(tabbar)
3200void QDockAreaLayout::tabifyDockWidget(QDockWidget *first, QDockWidget *second)
3201{
3202 const QList<int> path = indexOf(first);
3203 if (path.isEmpty())
3204 return;
3205
3206 QDockAreaLayoutInfo *info = this->info(path);
3207 Q_ASSERT(info != nullptr);
3208 info->tab(path.last(), new QDockWidgetItem(second));
3209
3210 removePlaceHolder(second->objectName());
3211}
3212#endif // QT_CONFIG(tabbar)
3213
3214void QDockAreaLayout::resizeDocks(const QList<QDockWidget *> &docks,
3215 const QList<int> &sizes, Qt::Orientation o)
3216{
3217 if (Q_UNLIKELY(docks.size() != sizes.size())) {
3218 qWarning("QMainWidget::resizeDocks: size of the lists are not the same");
3219 return;
3220 }
3221 int count = docks.size();
3222 fallbackToSizeHints = false;
3223 for (int i = 0; i < count; ++i) {
3224 QList<int> path = indexOf(docks[i]);
3225 if (Q_UNLIKELY(path.isEmpty())) {
3226 qWarning("QMainWidget::resizeDocks: one QDockWidget is not part of the layout");
3227 continue;
3228 }
3229 int size = sizes[i];
3230 if (Q_UNLIKELY(size <= 0)) {
3231 qWarning("QMainWidget::resizeDocks: all sizes need to be larger than 0");
3232 size = 1;
3233 }
3234
3235 while (path.size() > 1) {
3236 QDockAreaLayoutInfo *info = this->info(path);
3237#if QT_CONFIG(tabbar)
3238 if (!info->tabbed && info->o == o) {
3239 info->item_list[path.constLast()].size = size;
3240 int totalSize = 0;
3241 for (const QDockAreaLayoutItem &item : std::as_const(info->item_list)) {
3242 if (!item.skip()) {
3243 if (totalSize != 0)
3244 totalSize += sep;
3245 totalSize += item.size == -1 ? pick(o, item.sizeHint()) : item.size;
3246 }
3247 }
3248 size = totalSize;
3249 }
3250#endif // QT_CONFIG(tabbar)
3251 path.removeLast();
3252 }
3253
3254 const int dockNum = path.constFirst();
3255 Q_ASSERT(dockNum < QInternal::DockCount);
3256 QRect &r = this->docks[dockNum].rect;
3257 QSize s = r.size();
3258 rpick(o, s) = size;
3259 r.setSize(s);
3260 }
3261}
3262
3263void QDockAreaLayout::splitDockWidget(QDockWidget *after,
3264 QDockWidget *dockWidget,
3265 Qt::Orientation orientation)
3266{
3267 const QList<int> path = indexOf(after);
3268 if (path.isEmpty())
3269 return;
3270
3271 QDockAreaLayoutInfo *info = this->info(path);
3272 Q_ASSERT(info != nullptr);
3273 info->split(path.last(), orientation, new QDockWidgetItem(dockWidget));
3274
3275 removePlaceHolder(dockWidget->objectName());
3276}
3277
3278void QDockAreaLayout::apply(bool animate)
3279{
3280 QWidgetAnimator &widgetAnimator = qt_mainwindow_layout(mainWindow)->widgetAnimator;
3281
3282 for (int i = 0; i < QInternal::DockCount; ++i)
3283 docks[i].apply(animate);
3284 if (centralWidgetItem != nullptr && !centralWidgetItem->isEmpty()) {
3285 widgetAnimator.animate(centralWidgetItem->widget(), centralWidgetRect,
3286 animate);
3287 }
3288#if QT_CONFIG(tabbar)
3289 if (sep == 1)
3290 updateSeparatorWidgets();
3291#endif // QT_CONFIG(tabbar)
3292}
3293
3294void QDockAreaLayout::paintSeparators(QPainter *p, QWidget *widget,
3295 const QRegion &clip,
3296 const QPoint &mouse) const
3297{
3298 for (int i = 0; i < QInternal::DockCount; ++i) {
3299 const QDockAreaLayoutInfo &dock = docks[i];
3300 if (dock.isEmpty())
3301 continue;
3302 QRect r = separatorRect(i);
3303 if (clip.contains(r) && !dock.hasFixedSize()) {
3304 Qt::Orientation opposite = dock.o == Qt::Horizontal
3305 ? Qt::Vertical : Qt::Horizontal;
3306 paintSep(p, widget, r, opposite, r.contains(mouse));
3307 }
3308 if (clip.contains(dock.rect))
3309 dock.paintSeparators(p, widget, clip, mouse);
3310 }
3311}
3312
3313QRegion QDockAreaLayout::separatorRegion() const
3314{
3315 QRegion result;
3316
3317 for (int i = 0; i < QInternal::DockCount; ++i) {
3318 const QDockAreaLayoutInfo &dock = docks[i];
3319 if (dock.isEmpty())
3320 continue;
3321 result |= separatorRect(i);
3322 result |= dock.separatorRegion();
3323 }
3324
3325 return result;
3326}
3327
3328int QDockAreaLayout::separatorMove(const QList<int> &separator, const QPoint &origin,
3329 const QPoint &dest)
3330{
3331 int delta = 0;
3332 const auto dockPosition = static_cast<QInternal::DockPosition>(separator.last());
3333 const bool isHorizontal =
3334 dockPosition == QInternal::LeftDock || dockPosition == QInternal::RightDock;
3335 const bool isLeftOrTop = dockPosition == QInternal::LeftDock || dockPosition == QInternal::TopDock;
3336 const bool separatorIsWithinDock = separator.size() > 1;
3337
3338 if (separatorIsWithinDock) {
3339 // The dock area contains more than one dock widget and therefore an internal separator,
3340 // which is being moved. The move changes the sizes of the dock widgets docked in the dock area.
3341 // The dock area's geometry remains unchanged.
3342 QDockAreaLayoutInfo *info = this->info(separator);
3343 delta = pick(info->o, dest - origin);
3344 if (delta != 0)
3345 delta = info->separatorMove(dockPosition, delta);
3346 info->apply(/* animate = */ false);
3347 return delta;
3348 }
3349
3350 // The dock area's external separator is moved. The move changes the size of the contained
3351 // dock widgets, as well as the size of neighbouring dock areas and their docked dock widgets.
3352 // If the move shrinks the contained dock widgets to their minimum size,
3353 // the entire dock area will attempted to be moved, retaining the minumum size.
3354 // If the contained dock widgets have to be shrunk below minimum size, they will be collapsed.
3355
3356 QList<QLayoutStruct> list;
3357
3358 if (isHorizontal)
3359 getGrid(nullptr, &list);
3360 else
3361 getGrid(&list, nullptr);
3362
3363 const int sep_index = isLeftOrTop ? 0 : 1;
3364 const Qt::Orientation o = isHorizontal ? Qt::Horizontal : Qt::Vertical;
3365
3366 delta = pick(o, dest - origin);
3367 delta = separatorMoveHelper(list, sep_index, delta, sep);
3368
3369 fallbackToSizeHints = false;
3370
3371 if (isHorizontal)
3372 setGrid(nullptr, &list);
3373 else
3374 setGrid(&list, nullptr);
3375
3376 apply(/* animate = */ false);
3377
3378 return delta;
3379}
3380
3381int QDockAreaLayoutInfo::separatorMove(const QList<int> &separator, const QPoint &origin,
3382 const QPoint &dest)
3383{
3384 int delta = 0;
3385 int index = separator.last();
3386 QDockAreaLayoutInfo *info = this->info(separator);
3387 delta = pick(info->o, dest - origin);
3388 if (delta != 0)
3389 delta = info->separatorMove(index, delta);
3390 info->apply(/* animate = */ false);
3391 return delta;
3392}
3393
3394#if QT_CONFIG(tabbar)
3395// Sets the correct positions for the separator widgets
3396// Allocates new separator widgets with getSeparatorWidget
3397void QDockAreaLayout::updateSeparatorWidgets() const
3398{
3399 int j = 0;
3400
3401 for (int i = 0; i < QInternal::DockCount; ++i) {
3402 const QDockAreaLayoutInfo &dock = docks[i];
3403 if (dock.isEmpty())
3404 continue;
3405
3406 QWidget *sepWidget;
3407 if (j < separatorWidgets.size()) {
3408 sepWidget = separatorWidgets.at(j);
3409 if (!sepWidget) {
3410 qWarning("QDockAreaLayout::updateSeparatorWidgets: null separator widget");
3411 sepWidget = qt_mainwindow_layout(mainWindow)->getSeparatorWidget();
3412 separatorWidgets[j] = sepWidget;
3413 }
3414 } else {
3415 sepWidget = qt_mainwindow_layout(mainWindow)->getSeparatorWidget();
3416 separatorWidgets.append(sepWidget);
3417 }
3418 j++;
3419
3420 Q_ASSERT(sepWidget);
3421 raiseSeparatorWidget(sepWidget);
3422
3423 QRect sepRect = separatorRect(i).adjusted(-2, -2, 2, 2);
3424 sepWidget->setGeometry(sepRect);
3425 sepWidget->setMask( QRegion(separatorRect(i).translated( - sepRect.topLeft())));
3426 sepWidget->show();
3427 }
3428 for (int i = j; i < separatorWidgets.size(); ++i)
3429 separatorWidgets.at(i)->hide();
3430
3431 separatorWidgets.resize(j);
3432}
3433#endif // QT_CONFIG(tabbar)
3434
3435QLayoutItem *QDockAreaLayout::itemAt(int *x, int index) const
3436{
3437 Q_ASSERT(x != nullptr);
3438
3439 for (int i = 0; i < QInternal::DockCount; ++i) {
3440 const QDockAreaLayoutInfo &dock = docks[i];
3441 if (QLayoutItem *ret = dock.itemAt(x, index))
3442 return ret;
3443 }
3444
3445 if (centralWidgetItem && (*x)++ == index)
3446 return centralWidgetItem;
3447
3448 return nullptr;
3449}
3450
3451QLayoutItem *QDockAreaLayout::takeAt(int *x, int index)
3452{
3453 Q_ASSERT(x != nullptr);
3454
3455 for (int i = 0; i < QInternal::DockCount; ++i) {
3456 QDockAreaLayoutInfo &dock = docks[i];
3457 if (QLayoutItem *ret = dock.takeAt(x, index))
3458 return ret;
3459 }
3460
3461 if (centralWidgetItem && (*x)++ == index) {
3462 QLayoutItem *ret = centralWidgetItem;
3463 centralWidgetItem = nullptr;
3464 return ret;
3465 }
3466
3467 return nullptr;
3468}
3469
3470void QDockAreaLayout::deleteAllLayoutItems()
3471{
3472 for (int i = 0; i < QInternal::DockCount; ++i)
3473 docks[i].deleteAllLayoutItems();
3474}
3475
3476#if QT_CONFIG(tabbar)
3477QSet<QTabBar*> QDockAreaLayout::usedTabBars() const
3478{
3479 QSet<QTabBar*> result;
3480 for (int i = 0; i < QInternal::DockCount; ++i) {
3481 const QDockAreaLayoutInfo &dock = docks[i];
3482 result += dock.usedTabBars();
3483 }
3484 return result;
3485}
3486
3487// Returns the set of all used separator widgets
3488QSet<QWidget*> QDockAreaLayout::usedSeparatorWidgets() const
3489{
3490 QSet<QWidget*> result;
3491 const int numSeparators = separatorWidgets.size();
3492 result.reserve(numSeparators);
3493 for (int i = 0; i < numSeparators; ++i)
3494 result << separatorWidgets.at(i);
3495 for (int i = 0; i < QInternal::DockCount; ++i) {
3496 const QDockAreaLayoutInfo &dock = docks[i];
3497 result += dock.usedSeparatorWidgets();
3498 }
3499 return result;
3500}
3501#endif
3502
3503QRect QDockAreaLayout::gapRect(const QList<int> &path) const
3504{
3505 const QDockAreaLayoutInfo *info = this->info(path);
3506 if (info == nullptr)
3507 return QRect();
3508 int index = path.last();
3509 if (index < 0 || index >= info->item_list.size())
3510 return QRect();
3511 return info->itemRect(index, true);
3512}
3513
3514void QDockAreaLayout::keepSize(QDockWidget *w)
3515{
3516 QList<int> path = indexOf(w);
3517 if (path.isEmpty())
3518 return;
3519 QDockAreaLayoutItem &item = this->item(path);
3520 if (item.size != -1)
3521 item.flags |= QDockAreaLayoutItem::KeepSize;
3522}
3523
3524void QDockAreaLayout::styleChangedEvent()
3525{
3526 sep = mainWindow->style()->pixelMetric(QStyle::PM_DockWidgetSeparatorExtent, nullptr, mainWindow);
3527 if (isValid())
3528 fitLayout();
3529}
3530
3531QT_END_NAMESPACE
friend class QWidget
Definition qpainter.h:431
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