11#include <QtCore/qmath.h>
15using namespace Qt::StringLiterals;
17#define LAYOUTITEMSIZE_MAX (1
<< 24
)
22 int count = items.size();
25 items.insert(index, delta, T());
26 }
else if (delta < 0) {
27 items.remove(index, qMin(-delta, count - index));
34 Q_ASSERT(sumDesired != 0.0);
35 return desired * qPow(sumAvailable / sumDesired, desired / sumDesired);
43 Q_ASSERT(descent >= 0.0);
44 Q_ASSERT(ascent >= 0.0);
45 Q_ASSERT(targetSize >= ascent + descent);
47 qreal extra = targetSize - (ascent + descent);
48 return descent + (extra / 2.0);
51static qreal compare(
const QGridLayoutBox &box1,
const QGridLayoutBox &box2,
int which)
53 qreal size1 = box1.q_sizes(which);
54 qreal size2 = box2.q_sizes(which);
63void QGridLayoutBox::add(
const QGridLayoutBox &other,
int stretch, qreal spacing)
65 Q_ASSERT(q_minimumDescent < 0.0);
67 q_minimumSize += other.q_minimumSize + spacing;
68 q_preferredSize += other.q_preferredSize + spacing;
69 q_maximumSize += ((stretch == 0) ? other.q_preferredSize : other.q_maximumSize) + spacing;
72void QGridLayoutBox::combine(
const QGridLayoutBox &other)
74 q_minimumDescent = qMax(q_minimumDescent, other.q_minimumDescent);
75 q_minimumAscent = qMax(q_minimumAscent, other.q_minimumAscent);
77 q_minimumSize = qMax(q_minimumAscent + q_minimumDescent,
78 qMax(q_minimumSize, other.q_minimumSize));
80 if (q_maximumSize == FLT_MAX && other.q_maximumSize != FLT_MAX)
81 maxMax = other.q_maximumSize;
82 else if (other.q_maximumSize == FLT_MAX && q_maximumSize != FLT_MAX)
83 maxMax = q_maximumSize;
85 maxMax = qMax(q_maximumSize, other.q_maximumSize);
87 q_maximumSize = qMax(q_minimumSize, maxMax);
88 q_preferredSize = qBound(q_minimumSize, qMax(q_preferredSize, other.q_preferredSize),
92void QGridLayoutBox::normalize()
94 q_maximumSize = qMax(qreal(0.0), q_maximumSize);
95 q_minimumSize = qBound(qreal(0.0), q_minimumSize, q_maximumSize);
96 q_preferredSize = qBound(q_minimumSize, q_preferredSize, q_maximumSize);
97 q_minimumDescent = qMin(q_minimumDescent, q_minimumSize);
99 Q_ASSERT((q_minimumDescent < 0.0) == (q_minimumAscent < 0.0));
102#ifdef QGRIDLAYOUTENGINE_DEBUG
103void QGridLayoutBox::dump(
int indent)
const
105 qDebug(
"%*sBox (%g <= %g <= %g [%g/%g])", indent,
"", q_minimumSize, q_preferredSize,
106 q_maximumSize, q_minimumAscent, q_minimumDescent);
110bool operator==(
const QGridLayoutBox &box1,
const QGridLayoutBox &box2)
112 for (
int i = 0; i <
NSizes; ++i) {
113 if (box1.q_sizes(i) != box2.q_sizes(i))
116 return box1.q_minimumDescent == box2.q_minimumDescent
117 && box1.q_minimumAscent == box2.q_minimumAscent;
122 ignore.fill(
false, count);
123 boxes.fill(QGridLayoutBox(), count);
125 stretches.fill(0, count);
126 spacings.fill(0.0, count);
132 MultiCellMap::const_iterator i =
multiCellMap.constBegin();
134 int start = i.key().first;
135 int span = i.key().second;
136 int end = start + span;
137 const QGridLayoutBox &box = i.value().q_box;
138 int stretch = i.value().q_stretch;
141 QVarLengthArray<QGridLayoutBox> extras(span);
142 QVarLengthArray<qreal> dummy(span);
143 QVarLengthArray<qreal> newSizes(span);
145 for (
int j = 0; j <
NSizes; ++j) {
146 qreal extra = compare(box, totalBox, j);
148 calculateGeometries(start, end, box.q_sizes(j), dummy.data(), newSizes.data(),
149 nullptr, totalBox, rowInfo, snapToPixelGrid);
151 for (
int k = 0; k < span; ++k)
152 extras[k].q_sizes(j) = newSizes[k];
156 for (
int k = 0; k < span; ++k) {
157 boxes[start + k].combine(extras[k]);
159 stretches[start + k] = qMax(stretches[start + k], stretch);
167static inline qreal qround(qreal f)
169 return std::floor(f + qreal(0.5));
174 qreal *sizes, qreal *descents,
175 const QGridLayoutBox &totalBox,
178 Q_ASSERT(end > start);
180 targetSize = qMax(totalBox.q_minimumSize, targetSize);
183 QVarLengthArray<qreal> newSizes(n);
184 QVarLengthArray<qreal> factors(n);
185 qreal sumFactors = 0.0;
186 int sumStretches = 0;
189 for (
int i = 0; i < n; ++i) {
190 const int stretch = stretches.at(start + i);
192 sumStretches += stretch;
195 if (targetSize < totalBox.q_preferredSize) {
196 stealBox(start, end, MinimumSize, positions, sizes);
198 sumAvailable = targetSize - totalBox.q_minimumSize;
199 if (sumAvailable > 0.0) {
200 const qreal totalBox_preferredSize = qMin(totalBox.q_preferredSize, qreal(
LAYOUTITEMSIZE_MAX));
201 qreal sumDesired = totalBox_preferredSize - totalBox.q_minimumSize;
203 for (
int i = 0; i < n; ++i) {
204 if (ignore.testBit(start + i)) {
209 const QGridLayoutBox &box = boxes.at(start + i);
211 qreal desired = box_preferredSize - box.q_minimumSize;
212 factors[i] = growthFactorBelowPreferredSize(desired, sumAvailable, sumDesired);
213 sumFactors += factors[i];
216 for (
int i = 0; i < n; ++i) {
217 Q_ASSERT(sumFactors > 0.0);
218 qreal delta = sumAvailable * factors[i] / sumFactors;
219 newSizes[i] = sizes[i] + delta;
223 bool isLargerThanMaximum = (targetSize > totalBox.q_maximumSize);
224 if (isLargerThanMaximum) {
225 stealBox(start, end, MaximumSize, positions, sizes);
226 sumAvailable = targetSize - totalBox.q_maximumSize;
228 stealBox(start, end, PreferredSize, positions, sizes);
229 sumAvailable = targetSize - totalBox.q_preferredSize;
232 if (sumAvailable > 0.0) {
233 qreal sumCurrentAvailable = sumAvailable;
234 bool somethingHasAMaximumSize =
false;
236 qreal sumSizes = 0.0;
237 for (
int i = 0; i < n; ++i)
238 sumSizes += sizes[i];
240 for (
int i = 0; i < n; ++i) {
241 if (ignore.testBit(start + i)) {
247 const QGridLayoutBox &box = boxes.at(start + i);
251 if (isLargerThanMaximum) {
252 boxSize = box.q_maximumSize;
253 desired = rowInfo.boxes.value(start + i).q_maximumSize - boxSize;
255 boxSize = box.q_preferredSize;
256 desired = box.q_maximumSize - boxSize;
258 if (desired == 0.0) {
259 newSizes[i] = sizes[i];
262 Q_ASSERT(desired > 0.0);
264 int stretch = stretches[start + i];
265 if (sumStretches == 0) {
267 factors[i] = (stretch < 0) ? 1.0 : 0.0;
269 factors[i] = (stretch < 0) ? sizes[i] : 0.0;
271 }
else if (stretch == sumStretches) {
273 }
else if (stretch <= 0) {
277 qreal ultimateSumSizes;
278 qreal x = ((stretch * sumSizes)
279 - (sumStretches * boxSize))
280 / (sumStretches - stretch);
282 ultimateSize = boxSize + x;
283 ultimateSumSizes = sumSizes + x;
285 ultimateSize = boxSize;
286 ultimateSumSizes = (sumStretches * boxSize)
291
292
293
294
295 ultimateSize = ultimateSize * 3 / 2;
296 ultimateSumSizes = ultimateSumSizes * 3 / 2;
298 qreal beta = ultimateSumSizes - sumSizes;
302 qreal alpha = qMin(sumCurrentAvailable, beta);
303 qreal ultimateFactor = (stretch * ultimateSumSizes / sumStretches)
305 qreal transitionalFactor = sumCurrentAvailable * (ultimateSize - boxSize) / beta;
307 factors[i] = ((alpha * ultimateFactor)
308 + ((beta - alpha) * transitionalFactor)) / beta;
312 sumFactors += factors[i];
313 if (desired < sumCurrentAvailable)
314 somethingHasAMaximumSize =
true;
320 bool keepGoing = somethingHasAMaximumSize;
325 for (
int i = 0; i < n; ++i) {
326 if (newSizes[i] >= 0.0)
329 const QList<QGridLayoutBox> &rBoxes = isLargerThanMaximum ? rowInfo.boxes : boxes;
330 const QGridLayoutBox &box = rBoxes.value(start + i);
331 qreal maxBoxSize = box.q_maximumSize;
334 maxBoxSize = qMax(box.q_minimumSize, std::floor(maxBoxSize));
336 qreal avail = sumCurrentAvailable * factors[i] / sumFactors;
337 if (sizes[i] + avail >= maxBoxSize) {
338 newSizes[i] = maxBoxSize;
339 sumCurrentAvailable -= maxBoxSize - sizes[i];
340 sumFactors -= factors[i];
341 keepGoing = (sumCurrentAvailable > 0.0);
347 for (
int i = 0; i < n; ++i) {
348 if (newSizes[i] < 0.0) {
349 qreal delta = (sumFactors == 0.0) ? 0.0
350 : sumCurrentAvailable * factors[i] / sumFactors;
351 newSizes[i] = sizes[i] + delta;
357 if (sumAvailable > 0) {
359 for (
int i = 0; i < n; ++i) {
360 qreal delta = newSizes[i] - sizes[i];
361 positions[i] += offset;
367 int surplus = targetSize - (positions[n - 1] + sizes[n - 1]);
368 Q_ASSERT(surplus >= 0 && surplus <= n);
370 int prevSurplus = -1;
371 while (surplus > 0 && surplus != prevSurplus) {
372 prevSurplus = surplus;
375 for (
int i = 0; i < n; ++i) {
376 const QGridLayoutBox &box = boxes.at(start + i);
377 int delta = (!ignore.testBit(start + i) && surplus > 0
378 && factors[i] > 0 && sizes[i] < box.q_maximumSize)
381 positions[i] += offset;
387 Q_ASSERT(surplus == 0);
390 if (snapToPixelGrid) {
391 for (
int i = 0; i < n; ++i) {
392 const qreal oldpos = positions[i];
393 positions[i] = qround(oldpos);
394 const qreal delta = positions[i] - oldpos;
397 sizes[i - 1] += delta;
400 sizes[n - 1] = targetSize - positions[n - 1];
404 for (
int i = 0; i < n; ++i) {
405 const QGridLayoutBox &box = boxes.at(start + i);
406 sizes[i] = qMax(box.q_minimumSize, qround(sizes[i]));
411 for (
int i = 0; i < n; ++i) {
412 if (ignore.testBit(start + i))
414 const QGridLayoutBox &box = boxes.at(start + i);
415 descents[i] = fixedDescent(box.q_minimumDescent, box.q_minimumAscent, sizes[i]);
424 result.q_maximumSize = 0.0;
425 qreal nextSpacing = 0.0;
426 for (
int i = start; i < end; ++i) {
427 if (ignore.testBit(i))
429 result.add(boxes.at(i), stretches.at(i), nextSpacing);
430 nextSpacing = spacings.at(i);
439 qreal nextSpacing = 0.0;
441 for (
int i = start; i < end; ++i) {
444 if (!ignore.testBit(i)) {
445 const QGridLayoutBox &box = boxes.at(i);
446 avail = box.q_sizes(which);
447 offset += nextSpacing;
448 nextSpacing = spacings.at(i);
451 *positions++ = offset;
457#ifdef QGRIDLAYOUTENGINE_DEBUG
458void QGridLayoutRowData::dump(
int indent)
const
460 qDebug(
"%*sData", indent,
"");
462 for (
int i = 0; i < ignore.count(); ++i) {
463 qDebug(
"%*s Row %d (stretch %d, spacing %g)", indent,
"", i, stretches.at(i),
465 if (ignore.testBit(i))
466 qDebug(
"%*s Ignored", indent,
"");
467 boxes.at(i).dump(indent + 2);
470 MultiCellMap::const_iterator it = multiCellMap.constBegin();
471 while (it != multiCellMap.constEnd()) {
472 qDebug(
"%*s Multi-cell entry <%d, %d> (stretch %d)", indent,
"", it.key().first,
473 it.key().second, it.value().q_stretch);
474 it.value().q_box.dump(indent + 2);
480QGridLayoutItem::QGridLayoutItem(
int row,
int column,
int rowSpan,
int columnSpan,
481 Qt::Alignment alignment)
482 : q_firstRows{column, row},
483 q_rowSpans{columnSpan, rowSpan},
485 q_alignment(alignment)
489int QGridLayoutItem::firstRow(Qt::Orientation orientation)
const
491 return q_firstRows[orientation];
494int QGridLayoutItem::firstColumn(Qt::Orientation orientation)
const
496 return q_firstRows.transposed()[orientation];
499int QGridLayoutItem::lastRow(Qt::Orientation orientation)
const
501 return firstRow(orientation) + rowSpan(orientation) - 1;
504int QGridLayoutItem::lastColumn(Qt::Orientation orientation)
const
506 return firstColumn(orientation) + columnSpan(orientation) - 1;
509int QGridLayoutItem::rowSpan(Qt::Orientation orientation)
const
511 return q_rowSpans[orientation];
514int QGridLayoutItem::columnSpan(Qt::Orientation orientation)
const
516 return q_rowSpans.transposed()[orientation];
519void QGridLayoutItem::setFirstRow(
int row, Qt::Orientation orientation)
521 q_firstRows[orientation] = row;
524void QGridLayoutItem::setRowSpan(
int rowSpan, Qt::Orientation orientation)
526 q_rowSpans[orientation] = rowSpan;
529int QGridLayoutItem::stretchFactor(Qt::Orientation orientation)
const
531 int stretch = q_stretches[orientation];
535 QLayoutPolicy::Policy policy = sizePolicy(orientation);
537 if (policy & QLayoutPolicy::ExpandFlag) {
539 }
else if (policy & QLayoutPolicy::GrowFlag) {
546void QGridLayoutItem::setStretchFactor(
int stretch, Qt::Orientation orientation)
548 Q_ASSERT(stretch >= 0);
549 q_stretches[orientation] = stretch;
552QLayoutPolicy::ControlTypes QGridLayoutItem::controlTypes(LayoutSide )
const
554 return QLayoutPolicy::DefaultType;
557QGridLayoutBox QGridLayoutItem::box(Qt::Orientation orientation,
bool snapToPixelGrid, qreal constraint)
const
559 QGridLayoutBox result;
560 QLayoutPolicy::Policy policy = sizePolicy(orientation);
562 if (orientation == Qt::Horizontal) {
563 QSizeF constraintSize(-1.0, constraint);
565 result.q_preferredSize = sizeHint(Qt::PreferredSize, constraintSize).width();
567 if (policy & QLayoutPolicy::ShrinkFlag) {
568 result.q_minimumSize = sizeHint(Qt::MinimumSize, constraintSize).width();
570 result.q_minimumSize = result.q_preferredSize;
573 result.q_minimumSize = qCeil(result.q_minimumSize);
575 if (policy & (QLayoutPolicy::GrowFlag | QLayoutPolicy::ExpandFlag)) {
576 result.q_maximumSize = sizeHint(Qt::MaximumSize, constraintSize).width();
578 result.q_maximumSize = result.q_preferredSize;
581 QSizeF constraintSize(constraint, -1.0);
583 result.q_preferredSize = sizeHint(Qt::PreferredSize, constraintSize).height();
585 if (policy & QLayoutPolicy::ShrinkFlag) {
586 result.q_minimumSize = sizeHint(Qt::MinimumSize, constraintSize).height();
588 result.q_minimumSize = result.q_preferredSize;
591 result.q_minimumSize = qCeil(result.q_minimumSize);
593 if (policy & (QLayoutPolicy::GrowFlag | QLayoutPolicy::ExpandFlag)) {
594 result.q_maximumSize = sizeHint(Qt::MaximumSize, constraintSize).height();
596 result.q_maximumSize = result.q_preferredSize;
599 if (alignment() & Qt::AlignBaseline) {
600 result.q_minimumDescent = sizeHint(Qt::MinimumDescent, constraintSize).height();
601 if (result.q_minimumDescent != -1.0) {
602 const qreal minSizeHint = sizeHint(Qt::MinimumSize, constraintSize).height();
603 result.q_minimumDescent -= (minSizeHint - result.q_minimumSize);
604 result.q_minimumAscent = result.q_minimumSize - result.q_minimumDescent;
608 if (policy & QLayoutPolicy::IgnoreFlag)
609 result.q_preferredSize = result.q_minimumSize;
614QRectF QGridLayoutItem::geometryWithin(qreal x, qreal y, qreal width, qreal height,
615 qreal rowDescent, Qt::Alignment align,
bool snapToPixelGrid)
const
617 const qreal cellWidth = width;
618 const qreal cellHeight = height;
620 QSizeF size = effectiveMaxSize(QSizeF(-1,-1));
621 if (hasDynamicConstraint()) {
622 if (dynamicConstraintOrientation() == Qt::Vertical) {
623 if (size.width() > cellWidth)
624 size = effectiveMaxSize(QSizeF(cellWidth, -1));
625 }
else if (size.height() > cellHeight) {
626 size = effectiveMaxSize(QSizeF(-1, cellHeight));
629 size = size.boundedTo(QSizeF(cellWidth, cellHeight));
630 width = size.width();
631 height = size.height();
633 switch (align & Qt::AlignHorizontal_Mask) {
634 case Qt::AlignHCenter:
635 x += (cellWidth - width)/2;
638 x += cellWidth - width;
644 switch (align & Qt::AlignVertical_Mask) {
645 case Qt::AlignVCenter:
646 y += (cellHeight - height)/2;
648 case Qt::AlignBottom:
649 y += cellHeight - height;
651 case Qt::AlignBaseline: {
652 width = qMin(effectiveMaxSize(QSizeF(-1,-1)).width(), width);
653 QGridLayoutBox vBox = box(Qt::Vertical, snapToPixelGrid);
654 const qreal descent = vBox.q_minimumDescent;
655 const qreal ascent = vBox.q_minimumSize - descent;
656 y += (cellHeight - rowDescent - ascent);
657 height = ascent + descent;
662 return QRectF(x, y, width, height);
665void QGridLayoutItem::transpose()
667 q_firstRows.transpose();
668 q_rowSpans.transpose();
669 q_stretches.transpose();
672void QGridLayoutItem::insertOrRemoveRows(
int row,
int delta, Qt::Orientation orientation)
674 int oldFirstRow = firstRow(orientation);
675 if (oldFirstRow >= row) {
676 setFirstRow(oldFirstRow + delta, orientation);
677 }
else if (lastRow(orientation) >= row) {
678 setRowSpan(rowSpan(orientation) + delta, orientation);
682
683
684
685
686
687
688
689QSizeF QGridLayoutItem::effectiveMaxSize(
const QSizeF &constraint)
const
691 QSizeF size = constraint;
692 bool vGrow = (sizePolicy(Qt::Vertical) & QLayoutPolicy::GrowFlag) == QLayoutPolicy::GrowFlag;
693 bool hGrow = (sizePolicy(Qt::Horizontal) & QLayoutPolicy::GrowFlag) == QLayoutPolicy::GrowFlag;
694 if (!vGrow || !hGrow) {
695 QSizeF pref = sizeHint(Qt::PreferredSize, constraint);
697 size.setHeight(pref.height());
699 size.setWidth(pref.width());
702 if (!size.isValid()) {
703 QSizeF maxSize = sizeHint(Qt::MaximumSize, size);
704 if (size.width() == -1)
705 size.setWidth(maxSize.width());
706 if (size.height() == -1)
707 size.setHeight(maxSize.height());
712#ifdef QGRIDLAYOUTENGINE_DEBUG
713void QGridLayoutItem::dump(
int indent)
const
715 qDebug(
"%*s (%d, %d) %d x %d", indent,
"", firstRow(), firstColumn(),
716 rowSpan(), columnSpan());
718 if (q_stretches[Qt::Horizontal] >= 0)
719 qDebug(
"%*s Horizontal stretch: %d", indent,
"", q_stretches[Qt::Horizontal]);
720 if (q_stretches[Qt::Vertical] >= 0)
721 qDebug(
"%*s Vertical stretch: %d", indent,
"", q_stretches[Qt::Vertical]);
722 if (q_alignment != 0)
723 qDebug(
"%*s Alignment: %x", indent,
"", uint(q_alignment));
724 qDebug(
"%*s Horizontal size policy: %x Vertical size policy: %x",
725 indent,
"", (
unsigned int)sizePolicy(Qt::Horizontal), (
unsigned int)sizePolicy(Qt::Vertical));
733 insertOrRemoveItems(stretches, row, delta);
734 insertOrRemoveItems(spacings, row, delta);
735 insertOrRemoveItems(alignments, row, delta);
736 insertOrRemoveItems(boxes, row, delta);
739#ifdef QGRIDLAYOUTENGINE_DEBUG
740void QGridLayoutRowInfo::dump(
int indent)
const
742 qDebug(
"%*sInfo (count: %d)", indent,
"", count);
743 for (
int i = 0; i < count; ++i) {
746 if (stretches.value(i).value() >= 0)
747 message += QString::fromLatin1(
" stretch %1").arg(stretches.value(i).value());
748 if (spacings.value(i).value() >= 0.0)
749 message += QString::fromLatin1(
" spacing %1").arg(spacings.value(i).value());
750 if (alignments.value(i) != 0)
751 message += QString::fromLatin1(
" alignment %1").arg(
int(alignments.value(i)), 16);
753 if (!message.isEmpty() || boxes.value(i) != QGridLayoutBox()) {
754 qDebug(
"%*s Row %d:%s", indent,
"", i, qPrintable(message));
755 if (boxes.value(i) != QGridLayoutBox())
756 boxes.value(i).dump(indent + 1);
762QGridLayoutEngine::QGridLayoutEngine(Qt::Alignment defaultAlignment,
bool snapToPixelGrid)
764 m_visualDirection = Qt::LeftToRight;
765 m_defaultAlignment = defaultAlignment;
766 m_snapToPixelGrid = snapToPixelGrid;
767 m_uniformCellWidths =
false;
768 m_uniformCellHeights =
false;
772int QGridLayoutEngine::rowCount(Qt::Orientation orientation)
const
774 return q_infos[orientation].count;
777int QGridLayoutEngine::columnCount(Qt::Orientation orientation)
const
779 return q_infos.transposed()[orientation].count;
782int QGridLayoutEngine::itemCount()
const
784 return q_items.size();
787QGridLayoutItem *QGridLayoutEngine::itemAt(
int index)
const
789 Q_ASSERT(index >= 0 && index < itemCount());
790 return q_items.at(index);
793int QGridLayoutEngine::effectiveFirstRow(Qt::Orientation orientation)
const
795 ensureEffectiveFirstAndLastRows();
796 return q_cachedEffectiveFirstRows[orientation];
799int QGridLayoutEngine::effectiveLastRow(Qt::Orientation orientation)
const
801 ensureEffectiveFirstAndLastRows();
802 return q_cachedEffectiveLastRows[orientation];
805void QGridLayoutEngine::setSpacing(qreal spacing, Qt::Orientations orientations)
807 if (orientations & Qt::Horizontal)
808 q_defaultSpacings[Qt::Horizontal].setUserValue(spacing);
809 if (orientations & Qt::Vertical)
810 q_defaultSpacings[Qt::Vertical].setUserValue(spacing);
815qreal QGridLayoutEngine::spacing(Qt::Orientation orientation,
const QAbstractLayoutStyleInfo *styleInfo)
const
817 if (!q_defaultSpacings[orientation].isUser()) {
818 qreal defaultSpacing = styleInfo->spacing(orientation);
819 q_defaultSpacings[orientation].setCachedValue(defaultSpacing);
821 return q_defaultSpacings[orientation].value();
824void QGridLayoutEngine::setRowSpacing(
int row, qreal spacing, Qt::Orientation orientation)
828 QGridLayoutRowInfo &rowInfo = q_infos[orientation];
829 if (row >= rowInfo.spacings.size())
830 rowInfo.spacings.resize(row + 1);
832 rowInfo.spacings[row].setUserValue(spacing);
834 rowInfo.spacings[row] = QLayoutParameter<qreal>();
838qreal QGridLayoutEngine::rowSpacing(
int row, Qt::Orientation orientation)
const
840 QLayoutParameter<qreal> spacing = q_infos[orientation].spacings.value(row);
841 if (!spacing.isDefault())
842 return spacing.value();
843 return q_defaultSpacings[orientation].value();
846void QGridLayoutEngine::setRowStretchFactor(
int row,
int stretch, Qt::Orientation orientation)
849 Q_ASSERT(stretch >= 0);
851 maybeExpandGrid(row, -1, orientation);
853 QGridLayoutRowInfo &rowInfo = q_infos[orientation];
854 if (row >= rowInfo.stretches.size())
855 rowInfo.stretches.resize(row + 1);
856 rowInfo.stretches[row].setUserValue(stretch);
859int QGridLayoutEngine::rowStretchFactor(
int row, Qt::Orientation orientation)
const
861 QStretchParameter stretch = q_infos[orientation].stretches.value(row);
862 if (!stretch.isDefault())
863 return stretch.value();
867void QGridLayoutEngine::setRowSizeHint(Qt::SizeHint which,
int row, qreal size,
868 Qt::Orientation orientation)
871 Q_ASSERT(size >= 0.0);
873 maybeExpandGrid(row, -1, orientation);
875 QGridLayoutRowInfo &rowInfo = q_infos[orientation];
876 if (row >= rowInfo.boxes.size())
877 rowInfo.boxes.resize(row + 1);
878 rowInfo.boxes[row].q_sizes(which) = size;
881qreal QGridLayoutEngine::rowSizeHint(Qt::SizeHint which,
int row, Qt::Orientation orientation)
const
883 return q_infos[orientation].boxes.value(row).q_sizes(which);
886bool QGridLayoutEngine::uniformCellWidths()
const
888 return m_uniformCellWidths;
891void QGridLayoutEngine::setUniformCellWidths(
bool uniformCellWidths)
893 if (m_uniformCellWidths == uniformCellWidths)
896 m_uniformCellWidths = uniformCellWidths;
900bool QGridLayoutEngine::uniformCellHeights()
const
902 return m_uniformCellHeights;
905void QGridLayoutEngine::setUniformCellHeights(
bool uniformCellHeights)
907 if (m_uniformCellHeights == uniformCellHeights)
910 m_uniformCellHeights = uniformCellHeights;
914void QGridLayoutEngine::setRowAlignment(
int row, Qt::Alignment alignment,
915 Qt::Orientation orientation)
919 maybeExpandGrid(row, -1, orientation);
921 QGridLayoutRowInfo &rowInfo = q_infos[orientation];
922 if (row >= rowInfo.alignments.size())
923 rowInfo.alignments.resize(row + 1);
924 rowInfo.alignments[row] = alignment;
927Qt::Alignment QGridLayoutEngine::rowAlignment(
int row, Qt::Orientation orientation)
const
930 return q_infos[orientation].alignments.value(row);
933Qt::Alignment QGridLayoutEngine::effectiveAlignment(
const QGridLayoutItem *layoutItem)
const
935 Qt::Alignment align = layoutItem->alignment();
936 if (!(align & Qt::AlignVertical_Mask)) {
938 int y = layoutItem->firstRow();
939 align |= (rowAlignment(y, Qt::Vertical) & Qt::AlignVertical_Mask);
940 if (!(align & Qt::AlignVertical_Mask))
941 align |= (m_defaultAlignment & Qt::AlignVertical_Mask);
943 if (!(align & Qt::AlignHorizontal_Mask)) {
945 int x = layoutItem->firstColumn();
946 align |= (rowAlignment(x, Qt::Horizontal) & Qt::AlignHorizontal_Mask);
953
954
955
956
957
958void QGridLayoutEngine::insertItem(QGridLayoutItem *item,
int index)
960 maybeExpandGrid(item->lastRow(), item->lastColumn());
962 if (index < 0 || index >= q_items.size())
963 q_items.append(item);
965 q_items.insert(index, item);
967 for (
int i = item->firstRow(); i <= item->lastRow(); ++i) {
968 for (
int j = item->firstColumn(); j <= item->lastColumn(); ++j) {
969 const auto existingItem = itemAt(i, j);
971 qWarning(
"QGridLayoutEngine::addItem: Can't add %s at cell (%d, %d) because it's already taken by %s",
972 qPrintable(item->toString()), i, j, qPrintable(existingItem->toString()));
974 setItemAt(i, j, item);
979void QGridLayoutEngine::addItem(QGridLayoutItem *item)
981 insertItem(item, -1);
984void QGridLayoutEngine::removeItem(QGridLayoutItem *item)
986 Q_ASSERT(q_items.contains(item));
990 for (
int i = item->firstRow(); i <= item->lastRow(); ++i) {
991 for (
int j = item->firstColumn(); j <= item->lastColumn(); ++j) {
992 if (itemAt(i, j) == item)
993 setItemAt(i, j,
nullptr);
997 q_items.removeAll(item);
1001QGridLayoutItem *QGridLayoutEngine::itemAt(
int row,
int column, Qt::Orientation orientation)
const
1003 if (orientation == Qt::Horizontal)
1005 if (uint(row) >= uint(rowCount()) || uint(column) >= uint(columnCount()))
1007 return q_grid.at((row * internalGridColumnCount()) + column);
1010void QGridLayoutEngine::invalidate()
1012 q_cachedEffectiveFirstRows = {-1, -1};
1013 q_cachedEffectiveLastRows = {-1, -1};
1015 q_totalBoxCachedConstraints = {NotCached, NotCached};
1017 q_cachedSize = QSizeF();
1018 q_cachedConstraintOrientation = UnknownConstraint;
1021static void visualRect(QRectF *geom, Qt::LayoutDirection dir,
const QRectF &contentsRect)
1023 if (dir == Qt::RightToLeft)
1024 geom->moveRight(contentsRect.right() - (geom->left() - contentsRect.left()));
1027void QGridLayoutEngine::setGeometries(
const QRectF &contentsGeometry,
const QAbstractLayoutStyleInfo *styleInfo)
1029 if (rowCount() < 1 || columnCount() < 1)
1032 ensureGeometries(contentsGeometry.size(), styleInfo);
1034 for (
int i = q_items.size() - 1; i >= 0; --i) {
1035 QGridLayoutItem *item = q_items.at(i);
1037 qreal x = q_xx.at(item->firstColumn());
1038 qreal y = q_yy.at(item->firstRow());
1039 qreal width = q_widths.at(item->lastColumn());
1040 qreal height = q_heights.at(item->lastRow());
1042 if (item->columnSpan() != 1)
1043 width += q_xx.at(item->lastColumn()) - x;
1044 if (item->rowSpan() != 1)
1045 height += q_yy.at(item->lastRow()) - y;
1047 const Qt::Alignment align = effectiveAlignment(item);
1048 QRectF geom = item->geometryWithin(contentsGeometry.x() + x, contentsGeometry.y() + y,
1049 width, height, q_descents.at(item->lastRow()), align, m_snapToPixelGrid);
1050 if (m_snapToPixelGrid) {
1055 geom.moveLeft(qround(geom.x()));
1057 if (align != Qt::AlignBaseline)
1058 geom.moveTop(qround(geom.y()));
1060 visualRect(&geom, visualDirection(), contentsGeometry);
1061 item->setGeometry(geom);
1066QRectF QGridLayoutEngine::cellRect(
const QRectF &contentsGeometry,
int row,
int column,
int rowSpan,
1067 int columnSpan,
const QAbstractLayoutStyleInfo *styleInfo)
const
1069 if (uint(row) >= uint(rowCount()) || uint(column) >= uint(columnCount())
1070 || rowSpan < 1 || columnSpan < 1)
1073 ensureGeometries(contentsGeometry.size(), styleInfo);
1075 int lastColumn = qMin(column + columnSpan, columnCount()) - 1;
1076 int lastRow = qMin(row + rowSpan, rowCount()) - 1;
1078 qreal x = q_xx[column];
1079 qreal y = q_yy[row];
1080 qreal width = q_widths[lastColumn];
1081 qreal height = q_heights[lastRow];
1083 if (columnSpan != 1)
1084 width += q_xx[lastColumn] - x;
1086 height += q_yy[lastRow] - y;
1088 return QRectF(contentsGeometry.x() + x, contentsGeometry.y() + y, width, height);
1091QSizeF QGridLayoutEngine::sizeHint(Qt::SizeHint which,
const QSizeF &constraint,
1092 const QAbstractLayoutStyleInfo *styleInfo)
const
1096 if (hasDynamicConstraint() && rowCount() > 0 && columnCount() > 0) {
1097 QHVContainer<QGridLayoutBox> sizehint_totalBoxes;
1098 bool sizeHintCalculated =
false;
1099 if (constraintOrientation() == Qt::Vertical) {
1101 if (constraint.width() >= 0) {
1102 ensureColumnAndRowData(&q_columnData, &sizehint_totalBoxes[Qt::Horizontal],
nullptr,
nullptr, Qt::Horizontal, styleInfo);
1103 QList<qreal> sizehint_xx;
1104 QList<qreal> sizehint_widths;
1106 sizehint_xx.resize(columnCount());
1107 sizehint_widths.resize(columnCount());
1108 qreal width = constraint.width();
1111 q_columnData.calculateGeometries(0, columnCount(), width, sizehint_xx.data(), sizehint_widths.data(),
1112 nullptr, sizehint_totalBoxes[Qt::Horizontal], q_infos[Qt::Horizontal], m_snapToPixelGrid);
1113 ensureColumnAndRowData(&q_rowData, &sizehint_totalBoxes[Qt::Vertical], sizehint_xx.data(), sizehint_widths.data(), Qt::Vertical, styleInfo);
1114 sizeHintCalculated =
true;
1117 if (constraint.height() >= 0) {
1119 ensureColumnAndRowData(&q_rowData, &sizehint_totalBoxes[Qt::Vertical],
nullptr,
nullptr, Qt::Vertical, styleInfo);
1120 QList<qreal> sizehint_yy;
1121 QList<qreal> sizehint_heights;
1123 sizehint_yy.resize(rowCount());
1124 sizehint_heights.resize(rowCount());
1125 qreal height = constraint.height();
1128 q_rowData.calculateGeometries(0, rowCount(), height, sizehint_yy.data(), sizehint_heights.data(),
1129 nullptr, sizehint_totalBoxes[Qt::Vertical], q_infos[Qt::Vertical], m_snapToPixelGrid);
1130 ensureColumnAndRowData(&q_columnData, &sizehint_totalBoxes[Qt::Horizontal], sizehint_yy.data(), sizehint_heights.data(), Qt::Horizontal, styleInfo);
1131 sizeHintCalculated =
true;
1134 if (sizeHintCalculated)
1135 return QSizeF{sizehint_totalBoxes[Qt::Horizontal].q_sizes(which),
1136 sizehint_totalBoxes[Qt::Vertical].q_sizes(which)};
1140 ensureColumnAndRowData(&q_columnData, &q_totalBoxes[Qt::Horizontal],
nullptr,
nullptr, Qt::Horizontal, styleInfo);
1141 ensureColumnAndRowData(&q_rowData, &q_totalBoxes[Qt::Vertical],
nullptr,
nullptr, Qt::Vertical, styleInfo);
1142 return QSizeF(q_totalBoxes[Qt::Horizontal].q_sizes(which), q_totalBoxes[Qt::Vertical].q_sizes(which));
1145QLayoutPolicy::ControlTypes QGridLayoutEngine::controlTypes(LayoutSide side)
const
1147 Qt::Orientation orientation = (side == Top || side == Bottom) ? Qt::Vertical : Qt::Horizontal;
1148 int row = (side == Top || side == Left) ? effectiveFirstRow(orientation)
1149 : effectiveLastRow(orientation);
1150 QLayoutPolicy::ControlTypes result;
1152 for (
int column = columnCount(orientation) - 1; column >= 0; --column) {
1153 if (QGridLayoutItem *item = itemAt(row, column, orientation))
1154 result |= item->controlTypes(side);
1159void QGridLayoutEngine::transpose()
1163 for (
int i = q_items.size() - 1; i >= 0; --i)
1164 q_items.at(i)->transpose();
1166 q_defaultSpacings.transpose();
1167 q_infos.transpose();
1172void QGridLayoutEngine::setVisualDirection(Qt::LayoutDirection direction)
1174 m_visualDirection = direction;
1177Qt::LayoutDirection QGridLayoutEngine::visualDirection()
const
1179 return m_visualDirection;
1182#ifdef QGRIDLAYOUTENGINE_DEBUG
1183void QGridLayoutEngine::dump(
int indent)
const
1185 qDebug(
"%*sEngine", indent,
"");
1187 qDebug(
"%*s Items (%lld)", indent,
"", q_items.count());
1189 for (i = 0; i < q_items.count(); ++i)
1190 q_items.at(i)->dump(indent + 2);
1192 qDebug(
"%*s Grid (%d x %d)", indent,
"", internalGridRowCount(),
1193 internalGridColumnCount());
1194 for (
int row = 0; row < internalGridRowCount(); ++row) {
1195 QString message =
"[ "_L1;
1196 for (
int column = 0; column < internalGridColumnCount(); ++column) {
1197 message += QString::number(q_items.indexOf(itemAt(row, column))).rightJustified(3);
1201 qDebug(
"%*s %s", indent,
"", qPrintable(message));
1204 if (q_defaultSpacings[Qt::Horizontal].value() >= 0.0 || q_defaultSpacings[Qt::Vertical].value() >= 0.0)
1205 qDebug(
"%*s Default spacings: %g %g", indent,
"",
1206 q_defaultSpacings[Qt::Horizontal].value(),
1207 q_defaultSpacings[Qt::Vertical].value());
1209 qDebug(
"%*s Column and row info", indent,
"");
1210 q_infos[Qt::Horizontal].dump(indent + 2);
1211 q_infos[Qt::Vertical].dump(indent + 2);
1213 qDebug(
"%*s Column and row data", indent,
"");
1214 q_columnData.dump(indent + 2);
1215 q_rowData.dump(indent + 2);
1217 qDebug(
"%*s Geometries output", indent,
"");
1218 QList<qreal> *cellPos = &q_yy;
1219 for (
int pass = 0; pass < 2; ++pass) {
1221 for (i = 0; i < cellPos->count(); ++i) {
1222 message += (message.isEmpty() ?
"["_L1 :
", "_L1);
1223 message += QString::number(cellPos->at(i));
1226 qDebug(
"%*s %s %s", indent,
"", (pass == 0 ?
"rows:" :
"columns:"), qPrintable(message));
1232void QGridLayoutEngine::maybeExpandGrid(
int row,
int column, Qt::Orientation orientation)
1236 if (orientation == Qt::Horizontal)
1239 if (row < rowCount() && column < columnCount())
1242 int oldGridRowCount = internalGridRowCount();
1243 int oldGridColumnCount = internalGridColumnCount();
1245 q_infos[Qt::Vertical].count = qMax(row + 1, rowCount());
1246 q_infos[Qt::Horizontal].count = qMax(column + 1, columnCount());
1248 int newGridRowCount = internalGridRowCount();
1249 int newGridColumnCount = internalGridColumnCount();
1251 int newGridSize = newGridRowCount * newGridColumnCount;
1252 if (newGridSize != q_grid.size()) {
1253 q_grid.resize(newGridSize);
1255 if (newGridColumnCount != oldGridColumnCount) {
1256 for (
int i = oldGridRowCount - 1; i >= 1; --i) {
1257 for (
int j = oldGridColumnCount - 1; j >= 0; --j) {
1258 int oldIndex = (i * oldGridColumnCount) + j;
1259 int newIndex = (i * newGridColumnCount) + j;
1261 Q_ASSERT(newIndex > oldIndex);
1262 q_grid[newIndex] = q_grid[oldIndex];
1263 q_grid[oldIndex] =
nullptr;
1270void QGridLayoutEngine::regenerateGrid()
1272 q_grid.fill(
nullptr);
1274 for (
int i = q_items.size() - 1; i >= 0; --i) {
1275 QGridLayoutItem *item = q_items.at(i);
1277 for (
int j = item->firstRow(); j <= item->lastRow(); ++j) {
1278 for (
int k = item->firstColumn(); k <= item->lastColumn(); ++k) {
1279 setItemAt(j, k, item);
1285void QGridLayoutEngine::setItemAt(
int row,
int column, QGridLayoutItem *item)
1287 Q_ASSERT(row >= 0 && row < rowCount());
1288 Q_ASSERT(column >= 0 && column < columnCount());
1289 q_grid[(row * internalGridColumnCount()) + column] = item;
1292void QGridLayoutEngine::insertOrRemoveRows(
int row,
int delta, Qt::Orientation orientation)
1294 int oldRowCount = rowCount(orientation);
1295 Q_ASSERT(uint(row) <= uint(oldRowCount));
1300 if (row == oldRowCount && delta > 0) {
1301 maybeExpandGrid(oldRowCount + delta - 1, -1, orientation);
1305 q_infos[orientation].insertOrRemoveRows(row, delta);
1307 for (
int i = q_items.size() - 1; i >= 0; --i)
1308 q_items.at(i)->insertOrRemoveRows(row, delta, orientation);
1310 q_grid.resize(internalGridRowCount() * internalGridColumnCount());
1314void QGridLayoutEngine::fillRowData(QGridLayoutRowData *rowData,
1315 const qreal *colPositions,
const qreal *colSizes,
1316 Qt::Orientation orientation,
1317 const QAbstractLayoutStyleInfo *styleInfo)
const
1319 const int ButtonMask = QLayoutPolicy::ButtonBox | QLayoutPolicy::PushButton;
1320 const QGridLayoutRowInfo &rowInfo = q_infos[orientation];
1321 const QGridLayoutRowInfo &columnInfo = q_infos.other(orientation);
1322 LayoutSide top = (orientation == Qt::Vertical) ? Top : Left;
1323 LayoutSide bottom = (orientation == Qt::Vertical) ? Bottom : Right;
1325 const QLayoutParameter<qreal> &defaultSpacing = q_defaultSpacings[orientation];
1326 qreal innerSpacing = styleInfo->spacing(orientation);
1327 if (innerSpacing >= 0.0)
1328 defaultSpacing.setCachedValue(innerSpacing);
1330 for (
int row = 0; row < rowInfo.count; ++row) {
1331 bool rowIsEmpty =
true;
1332 bool rowIsIdenticalToPrevious = (row > 0);
1334 for (
int column = 0; column < columnInfo.count; ++column) {
1335 QGridLayoutItem *item = itemAt(row, column, orientation);
1337 if (rowIsIdenticalToPrevious && item != itemAt(row - 1, column, orientation))
1338 rowIsIdenticalToPrevious =
false;
1340 if (item && !item->isEmpty())
1344 if ((rowIsEmpty || rowIsIdenticalToPrevious)
1345 && rowInfo.spacings.value(row).isDefault()
1346 && rowInfo.stretches.value(row).isDefault()
1347 && rowInfo.boxes.value(row) == QGridLayoutBox())
1348 rowData->ignore.setBit(row,
true);
1350 if (rowInfo.spacings.value(row).isUser()) {
1351 rowData->spacings[row] = rowInfo.spacings.at(row).value();
1352 }
else if (!defaultSpacing.isDefault()) {
1353 rowData->spacings[row] = defaultSpacing.value();
1356 rowData->stretches[row] = rowInfo.stretches.value(row).value();
1359 struct RowAdHocData {
1361 unsigned int q_hasButtons : 8;
1362 unsigned int q_hasNonButtons : 8;
1364 inline RowAdHocData() : q_row(-1), q_hasButtons(
false), q_hasNonButtons(
false) {}
1365 inline void init(
int row) {
1367 q_hasButtons =
false;
1368 q_hasNonButtons =
false;
1370 inline bool hasOnlyButtons()
const {
return q_hasButtons && !q_hasNonButtons; }
1371 inline bool hasOnlyNonButtons()
const {
return q_hasNonButtons && !q_hasButtons; }
1373 RowAdHocData lastRowAdHocData;
1374 RowAdHocData nextToLastRowAdHocData;
1375 RowAdHocData nextToNextToLastRowAdHocData;
1377 rowData->hasIgnoreFlag =
false;
1378 for (
int row = 0; row < rowInfo.count; ++row) {
1379 if (rowData->ignore.testBit(row))
1382 QGridLayoutBox &rowBox = rowData->boxes[row];
1383 if (styleInfo->isWindow()) {
1384 nextToNextToLastRowAdHocData = nextToLastRowAdHocData;
1385 nextToLastRowAdHocData = lastRowAdHocData;
1386 lastRowAdHocData.init(row);
1389 bool userRowStretch = rowInfo.stretches.value(row).isUser();
1390 int &rowStretch = rowData->stretches[row];
1392 bool hasIgnoreFlag =
true;
1393 for (
int column = 0; column < columnInfo.count; ++column) {
1394 QGridLayoutItem *item = itemAt(row, column, orientation);
1396 int itemRow = item->firstRow(orientation);
1397 int itemColumn = item->firstColumn(orientation);
1399 if (itemRow == row && itemColumn == column) {
1400 int itemStretch = item->stretchFactor(orientation);
1401 if (!(item->sizePolicy(orientation) & QLayoutPolicy::IgnoreFlag))
1402 hasIgnoreFlag =
false;
1403 int itemRowSpan = item->rowSpan(orientation);
1405 int effectiveRowSpan = 1;
1406 for (
int i = 1; i < itemRowSpan; ++i) {
1407 if (!rowData->ignore.testBit(i + itemRow))
1411 QGridLayoutBox *box;
1412 if (effectiveRowSpan == 1) {
1414 if (!userRowStretch && itemStretch != 0)
1415 rowStretch = qMax(rowStretch, itemStretch);
1417 QGridLayoutMultiCellData &multiCell =
1418 rowData->multiCellMap[std::pair(row, itemRowSpan)];
1419 box = &multiCell.q_box;
1420 multiCell.q_stretch = itemStretch;
1423 if (colSizes && colPositions && item->hasDynamicConstraint() && orientation == item->dynamicConstraintOrientation()) {
1425
1426
1427
1428
1429
1430 qreal length = colSizes[item->lastColumn(orientation)];
1431 if (item->columnSpan(orientation) != 1)
1432 length += colPositions[item->lastColumn(orientation)] - colPositions[item->firstColumn(orientation)];
1433 box->combine(item->box(orientation, m_snapToPixelGrid, length));
1435 box->combine(item->box(orientation, m_snapToPixelGrid));
1438 if (effectiveRowSpan == 1) {
1439 QLayoutPolicy::ControlTypes controls = item->controlTypes(top);
1440 if (controls & ButtonMask)
1441 lastRowAdHocData.q_hasButtons =
true;
1442 if (controls & ~ButtonMask)
1443 lastRowAdHocData.q_hasNonButtons =
true;
1448 if (row < rowInfo.boxes.size()) {
1449 QGridLayoutBox rowBoxInfo = rowInfo.boxes.at(row);
1450 rowBoxInfo.normalize();
1451 rowBox.q_minimumSize = qMax(rowBox.q_minimumSize, rowBoxInfo.q_minimumSize);
1452 rowBox.q_maximumSize = qMax(rowBox.q_minimumSize,
1453 (rowBoxInfo.q_maximumSize != FLT_MAX ?
1454 rowBoxInfo.q_maximumSize : rowBox.q_maximumSize));
1455 rowBox.q_preferredSize = qBound(rowBox.q_minimumSize,
1456 qMax(rowBox.q_preferredSize, rowBoxInfo.q_preferredSize),
1457 rowBox.q_maximumSize);
1460 rowData->hasIgnoreFlag =
true;
1464
1465
1466
1467 bool lastRowIsButtonBox = (lastRowAdHocData.hasOnlyButtons()
1468 && nextToLastRowAdHocData.hasOnlyNonButtons());
1469 bool lastTwoRowsIsButtonBox = (lastRowAdHocData.hasOnlyButtons()
1470 && nextToLastRowAdHocData.hasOnlyButtons()
1471 && nextToNextToLastRowAdHocData.hasOnlyNonButtons()
1472 && orientation == Qt::Vertical);
1474 if (defaultSpacing.isDefault()) {
1476 for (
int row = 0; row < rowInfo.count; ++row) {
1477 if (rowData->ignore.testBit(row))
1480 if (prevRow != -1 && !rowInfo.spacings.value(prevRow).isUser()) {
1481 qreal &rowSpacing = rowData->spacings[prevRow];
1482 for (
int column = 0; column < columnInfo.count; ++column) {
1483 QGridLayoutItem *item1 = itemAt(prevRow, column, orientation);
1484 QGridLayoutItem *item2 = itemAt(row, column, orientation);
1486 if (item1 && item2 && item1 != item2) {
1487 QLayoutPolicy::ControlTypes controls1 = item1->controlTypes(bottom);
1488 QLayoutPolicy::ControlTypes controls2 = item2->controlTypes(top);
1490 if (controls2 & QLayoutPolicy::PushButton) {
1491 if ((row == nextToLastRowAdHocData.q_row && lastTwoRowsIsButtonBox)
1492 || (row == lastRowAdHocData.q_row && lastRowIsButtonBox)) {
1493 controls2 &= ~QLayoutPolicy::PushButton;
1494 controls2 |= QLayoutPolicy::ButtonBox;
1498 qreal spacing = styleInfo->combinedLayoutSpacing(controls1, controls2,
1500 if (orientation == Qt::Horizontal) {
1501 qreal width1 = rowData->boxes.at(prevRow).q_minimumSize;
1502 qreal width2 = rowData->boxes.at(row).q_minimumSize;
1503 QRectF rect1 = item1->geometryWithin(0.0, 0.0, width1, FLT_MAX, -1.0, effectiveAlignment(item1), m_snapToPixelGrid);
1504 QRectF rect2 = item2->geometryWithin(0.0, 0.0, width2, FLT_MAX, -1.0, effectiveAlignment(item2), m_snapToPixelGrid);
1505 spacing -= (width1 - (rect1.x() + rect1.width())) + rect2.x();
1507 const QGridLayoutBox &box1 = rowData->boxes.at(prevRow);
1508 const QGridLayoutBox &box2 = rowData->boxes.at(row);
1509 qreal height1 = box1.q_minimumSize;
1510 qreal height2 = box2.q_minimumSize;
1511 qreal rowDescent1 = fixedDescent(box1.q_minimumDescent,
1512 box1.q_minimumAscent, height1);
1513 qreal rowDescent2 = fixedDescent(box2.q_minimumDescent,
1514 box2.q_minimumAscent, height2);
1515 QRectF rect1 = item1->geometryWithin(0.0, 0.0, FLT_MAX, height1,
1516 rowDescent1, effectiveAlignment(item1), m_snapToPixelGrid);
1517 QRectF rect2 = item2->geometryWithin(0.0, 0.0, FLT_MAX, height2,
1518 rowDescent2, effectiveAlignment(item2), m_snapToPixelGrid);
1519 spacing -= (height1 - (rect1.y() + rect1.height())) + rect2.y();
1521 rowSpacing = qMax(spacing, rowSpacing);
1527 }
else if (lastRowIsButtonBox || lastTwoRowsIsButtonBox) {
1529
1530
1531
1532
1533 int prevRow = lastRowIsButtonBox ? nextToLastRowAdHocData.q_row
1534 : nextToNextToLastRowAdHocData.q_row;
1535 if (!defaultSpacing.isUser() && !rowInfo.spacings.value(prevRow).isUser()) {
1536 qreal windowMargin = styleInfo->windowMargin(orientation);
1537 qreal &rowSpacing = rowData->spacings[prevRow];
1538 rowSpacing = qMax(windowMargin, rowSpacing);
1542 if (rowData->boxes.size() > 1 &&
1543 ((orientation == Qt::Horizontal && m_uniformCellWidths) ||
1544 (orientation == Qt::Vertical && m_uniformCellHeights))) {
1545 qreal averagePreferredSize = 0.;
1546 qreal minimumMaximumSize = std::numeric_limits<qreal>::max();
1547 qreal maximumMinimumSize = 0.;
1548 for (
const auto &box : rowData->boxes) {
1549 averagePreferredSize += box.q_preferredSize;
1550 minimumMaximumSize = qMin(minimumMaximumSize, box.q_maximumSize);
1551 maximumMinimumSize = qMax(maximumMinimumSize, box.q_minimumSize);
1553 averagePreferredSize /= rowData->boxes.size();
1554 minimumMaximumSize = qMax(minimumMaximumSize, maximumMinimumSize);
1555 averagePreferredSize = qBound(maximumMinimumSize, averagePreferredSize, minimumMaximumSize);
1556 for (
auto &box : rowData->boxes) {
1557 box.q_preferredSize = averagePreferredSize;
1558 box.q_minimumSize = maximumMinimumSize;
1559 box.q_maximumSize = minimumMaximumSize;
1564void QGridLayoutEngine::ensureEffectiveFirstAndLastRows()
const
1566 if (q_cachedEffectiveFirstRows[Qt::Horizontal] == -1 && !q_items.isEmpty()) {
1567 int rowCount =
this->rowCount();
1568 int columnCount =
this->columnCount();
1570 q_cachedEffectiveFirstRows = {columnCount, rowCount};
1571 q_cachedEffectiveLastRows = {-1, -1};
1573 for (
int i = q_items.size() - 1; i >= 0; --i) {
1574 const QGridLayoutItem *item = q_items.at(i);
1576 for (Qt::Orientation o : {Qt::Horizontal, Qt::Vertical}) {
1577 if (item->firstRow(o) < q_cachedEffectiveFirstRows[o])
1578 q_cachedEffectiveFirstRows[o] = item->firstRow(o);
1579 if (item->lastRow(o) > q_cachedEffectiveLastRows[o])
1580 q_cachedEffectiveLastRows[o] = item->lastRow(o);
1586void QGridLayoutEngine::ensureColumnAndRowData(QGridLayoutRowData *rowData, QGridLayoutBox *totalBox,
1587 const qreal *colPositions,
const qreal *colSizes,
1588 Qt::Orientation orientation,
1589 const QAbstractLayoutStyleInfo *styleInfo)
const
1591 const int cc = columnCount(orientation);
1593 const qreal constraint = (colPositions && colSizes && hasDynamicConstraint()) ? (colPositions[cc - 1] + colSizes[cc - 1]) : qreal(CachedWithNoConstraint);
1594 qreal &cachedConstraint = q_totalBoxCachedConstraints[orientation];
1595 if (cachedConstraint == constraint) {
1596 if (totalBox != &q_totalBoxes[orientation])
1597 *totalBox = q_totalBoxes[orientation];
1600 rowData->reset(rowCount(orientation));
1601 fillRowData(rowData, colPositions, colSizes, orientation, styleInfo);
1602 const QGridLayoutRowInfo &rowInfo = q_infos[orientation];
1603 rowData->distributeMultiCells(rowInfo, m_snapToPixelGrid);
1604 *totalBox = rowData->totalBox(0, rowCount(orientation));
1606 if (totalBox != &q_totalBoxes[orientation])
1607 q_totalBoxes[orientation] = *totalBox;
1609 cachedConstraint = constraint;
1613
1614
1615
1616bool QGridLayoutEngine::ensureDynamicConstraint()
const
1618 if (q_cachedConstraintOrientation == UnknownConstraint) {
1619 for (
int i = q_items.size() - 1; i >= 0; --i) {
1620 QGridLayoutItem *item = q_items.at(i);
1621 if (item->hasDynamicConstraint()) {
1622 Qt::Orientation itemConstraintOrientation = item->dynamicConstraintOrientation();
1623 if (q_cachedConstraintOrientation == UnknownConstraint) {
1624 q_cachedConstraintOrientation = itemConstraintOrientation;
1625 }
else if (q_cachedConstraintOrientation != itemConstraintOrientation) {
1626 q_cachedConstraintOrientation = UnfeasibleConstraint;
1627 qWarning(
"QGridLayoutEngine: Unfeasible, cannot mix horizontal and"
1628 " vertical constraint in the same layout");
1633 if (q_cachedConstraintOrientation == UnknownConstraint)
1634 q_cachedConstraintOrientation = NoConstraint;
1639bool QGridLayoutEngine::hasDynamicConstraint()
const
1641 if (!ensureDynamicConstraint())
1643 return q_cachedConstraintOrientation != NoConstraint;
1647
1648
1649Qt::Orientation QGridLayoutEngine::constraintOrientation()
const
1651 (
void)ensureDynamicConstraint();
1652 return (Qt::Orientation)q_cachedConstraintOrientation;
1655void QGridLayoutEngine::ensureGeometries(
const QSizeF &size,
1656 const QAbstractLayoutStyleInfo *styleInfo)
const
1658 if (q_cachedSize == size)
1661 q_cachedSize = size;
1663 q_xx.resize(columnCount());
1664 q_widths.resize(columnCount());
1665 q_yy.resize(rowCount());
1666 q_heights.resize(rowCount());
1667 q_descents.resize(rowCount());
1669 if (constraintOrientation() != Qt::Horizontal) {
1671 ensureColumnAndRowData(&q_columnData, &q_totalBoxes[Qt::Horizontal],
nullptr,
nullptr, Qt::Horizontal, styleInfo);
1674 q_columnData.calculateGeometries(0, columnCount(), size.width(), q_xx.data(), q_widths.data(),
1675 nullptr, q_totalBoxes[Qt::Horizontal], q_infos[Qt::Horizontal], m_snapToPixelGrid);
1676 ensureColumnAndRowData(&q_rowData, &q_totalBoxes[Qt::Vertical], q_xx.data(), q_widths.data(), Qt::Vertical, styleInfo);
1678 q_rowData.calculateGeometries(0, rowCount(), size.height(), q_yy.data(), q_heights.data(),
1679 q_descents.data(), q_totalBoxes[Qt::Vertical], q_infos[Qt::Vertical], m_snapToPixelGrid);
1682 ensureColumnAndRowData(&q_rowData, &q_totalBoxes[Qt::Vertical],
nullptr,
nullptr, Qt::Vertical, styleInfo);
1685 q_rowData.calculateGeometries(0, rowCount(), size.height(), q_yy.data(), q_heights.data(),
1686 q_descents.data(), q_totalBoxes[Qt::Vertical], q_infos[Qt::Vertical], m_snapToPixelGrid);
1687 ensureColumnAndRowData(&q_columnData, &q_totalBoxes[Qt::Horizontal], q_yy.data(), q_heights.data(), Qt::Horizontal, styleInfo);
1689 q_columnData.calculateGeometries(0, columnCount(), size.width(), q_xx.data(), q_widths.data(),
1690 nullptr, q_totalBoxes[Qt::Horizontal], q_infos[Qt::Horizontal], m_snapToPixelGrid);
friend bool operator==(const QByteArray::FromBase64Result &lhs, const QByteArray::FromBase64Result &rhs) noexcept
Returns true if lhs and rhs are equal, otherwise returns false.
QGridLayoutBox totalBox(int start, int end) const
void stealBox(int start, int end, int which, qreal *positions, qreal *sizes)
void calculateGeometries(int start, int end, qreal targetSize, qreal *positions, qreal *sizes, qreal *descents, const QGridLayoutBox &totalBox, const QGridLayoutRowInfo &rowInfo, bool snapToPixelGrid)
MultiCellMap multiCellMap
void distributeMultiCells(const QGridLayoutRowInfo &rowInfo, bool snapToPixelGrid)
void insertOrRemoveRows(int row, int delta)
static void insertOrRemoveItems(QList< T > &items, int index, int delta)
static qreal growthFactorBelowPreferredSize(qreal desired, qreal sumAvailable, qreal sumDesired)
static void visualRect(QRectF *geom, Qt::LayoutDirection dir, const QRectF &contentsRect)
static qreal fixedDescent(qreal descent, qreal ascent, qreal targetSize)
#define LAYOUTITEMSIZE_MAX
static qreal compare(const QGridLayoutBox &box1, const QGridLayoutBox &box2, int which)