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
layout.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
3// Qt-Security score:significant reason:default
4
5#include "layout_p.h"
12
13#include <QtDesigner/abstractformeditor.h>
14#include <QtDesigner/abstractformwindow.h>
15#include <QtDesigner/abstractmetadatabase.h>
16#include <QtDesigner/abstractwidgetdatabase.h>
17#include <QtDesigner/container.h>
18#include <QtDesigner/propertysheet.h>
19#include <QtDesigner/qextensionmanager.h>
20
21#include <QtCore/qdebug.h>
22#include <QtCore/qhash.h>
23#include <QtCore/qlist.h>
24#include <QtCore/qset.h>
25
26#include <QtGui/qbitmap.h>
27#include <QtGui/qevent.h>
28#include <QtGui/qpainter.h>
29
30#include <QtWidgets/qapplication.h>
31#include <QtWidgets/qformlayout.h>
32#include <QtWidgets/qgridlayout.h>
33#include <QtWidgets/qlabel.h>
34#include <QtWidgets/qmainwindow.h>
35#include <QtWidgets/qscrollarea.h>
36#include <QtWidgets/qsplitter.h>
37#include <QtWidgets/qwizard.h>
38
39#include <algorithm>
40
41QT_BEGIN_NAMESPACE
42
43using namespace Qt::StringLiterals;
44
45namespace qdesigner_internal {
46
47/* The wizard has a policy of setting a size policy of its external children
48 * according to the page being expanding or not (in the latter case, the
49 * page will be pushed to the top). When setting/breaking layouts, this needs
50 * to be updated, which happens via a fake style change event. */
51
52void updateWizardLayout(QWidget *layoutBase);
53
55 friend void updateWizardLayout(QWidget *);
56};
57
58void updateWizardLayout(QWidget *layoutBase)
59{
60 if (QWizardPage *wizardPage = qobject_cast<QWizardPage*>(layoutBase))
61 if (QWizard *wizard = static_cast<FriendlyWizardPage*>(wizardPage)->wizard()) {
62 QEvent event(QEvent::StyleChange);
63 QApplication::sendEvent(wizard, &event);
64 }
65}
66
67/*!
68 \class qdesigner_internal::Layout
69 \brief Baseclass for layouting widgets in the Designer (Helper for Layout commands)
70 \internal
71
72 Classes derived from this abstract base class are used for layouting
73 operations in the Designer (creating/breaking layouts).
74
75 Instances live in the Layout/BreakLayout commands.
76*/
77
78/*! \a p specifies the parent of the layoutBase \a lb. The parent
79 might be changed in setup(). If the layoutBase is a
80 container, the parent and the layoutBase are the same. Also they
81 always have to be a widget known to the designer (e.g. in the case
82 of the tabwidget parent and layoutBase are the tabwidget and not the
83 page which actually gets laid out. For actual usage the correct
84 widget is found later by Layout.)
85 */
86
99
100Layout::~Layout() = default;
101
102/*! The widget list we got in the constructor might contain too much
103 widgets (like widgets with different parents, already laid out
104 widgets, etc.). Here we set up the list and so the only the "best"
105 widgets get laid out.
106*/
107
109{
110 m_startPoint = QPoint(32767, 32767);
111
112 // Go through all widgets of the list we got. As we can only
113 // layout widgets which have the same parent, we first do some
114 // sorting which means create a list for each parent containing
115 // its child here. After that we keep working on the list of
116 // children which has the most entries.
117 // Widgets which are already laid out are thrown away here too
118
120 for (QWidget *w : std::as_const(m_widgets)) {
121 QWidget *p = w->parentWidget();
122
124 && m_formWindow->core()->metaDataBase()->item(p->layout()) != nullptr)
125 continue;
126
127 lists.insert(p, w);
128 }
129
131 const QWidgetList &parents = lists.keys();
132 for (QWidget *p : parents) {
133 if (lists.count(p) > lastList.size())
135 }
136
137
138 // If we found no list (because no widget did fit at all) or the
139 // best list has only one entry and we do not layout a container,
140 // we leave here.
142 if (lastList.size() < 2 &&
143 (!m_layoutBase ||
146 ) {
148 m_startPoint = QPoint(0, 0);
149 return;
150 }
151
152 // Now we have a new and clean widget list, which makes sense
153 // to layout
155 // Also use the only correct parent later, so store it
156
157 Q_ASSERT(m_widgets.isEmpty() == false);
158
160 // Now calculate the position where the layout-meta-widget should
161 // be placed and connect to widgetDestroyed() signals of the
162 // widgets to get informed if one gets deleted to be able to
163 // handle that and do not crash in this case
164 for (QWidget *w : std::as_const(m_widgets)) {
167 const QRect rc(w->geometry());
168
170 // Change the Z-order, as saving/loading uses the Z-order for
171 // writing/creating widgets and this has to be the same as in
172 // the layout. Else saving + loading will give different results
173 w->raise();
174 }
175
176 sort();
177}
178
180{
181 if (QWidget *w = qobject_cast<QWidget *>(sender())) {
184 }
185}
186
216
217static bool isMainContainer(QDesignerFormWindowInterface *fw, const QWidget *w)
218{
219 return w && (w == fw || w == fw->mainContainer());
220}
221
222static bool isPageOfContainerWidget(QDesignerFormWindowInterface *fw, QWidget *widget)
223{
224 QDesignerContainerExtension *c = qt_extension<QDesignerContainerExtension*>(
225 fw->core()->extensionManager(), widget->parentWidget());
226
227 if (c != nullptr) {
228 for (int i = 0; i<c->count(); ++i) {
229 if (widget == c->widget(i))
230 return true;
231 }
232 }
233
234 return false;
235}
237{
241
242 bool done = false;
243 while (!isMainContainer(m_formWindow, widget) && !done) {
246 continue;
247 }
250 continue;
251 }
254 continue;
255 }
256 if (widget->parentWidget()) {
258 if (area && area->widget() == widget) {
259 widget = area;
260 continue;
261 }
262 }
263
264 done = true;
265 }
268 // We don't want to resize the form window
271
272 return;
273 }
274
275 if (needMove)
277
278 const QRect g(m_layoutBase->pos(), m_layoutBase->size());
279
282 else if (m_isBreak)
284
286 if (layout)
289
294 }
295}
296
342
344{
346 /* Store the geometry of the widgets. The idea is to give the user space
347 * to rearrange them, so, we do a adjustSize() on them, unless they want
348 * to grow (expanding widgets like QTextEdit), in which the geometry is
349 * preserved. Note that historically, geometries were re-applied
350 * only after breaking splitters. */
351 for (QWidget *w : std::as_const(m_widgets)) {
352 const QRect geom = w->geometry();
353 const QSize sizeHint = w->sizeHint();
356 }
359
361
366 const bool add = m_geometries.isEmpty();
367
368 for (auto it = rects.cbegin(), end = rects.cend(); it != end; ++it) {
369 QWidget *w = it.key();
370 if (needReparent) {
373 w->show();
374 }
375
376 const QRect oldGeometry = it.value();
377 if (oldGeometry.isEmpty()) {
378 w->adjustSize();
379 } else {
381 }
382
383 if (add)
384 m_geometries.insert(w, QRect(w->pos(), w->size()));
385 }
386
387 if (needReparent) {
391 } else {
393 }
395
398 else
400}
401
402static QString suggestLayoutName(const char *className)
403{
404 // Legacy
405 if (!qstrcmp(className, "QHBoxLayout"))
406 return u"horizontalLayout"_s;
407 if (!qstrcmp(className, "QVBoxLayout"))
408 return u"verticalLayout"_s;
409 if (!qstrcmp(className, "QGridLayout"))
410 return u"gridLayout"_s;
411
412 return qtify(QString::fromUtf8(className));
413}
431
433{
434 if (w->parent() != m_layoutBase) {
436 w->move(QPoint(0,0));
437 }
438}
439
440namespace { // within qdesigner_internal
441
442// ----- PositionSortPredicate: Predicate to be usable as LessThan function to sort widgets by position
443class PositionSortPredicate {
444public:
445 PositionSortPredicate(Qt::Orientation orientation) : m_orientation(orientation) {}
446 bool operator()(const QWidget* w1, const QWidget* w2) {
447 return m_orientation == Qt::Horizontal ? w1->x() < w2->x() : w1->y() < w2->y();
448 }
449 private:
450 const Qt::Orientation m_orientation;
451};
452
453// -------- BoxLayout
454class BoxLayout : public Layout
455{
456public:
457 BoxLayout(const QWidgetList &wl, QWidget *p, QDesignerFormWindowInterface *fw, QWidget *lb,
458 Qt::Orientation orientation);
459
460 void doLayout() override;
461 void sort() override;
462
463private:
464 const Qt::Orientation m_orientation;
465};
466
467BoxLayout::BoxLayout(const QWidgetList &wl, QWidget *p, QDesignerFormWindowInterface *fw, QWidget *lb,
468 Qt::Orientation orientation) :
469 Layout(wl, p, fw, lb, orientation == Qt::Horizontal ? LayoutInfo::HBox : LayoutInfo::VBox),
470 m_orientation(orientation)
471{
472}
473
474void BoxLayout::sort()
475{
476 QWidgetList wl = widgets();
477 std::stable_sort(wl.begin(), wl.end(), PositionSortPredicate(m_orientation));
478 setWidgets(wl);
479}
480
481void BoxLayout::doLayout()
482{
483 bool needMove, needReparent;
484 if (!prepareLayout(needMove, needReparent))
485 return;
486
487 QBoxLayout *layout = static_cast<QBoxLayout *>(createLayout(m_orientation == Qt::Horizontal ? LayoutInfo::HBox : LayoutInfo::VBox));
488
489 QDesignerWidgetItemInstaller wii; // Make sure we use QDesignerWidgetItem.
490
491 for (auto *w : widgets()) {
492 if (needReparent)
493 reparentToLayoutBase(w);
494
495 if (const Spacer *spacer = qobject_cast<const Spacer*>(w))
496 layout->addWidget(w, 0, spacer->alignment());
497 else
498 layout->addWidget(w);
499 w->show();
500 }
501 finishLayout(needMove, layout);
502}
503
504// -------- SplitterLayout
505class SplitterLayout : public Layout
506{
507public:
508 SplitterLayout(const QWidgetList &wl, QWidget *p, QDesignerFormWindowInterface *fw, QWidget *lb,
509 Qt::Orientation orientation);
510
511 void doLayout() override;
512 void sort() override;
513
514private:
515 const Qt::Orientation m_orientation;
516};
517
518SplitterLayout::SplitterLayout(const QWidgetList &wl, QWidget *p, QDesignerFormWindowInterface *fw, QWidget *lb,
519 Qt::Orientation orientation) :
520 Layout(wl, p, fw, lb, orientation == Qt::Horizontal ? LayoutInfo::HSplitter : LayoutInfo::VSplitter),
521 m_orientation(orientation)
522{
523}
524
525void SplitterLayout::sort()
526{
527 QWidgetList wl = widgets();
528 std::stable_sort(wl.begin(), wl.end(), PositionSortPredicate(m_orientation));
529 setWidgets(wl);
530}
531
532void SplitterLayout::doLayout()
533{
534 bool needMove, needReparent;
535 if (!prepareLayout(needMove, needReparent))
536 return;
537
538 QSplitter *splitter = qobject_cast<QSplitter*>(layoutBaseWidget());
539 Q_ASSERT(splitter != nullptr);
540
541 for (auto *w : widgets()) {
542 if (needReparent)
543 reparentToLayoutBase(w);
544 splitter->addWidget(w);
545 w->show();
546 }
547
548 splitter->setOrientation(m_orientation);
549 finishLayout(needMove);
550}
551
552// ---------- Grid: Helper for laying out grids
553
554class GridHelper
555{
556 Q_DISABLE_COPY_MOVE(GridHelper);
557public:
558 enum { FormLayoutColumns = 2 };
559
560 enum Mode {
561 GridLayout, // Arbitrary size/supports span
562 FormLayout // 2-column/no span
563 };
564
565 GridHelper(Mode mode);
566 void resize(int nrows, int ncols);
567
568 ~GridHelper();
569
570 QWidget* cell(int row, int col) const { return m_cells[ row * m_ncols + col]; }
571
572 void setCells(const QRect &c, QWidget* w);
573
574 bool empty() const { return !m_nrows || !m_ncols; }
575 int numRows() const { return m_nrows; }
576 int numCols() const { return m_ncols; }
577
578 void simplify();
579 bool locateWidget(QWidget* w, int& row, int& col, int& rowspan, int& colspan) const;
580
581private:
582 void setCell(int row, int col, QWidget* w) { m_cells[ row * m_ncols + col] = w; }
583 void shrink();
584 void reallocFormLayout();
585 int countRow(int r, int c) const;
586 int countCol(int r, int c) const;
587 void setRow(int r, int c, QWidget* w, int count);
588 void setCol(int r, int c, QWidget* w, int count);
589 bool isWidgetStartCol(int c) const;
590 bool isWidgetEndCol(int c) const;
591 bool isWidgetStartRow(int r) const;
592 bool isWidgetEndRow(int r) const;
593 bool isWidgetTopLeft(int r, int c) const;
594 void extendLeft();
595 void extendRight();
596 void extendUp();
597 void extendDown();
598 bool shrinkFormLayoutSpans();
599
600 const Mode m_mode;
601 int m_nrows;
602 int m_ncols;
603
604 QWidget** m_cells; // widget matrix w11, w12, w21...
605};
606
607GridHelper::GridHelper(Mode mode) :
608 m_mode(mode),
609 m_nrows(0),
610 m_ncols(0),
611 m_cells(nullptr)
612{
613}
614
615GridHelper::~GridHelper()
616{
617 delete [] m_cells;
618}
619
620void GridHelper::resize(int nrows, int ncols)
621{
622 delete [] m_cells;
623 m_cells = nullptr;
624 m_nrows = nrows;
625 m_ncols = ncols;
626 if (const int allocSize = m_nrows * m_ncols) {
627 m_cells = new QWidget*[allocSize];
628 std::fill(m_cells, m_cells + allocSize, nullptr);
629 }
630}
631
632void GridHelper::setCells(const QRect &c, QWidget* w)
633{
634 const int bottom = c.top() + c.height();
635 const int width = c.width();
636
637 for (int r = c.top(); r < bottom; r++) {
638 QWidget **pos = m_cells + r * m_ncols + c.left();
639 std::fill(pos, pos + width, w);
640 }
641}
642
643int GridHelper::countRow(int r, int c) const
644{
645 QWidget* w = cell(r, c);
646 int i = c + 1;
647 while (i < m_ncols && cell(r, i) == w)
648 i++;
649 return i - c;
650}
651
652int GridHelper::countCol(int r, int c) const
653{
654 QWidget* w = cell(r, c);
655 int i = r + 1;
656 while (i < m_nrows && cell(i, c) == w)
657 i++;
658 return i - r;
659}
660
661void GridHelper::setCol(int r, int c, QWidget* w, int count)
662{
663 for (int i = 0; i < count; i++)
664 setCell(r + i, c, w);
665}
666
667void GridHelper::setRow(int r, int c, QWidget* w, int count)
668{
669 for (int i = 0; i < count; i++)
670 setCell(r, c + i, w);
671}
672
673bool GridHelper::isWidgetStartCol(int c) const
674{
675 for (int r = 0; r < m_nrows; r++) {
676 if (cell(r, c) && ((c==0) || (cell(r, c) != cell(r, c-1)))) {
677 return true;
678 }
679 }
680 return false;
681}
682
683bool GridHelper::isWidgetEndCol(int c) const
684{
685 for (int r = 0; r < m_nrows; r++) {
686 if (cell(r, c) && ((c == m_ncols-1) || (cell(r, c) != cell(r, c+1))))
687 return true;
688 }
689 return false;
690}
691
692bool GridHelper::isWidgetStartRow(int r) const
693{
694 for ( int c = 0; c < m_ncols; c++) {
695 if (cell(r, c) && ((r==0) || (cell(r, c) != cell(r-1, c))))
696 return true;
697 }
698 return false;
699}
700
701bool GridHelper::isWidgetEndRow(int r) const
702{
703 for (int c = 0; c < m_ncols; c++) {
704 if (cell(r, c) && ((r == m_nrows-1) || (cell(r, c) != cell(r+1, c))))
705 return true;
706 }
707 return false;
708}
709
710
711bool GridHelper::isWidgetTopLeft(int r, int c) const
712{
713 QWidget* w = cell(r, c);
714 if (!w)
715 return false;
716 return (!r || cell(r-1, c) != w) && (!c || cell(r, c-1) != w);
717}
718
719void GridHelper::extendLeft()
720{
721 for (int c = 1; c < m_ncols; c++) {
722 for (int r = 0; r < m_nrows; r++) {
723 QWidget* w = cell(r, c);
724 if (!w)
725 continue;
726
727 const int cc = countCol(r, c);
728 int stretch = 0;
729 for (int i = c-1; i >= 0; i--) {
730 if (cell(r, i))
731 break;
732 if (countCol(r, i) < cc)
733 break;
734 if (isWidgetEndCol(i))
735 break;
736 if (isWidgetStartCol(i)) {
737 stretch = c - i;
738 break;
739 }
740 }
741 if (stretch) {
742 for (int i = 0; i < stretch; i++)
743 setCol(r, c-i-1, w, cc);
744 }
745 }
746 }
747}
748
749
750void GridHelper::extendRight()
751{
752 for (int c = m_ncols - 2; c >= 0; c--) {
753 for (int r = 0; r < m_nrows; r++) {
754 QWidget* w = cell(r, c);
755 if (!w)
756 continue;
757 const int cc = countCol(r, c);
758 int stretch = 0;
759 for (int i = c+1; i < m_ncols; i++) {
760 if (cell(r, i))
761 break;
762 if (countCol(r, i) < cc)
763 break;
764 if (isWidgetStartCol(i))
765 break;
766 if (isWidgetEndCol(i)) {
767 stretch = i - c;
768 break;
769 }
770 }
771 if (stretch) {
772 for (int i = 0; i < stretch; i++)
773 setCol(r, c+i+1, w, cc);
774 }
775 }
776 }
777
778}
779
780void GridHelper::extendUp()
781{
782 for (int r = 1; r < m_nrows; r++) {
783 for (int c = 0; c < m_ncols; c++) {
784 QWidget* w = cell(r, c);
785 if (!w)
786 continue;
787 const int cr = countRow(r, c);
788 int stretch = 0;
789 for (int i = r-1; i >= 0; i--) {
790 if (cell(i, c))
791 break;
792 if (countRow(i, c) < cr)
793 break;
794 if (isWidgetEndRow(i))
795 break;
796 if (isWidgetStartRow(i)) {
797 stretch = r - i;
798 break;
799 }
800 }
801 if (stretch) {
802 for (int i = 0; i < stretch; i++)
803 setRow(r-i-1, c, w, cr);
804 }
805 }
806 }
807}
808
809void GridHelper::extendDown()
810{
811 for (int r = m_nrows - 2; r >= 0; r--) {
812 for (int c = 0; c < m_ncols; c++) {
813 QWidget* w = cell(r, c);
814 if (!w)
815 continue;
816 const int cr = countRow(r, c);
817 int stretch = 0;
818 for (int i = r+1; i < m_nrows; i++) {
819 if (cell(i, c))
820 break;
821 if (countRow(i, c) < cr)
822 break;
823 if (isWidgetStartRow(i))
824 break;
825 if (isWidgetEndRow(i)) {
826 stretch = i - r;
827 break;
828 }
829 }
830 if (stretch) {
831 for (int i = 0; i < stretch; i++)
832 setRow(r+i+1, c, w, cr);
833 }
834 }
835 }
836}
837
838void GridHelper::simplify()
839{
840 switch (m_mode) {
841 case GridLayout:
842 // Grid: Extend all widgets to occupy most space and delete
843 // rows/columns that are not bordering on a widget
844 extendLeft();
845 extendRight();
846 extendUp();
847 extendDown();
848 shrink();
849 break;
850 case FormLayout:
851 // Form: First treat it as a grid to get the same behaviour
852 // regarding spanning and shrinking. Then restrict the span to
853 // the horizontal span possible in the form, simplify again
854 // and spread the widgets over a 2-column layout
855 extendLeft();
856 extendRight();
857 extendUp();
858 extendDown();
859 shrink();
860 if (shrinkFormLayoutSpans())
861 shrink();
862 reallocFormLayout();
863 break;
864 }
865
866}
867
868void GridHelper::shrink()
869{
870 // tick off the occupied cols/rows (bordering on widget edges)
871 QList<bool> columns(m_ncols, false);
872 QList<bool> rows(m_nrows, false);
873
874 for (int c = 0; c < m_ncols; c++)
875 for (int r = 0; r < m_nrows; r++)
876 if (isWidgetTopLeft(r, c))
877 rows[r] = columns[c] = true;
878
879 // remove empty cols/rows
880 const int simplifiedNCols = columns.count(true);
881 const int simplifiedNRows = rows.count(true);
882 if (simplifiedNCols == m_ncols && simplifiedNRows == m_nrows)
883 return;
884 // reallocate and copy omitting the empty cells
885 QWidget **simplifiedCells = new QWidget*[simplifiedNCols * simplifiedNRows];
886 std::fill(simplifiedCells, simplifiedCells + simplifiedNCols * simplifiedNRows, nullptr);
887 QWidget **simplifiedPtr = simplifiedCells;
888
889 for (int r = 0; r < m_nrows; r++)
890 if (rows[r])
891 for (int c = 0; c < m_ncols; c++)
892 if (columns[c]) {
893 if (QWidget *w = cell(r, c))
894 *simplifiedPtr = w;
895 simplifiedPtr++;
896 }
897 Q_ASSERT(simplifiedPtr == simplifiedCells + simplifiedNCols * simplifiedNRows);
898 delete [] m_cells;
899 m_cells = simplifiedCells;
900 m_nrows = simplifiedNRows;
901 m_ncols = simplifiedNCols;
902}
903
904bool GridHelper::shrinkFormLayoutSpans()
905{
906 bool shrunk = false;
907 using WidgetSet = QSet<QWidget *>;
908 // Determine unique set of widgets
909 WidgetSet widgets;
910 QWidget **end = m_cells + m_ncols * m_nrows;
911 for (QWidget **wptr = m_cells; wptr < end; wptr++)
912 if (QWidget *w = *wptr)
913 widgets.insert(w);
914 // Restrict the widget span: max horizontal span at column 0: 2, anything else: 1
915 const int maxRowSpan = 1;
916 for (auto *w : std::as_const(widgets)) {
917 int row, col, rowspan, colspan;
918 if (!locateWidget(w, row, col, rowspan, colspan)) {
919 qDebug("ooops, widget '%s' does not fit in layout", w->objectName().toUtf8().constData());
920 row = col = rowspan = colspan = 0;
921 }
922 const int maxColSpan = col == 0 ? 2 : 1;
923 const int newColSpan = qMin(colspan, maxColSpan);
924 const int newRowSpan = qMin(rowspan, maxRowSpan);
925 if (newColSpan != colspan || newRowSpan != rowspan) {
926 // in case like this:
927 // W1 W1
928 // W1 W2
929 // do:
930 // W1 0
931 // 0 W2
932 for (int i = row; i < row + rowspan - 1; i++)
933 for (int j = col; j < col + colspan - 1; j++)
934 if (i > row + newColSpan - 1 || j > col + newRowSpan - 1)
935 if (cell(i, j) == w)
936 setCell(i, j, nullptr);
937 shrunk = true;
938 }
939 }
940 return shrunk;
941}
942
943void GridHelper::reallocFormLayout()
944{
945 // Columns matching? -> happy!
946 if (m_ncols == FormLayoutColumns)
947 return;
948
949 // If there are offset columns (starting past the field column),
950 // move them to the left and squeeze them. This also prevents the
951 // following reallocation from creating empty form rows.
952 int pastRightWidgetCount = 0;
953 if (m_ncols > FormLayoutColumns) {
954 for (int r = 0; r < m_nrows; r++) {
955 // Try to find a column where the form columns are empty and
956 // there are widgets further to the right.
957 if (cell(r, 0) == nullptr && cell(r, 1) == nullptr) {
958 int sourceCol = FormLayoutColumns;
959 QWidget *firstWidget = nullptr;
960 for ( ; sourceCol < m_ncols; sourceCol++)
961 if (QWidget *w = cell(r, sourceCol)) {
962 firstWidget = w;
963 break;
964 }
965 if (firstWidget) {
966 // Move/squeeze. Copy to beginning of column if it is a label, else field
967 int targetCol = qobject_cast<QLabel*>(firstWidget) ? 0 : 1;
968 for ( ; sourceCol < m_ncols; sourceCol++)
969 if (QWidget *w = cell(r, sourceCol))
970 setCell(r, targetCol++, w);
971 // Pad with zero
972 for ( ; targetCol < m_ncols; targetCol++)
973 setCell(r, targetCol, nullptr);
974 }
975 }
976 // Any protruding widgets left on that row?
977 for (int c = FormLayoutColumns; c < m_ncols; c++)
978 if (cell(r, c))
979 pastRightWidgetCount++;
980 }
981 }
982 // Reallocate with 2 columns. Just insert the protruding ones as fields.
983 const int formNRows = m_nrows + pastRightWidgetCount;
984 QWidget **formCells = new QWidget*[FormLayoutColumns * formNRows];
985 std::fill(formCells, formCells + FormLayoutColumns * formNRows, nullptr);
986 QWidget **formPtr = formCells;
987 const int matchingColumns = qMin(m_ncols, static_cast<int>(FormLayoutColumns));
988 for (int r = 0; r < m_nrows; r++) {
989 int c = 0;
990 for ( ; c < matchingColumns; c++) // Just copy over matching columns
991 *formPtr++ = cell(r, c);
992 formPtr += FormLayoutColumns - matchingColumns; // In case old format was 1 column
993 // protruding widgets: Insert as single-field rows
994 for ( ; c < m_ncols; c++)
995 if (QWidget *w = cell(r, c)) {
996 formPtr++;
997 *formPtr++ = w;
998 }
999 }
1000 Q_ASSERT(formPtr == formCells + FormLayoutColumns * formNRows);
1001 delete [] m_cells;
1002 m_cells = formCells;
1003 m_nrows = formNRows;
1004 m_ncols = FormLayoutColumns;
1005}
1006
1007bool GridHelper::locateWidget(QWidget *w, int &row, int &col, int &rowspan, int &colspan) const
1008{
1009 const int end = m_nrows * m_ncols;
1010 const int startIndex = std::find(m_cells, m_cells + end, w) - m_cells;
1011 if (startIndex == end)
1012 return false;
1013
1014 row = startIndex / m_ncols;
1015 col = startIndex % m_ncols;
1016 for (rowspan = 1; row + rowspan < m_nrows && cell(row + rowspan, col) == w; rowspan++) {}
1017 for (colspan = 1; col + colspan < m_ncols && cell(row, col + colspan) == w; colspan++) {}
1018 return true;
1019}
1020
1021// QGridLayout/QFormLayout Helpers: get item position/add item (overloads to make templates work)
1022
1023void addWidgetToGrid(QGridLayout *lt, QWidget * widget, int row, int column, int rowSpan, int columnSpan, Qt::Alignment alignment)
1024{
1025 lt->addWidget(widget, row, column, rowSpan, columnSpan, alignment);
1026}
1027
1028inline void addWidgetToGrid(QFormLayout *lt, QWidget * widget, int row, int column, int, int columnSpan, Qt::Alignment)
1029{
1030 formLayoutAddWidget(lt, widget, QRect(column, row, columnSpan, 1), false);
1031}
1032
1033// ----------- Base template for grid like layouts
1034template <class GridLikeLayout, int LayoutType, int GridMode>
1035class GridLayout : public Layout
1036{
1037public:
1038 GridLayout(const QWidgetList &wl, QWidget *p, QDesignerFormWindowInterface *fw, QWidget *lb);
1039
1040 void doLayout() override;
1041 void sort() override { setWidgets(buildGrid(widgets())); }
1042
1043protected:
1044 QWidgetList buildGrid(const QWidgetList &);
1045 GridHelper m_grid;
1046};
1047
1048template <class GridLikeLayout, int LayoutType, int GridMode>
1049GridLayout<GridLikeLayout, LayoutType, GridMode>::GridLayout(const QWidgetList &wl, QWidget *p, QDesignerFormWindowInterface *fw, QWidget *lb) :
1050 Layout(wl, p, fw, lb, LayoutInfo::Grid),
1051 m_grid(static_cast<GridHelper::Mode>(GridMode))
1052{
1053}
1054
1055template <class GridLikeLayout, int LayoutType, int GridMode>
1056void GridLayout<GridLikeLayout, LayoutType, GridMode>::doLayout()
1057{
1058 bool needMove, needReparent;
1059 if (!prepareLayout(needMove, needReparent))
1060 return;
1061
1062 GridLikeLayout *layout = static_cast<GridLikeLayout *>(createLayout(LayoutType));
1063
1064 if (!m_grid.empty())
1065 sort();
1066
1067 QDesignerWidgetItemInstaller wii; // Make sure we use QDesignerWidgetItem.
1068
1069 for (auto *w : widgets()) {
1070 int r = 0, c = 0, rs = 0, cs = 0;
1071
1072 if (m_grid.locateWidget(w, r, c, rs, cs)) {
1073 if (needReparent)
1074 reparentToLayoutBase(w);
1075
1076 Qt::Alignment alignment;
1077 if (const Spacer *spacer = qobject_cast<const Spacer*>(w))
1078 alignment = spacer->alignment();
1079
1080 addWidgetToGrid(layout, w, r, c, rs, cs, alignment);
1081
1082 w->show();
1083 } else {
1084 qDebug("ooops, widget '%s' does not fit in layout", w->objectName().toUtf8().constData());
1085 }
1086 }
1087
1088 QLayoutSupport::createEmptyCells(layout);
1089
1090 finishLayout(needMove, layout);
1091}
1092
1093// Remove duplicate entries (Remove next, if equal to current)
1094void removeIntVecDuplicates(QList<int> &v)
1095{
1096 if (v.size() < 2)
1097 return;
1098
1099 for (auto current = v.begin() ; (current != v.end()) && ((current + 1) != v.end()) ; )
1100 if ( *current == *(current+1) )
1101 v.erase(current+1);
1102 else
1103 ++current;
1104}
1105
1106// Ensure a non-zero size for a widget geometry (squeezed spacers)
1107inline QRect expandGeometry(const QRect &rect)
1108{
1109 return rect.isEmpty() ? QRect(rect.topLeft(), rect.size().expandedTo(QSize(1, 1))) : rect;
1110}
1111
1112template <class GridLikeLayout, int LayoutType, int GridMode>
1113QWidgetList GridLayout<GridLikeLayout, LayoutType, GridMode>::buildGrid(const QWidgetList &widgetList)
1114{
1115 if (widgetList.isEmpty())
1116 return QWidgetList();
1117
1118 // Pixel to cell conversion:
1119 // By keeping a list of start'n'stop values (x & y) for each widget,
1120 // it is possible to create a very small grid of cells to represent
1121 // the widget layout.
1122 // -----------------------------------------------------------------
1123
1124 // We need a list of both start and stop values for x- & y-axis
1125 const auto widgetCount = widgetList.size();
1126 QList<int> x( widgetCount * 2 );
1127 QList<int> y( widgetCount * 2 );
1128
1129 // Using push_back would look nicer, but operator[] is much faster
1130 qsizetype index = 0;
1131 for (const auto *w : widgetList) {
1132 const QRect widgetPos = expandGeometry(w->geometry());
1133 x[index] = widgetPos.left();
1134 x[index+1] = widgetPos.right();
1135 y[index] = widgetPos.top();
1136 y[index+1] = widgetPos.bottom();
1137 index += 2;
1138 }
1139
1140 std::sort(x.begin(), x.end());
1141 std::sort(y.begin(), y.end());
1142
1143 // Remove duplicate x entries (Remove next, if equal to current)
1144 removeIntVecDuplicates(x);
1145 removeIntVecDuplicates(y);
1146
1147 // Note that left == right and top == bottom for size 1 items; reserve
1148 // enough space
1149 m_grid.resize(y.size(), x.size());
1150
1151 for (auto *w : widgetList) {
1152 // Mark the cells in the grid that contains a widget
1153 const QRect widgetPos = expandGeometry(w->geometry());
1154 QRect c(0, 0, 0, 0); // rect of columns/rows
1155
1156 // From left til right (not including)
1157 const int leftIdx = x.indexOf(widgetPos.left());
1158 Q_ASSERT(leftIdx != -1);
1159 c.setLeft(leftIdx);
1160 c.setRight(leftIdx);
1161 for (qsizetype cw = leftIdx; cw < x.size(); ++cw)
1162 if (x.at(cw) < widgetPos.right())
1163 c.setRight(cw);
1164 else
1165 break;
1166 // From top til bottom (not including)
1167 const int topIdx = y.indexOf(widgetPos.top());
1168 Q_ASSERT(topIdx != -1);
1169 c.setTop(topIdx);
1170 c.setBottom(topIdx);
1171 for (qsizetype ch = topIdx; ch < y.size(); ++ch)
1172 if (y.at(ch) < widgetPos.bottom())
1173 c.setBottom(ch);
1174 else
1175 break;
1176 m_grid.setCells(c, w); // Mark cellblock
1177 }
1178
1179 m_grid.simplify();
1180
1181 QWidgetList ordered;
1182 for (int i = 0; i < m_grid.numRows(); i++)
1183 for (int j = 0; j < m_grid.numCols(); j++) {
1184 QWidget *w = m_grid.cell(i, j);
1185 if (w && !ordered.contains(w))
1186 ordered.append(w);
1187 }
1188 return ordered;
1189}
1190} // anonymous
1191
1217
1218} // namespace qdesigner_internal
1219
1220QT_END_NAMESPACE
friend class QWidget
Definition qpainter.h:432
Auxiliary methods to store/retrieve settings.
static bool isPageOfContainerWidget(QDesignerFormWindowInterface *fw, QWidget *widget)
Definition layout.cpp:222
static QString suggestLayoutName(const char *className)
Definition layout.cpp:402
static bool isMainContainer(QDesignerFormWindowInterface *fw, const QWidget *w)
Definition layout.cpp:217
void updateWizardLayout(QWidget *layoutBase)
Definition layout.cpp:58