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