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