12#include <QtDesigner/abstractformeditor.h>
13#include <QtDesigner/abstractformwindow.h>
14#include <QtDesigner/abstractmetadatabase.h>
15#include <QtDesigner/abstractwidgetdatabase.h>
16#include <QtDesigner/container.h>
17#include <QtDesigner/propertysheet.h>
18#include <QtDesigner/qextensionmanager.h>
20#include <QtCore/qdebug.h>
21#include <QtCore/qhash.h>
22#include <QtCore/qlist.h>
23#include <QtCore/qset.h>
25#include <QtGui/qbitmap.h>
26#include <QtGui/qevent.h>
27#include <QtGui/qpainter.h>
29#include <QtWidgets/qapplication.h>
30#include <QtWidgets/qformlayout.h>
31#include <QtWidgets/qgridlayout.h>
32#include <QtWidgets/qlabel.h>
33#include <QtWidgets/qmainwindow.h>
34#include <QtWidgets/qscrollarea.h>
35#include <QtWidgets/qsplitter.h>
36#include <QtWidgets/qwizard.h>
42using namespace Qt::StringLiterals;
47
48
49
59 if (QWizardPage *wizardPage = qobject_cast<QWizardPage*>(layoutBase))
60 if (QWizard *wizard =
static_cast<FriendlyWizardPage*>(wizardPage)->wizard()) {
61 QEvent event(QEvent::StyleChange);
62 QApplication::sendEvent(wizard, &event);
67
68
69
70
71
72
73
74
75
78
79
80
81
82
83
84
102
103
104
105
218 return w && (w == fw || w == fw->mainContainer());
223 QDesignerContainerExtension *c = qt_extension<QDesignerContainerExtension*>(
224 fw->core()->extensionManager(), widget->parentWidget());
227 for (
int i = 0; i<c->count(); ++i) {
228 if (widget == c->widget(i))
404 if (!qstrcmp(className,
"QHBoxLayout"))
405 return u"horizontalLayout"_s;
406 if (!qstrcmp(className,
"QVBoxLayout"))
407 return u"verticalLayout"_s;
408 if (!qstrcmp(className,
"QGridLayout"))
409 return u"gridLayout"_s;
411 return qtify(QString::fromUtf8(className));
442class PositionSortPredicate {
444 PositionSortPredicate(Qt::Orientation orientation) : m_orientation(orientation) {}
446 return m_orientation == Qt::Horizontal ? w1->x() < w2->x() : w1->y() < w2->y();
449 const Qt::Orientation m_orientation;
453class BoxLayout :
public Layout
456 BoxLayout(
const QWidgetList &wl,
QWidget *p, QDesignerFormWindowInterface *fw,
QWidget *lb,
457 Qt::Orientation orientation);
459 void doLayout() override;
460 void sort() override;
463 const Qt::Orientation m_orientation;
466BoxLayout::BoxLayout(
const QWidgetList &wl,
QWidget *p, QDesignerFormWindowInterface *fw,
QWidget *lb,
467 Qt::Orientation orientation) :
468 Layout(wl, p, fw, lb, orientation == Qt::Horizontal ? LayoutInfo::HBox : LayoutInfo::VBox),
469 m_orientation(orientation)
473void BoxLayout::sort()
475 QWidgetList wl = widgets();
476 std::stable_sort(wl.begin(), wl.end(), PositionSortPredicate(m_orientation));
480void BoxLayout::doLayout()
482 bool needMove, needReparent;
483 if (!prepareLayout(needMove, needReparent))
486 QBoxLayout *layout =
static_cast<QBoxLayout *>(createLayout(m_orientation == Qt::Horizontal ? LayoutInfo::HBox : LayoutInfo::VBox));
490 for (
auto *w : widgets()) {
492 reparentToLayoutBase(w);
494 if (
const Spacer *spacer = qobject_cast<
const Spacer*>(w))
495 layout->addWidget(w, 0, spacer->alignment());
497 layout->addWidget(w);
500 finishLayout(needMove, layout);
504class SplitterLayout :
public Layout
507 SplitterLayout(
const QWidgetList &wl,
QWidget *p, QDesignerFormWindowInterface *fw,
QWidget *lb,
508 Qt::Orientation orientation);
510 void doLayout() override;
511 void sort() override;
514 const Qt::Orientation m_orientation;
517SplitterLayout::SplitterLayout(
const QWidgetList &wl,
QWidget *p, QDesignerFormWindowInterface *fw,
QWidget *lb,
518 Qt::Orientation orientation) :
519 Layout(wl, p, fw, lb, orientation == Qt::Horizontal ? LayoutInfo::HSplitter : LayoutInfo::VSplitter),
520 m_orientation(orientation)
524void SplitterLayout::sort()
526 QWidgetList wl = widgets();
527 std::stable_sort(wl.begin(), wl.end(), PositionSortPredicate(m_orientation));
531void SplitterLayout::doLayout()
533 bool needMove, needReparent;
534 if (!prepareLayout(needMove, needReparent))
537 QSplitter *splitter = qobject_cast<QSplitter*>(layoutBaseWidget());
538 Q_ASSERT(splitter !=
nullptr);
540 for (
auto *w : widgets()) {
542 reparentToLayoutBase(w);
543 splitter->addWidget(w);
547 splitter->setOrientation(m_orientation);
548 finishLayout(needMove);
555 Q_DISABLE_COPY_MOVE(GridHelper);
557 enum { FormLayoutColumns = 2 };
564 GridHelper(Mode mode);
565 void resize(
int nrows,
int ncols);
569 QWidget* cell(
int row,
int col)
const {
return m_cells[ row * m_ncols + col]; }
571 void setCells(
const QRect &c,
QWidget* w);
573 bool empty()
const {
return !m_nrows || !m_ncols; }
574 int numRows()
const {
return m_nrows; }
575 int numCols()
const {
return m_ncols; }
578 bool locateWidget(
QWidget* w,
int& row,
int& col,
int& rowspan,
int& colspan)
const;
581 void setCell(
int row,
int col,
QWidget* w) { m_cells[ row * m_ncols + col] = w; }
583 void reallocFormLayout();
584 int countRow(
int r,
int c)
const;
585 int countCol(
int r,
int c)
const;
586 void setRow(
int r,
int c,
QWidget* w,
int count);
587 void setCol(
int r,
int c,
QWidget* w,
int count);
588 bool isWidgetStartCol(
int c)
const;
589 bool isWidgetEndCol(
int c)
const;
590 bool isWidgetStartRow(
int r)
const;
591 bool isWidgetEndRow(
int r)
const;
592 bool isWidgetTopLeft(
int r,
int c)
const;
597 bool shrinkFormLayoutSpans();
606GridHelper::GridHelper(Mode mode) :
614GridHelper::~GridHelper()
619void GridHelper::resize(
int nrows,
int ncols)
625 if (
const int allocSize = m_nrows * m_ncols) {
626 m_cells =
new QWidget*[allocSize];
627 std::fill(m_cells, m_cells + allocSize,
nullptr);
631void GridHelper::setCells(
const QRect &c,
QWidget* w)
633 const int bottom = c.top() + c.height();
634 const int width = c.width();
636 for (
int r = c.top(); r < bottom; r++) {
637 QWidget **pos = m_cells + r * m_ncols + c.left();
638 std::fill(pos, pos + width, w);
642int GridHelper::countRow(
int r,
int c)
const
646 while (i < m_ncols && cell(r, i) == w)
651int GridHelper::countCol(
int r,
int c)
const
655 while (i < m_nrows && cell(i, c) == w)
660void GridHelper::setCol(
int r,
int c,
QWidget* w,
int count)
662 for (
int i = 0; i < count; i++)
663 setCell(r + i, c, w);
666void GridHelper::setRow(
int r,
int c,
QWidget* w,
int count)
668 for (
int i = 0; i < count; i++)
669 setCell(r, c + i, w);
672bool GridHelper::isWidgetStartCol(
int c)
const
674 for (
int r = 0; r < m_nrows; r++) {
675 if (cell(r, c) && ((c==0) || (cell(r, c) != cell(r, c-1)))) {
682bool GridHelper::isWidgetEndCol(
int c)
const
684 for (
int r = 0; r < m_nrows; r++) {
685 if (cell(r, c) && ((c == m_ncols-1) || (cell(r, c) != cell(r, c+1))))
691bool GridHelper::isWidgetStartRow(
int r)
const
693 for (
int c = 0; c < m_ncols; c++) {
694 if (cell(r, c) && ((r==0) || (cell(r, c) != cell(r-1, c))))
700bool GridHelper::isWidgetEndRow(
int r)
const
702 for (
int c = 0; c < m_ncols; c++) {
703 if (cell(r, c) && ((r == m_nrows-1) || (cell(r, c) != cell(r+1, c))))
710bool GridHelper::isWidgetTopLeft(
int r,
int c)
const
715 return (!r || cell(r-1, c) != w) && (!c || cell(r, c-1) != w);
718void GridHelper::extendLeft()
720 for (
int c = 1; c < m_ncols; c++) {
721 for (
int r = 0; r < m_nrows; r++) {
726 const int cc = countCol(r, c);
728 for (
int i = c-1; i >= 0; i--) {
731 if (countCol(r, i) < cc)
733 if (isWidgetEndCol(i))
735 if (isWidgetStartCol(i)) {
741 for (
int i = 0; i < stretch; i++)
742 setCol(r, c-i-1, w, cc);
749void GridHelper::extendRight()
751 for (
int c = m_ncols - 2; c >= 0; c--) {
752 for (
int r = 0; r < m_nrows; r++) {
756 const int cc = countCol(r, c);
758 for (
int i = c+1; i < m_ncols; i++) {
761 if (countCol(r, i) < cc)
763 if (isWidgetStartCol(i))
765 if (isWidgetEndCol(i)) {
771 for (
int i = 0; i < stretch; i++)
772 setCol(r, c+i+1, w, cc);
779void GridHelper::extendUp()
781 for (
int r = 1; r < m_nrows; r++) {
782 for (
int c = 0; c < m_ncols; c++) {
786 const int cr = countRow(r, c);
788 for (
int i = r-1; i >= 0; i--) {
791 if (countRow(i, c) < cr)
793 if (isWidgetEndRow(i))
795 if (isWidgetStartRow(i)) {
801 for (
int i = 0; i < stretch; i++)
802 setRow(r-i-1, c, w, cr);
808void GridHelper::extendDown()
810 for (
int r = m_nrows - 2; r >= 0; r--) {
811 for (
int c = 0; c < m_ncols; c++) {
815 const int cr = countRow(r, c);
817 for (
int i = r+1; i < m_nrows; i++) {
820 if (countRow(i, c) < cr)
822 if (isWidgetStartRow(i))
824 if (isWidgetEndRow(i)) {
830 for (
int i = 0; i < stretch; i++)
831 setRow(r+i+1, c, w, cr);
837void GridHelper::simplify()
859 if (shrinkFormLayoutSpans())
867void GridHelper::shrink()
870 QList<
bool> columns(m_ncols,
false);
871 QList<
bool> rows(m_nrows,
false);
873 for (
int c = 0; c < m_ncols; c++)
874 for (
int r = 0; r < m_nrows; r++)
875 if (isWidgetTopLeft(r, c))
876 rows[r] = columns[c] =
true;
879 const int simplifiedNCols = columns.count(
true);
880 const int simplifiedNRows = rows.count(
true);
881 if (simplifiedNCols == m_ncols && simplifiedNRows == m_nrows)
884 QWidget **simplifiedCells =
new QWidget*[simplifiedNCols * simplifiedNRows];
885 std::fill(simplifiedCells, simplifiedCells + simplifiedNCols * simplifiedNRows,
nullptr);
886 QWidget **simplifiedPtr = simplifiedCells;
888 for (
int r = 0; r < m_nrows; r++)
890 for (
int c = 0; c < m_ncols; c++)
896 Q_ASSERT(simplifiedPtr == simplifiedCells + simplifiedNCols * simplifiedNRows);
898 m_cells = simplifiedCells;
899 m_nrows = simplifiedNRows;
900 m_ncols = simplifiedNCols;
903bool GridHelper::shrinkFormLayoutSpans()
906 using WidgetSet = QSet<QWidget *>;
909 QWidget **end = m_cells + m_ncols * m_nrows;
910 for (
QWidget **wptr = m_cells; wptr < end; wptr++)
914 const int maxRowSpan = 1;
915 for (
auto *w : std::as_const(widgets)) {
916 int row, col, rowspan, colspan;
917 if (!locateWidget(w, row, col, rowspan, colspan)) {
918 qDebug(
"ooops, widget '%s' does not fit in layout", w->objectName().toUtf8().constData());
919 row = col = rowspan = colspan = 0;
921 const int maxColSpan = col == 0 ? 2 : 1;
922 const int newColSpan = qMin(colspan, maxColSpan);
923 const int newRowSpan = qMin(rowspan, maxRowSpan);
924 if (newColSpan != colspan || newRowSpan != rowspan) {
931 for (
int i = row; i < row + rowspan - 1; i++)
932 for (
int j = col; j < col + colspan - 1; j++)
933 if (i > row + newColSpan - 1 || j > col + newRowSpan - 1)
935 setCell(i, j,
nullptr);
942void GridHelper::reallocFormLayout()
945 if (m_ncols == FormLayoutColumns)
951 int pastRightWidgetCount = 0;
952 if (m_ncols > FormLayoutColumns) {
953 for (
int r = 0; r < m_nrows; r++) {
956 if (cell(r, 0) ==
nullptr && cell(r, 1) ==
nullptr) {
957 int sourceCol = FormLayoutColumns;
958 QWidget *firstWidget =
nullptr;
959 for ( ; sourceCol < m_ncols; sourceCol++)
960 if (
QWidget *w = cell(r, sourceCol)) {
966 int targetCol = qobject_cast<QLabel*>(firstWidget) ? 0 : 1;
967 for ( ; sourceCol < m_ncols; sourceCol++)
968 if (
QWidget *w = cell(r, sourceCol))
969 setCell(r, targetCol++, w);
971 for ( ; targetCol < m_ncols; targetCol++)
972 setCell(r, targetCol,
nullptr);
976 for (
int c = FormLayoutColumns; c < m_ncols; c++)
978 pastRightWidgetCount++;
982 const int formNRows = m_nrows + pastRightWidgetCount;
983 QWidget **formCells =
new QWidget*[FormLayoutColumns * formNRows];
984 std::fill(formCells, formCells + FormLayoutColumns * formNRows,
nullptr);
986 const int matchingColumns = qMin(m_ncols,
static_cast<
int>(FormLayoutColumns));
987 for (
int r = 0; r < m_nrows; r++) {
989 for ( ; c < matchingColumns; c++)
990 *formPtr++ = cell(r, c);
991 formPtr += FormLayoutColumns - matchingColumns;
993 for ( ; c < m_ncols; c++)
999 Q_ASSERT(formPtr == formCells + FormLayoutColumns * formNRows);
1001 m_cells = formCells;
1002 m_nrows = formNRows;
1003 m_ncols = FormLayoutColumns;
1006bool GridHelper::locateWidget(
QWidget *w,
int &row,
int &col,
int &rowspan,
int &colspan)
const
1008 const int end = m_nrows * m_ncols;
1009 const int startIndex =
std::find(m_cells, m_cells + end, w) - m_cells;
1010 if (startIndex == end)
1013 row = startIndex / m_ncols;
1014 col = startIndex % m_ncols;
1015 for (rowspan = 1; row + rowspan < m_nrows && cell(row + rowspan, col) == w; rowspan++) {}
1016 for (colspan = 1; col + colspan < m_ncols && cell(row, col + colspan) == w; colspan++) {}
1022void addWidgetToGrid(QGridLayout *lt,
QWidget * widget,
int row,
int column,
int rowSpan,
int columnSpan, Qt::Alignment alignment)
1024 lt->addWidget(widget, row, column, rowSpan, columnSpan, alignment);
1027inline void addWidgetToGrid(QFormLayout *lt,
QWidget * widget,
int row,
int column,
int,
int columnSpan, Qt::Alignment)
1029 formLayoutAddWidget(lt, widget, QRect(column, row, columnSpan, 1),
false);
1033template <
class GridLikeLayout,
int LayoutType,
int GridMode>
1034class GridLayout :
public Layout
1037 GridLayout(
const QWidgetList &wl,
QWidget *p, QDesignerFormWindowInterface *fw,
QWidget *lb);
1039 void doLayout() override;
1040 void sort() override { setWidgets(buildGrid(widgets())); }
1043 QWidgetList buildGrid(
const QWidgetList &);
1047template <
class GridLikeLayout,
int LayoutType,
int GridMode>
1048GridLayout<GridLikeLayout, LayoutType, GridMode>::GridLayout(
const QWidgetList &wl,
QWidget *p, QDesignerFormWindowInterface *fw,
QWidget *lb) :
1049 Layout(wl, p, fw, lb, LayoutInfo::Grid),
1050 m_grid(
static_cast<GridHelper::Mode>(GridMode))
1054template <
class GridLikeLayout,
int LayoutType,
int GridMode>
1055void GridLayout<GridLikeLayout, LayoutType, GridMode>::doLayout()
1057 bool needMove, needReparent;
1058 if (!prepareLayout(needMove, needReparent))
1061 GridLikeLayout *layout =
static_cast<GridLikeLayout *>(createLayout(LayoutType));
1063 if (!m_grid.empty())
1068 for (
auto *w : widgets()) {
1069 int r = 0, c = 0, rs = 0, cs = 0;
1071 if (m_grid.locateWidget(w, r, c, rs, cs)) {
1073 reparentToLayoutBase(w);
1075 Qt::Alignment alignment;
1076 if (
const Spacer *spacer = qobject_cast<
const Spacer*>(w))
1077 alignment = spacer->alignment();
1079 addWidgetToGrid(layout, w, r, c, rs, cs, alignment);
1083 qDebug(
"ooops, widget '%s' does not fit in layout", w->objectName().toUtf8().constData());
1087 QLayoutSupport::createEmptyCells(layout);
1089 finishLayout(needMove, layout);
1093void removeIntVecDuplicates(QList<
int> &v)
1098 for (
auto current = v.begin() ; (current != v.end()) && ((current + 1) != v.end()) ; )
1099 if ( *current == *(current+1) )
1106inline QRect expandGeometry(
const QRect &rect)
1108 return rect.isEmpty() ? QRect(rect.topLeft(), rect.size().expandedTo(QSize(1, 1))) : rect;
1111template <
class GridLikeLayout,
int LayoutType,
int GridMode>
1112QWidgetList GridLayout<GridLikeLayout, LayoutType, GridMode>::buildGrid(
const QWidgetList &widgetList)
1114 if (widgetList.isEmpty())
1115 return QWidgetList();
1124 const auto widgetCount = widgetList.size();
1125 QList<
int> x( widgetCount * 2 );
1126 QList<
int> y( widgetCount * 2 );
1129 qsizetype index = 0;
1130 for (
const auto *w : widgetList) {
1131 const QRect widgetPos = expandGeometry(w->geometry());
1132 x[index] = widgetPos.left();
1133 x[index+1] = widgetPos.right();
1134 y[index] = widgetPos.top();
1135 y[index+1] = widgetPos.bottom();
1139 std::sort(x.begin(), x.end());
1140 std::sort(y.begin(), y.end());
1143 removeIntVecDuplicates(x);
1144 removeIntVecDuplicates(y);
1148 m_grid.resize(y.size(), x.size());
1150 for (
auto *w : widgetList) {
1152 const QRect widgetPos = expandGeometry(w->geometry());
1153 QRect c(0, 0, 0, 0);
1156 const int leftIdx = x.indexOf(widgetPos.left());
1157 Q_ASSERT(leftIdx != -1);
1159 c.setRight(leftIdx);
1160 for (qsizetype cw = leftIdx; cw < x.size(); ++cw)
1161 if (x.at(cw) < widgetPos.right())
1166 const int topIdx = y.indexOf(widgetPos.top());
1167 Q_ASSERT(topIdx != -1);
1169 c.setBottom(topIdx);
1170 for (qsizetype ch = topIdx; ch < y.size(); ++ch)
1171 if (y.at(ch) < widgetPos.bottom())
1175 m_grid.setCells(c, w);
1180 QWidgetList ordered;
1181 for (
int i = 0; i < m_grid.numRows(); i++)
1182 for (
int j = 0; j < m_grid.numCols(); j++) {
1183 QWidget *w = m_grid.cell(i, j);
1184 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)