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