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