1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include <QWidgetItem>
5#include <QToolBar>
6#include <QStyleOption>
7#include <QApplication>
8#include <qdebug.h>
9
10#include "qtoolbararealayout_p.h"
11#include "qmainwindowlayout_p.h"
12#include "qwidgetanimator_p.h"
13#include "qtoolbarlayout_p.h"
14#include "qtoolbar_p.h"
15
16/******************************************************************************
17** QToolBarAreaLayoutItem
18*/
19
20QT_BEGIN_NAMESPACE
21
22// qmainwindow.cpp
23extern QMainWindowLayout *qt_mainwindow_layout(const QMainWindow *mainWindow);
24
25QSize QToolBarAreaLayoutItem::minimumSize() const
26{
27 if (skip())
28 return QSize(0, 0);
29 return qSmartMinSize(i: static_cast<QWidgetItem*>(widgetItem));
30}
31
32QSize QToolBarAreaLayoutItem::sizeHint() const
33{
34 if (skip())
35 return QSize(0, 0);
36
37 return realSizeHint();
38}
39
40//returns the real size hint not taking into account the visibility of the widget
41QSize QToolBarAreaLayoutItem::realSizeHint() const
42{
43 QWidget *wid = widgetItem->widget();
44 QSize s = wid->sizeHint().expandedTo(otherSize: wid->minimumSizeHint());
45 if (wid->sizePolicy().horizontalPolicy() == QSizePolicy::Ignored)
46 s.setWidth(0);
47 if (wid->sizePolicy().verticalPolicy() == QSizePolicy::Ignored)
48 s.setHeight(0);
49 s = s.boundedTo(otherSize: wid->maximumSize())
50 .expandedTo(otherSize: wid->minimumSize());
51 return s;
52}
53
54bool QToolBarAreaLayoutItem::skip() const
55{
56 if (gap)
57 return false;
58 return widgetItem == nullptr || widgetItem->isEmpty();
59}
60
61/******************************************************************************
62** QToolBarAreaLayoutLine
63*/
64
65QToolBarAreaLayoutLine::QToolBarAreaLayoutLine(Qt::Orientation orientation)
66 : o(orientation)
67{
68}
69
70QSize QToolBarAreaLayoutLine::sizeHint() const
71{
72 int a = 0, b = 0;
73 for (int i = 0; i < toolBarItems.size(); ++i) {
74 const QToolBarAreaLayoutItem &item = toolBarItems.at(i);
75 if (item.skip())
76 continue;
77
78 QSize sh = item.sizeHint();
79 a += item.preferredSize > 0 ? item.preferredSize : pick(o, size: sh);
80 b = qMax(a: b, b: perp(o, size: sh));
81 }
82
83 QSize result;
84 rpick(o, size&: result) = a;
85 rperp(o, size&: result) = b;
86
87 return result;
88}
89
90QSize QToolBarAreaLayoutLine::minimumSize() const
91{
92 int a = 0, b = 0;
93 for (int i = 0; i < toolBarItems.size(); ++i) {
94 const QToolBarAreaLayoutItem &item = toolBarItems[i];
95 if (item.skip())
96 continue;
97
98 QSize ms = item.minimumSize();
99 a += pick(o, size: ms);
100 b = qMax(a: b, b: perp(o, size: ms));
101 }
102
103 QSize result;
104 rpick(o, size&: result) = a;
105 rperp(o, size&: result) = b;
106
107 return result;
108}
109
110void QToolBarAreaLayoutLine::fitLayout()
111{
112 int last = -1;
113 int min = pick(o, size: minimumSize());
114 int space = pick(o, size: rect.size());
115 int extra = qMax(a: 0, b: space - min);
116
117 for (int i = 0; i < toolBarItems.size(); ++i) {
118 QToolBarAreaLayoutItem &item = toolBarItems[i];
119 if (item.skip())
120 continue;
121
122 if (QToolBarLayout *tblayout = qobject_cast<QToolBarLayout*>(object: item.widgetItem->widget()->layout()))
123 tblayout->checkUsePopupMenu();
124
125 const int itemMin = pick(o, size: item.minimumSize());
126 //preferredSize is the default if it is set, otherwise, we take the sizehint
127 item.size = item.preferredSize > 0 ? item.preferredSize : pick(o, size: item.sizeHint());
128
129 //the extraspace is the space above the item minimum sizehint
130 const int extraSpace = qMin(a: item.size - itemMin, b: extra);
131 item.size = itemMin + extraSpace; //that is the real size
132
133 extra -= extraSpace;
134
135 last = i;
136 }
137
138 // calculate the positions from the sizes
139 int pos = 0;
140 for (int i = 0; i < toolBarItems.size(); ++i) {
141 QToolBarAreaLayoutItem &item = toolBarItems[i];
142 if (item.skip())
143 continue;
144
145 item.pos = pos;
146 if (i == last) // stretch the last item to the end of the line
147 item.size = qMax(a: 0, b: pick(o, size: rect.size()) - item.pos);
148 pos += item.size;
149 }
150}
151
152bool QToolBarAreaLayoutLine::skip() const
153{
154 for (int i = 0; i < toolBarItems.size(); ++i) {
155 if (!toolBarItems.at(i).skip())
156 return false;
157 }
158 return true;
159}
160
161/******************************************************************************
162** QToolBarAreaLayoutInfo
163*/
164
165QToolBarAreaLayoutInfo::QToolBarAreaLayoutInfo(QInternal::DockPosition pos)
166 : dockPos(pos), dirty(false)
167{
168 switch (pos) {
169 case QInternal::LeftDock:
170 case QInternal::RightDock:
171 o = Qt::Vertical;
172 break;
173 case QInternal::TopDock:
174 case QInternal::BottomDock:
175 o = Qt::Horizontal;
176 break;
177 default:
178 o = Qt::Horizontal;
179 break;
180 }
181}
182
183QSize QToolBarAreaLayoutInfo::sizeHint() const
184{
185 int a = 0, b = 0;
186 for (int i = 0; i < lines.size(); ++i) {
187 const QToolBarAreaLayoutLine &l = lines.at(i);
188 if (l.skip())
189 continue;
190
191 QSize hint = l.sizeHint();
192 a = qMax(a, b: pick(o, size: hint));
193 b += perp(o, size: hint);
194 }
195
196 QSize result;
197 rpick(o, size&: result) = a;
198 rperp(o, size&: result) = b;
199
200 return result;
201}
202
203QSize QToolBarAreaLayoutInfo::minimumSize() const
204{
205 int a = 0, b = 0;
206 for (int i = 0; i < lines.size(); ++i) {
207 const QToolBarAreaLayoutLine &l = lines.at(i);
208 if (l.skip())
209 continue;
210
211 QSize m = l.minimumSize();
212 a = qMax(a, b: pick(o, size: m));
213 b += perp(o, size: m);
214 }
215
216 QSize result;
217 rpick(o, size&: result) = a;
218 rperp(o, size&: result) = b;
219
220 return result;
221}
222
223void QToolBarAreaLayoutInfo::fitLayout()
224{
225 dirty = false;
226
227 int b = 0;
228
229 bool reverse = dockPos == QInternal::RightDock || dockPos == QInternal::BottomDock;
230
231 int i = reverse ? lines.size() - 1 : 0;
232 for (;;) {
233 if ((reverse && i < 0) || (!reverse && i == lines.size()))
234 break;
235
236 QToolBarAreaLayoutLine &l = lines[i];
237 if (!l.skip()) {
238 if (o == Qt::Horizontal) {
239 l.rect.setLeft(rect.left());
240 l.rect.setRight(rect.right());
241 l.rect.setTop(b + rect.top());
242 b += l.sizeHint().height();
243 l.rect.setBottom(b - 1 + rect.top());
244 } else {
245 l.rect.setTop(rect.top());
246 l.rect.setBottom(rect.bottom());
247 l.rect.setLeft(b + rect.left());
248 b += l.sizeHint().width();
249 l.rect.setRight(b - 1 + rect.left());
250 }
251
252 l.fitLayout();
253 }
254
255 i += reverse ? -1 : 1;
256 }
257}
258
259QLayoutItem *QToolBarAreaLayoutInfo::insertToolBar(QToolBar *before, QToolBar *toolBar)
260{
261 toolBar->setOrientation(o);
262 QLayoutItem *item = new QWidgetItemV2(toolBar);
263 insertItem(before, item);
264 return item;
265}
266
267void QToolBarAreaLayoutInfo::insertItem(QToolBar *before, QLayoutItem *item)
268{
269 if (before == nullptr) {
270 if (lines.isEmpty())
271 lines.append(t: QToolBarAreaLayoutLine(o));
272 lines.last().toolBarItems.append(t: item);
273 return;
274 }
275
276 for (int j = 0; j < lines.size(); ++j) {
277 QToolBarAreaLayoutLine &line = lines[j];
278
279 for (int k = 0; k < line.toolBarItems.size(); ++k) {
280 if (line.toolBarItems.at(i: k).widgetItem->widget() == before) {
281 line.toolBarItems.insert(i: k, t: item);
282 return;
283 }
284 }
285 }
286}
287
288void QToolBarAreaLayoutInfo::removeToolBar(QToolBar *toolBar)
289{
290 for (int j = 0; j < lines.size(); ++j) {
291 QToolBarAreaLayoutLine &line = lines[j];
292
293 for (int k = 0; k < line.toolBarItems.size(); ++k) {
294 QToolBarAreaLayoutItem &item = line.toolBarItems[k];
295 if (item.widgetItem->widget() == toolBar) {
296 delete item.widgetItem;
297 item.widgetItem = nullptr;
298 line.toolBarItems.removeAt(i: k);
299
300 if (line.toolBarItems.isEmpty() && j < lines.size() - 1)
301 lines.removeAt(i: j);
302
303 return;
304 }
305 }
306 }
307}
308
309void QToolBarAreaLayoutInfo::insertToolBarBreak(QToolBar *before)
310{
311 if (before == nullptr) {
312 if (!lines.isEmpty() && lines.constLast().toolBarItems.isEmpty())
313 return;
314 lines.append(t: QToolBarAreaLayoutLine(o));
315 return;
316 }
317
318 for (int j = 0; j < lines.size(); ++j) {
319 QToolBarAreaLayoutLine &line = lines[j];
320
321 for (int k = 0; k < line.toolBarItems.size(); ++k) {
322 if (line.toolBarItems.at(i: k).widgetItem->widget() == before) {
323 if (k == 0)
324 return;
325
326 QToolBarAreaLayoutLine newLine(o);
327 newLine.toolBarItems = line.toolBarItems.mid(pos: k);
328 line.toolBarItems = line.toolBarItems.mid(pos: 0, len: k);
329 lines.insert(i: j + 1, t: newLine);
330
331 return;
332 }
333 }
334 }
335}
336
337void QToolBarAreaLayoutInfo::removeToolBarBreak(QToolBar *before)
338{
339 for (int j = 0; j < lines.size(); ++j) {
340 const QToolBarAreaLayoutLine &line = lines.at(i: j);
341
342 for (int k = 0; k < line.toolBarItems.size(); ++k) {
343 if (line.toolBarItems.at(i: k).widgetItem->widget() == before) {
344 if (k != 0)
345 return;
346 if (j == 0)
347 return;
348
349 lines[j - 1].toolBarItems += lines[j].toolBarItems;
350 lines.removeAt(i: j);
351
352 return;
353 }
354 }
355 }
356}
357
358void QToolBarAreaLayoutInfo::moveToolBar(QToolBar *toolbar, int pos)
359{
360 if (dirty)
361 fitLayout();
362
363 dirty = true;
364
365 if (o == Qt::Vertical)
366 pos -= rect.top();
367
368 //here we actually update the preferredSize for the line containing the toolbar so that we move it
369 for (int j = 0; j < lines.size(); ++j) {
370 QToolBarAreaLayoutLine &line = lines[j];
371
372 int previousIndex = -1;
373 int minPos = 0;
374 for (int k = 0; k < line.toolBarItems.size(); ++k) {
375 QToolBarAreaLayoutItem &current = line.toolBarItems[k];
376 if (current.widgetItem->widget() == toolbar) {
377 int newPos = current.pos;
378
379 if (previousIndex >= 0) {
380 QToolBarAreaLayoutItem &previous = line.toolBarItems[previousIndex];
381 if (pos < current.pos) {
382 newPos = qMax(a: pos, b: minPos);
383 } else {
384 //we check the max value for the position (until everything at the right is "compressed")
385 int maxPos = pick(o, size: rect.size());
386 for(int l = k; l < line.toolBarItems.size(); ++l) {
387 const QToolBarAreaLayoutItem &item = line.toolBarItems.at(i: l);
388 if (!item.skip()) {
389 maxPos -= pick(o, size: item.minimumSize());
390 }
391 }
392 newPos = qMin(a: pos, b: maxPos);
393 }
394
395 //extra is the number of pixels to add to the previous toolbar
396 int extra = newPos - current.pos;
397
398 //we check if the previous is near its size hint
399 //in which case we try to stick to it
400 const int diff = pick(o, size: previous.sizeHint()) - (previous.size + extra);
401 if (qAbs(t: diff) < QApplication::startDragDistance()) {
402 //we stick to the default place and size
403 extra += diff;
404 }
405
406 //update for the current item
407 current.extendSize(o: line.o, extent: -extra);
408
409 if (extra >= 0) {
410 previous.extendSize(o: line.o, extent: extra);
411 } else {
412 //we need to push the toolbars on the left starting with previous
413 extra = -extra; // we just need to know the number of pixels
414 ///at this point we need to get extra pixels from the toolbars at the left
415 for(int l = previousIndex; l >=0; --l) {
416 QToolBarAreaLayoutItem &item = line.toolBarItems[l];
417 if (!item.skip()) {
418 const int minPreferredSize = pick(o, size: item.minimumSize());
419 const int margin = item.size - minPreferredSize;
420 if (margin < extra) {
421 item.resize(o: line.o, newSize: minPreferredSize);
422 extra -= margin;
423 } else {
424 item.extendSize(o: line.o, extent: -extra);
425 extra = 0;
426 }
427 }
428 }
429 Q_ASSERT(extra == 0);
430 }
431 } else {
432 //the item is the first one, it should be at position 0
433 }
434
435 return;
436
437 } else if (!current.skip()) {
438 previousIndex = k;
439 minPos += pick(o, size: current.minimumSize());
440 }
441 }
442 }
443}
444
445
446QList<int> QToolBarAreaLayoutInfo::gapIndex(const QPoint &pos, int *minDistance) const
447{
448 if (rect.contains(p: pos)) {
449 // <pos> is in QToolBarAreaLayout coordinates.
450 // <item.pos> is in local dockarea coordinates (see ~20 lines below)
451 // Since we're comparing p with item.pos, we put them in the same coordinate system.
452 const int p = pick(o, pos: pos - rect.topLeft());
453
454 for (int j = 0; j < lines.size(); ++j) {
455 const QToolBarAreaLayoutLine &line = lines.at(i: j);
456 if (line.skip())
457 continue;
458 if (!line.rect.contains(p: pos))
459 continue;
460
461 int k = 0;
462 for (; k < line.toolBarItems.size(); ++k) {
463 const QToolBarAreaLayoutItem &item = line.toolBarItems.at(i: k);
464 if (item.skip())
465 continue;
466
467 int size = qMin(a: item.size, b: pick(o, size: item.sizeHint()));
468
469 if (p > item.pos + size)
470 continue;
471 if (p > item.pos + size/2)
472 ++k;
473 break;
474 }
475
476 QList<int> result;
477 result << j << k;
478 *minDistance = 0; //we found a perfect match
479 return result;
480 }
481 } else {
482 const int dist = distance(pos);
483 //it will only return a path if the minDistance is higher than the current distance
484 if (dist >= 0 && *minDistance > dist) {
485 *minDistance = dist;
486
487 QList<int> result;
488 result << lines.size() << 0;
489 return result;
490 }
491 }
492
493 return QList<int>();
494}
495
496bool QToolBarAreaLayoutInfo::insertGap(const QList<int> &path, QLayoutItem *item)
497{
498 Q_ASSERT(path.size() == 2);
499 int j = path.first();
500 if (j == lines.size())
501 lines.append(t: QToolBarAreaLayoutLine(o));
502
503 QToolBarAreaLayoutLine &line = lines[j];
504 const int k = path.at(i: 1);
505
506 QToolBarAreaLayoutItem gap_item;
507 gap_item.gap = true;
508 gap_item.widgetItem = item;
509
510 //update the previous item's preferred size
511 for(int p = k - 1 ; p >= 0; --p) {
512 QToolBarAreaLayoutItem &previous = line.toolBarItems[p];
513 if (!previous.skip()) {
514 //we found the previous one
515 int previousSizeHint = pick(o: line.o, size: previous.sizeHint());
516 int previousExtraSpace = previous.size - previousSizeHint;
517
518 if (previousExtraSpace > 0) {
519 //in this case we reset the space
520 previous.preferredSize = -1;
521 previous.size = previousSizeHint;
522
523 gap_item.resize(o, newSize: previousExtraSpace);
524 }
525
526 break;
527 }
528 }
529
530 line.toolBarItems.insert(i: k, t: gap_item);
531 return true;
532
533}
534
535void QToolBarAreaLayoutInfo::clear()
536{
537 lines.clear();
538 rect = QRect();
539}
540
541QRect QToolBarAreaLayoutInfo::itemRect(const QList<int> &path) const
542{
543 Q_ASSERT(path.size() == 2);
544 int j = path.at(i: 0);
545 int k = path.at(i: 1);
546
547 const QToolBarAreaLayoutLine &line = lines.at(i: j);
548 const QToolBarAreaLayoutItem &item = line.toolBarItems.at(i: k);
549
550 QRect result = line.rect;
551
552 if (o == Qt::Horizontal) {
553 result.setLeft(item.pos + line.rect.left());
554 result.setWidth(item.size);
555 } else {
556 result.setTop(item.pos + line.rect.top());
557 result.setHeight(item.size);
558 }
559
560 return result;
561}
562
563int QToolBarAreaLayoutInfo::distance(const QPoint &pos) const
564{
565 switch (dockPos) {
566 case QInternal::LeftDock:
567 if (pos.y() < rect.bottom())
568 return pos.x() - rect.right();
569 break;
570 case QInternal::RightDock:
571 if (pos.y() < rect.bottom())
572 return rect.left() - pos.x();
573 break;
574 case QInternal::TopDock:
575 if (pos.x() < rect.right())
576 return pos.y() - rect.bottom();
577 break;
578 case QInternal::BottomDock:
579 if (pos.x() < rect.right())
580 return rect.top() - pos.y();
581 break;
582
583 case QInternal::DockCount:
584 break;
585 }
586 return -1;
587}
588
589/******************************************************************************
590** QToolBarAreaLayout
591*/
592
593QToolBarAreaLayout::QToolBarAreaLayout(const QMainWindow *win) : mainWindow(win), visible(true)
594{
595 for (int i = 0; i < QInternal::DockCount; ++i) {
596 QInternal::DockPosition pos = static_cast<QInternal::DockPosition>(i);
597 docks[i] = QToolBarAreaLayoutInfo(pos);
598 }
599}
600
601QRect QToolBarAreaLayout::fitLayout()
602{
603 if (!visible)
604 return rect;
605
606 QSize left_hint = docks[QInternal::LeftDock].sizeHint();
607 QSize right_hint = docks[QInternal::RightDock].sizeHint();
608 QSize top_hint = docks[QInternal::TopDock].sizeHint();
609 QSize bottom_hint = docks[QInternal::BottomDock].sizeHint();
610
611 QRect center = rect.adjusted(xp1: left_hint.width(), yp1: top_hint.height(),
612 xp2: -right_hint.width(), yp2: -bottom_hint.height());
613
614 docks[QInternal::TopDock].rect = QRect(rect.left(), rect.top(),
615 rect.width(), top_hint.height());
616 docks[QInternal::LeftDock].rect = QRect(rect.left(), center.top(),
617 left_hint.width(), center.height());
618 docks[QInternal::RightDock].rect = QRect(center.right() + 1, center.top(),
619 right_hint.width(), center.height());
620 docks[QInternal::BottomDock].rect = QRect(rect.left(), center.bottom() + 1,
621 rect.width(), bottom_hint.height());
622
623 docks[QInternal::TopDock].fitLayout();
624 docks[QInternal::LeftDock].fitLayout();
625 docks[QInternal::RightDock].fitLayout();
626 docks[QInternal::BottomDock].fitLayout();
627
628 return center;
629}
630
631QSize QToolBarAreaLayout::minimumSize(const QSize &centerMin) const
632{
633 if (!visible)
634 return centerMin;
635
636 QSize result = centerMin;
637
638 QSize left_min = docks[QInternal::LeftDock].minimumSize();
639 QSize right_min = docks[QInternal::RightDock].minimumSize();
640 QSize top_min = docks[QInternal::TopDock].minimumSize();
641 QSize bottom_min = docks[QInternal::BottomDock].minimumSize();
642
643 result.setWidth(qMax(a: top_min.width(), b: result.width()));
644 result.setWidth(qMax(a: bottom_min.width(), b: result.width()));
645 result.setHeight(qMax(a: left_min.height(), b: result.height()));
646 result.setHeight(qMax(a: right_min.height(), b: result.height()));
647
648 result.rwidth() += left_min.width() + right_min.width();
649 result.rheight() += top_min.height() + bottom_min.height();
650
651 return result;
652}
653
654QSize QToolBarAreaLayout::sizeHint(const QSize &centerHint) const
655{
656 if (!visible)
657 return centerHint;
658
659 QSize result = centerHint;
660
661 QSize left_hint = docks[QInternal::LeftDock].sizeHint();
662 QSize right_hint = docks[QInternal::RightDock].sizeHint();
663 QSize top_hint = docks[QInternal::TopDock].sizeHint();
664 QSize bottom_hint = docks[QInternal::BottomDock].sizeHint();
665
666 result.setWidth(qMax(a: top_hint.width(), b: result.width()));
667 result.setWidth(qMax(a: bottom_hint.width(), b: result.width()));
668 result.setHeight(qMax(a: left_hint.height(), b: result.height()));
669 result.setHeight(qMax(a: right_hint.height(), b: result.height()));
670
671 result.rwidth() += left_hint.width() + right_hint.width();
672 result.rheight() += top_hint.height() + bottom_hint.height();
673
674 return result;
675}
676
677QRect QToolBarAreaLayout::rectHint(const QRect &r) const
678{
679 int coef = visible ? 1 : -1;
680
681 QRect result = r;
682
683 QSize left_hint = docks[QInternal::LeftDock].sizeHint();
684 QSize right_hint = docks[QInternal::RightDock].sizeHint();
685 QSize top_hint = docks[QInternal::TopDock].sizeHint();
686 QSize bottom_hint = docks[QInternal::BottomDock].sizeHint();
687
688 result.adjust(dx1: -left_hint.width()*coef, dy1: -top_hint.height()*coef,
689 dx2: right_hint.width()*coef, dy2: bottom_hint.height()*coef);
690
691 return result;
692}
693
694QLayoutItem *QToolBarAreaLayout::itemAt(int *x, int index) const
695{
696 Q_ASSERT(x != nullptr);
697
698 for (int i = 0; i < QInternal::DockCount; ++i) {
699 const QToolBarAreaLayoutInfo &dock = docks[i];
700
701 for (int j = 0; j < dock.lines.size(); ++j) {
702 const QToolBarAreaLayoutLine &line = dock.lines.at(i: j);
703
704 for (int k = 0; k < line.toolBarItems.size(); ++k) {
705 if ((*x)++ == index)
706 return line.toolBarItems.at(i: k).widgetItem;
707 }
708 }
709 }
710
711 return nullptr;
712}
713
714QLayoutItem *QToolBarAreaLayout::takeAt(int *x, int index)
715{
716 Q_ASSERT(x != nullptr);
717
718 for (int i = 0; i < QInternal::DockCount; ++i) {
719 QToolBarAreaLayoutInfo &dock = docks[i];
720
721 for (int j = 0; j < dock.lines.size(); ++j) {
722 QToolBarAreaLayoutLine &line = dock.lines[j];
723
724 for (int k = 0; k < line.toolBarItems.size(); ++k) {
725 if ((*x)++ == index) {
726 QLayoutItem *result = line.toolBarItems.takeAt(i: k).widgetItem;
727 if (line.toolBarItems.isEmpty())
728 dock.lines.removeAt(i: j);
729 return result;
730 }
731 }
732 }
733 }
734
735 return nullptr;
736}
737
738void QToolBarAreaLayout::deleteAllLayoutItems()
739{
740 for (int i = 0; i < QInternal::DockCount; ++i) {
741 QToolBarAreaLayoutInfo &dock = docks[i];
742
743 for (int j = 0; j < dock.lines.size(); ++j) {
744 QToolBarAreaLayoutLine &line = dock.lines[j];
745
746 for (int k = 0; k < line.toolBarItems.size(); ++k) {
747 QToolBarAreaLayoutItem &item = line.toolBarItems[k];
748 if (!item.gap)
749 delete item.widgetItem;
750 item.widgetItem = nullptr;
751 }
752 }
753 }
754}
755
756QInternal::DockPosition QToolBarAreaLayout::findToolBar(const QToolBar *toolBar) const
757{
758 for (int i = 0; i < QInternal::DockCount; ++i) {
759 const QToolBarAreaLayoutInfo &dock = docks[i];
760
761 for (int j = 0; j < dock.lines.size(); ++j) {
762 const QToolBarAreaLayoutLine &line = dock.lines.at(i: j);
763
764 for (int k = 0; k < line.toolBarItems.size(); ++k) {
765 if (line.toolBarItems.at(i: k).widgetItem->widget() == toolBar)
766 return static_cast<QInternal::DockPosition>(i);
767 }
768 }
769 }
770
771 return QInternal::DockCount;
772}
773
774QLayoutItem *QToolBarAreaLayout::insertToolBar(QToolBar *before, QToolBar *toolBar)
775{
776 QInternal::DockPosition pos = findToolBar(toolBar: before);
777 if (pos == QInternal::DockCount)
778 return nullptr;
779
780 return docks[pos].insertToolBar(before, toolBar);
781}
782
783void QToolBarAreaLayout::removeToolBar(QToolBar *toolBar)
784{
785 QInternal::DockPosition pos = findToolBar(toolBar);
786 if (pos == QInternal::DockCount)
787 return;
788 docks[pos].removeToolBar(toolBar);
789}
790
791QLayoutItem *QToolBarAreaLayout::addToolBar(QInternal::DockPosition pos, QToolBar *toolBar)
792{
793 return docks[pos].insertToolBar(before: nullptr, toolBar);
794}
795
796void QToolBarAreaLayout::insertToolBarBreak(QToolBar *before)
797{
798 QInternal::DockPosition pos = findToolBar(toolBar: before);
799 if (pos == QInternal::DockCount)
800 return;
801 docks[pos].insertToolBarBreak(before);
802}
803
804void QToolBarAreaLayout::removeToolBarBreak(QToolBar *before)
805{
806 QInternal::DockPosition pos = findToolBar(toolBar: before);
807 if (pos == QInternal::DockCount)
808 return;
809 docks[pos].removeToolBarBreak(before);
810}
811
812void QToolBarAreaLayout::addToolBarBreak(QInternal::DockPosition pos)
813{
814 docks[pos].insertToolBarBreak(before: nullptr);
815}
816
817void QToolBarAreaLayout::moveToolBar(QToolBar *toolbar, int p)
818{
819 QInternal::DockPosition pos = findToolBar(toolBar: toolbar);
820 if (pos == QInternal::DockCount)
821 return;
822 docks[pos].moveToolBar(toolbar, pos: p);
823}
824
825
826void QToolBarAreaLayout::insertItem(QInternal::DockPosition pos, QLayoutItem *item)
827{
828 if (docks[pos].lines.isEmpty())
829 docks[pos].lines.append(t: QToolBarAreaLayoutLine(docks[pos].o));
830 docks[pos].lines.last().toolBarItems.append(t: item);
831}
832
833void QToolBarAreaLayout::insertItem(QToolBar *before, QLayoutItem *item)
834{
835 QInternal::DockPosition pos = findToolBar(toolBar: before);
836 if (pos == QInternal::DockCount)
837 return;
838
839 docks[pos].insertItem(before, item);
840}
841
842void QToolBarAreaLayout::apply(bool animate)
843{
844 QMainWindowLayout *layout = qt_mainwindow_layout(mainWindow);
845 Q_ASSERT(layout != nullptr);
846
847 Qt::LayoutDirection dir = mainWindow->layoutDirection();
848
849 for (int i = 0; i < QInternal::DockCount; ++i) {
850 const QToolBarAreaLayoutInfo &dock = docks[i];
851
852 for (int j = 0; j < dock.lines.size(); ++j) {
853 const QToolBarAreaLayoutLine &line = dock.lines.at(i: j);
854 if (line.skip())
855 continue;
856
857 for (int k = 0; k < line.toolBarItems.size(); ++k) {
858 const QToolBarAreaLayoutItem &item = line.toolBarItems.at(i: k);
859 if (item.skip() || item.gap)
860 continue;
861
862 QRect geo;
863 if (visible) {
864 if (line.o == Qt::Horizontal) {
865 geo.setTop(line.rect.top());
866 geo.setBottom(line.rect.bottom());
867 geo.setLeft(line.rect.left() + item.pos);
868 geo.setRight(line.rect.left() + item.pos + item.size - 1);
869 } else {
870 geo.setLeft(line.rect.left());
871 geo.setRight(line.rect.right());
872 geo.setTop(line.rect.top() + item.pos);
873 geo.setBottom(line.rect.top() + item.pos + item.size - 1);
874 }
875 }
876
877 QWidget *widget = item.widgetItem->widget();
878 if (QToolBar *toolBar = qobject_cast<QToolBar*>(object: widget)) {
879 QToolBarLayout *tbl = qobject_cast<QToolBarLayout*>(object: toolBar->layout());
880 if (tbl->expanded) {
881 QPoint tr = geo.topRight();
882 QSize size = tbl->expandedSize(size: geo.size());
883 geo.setSize(size);
884 geo.moveTopRight(p: tr);
885 if (geo.bottom() > rect.bottom())
886 geo.moveBottom(pos: rect.bottom());
887 if (geo.right() > rect.right())
888 geo.moveRight(pos: rect.right());
889 if (geo.left() < 0)
890 geo.moveLeft(pos: 0);
891 if (geo.top() < 0)
892 geo.moveTop(pos: 0);
893 }
894 }
895
896 if (visible && dock.o == Qt::Horizontal)
897 geo = QStyle::visualRect(direction: dir, boundingRect: line.rect, logicalRect: geo);
898
899 layout->widgetAnimator.animate(widget, final_geometry: geo, animate);
900 }
901 }
902 }
903}
904
905bool QToolBarAreaLayout::toolBarBreak(QToolBar *toolBar) const
906{
907 for (int i = 0; i < QInternal::DockCount; ++i) {
908 const QToolBarAreaLayoutInfo &dock = docks[i];
909
910 for (int j = 0; j < dock.lines.size(); ++j) {
911 const QToolBarAreaLayoutLine &line = dock.lines.at(i: j);
912
913 for (int k = 0; k < line.toolBarItems.size(); ++k) {
914 if (line.toolBarItems.at(i: k).widgetItem->widget() == toolBar)
915 return j > 0 && k == 0;
916 }
917 }
918 }
919
920 return false;
921}
922
923void QToolBarAreaLayout::getStyleOptionInfo(QStyleOptionToolBar *option, QToolBar *toolBar) const
924{
925 for (int i = 0; i < QInternal::DockCount; ++i) {
926 const QToolBarAreaLayoutInfo &dock = docks[i];
927
928 for (int j = 0; j < dock.lines.size(); ++j) {
929 const QToolBarAreaLayoutLine &line = dock.lines.at(i: j);
930
931 for (int k = 0; k < line.toolBarItems.size(); ++k) {
932 if (line.toolBarItems.at(i: k).widgetItem->widget() == toolBar) {
933 if (line.toolBarItems.size() == 1)
934 option->positionWithinLine = QStyleOptionToolBar::OnlyOne;
935 else if (k == 0)
936 option->positionWithinLine = QStyleOptionToolBar::Beginning;
937 else if (k == line.toolBarItems.size() - 1)
938 option->positionWithinLine = QStyleOptionToolBar::End;
939 else
940 option->positionWithinLine = QStyleOptionToolBar::Middle;
941
942 if (dock.lines.size() == 1)
943 option->positionOfLine = QStyleOptionToolBar::OnlyOne;
944 else if (j == 0)
945 option->positionOfLine = QStyleOptionToolBar::Beginning;
946 else if (j == dock.lines.size() - 1)
947 option->positionOfLine = QStyleOptionToolBar::End;
948 else
949 option->positionOfLine = QStyleOptionToolBar::Middle;
950
951 return;
952 }
953 }
954 }
955 }
956}
957
958QList<int> QToolBarAreaLayout::indexOf(QWidget *toolBar) const
959{
960 QList<int> result;
961
962 bool found = false;
963
964 for (int i = 0; i < QInternal::DockCount; ++i) {
965 const QToolBarAreaLayoutInfo &dock = docks[i];
966
967 for (int j = 0; j < dock.lines.size(); ++j) {
968 const QToolBarAreaLayoutLine &line = dock.lines.at(i: j);
969
970 for (int k = 0; k < line.toolBarItems.size(); ++k) {
971 const QToolBarAreaLayoutItem &item = line.toolBarItems.at(i: k);
972 if (!item.gap && item.widgetItem->widget() == toolBar) {
973 found = true;
974 result.prepend(t: k);
975 break;
976 }
977 }
978
979 if (found) {
980 result.prepend(t: j);
981 break;
982 }
983 }
984
985 if (found) {
986 result.prepend(t: i);
987 break;
988 }
989 }
990
991 return result;
992}
993
994//this functions returns the path to the possible gapindex for the position pos
995QList<int> QToolBarAreaLayout::gapIndex(const QPoint &pos) const
996{
997 Qt::LayoutDirection dir = mainWindow->layoutDirection();
998 int minDistance = 80; // when a dock area is empty, how "wide" is it?
999 QList<int> ret; //return value
1000 for (int i = 0; i < QInternal::DockCount; ++i) {
1001 QPoint p = pos;
1002 if (docks[i].o == Qt::Horizontal)
1003 p = QStyle::visualPos(direction: dir, boundingRect: docks[i].rect, logicalPos: p);
1004 QList<int> result = docks[i].gapIndex(pos: p, minDistance: &minDistance);
1005 if (!result.isEmpty()) {
1006 result.prepend(t: i);
1007 ret = result;
1008 }
1009 }
1010
1011 return ret;
1012}
1013
1014QList<int> QToolBarAreaLayout::currentGapIndex() const
1015{
1016 for (int i = 0; i < QInternal::DockCount; ++i) {
1017 const QToolBarAreaLayoutInfo &dock = docks[i];
1018
1019 for (int j = 0; j < dock.lines.size(); ++j) {
1020 const QToolBarAreaLayoutLine &line = dock.lines[j];
1021
1022 for (int k = 0; k < line.toolBarItems.size(); k++) {
1023 if (line.toolBarItems[k].gap) {
1024 QList<int> result;
1025 result << i << j << k;
1026 return result;
1027 }
1028 }
1029 }
1030 }
1031 return QList<int>();
1032}
1033
1034bool QToolBarAreaLayout::insertGap(const QList<int> &path, QLayoutItem *item)
1035{
1036 Q_ASSERT(path.size() == 3);
1037 const int i = path.first();
1038 Q_ASSERT(i >= 0 && i < QInternal::DockCount);
1039 return docks[i].insertGap(path: path.mid(pos: 1), item);
1040}
1041
1042void QToolBarAreaLayout::remove(const QList<int> &path)
1043{
1044 Q_ASSERT(path.size() == 3);
1045 QToolBarAreaLayoutInfo &dock = docks[path.at(i: 0)];
1046 QToolBarAreaLayoutLine &line = dock.lines[path.at(i: 1)];
1047 line.toolBarItems.removeAt(i: path.at(i: 2));
1048 if (line.toolBarItems.isEmpty())
1049 dock.lines.removeAt(i: path.at(i: 1));
1050}
1051
1052void QToolBarAreaLayout::remove(QLayoutItem *item)
1053{
1054 for (int i = 0; i < QInternal::DockCount; ++i) {
1055 QToolBarAreaLayoutInfo &dock = docks[i];
1056
1057 for (int j = 0; j < dock.lines.size(); ++j) {
1058 QToolBarAreaLayoutLine &line = dock.lines[j];
1059
1060 for (int k = 0; k < line.toolBarItems.size(); k++) {
1061 if (line.toolBarItems[k].widgetItem == item) {
1062 line.toolBarItems.removeAt(i: k);
1063 if (line.toolBarItems.isEmpty())
1064 dock.lines.removeAt(i: j);
1065 return;
1066 }
1067 }
1068 }
1069 }
1070}
1071
1072void QToolBarAreaLayout::clear()
1073{
1074 for (int i = 0; i < QInternal::DockCount; ++i)
1075 docks[i].clear();
1076 rect = QRect();
1077}
1078
1079QToolBarAreaLayoutItem *QToolBarAreaLayout::item(const QList<int> &path)
1080{
1081 Q_ASSERT(path.size() == 3);
1082
1083 if (path.at(i: 0) < 0 || path.at(i: 0) >= QInternal::DockCount)
1084 return nullptr;
1085 QToolBarAreaLayoutInfo &info = docks[path.at(i: 0)];
1086 if (path.at(i: 1) < 0 || path.at(i: 1) >= info.lines.size())
1087 return nullptr;
1088 QToolBarAreaLayoutLine &line = info.lines[path.at(i: 1)];
1089 if (path.at(i: 2) < 0 || path.at(i: 2) >= line.toolBarItems.size())
1090 return nullptr;
1091 return &(line.toolBarItems[path.at(i: 2)]);
1092}
1093
1094QRect QToolBarAreaLayout::itemRect(const QList<int> &path) const
1095{
1096 const int i = path.first();
1097
1098 QRect r = docks[i].itemRect(path: path.mid(pos: 1));
1099 if (docks[i].o == Qt::Horizontal)
1100 r = QStyle::visualRect(direction: mainWindow->layoutDirection(),
1101 boundingRect: docks[i].rect, logicalRect: r);
1102 return r;
1103}
1104
1105QLayoutItem *QToolBarAreaLayout::plug(const QList<int> &path)
1106{
1107 QToolBarAreaLayoutItem *item = this->item(path);
1108 if (Q_UNLIKELY(!item)) {
1109 qWarning() << "No item at" << path;
1110 return nullptr;
1111 }
1112 Q_ASSERT(item->gap);
1113 Q_ASSERT(item->widgetItem != nullptr);
1114 item->gap = false;
1115 return item->widgetItem;
1116}
1117
1118QLayoutItem *QToolBarAreaLayout::unplug(const QList<int> &path, QToolBarAreaLayout *other)
1119{
1120 //other needs to be update as well
1121 Q_ASSERT(path.size() == 3);
1122 QToolBarAreaLayoutItem *item = this->item(path);
1123 Q_ASSERT(item);
1124
1125 //update the leading space here
1126 QToolBarAreaLayoutInfo &info = docks[path.at(i: 0)];
1127 QToolBarAreaLayoutLine &line = info.lines[path.at(i: 1)];
1128 if (item->size != pick(o: line.o, size: item->realSizeHint())) {
1129 //the item doesn't have its default size
1130 //so we'll give this to the next item
1131 int newExtraSpace = 0;
1132 //let's iterate over the siblings of the current item that pare placed before it
1133 //we need to find just the one before
1134 for (int i = path.at(i: 2) - 1; i >= 0; --i) {
1135 QToolBarAreaLayoutItem &previous = line.toolBarItems[i];
1136 if (!previous.skip()) {
1137 //we need to check if it has a previous element and a next one
1138 //the previous will get its size changed
1139 for (int j = path.at(i: 2) + 1; j < line.toolBarItems.size(); ++j) {
1140 const QToolBarAreaLayoutItem &next = line.toolBarItems.at(i: j);
1141 if (!next.skip()) {
1142 newExtraSpace = next.pos - previous.pos - pick(o: line.o, size: previous.sizeHint());
1143 previous.resize(o: line.o, newSize: next.pos - previous.pos);
1144 break;
1145 }
1146 }
1147 break;
1148 }
1149 }
1150
1151 if (other) {
1152 QToolBarAreaLayoutInfo &info = other->docks[path.at(i: 0)];
1153 QToolBarAreaLayoutLine &line = info.lines[path.at(i: 1)];
1154 for (int i = path.at(i: 2) - 1; i >= 0; --i) {
1155 QToolBarAreaLayoutItem &previous = line.toolBarItems[i];
1156 if (!previous.skip()) {
1157 previous.resize(o: line.o, newSize: pick(o: line.o, size: previous.sizeHint()) + newExtraSpace);
1158 break;
1159 }
1160 }
1161
1162 }
1163 }
1164
1165 Q_ASSERT(!item->gap);
1166 item->gap = true;
1167 return item->widgetItem;
1168}
1169
1170static QRect unpackRect(uint geom0, uint geom1, bool *floating)
1171{
1172 *floating = geom0 & 1;
1173 if (!*floating)
1174 return QRect();
1175
1176 geom0 >>= 1;
1177
1178 int x = (int)(geom0 & 0x0000ffff) - 0x7FFF;
1179 int y = (int)(geom1 & 0x0000ffff) - 0x7FFF;
1180
1181 geom0 >>= 16;
1182 geom1 >>= 16;
1183
1184 int w = geom0 & 0x0000ffff;
1185 int h = geom1 & 0x0000ffff;
1186
1187 return QRect(x, y, w, h);
1188}
1189
1190static void packRect(uint *geom0, uint *geom1, const QRect &rect, bool floating)
1191{
1192 *geom0 = 0;
1193 *geom1 = 0;
1194
1195 if (!floating)
1196 return;
1197
1198 // The 0x7FFF is half of 0xFFFF. We add it so we can handle negative coordinates on
1199 // dual monitors. It's subtracted when unpacking.
1200
1201 *geom0 |= qMax(a: 0, b: rect.width()) & 0x0000ffff;
1202 *geom1 |= qMax(a: 0, b: rect.height()) & 0x0000ffff;
1203
1204 *geom0 <<= 16;
1205 *geom1 <<= 16;
1206
1207 *geom0 |= qMax(a: 0, b: rect.x() + 0x7FFF) & 0x0000ffff;
1208 *geom1 |= qMax(a: 0, b: rect.y() + 0x7FFF) & 0x0000ffff;
1209
1210 // yeah, we chop one bit off the width, but it still has a range up to 32512
1211
1212 *geom0 <<= 1;
1213 *geom0 |= 1;
1214}
1215
1216
1217void QToolBarAreaLayout::saveState(QDataStream &stream) const
1218{
1219 // save toolbar state
1220 stream << (uchar) ToolBarStateMarkerEx;
1221
1222 int lineCount = 0;
1223 for (int i = 0; i < QInternal::DockCount; ++i)
1224 lineCount += docks[i].lines.size();
1225
1226 stream << lineCount;
1227
1228 for (int i = 0; i < QInternal::DockCount; ++i) {
1229 const QToolBarAreaLayoutInfo &dock = docks[i];
1230
1231 for (int j = 0; j < dock.lines.size(); ++j) {
1232 const QToolBarAreaLayoutLine &line = dock.lines.at(i: j);
1233
1234 stream << i << int(line.toolBarItems.size());
1235
1236 for (int k = 0; k < line.toolBarItems.size(); ++k) {
1237 const QToolBarAreaLayoutItem &item = line.toolBarItems.at(i: k);
1238 QWidget *widget = const_cast<QLayoutItem*>(item.widgetItem)->widget();
1239 QString objectName = widget->objectName();
1240 if (Q_UNLIKELY(objectName.isEmpty())) {
1241 qWarning(msg: "QMainWindow::saveState(): 'objectName' not set for QToolBar %p '%s'",
1242 widget, widget->windowTitle().toLocal8Bit().constData());
1243 }
1244 stream << objectName;
1245 // we store information as:
1246 // 1st bit: 1 if shown
1247 // 2nd bit: 1 if orientation is vertical (default is horizontal)
1248 uchar shownOrientation = (uchar)!widget->isHidden();
1249 if (QToolBar * tb= qobject_cast<QToolBar*>(object: widget)) {
1250 if (tb->orientation() == Qt::Vertical)
1251 shownOrientation |= 2;
1252 }
1253 stream << shownOrientation;
1254 stream << item.pos;
1255 //we store the preferred size. If the use rdidn't resize the toolbars it will be -1
1256 stream << item.preferredSize;
1257
1258 uint geom0, geom1;
1259 packRect(geom0: &geom0, geom1: &geom1, rect: widget->geometry(), floating: widget->isWindow());
1260 stream << geom0 << geom1;
1261 }
1262 }
1263 }
1264}
1265
1266static inline int getInt(QDataStream &stream)
1267{
1268 int x;
1269 stream >> x;
1270 return x;
1271}
1272
1273
1274bool QToolBarAreaLayout::restoreState(QDataStream &stream, const QList<QToolBar*> &_toolBars, uchar tmarker, bool testing)
1275{
1276 QList<QToolBar*> toolBars = _toolBars;
1277 int lines;
1278 stream >> lines;
1279
1280 for (int j = 0; j < lines; ++j) {
1281 int pos;
1282 stream >> pos;
1283 if (pos < 0 || pos >= QInternal::DockCount)
1284 return false;
1285 int cnt;
1286 stream >> cnt;
1287
1288 QToolBarAreaLayoutInfo &dock = docks[pos];
1289 const bool applyingLayout = !testing;
1290 QToolBarAreaLayoutLine line(dock.o);
1291
1292 for (int k = 0; k < cnt; ++k) {
1293 QToolBarAreaLayoutItem item;
1294
1295 QString objectName;
1296 stream >> objectName;
1297 uchar shown;
1298 stream >> shown;
1299 item.pos = getInt(stream);
1300 item.size = getInt(stream);
1301
1302 /*
1303 4.3.0 added floating toolbars, but failed to add the ability to restore them.
1304 We need to store there geometry (four ints). We cannot change the format in a
1305 patch release (4.3.1) by adding ToolBarStateMarkerEx2 to signal extra data. So
1306 for now we'll pack it in the two legacy ints we no longer used in Qt4.3.0.
1307 In 4.4, we should add ToolBarStateMarkerEx2 and fix this properly.
1308 */
1309
1310 QRect rect;
1311 bool floating = false;
1312 uint geom0, geom1;
1313 geom0 = getInt(stream);
1314 if (tmarker == ToolBarStateMarkerEx) {
1315 geom1 = getInt(stream);
1316 rect = unpackRect(geom0, geom1, floating: &floating);
1317 }
1318
1319 QToolBar *toolBar = nullptr;
1320 for (int x = 0; x < toolBars.size(); ++x) {
1321 if (toolBars.at(i: x)->objectName() == objectName) {
1322 toolBar = toolBars.takeAt(i: x);
1323 break;
1324 }
1325 }
1326 if (toolBar == nullptr) {
1327 continue;
1328 }
1329
1330 if (applyingLayout) {
1331 // Clear the previous widgetItem for the toolBar, so that it's
1332 // assigned correctly in QWidgetItemV2 constructor.
1333 auto *toolBarPrivate = QWidgetPrivate::get(w: toolBar);
1334 toolBarPrivate->widgetItem = nullptr;
1335 item.widgetItem = new QWidgetItemV2(toolBar);
1336 toolBar->setOrientation(floating ? ((shown & 2) ? Qt::Vertical : Qt::Horizontal) : dock.o);
1337 toolBar->setVisible(shown & 1);
1338 toolBar->d_func()->setWindowState(floating, unplug: false, rect);
1339
1340 item.preferredSize = item.size;
1341 line.toolBarItems.append(t: item);
1342 }
1343 }
1344
1345 if (applyingLayout) {
1346 dock.lines.append(t: line);
1347 }
1348 }
1349
1350
1351 return stream.status() == QDataStream::Ok;
1352}
1353
1354bool QToolBarAreaLayout::isEmpty() const
1355{
1356 for (int i = 0; i < QInternal::DockCount; ++i) {
1357 if (!docks[i].lines.isEmpty())
1358 return false;
1359 }
1360 return true;
1361}
1362
1363QT_END_NAMESPACE
1364

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