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>
21#include <QtCore/qdebug.h>
22#include <QtCore/qhash.h>
23#include <QtCore/qlist.h>
24#include <QtCore/qset.h>
26#include <QtGui/qbitmap.h>
27#include <QtGui/qevent.h>
28#include <QtGui/qpainter.h>
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>
43using namespace Qt::StringLiterals;
48
49
50
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);
68
69
70
71
72
73
74
75
76
79
80
81
82
83
84
85
103
104
105
106
219 return w && (w == fw || w == fw->mainContainer());
224 QDesignerContainerExtension *c = qt_extension<QDesignerContainerExtension*>(
225 fw->core()->extensionManager(), widget->parentWidget());
228 for (
int i = 0; i<c->count(); ++i) {
229 if (widget == c->widget(i))
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;
412 return qtify(QString::fromUtf8(className));
443class PositionSortPredicate {
445 PositionSortPredicate(Qt::Orientation orientation) : m_orientation(orientation) {}
447 return m_orientation == Qt::Horizontal ? w1->x() < w2->x() : w1->y() < w2->y();
450 const Qt::Orientation m_orientation;
454class BoxLayout :
public Layout
457 BoxLayout(
const QWidgetList &wl,
QWidget *p, QDesignerFormWindowInterface *fw,
QWidget *lb,
458 Qt::Orientation orientation);
460 void doLayout() override;
461 void sort() override;
464 const Qt::Orientation m_orientation;
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)
474void BoxLayout::sort()
476 QWidgetList wl = widgets();
477 std::stable_sort(wl.begin(), wl.end(), PositionSortPredicate(m_orientation));
481void BoxLayout::doLayout()
483 bool needMove, needReparent;
484 if (!prepareLayout(needMove, needReparent))
487 QBoxLayout *layout =
static_cast<QBoxLayout *>(createLayout(m_orientation == Qt::Horizontal ? LayoutInfo::HBox : LayoutInfo::VBox));
491 for (
auto *w : widgets()) {
493 reparentToLayoutBase(w);
495 if (
const Spacer *spacer = qobject_cast<
const Spacer*>(w))
496 layout->addWidget(w, 0, spacer->alignment());
498 layout->addWidget(w);
501 finishLayout(needMove, layout);
505class SplitterLayout :
public Layout
508 SplitterLayout(
const QWidgetList &wl,
QWidget *p, QDesignerFormWindowInterface *fw,
QWidget *lb,
509 Qt::Orientation orientation);
511 void doLayout() override;
512 void sort() override;
515 const Qt::Orientation m_orientation;
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)
525void SplitterLayout::sort()
527 QWidgetList wl = widgets();
528 std::stable_sort(wl.begin(), wl.end(), PositionSortPredicate(m_orientation));
532void SplitterLayout::doLayout()
534 bool needMove, needReparent;
535 if (!prepareLayout(needMove, needReparent))
538 QSplitter *splitter = qobject_cast<QSplitter*>(layoutBaseWidget());
539 Q_ASSERT(splitter !=
nullptr);
541 for (
auto *w : widgets()) {
543 reparentToLayoutBase(w);
544 splitter->addWidget(w);
548 splitter->setOrientation(m_orientation);
549 finishLayout(needMove);
556 Q_DISABLE_COPY_MOVE(GridHelper);
558 enum { FormLayoutColumns = 2 };
565 GridHelper(Mode mode);
566 void resize(
int nrows,
int ncols);
570 QWidget* cell(
int row,
int col)
const {
return m_cells[ row * m_ncols + col]; }
572 void setCells(
const QRect &c,
QWidget* w);
574 bool empty()
const {
return !m_nrows || !m_ncols; }
575 int numRows()
const {
return m_nrows; }
576 int numCols()
const {
return m_ncols; }
579 bool locateWidget(
QWidget* w,
int& row,
int& col,
int& rowspan,
int& colspan)
const;
582 void setCell(
int row,
int col,
QWidget* w) { m_cells[ row * m_ncols + col] = w; }
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;
598 bool shrinkFormLayoutSpans();
607GridHelper::GridHelper(Mode mode) :
615GridHelper::~GridHelper()
620void GridHelper::resize(
int nrows,
int 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);
632void GridHelper::setCells(
const QRect &c,
QWidget* w)
634 const int bottom = c.top() + c.height();
635 const int width = c.width();
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);
643int GridHelper::countRow(
int r,
int c)
const
647 while (i < m_ncols && cell(r, i) == w)
652int GridHelper::countCol(
int r,
int c)
const
656 while (i < m_nrows && cell(i, c) == w)
661void GridHelper::setCol(
int r,
int c,
QWidget* w,
int count)
663 for (
int i = 0; i < count; i++)
664 setCell(r + i, c, w);
667void GridHelper::setRow(
int r,
int c,
QWidget* w,
int count)
669 for (
int i = 0; i < count; i++)
670 setCell(r, c + i, w);
673bool GridHelper::isWidgetStartCol(
int c)
const
675 for (
int r = 0; r < m_nrows; r++) {
676 if (cell(r, c) && ((c==0) || (cell(r, c) != cell(r, c-1)))) {
683bool GridHelper::isWidgetEndCol(
int c)
const
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))))
692bool GridHelper::isWidgetStartRow(
int r)
const
694 for (
int c = 0; c < m_ncols; c++) {
695 if (cell(r, c) && ((r==0) || (cell(r, c) != cell(r-1, c))))
701bool GridHelper::isWidgetEndRow(
int r)
const
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))))
711bool GridHelper::isWidgetTopLeft(
int r,
int c)
const
716 return (!r || cell(r-1, c) != w) && (!c || cell(r, c-1) != w);
719void GridHelper::extendLeft()
721 for (
int c = 1; c < m_ncols; c++) {
722 for (
int r = 0; r < m_nrows; r++) {
727 const int cc = countCol(r, c);
729 for (
int i = c-1; i >= 0; i--) {
732 if (countCol(r, i) < cc)
734 if (isWidgetEndCol(i))
736 if (isWidgetStartCol(i)) {
742 for (
int i = 0; i < stretch; i++)
743 setCol(r, c-i-1, w, cc);
750void GridHelper::extendRight()
752 for (
int c = m_ncols - 2; c >= 0; c--) {
753 for (
int r = 0; r < m_nrows; r++) {
757 const int cc = countCol(r, c);
759 for (
int i = c+1; i < m_ncols; i++) {
762 if (countCol(r, i) < cc)
764 if (isWidgetStartCol(i))
766 if (isWidgetEndCol(i)) {
772 for (
int i = 0; i < stretch; i++)
773 setCol(r, c+i+1, w, cc);
780void GridHelper::extendUp()
782 for (
int r = 1; r < m_nrows; r++) {
783 for (
int c = 0; c < m_ncols; c++) {
787 const int cr = countRow(r, c);
789 for (
int i = r-1; i >= 0; i--) {
792 if (countRow(i, c) < cr)
794 if (isWidgetEndRow(i))
796 if (isWidgetStartRow(i)) {
802 for (
int i = 0; i < stretch; i++)
803 setRow(r-i-1, c, w, cr);
809void GridHelper::extendDown()
811 for (
int r = m_nrows - 2; r >= 0; r--) {
812 for (
int c = 0; c < m_ncols; c++) {
816 const int cr = countRow(r, c);
818 for (
int i = r+1; i < m_nrows; i++) {
821 if (countRow(i, c) < cr)
823 if (isWidgetStartRow(i))
825 if (isWidgetEndRow(i)) {
831 for (
int i = 0; i < stretch; i++)
832 setRow(r+i+1, c, w, cr);
838void GridHelper::simplify()
860 if (shrinkFormLayoutSpans())
868void GridHelper::shrink()
871 QList<
bool> columns(m_ncols,
false);
872 QList<
bool> rows(m_nrows,
false);
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;
880 const int simplifiedNCols = columns.count(
true);
881 const int simplifiedNRows = rows.count(
true);
882 if (simplifiedNCols == m_ncols && simplifiedNRows == m_nrows)
885 QWidget **simplifiedCells =
new QWidget*[simplifiedNCols * simplifiedNRows];
886 std::fill(simplifiedCells, simplifiedCells + simplifiedNCols * simplifiedNRows,
nullptr);
887 QWidget **simplifiedPtr = simplifiedCells;
889 for (
int r = 0; r < m_nrows; r++)
891 for (
int c = 0; c < m_ncols; c++)
897 Q_ASSERT(simplifiedPtr == simplifiedCells + simplifiedNCols * simplifiedNRows);
899 m_cells = simplifiedCells;
900 m_nrows = simplifiedNRows;
901 m_ncols = simplifiedNCols;
904bool GridHelper::shrinkFormLayoutSpans()
907 using WidgetSet = QSet<QWidget *>;
910 QWidget **end = m_cells + m_ncols * m_nrows;
911 for (
QWidget **wptr = m_cells; wptr < end; wptr++)
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;
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) {
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)
936 setCell(i, j,
nullptr);
943void GridHelper::reallocFormLayout()
946 if (m_ncols == FormLayoutColumns)
952 int pastRightWidgetCount = 0;
953 if (m_ncols > FormLayoutColumns) {
954 for (
int r = 0; r < m_nrows; r++) {
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)) {
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);
972 for ( ; targetCol < m_ncols; targetCol++)
973 setCell(r, targetCol,
nullptr);
977 for (
int c = FormLayoutColumns; c < m_ncols; c++)
979 pastRightWidgetCount++;
983 const int formNRows = m_nrows + pastRightWidgetCount;
984 QWidget **formCells =
new QWidget*[FormLayoutColumns * formNRows];
985 std::fill(formCells, formCells + FormLayoutColumns * formNRows,
nullptr);
987 const int matchingColumns = qMin(m_ncols,
static_cast<
int>(FormLayoutColumns));
988 for (
int r = 0; r < m_nrows; r++) {
990 for ( ; c < matchingColumns; c++)
991 *formPtr++ = cell(r, c);
992 formPtr += FormLayoutColumns - matchingColumns;
994 for ( ; c < m_ncols; c++)
1000 Q_ASSERT(formPtr == formCells + FormLayoutColumns * formNRows);
1002 m_cells = formCells;
1003 m_nrows = formNRows;
1004 m_ncols = FormLayoutColumns;
1007bool GridHelper::locateWidget(
QWidget *w,
int &row,
int &col,
int &rowspan,
int &colspan)
const
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)
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++) {}
1023void addWidgetToGrid(QGridLayout *lt,
QWidget * widget,
int row,
int column,
int rowSpan,
int columnSpan, Qt::Alignment alignment)
1025 lt->addWidget(widget, row, column, rowSpan, columnSpan, alignment);
1028inline void addWidgetToGrid(QFormLayout *lt,
QWidget * widget,
int row,
int column,
int,
int columnSpan, Qt::Alignment)
1030 formLayoutAddWidget(lt, widget, QRect(column, row, columnSpan, 1),
false);
1034template <
class GridLikeLayout,
int LayoutType,
int GridMode>
1035class GridLayout :
public Layout
1038 GridLayout(
const QWidgetList &wl,
QWidget *p, QDesignerFormWindowInterface *fw,
QWidget *lb);
1040 void doLayout() override;
1041 void sort() override { setWidgets(buildGrid(widgets())); }
1044 QWidgetList buildGrid(
const QWidgetList &);
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))
1055template <
class GridLikeLayout,
int LayoutType,
int GridMode>
1056void GridLayout<GridLikeLayout, LayoutType, GridMode>::doLayout()
1058 bool needMove, needReparent;
1059 if (!prepareLayout(needMove, needReparent))
1062 GridLikeLayout *layout =
static_cast<GridLikeLayout *>(createLayout(LayoutType));
1064 if (!m_grid.empty())
1069 for (
auto *w : widgets()) {
1070 int r = 0, c = 0, rs = 0, cs = 0;
1072 if (m_grid.locateWidget(w, r, c, rs, cs)) {
1074 reparentToLayoutBase(w);
1076 Qt::Alignment alignment;
1077 if (
const Spacer *spacer = qobject_cast<
const Spacer*>(w))
1078 alignment = spacer->alignment();
1080 addWidgetToGrid(layout, w, r, c, rs, cs, alignment);
1084 qDebug(
"ooops, widget '%s' does not fit in layout", w->objectName().toUtf8().constData());
1088 QLayoutSupport::createEmptyCells(layout);
1090 finishLayout(needMove, layout);
1094void removeIntVecDuplicates(QList<
int> &v)
1099 for (
auto current = v.begin() ; (current != v.end()) && ((current + 1) != v.end()) ; )
1100 if ( *current == *(current+1) )
1107inline QRect expandGeometry(
const QRect &rect)
1109 return rect.isEmpty() ? QRect(rect.topLeft(), rect.size().expandedTo(QSize(1, 1))) : rect;
1112template <
class GridLikeLayout,
int LayoutType,
int GridMode>
1113QWidgetList GridLayout<GridLikeLayout, LayoutType, GridMode>::buildGrid(
const QWidgetList &widgetList)
1115 if (widgetList.isEmpty())
1116 return QWidgetList();
1125 const auto widgetCount = widgetList.size();
1126 QList<
int> x( widgetCount * 2 );
1127 QList<
int> y( widgetCount * 2 );
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();
1140 std::sort(x.begin(), x.end());
1141 std::sort(y.begin(), y.end());
1144 removeIntVecDuplicates(x);
1145 removeIntVecDuplicates(y);
1149 m_grid.resize(y.size(), x.size());
1151 for (
auto *w : widgetList) {
1153 const QRect widgetPos = expandGeometry(w->geometry());
1154 QRect c(0, 0, 0, 0);
1157 const int leftIdx = x.indexOf(widgetPos.left());
1158 Q_ASSERT(leftIdx != -1);
1160 c.setRight(leftIdx);
1161 for (qsizetype cw = leftIdx; cw < x.size(); ++cw)
1162 if (x.at(cw) < widgetPos.right())
1167 const int topIdx = y.indexOf(widgetPos.top());
1168 Q_ASSERT(topIdx != -1);
1170 c.setBottom(topIdx);
1171 for (qsizetype ch = topIdx; ch < y.size(); ++ch)
1172 if (y.at(ch) < widgetPos.bottom())
1176 m_grid.setCells(c, w);
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))
Auxiliary methods to store/retrieve settings.
static bool isPageOfContainerWidget(QDesignerFormWindowInterface *fw, QWidget *widget)
static QString suggestLayoutName(const char *className)
static bool isMainContainer(QDesignerFormWindowInterface *fw, const QWidget *w)
void updateWizardLayout(QWidget *layoutBase)