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

source code of qtbase/src/widgets/widgets/qdockarealayout.cpp