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