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