1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qglobal.h"
5
6#include "qgridlayoutengine_p.h"
7#include "qvarlengtharray.h"
8
9#include <QtDebug>
10#include <QtCore/qmath.h>
11
12QT_BEGIN_NAMESPACE
13
14using namespace Qt::StringLiterals;
15
16template<typename T>
17static void insertOrRemoveItems(QList<T> &items, int index, int delta)
18{
19 int count = items.size();
20 if (index < count) {
21 if (delta > 0) {
22 items.insert(index, delta, T());
23 } else if (delta < 0) {
24 items.remove(index, qMin(a: -delta, b: count - index));
25 }
26 }
27}
28
29static qreal growthFactorBelowPreferredSize(qreal desired, qreal sumAvailable, qreal sumDesired)
30{
31 Q_ASSERT(sumDesired != 0.0);
32 return desired * qPow(x: sumAvailable / sumDesired, y: desired / sumDesired);
33}
34
35static qreal fixedDescent(qreal descent, qreal ascent, qreal targetSize)
36{
37 if (descent < 0.0)
38 return -1.0;
39
40 Q_ASSERT(descent >= 0.0);
41 Q_ASSERT(ascent >= 0.0);
42 Q_ASSERT(targetSize >= ascent + descent);
43
44 qreal extra = targetSize - (ascent + descent);
45 return descent + (extra / 2.0);
46}
47
48static qreal compare(const QGridLayoutBox &box1, const QGridLayoutBox &box2, int which)
49{
50 qreal size1 = box1.q_sizes(which);
51 qreal size2 = box2.q_sizes(which);
52
53 if (which == MaximumSize) {
54 return size2 - size1;
55 } else {
56 return size1 - size2;
57 }
58}
59
60void QGridLayoutBox::add(const QGridLayoutBox &other, int stretch, qreal spacing)
61{
62 Q_ASSERT(q_minimumDescent < 0.0);
63
64 q_minimumSize += other.q_minimumSize + spacing;
65 q_preferredSize += other.q_preferredSize + spacing;
66 q_maximumSize += ((stretch == 0) ? other.q_preferredSize : other.q_maximumSize) + spacing;
67}
68
69void QGridLayoutBox::combine(const QGridLayoutBox &other)
70{
71 q_minimumDescent = qMax(a: q_minimumDescent, b: other.q_minimumDescent);
72 q_minimumAscent = qMax(a: q_minimumAscent, b: other.q_minimumAscent);
73
74 q_minimumSize = qMax(a: q_minimumAscent + q_minimumDescent,
75 b: qMax(a: q_minimumSize, b: other.q_minimumSize));
76 qreal maxMax;
77 if (q_maximumSize == FLT_MAX && other.q_maximumSize != FLT_MAX)
78 maxMax = other.q_maximumSize;
79 else if (other.q_maximumSize == FLT_MAX && q_maximumSize != FLT_MAX)
80 maxMax = q_maximumSize;
81 else
82 maxMax = qMax(a: q_maximumSize, b: other.q_maximumSize);
83
84 q_maximumSize = qMax(a: q_minimumSize, b: maxMax);
85 q_preferredSize = qBound(min: q_minimumSize, val: qMax(a: q_preferredSize, b: other.q_preferredSize),
86 max: q_maximumSize);
87}
88
89void QGridLayoutBox::normalize()
90{
91 q_maximumSize = qMax(a: qreal(0.0), b: q_maximumSize);
92 q_minimumSize = qBound(min: qreal(0.0), val: q_minimumSize, max: q_maximumSize);
93 q_preferredSize = qBound(min: q_minimumSize, val: q_preferredSize, max: q_maximumSize);
94 q_minimumDescent = qMin(a: q_minimumDescent, b: q_minimumSize);
95
96 Q_ASSERT((q_minimumDescent < 0.0) == (q_minimumAscent < 0.0));
97}
98
99#ifdef QGRIDLAYOUTENGINE_DEBUG
100void QGridLayoutBox::dump(int indent) const
101{
102 qDebug("%*sBox (%g <= %g <= %g [%g/%g])", indent, "", q_minimumSize, q_preferredSize,
103 q_maximumSize, q_minimumAscent, q_minimumDescent);
104}
105#endif
106
107bool operator==(const QGridLayoutBox &box1, const QGridLayoutBox &box2)
108{
109 for (int i = 0; i < NSizes; ++i) {
110 if (box1.q_sizes(which: i) != box2.q_sizes(which: i))
111 return false;
112 }
113 return box1.q_minimumDescent == box2.q_minimumDescent
114 && box1.q_minimumAscent == box2.q_minimumAscent;
115}
116
117void QGridLayoutRowData::reset(int count)
118{
119 ignore.fill(aval: false, asize: count);
120 boxes.fill(t: QGridLayoutBox(), newSize: count);
121 multiCellMap.clear();
122 stretches.fill(t: 0, newSize: count);
123 spacings.fill(t: 0.0, newSize: count);
124 hasIgnoreFlag = false;
125}
126
127void QGridLayoutRowData::distributeMultiCells(const QGridLayoutRowInfo &rowInfo, bool snapToPixelGrid)
128{
129 MultiCellMap::const_iterator i = multiCellMap.constBegin();
130 for (; i != multiCellMap.constEnd(); ++i) {
131 int start = i.key().first;
132 int span = i.key().second;
133 int end = start + span;
134 const QGridLayoutBox &box = i.value().q_box;
135 int stretch = i.value().q_stretch;
136
137 QGridLayoutBox totalBox = this->totalBox(start, end);
138 QVarLengthArray<QGridLayoutBox> extras(span);
139 QVarLengthArray<qreal> dummy(span);
140 QVarLengthArray<qreal> newSizes(span);
141
142 for (int j = 0; j < NSizes; ++j) {
143 qreal extra = compare(box1: box, box2: totalBox, which: j);
144 if (extra > 0.0) {
145 calculateGeometries(start, end, targetSize: box.q_sizes(which: j), positions: dummy.data(), sizes: newSizes.data(),
146 descents: nullptr, totalBox, rowInfo, snapToPixelGrid);
147
148 for (int k = 0; k < span; ++k)
149 extras[k].q_sizes(which: j) = newSizes[k];
150 }
151 }
152
153 for (int k = 0; k < span; ++k) {
154 boxes[start + k].combine(other: extras[k]);
155 if (stretch != 0)
156 stretches[start + k] = qMax(a: stretches[start + k], b: stretch);
157 }
158 }
159 multiCellMap.clear();
160}
161namespace {
162
163// does not return int
164static inline qreal qround(qreal f)
165{
166 return std::floor(x: f + qreal(0.5));
167}
168
169}
170void QGridLayoutRowData::calculateGeometries(int start, int end, qreal targetSize, qreal *positions,
171 qreal *sizes, qreal *descents,
172 const QGridLayoutBox &totalBox,
173 const QGridLayoutRowInfo &rowInfo, bool snapToPixelGrid)
174{
175 Q_ASSERT(end > start);
176
177 targetSize = qMax(a: totalBox.q_minimumSize, b: targetSize);
178
179 int n = end - start;
180 QVarLengthArray<qreal> newSizes(n);
181 QVarLengthArray<qreal> factors(n);
182 qreal sumFactors = 0.0;
183 int sumStretches = 0;
184 qreal sumAvailable;
185
186 for (int i = 0; i < n; ++i) {
187 const int stretch = stretches.at(i: start + i);
188 if (stretch > 0)
189 sumStretches += stretch;
190 }
191
192 if (targetSize < totalBox.q_preferredSize) {
193 stealBox(start, end, which: MinimumSize, positions, sizes);
194
195 sumAvailable = targetSize - totalBox.q_minimumSize;
196 if (sumAvailable > 0.0) {
197 qreal sumDesired = totalBox.q_preferredSize - totalBox.q_minimumSize;
198
199 for (int i = 0; i < n; ++i) {
200 if (ignore.testBit(i: start + i)) {
201 factors[i] = 0.0;
202 continue;
203 }
204
205 const QGridLayoutBox &box = boxes.at(i: start + i);
206 qreal desired = box.q_preferredSize - box.q_minimumSize;
207 factors[i] = growthFactorBelowPreferredSize(desired, sumAvailable, sumDesired);
208 sumFactors += factors[i];
209 }
210
211 for (int i = 0; i < n; ++i) {
212 Q_ASSERT(sumFactors > 0.0);
213 qreal delta = sumAvailable * factors[i] / sumFactors;
214 newSizes[i] = sizes[i] + delta;
215 }
216 }
217 } else {
218 bool isLargerThanMaximum = (targetSize > totalBox.q_maximumSize);
219 if (isLargerThanMaximum) {
220 stealBox(start, end, which: MaximumSize, positions, sizes);
221 sumAvailable = targetSize - totalBox.q_maximumSize;
222 } else {
223 stealBox(start, end, which: PreferredSize, positions, sizes);
224 sumAvailable = targetSize - totalBox.q_preferredSize;
225 }
226
227 if (sumAvailable > 0.0) {
228 qreal sumCurrentAvailable = sumAvailable;
229 bool somethingHasAMaximumSize = false;
230
231 qreal sumSizes = 0.0;
232 for (int i = 0; i < n; ++i)
233 sumSizes += sizes[i];
234
235 for (int i = 0; i < n; ++i) {
236 if (ignore.testBit(i: start + i)) {
237 newSizes[i] = 0.0;
238 factors[i] = 0.0;
239 continue;
240 }
241
242 const QGridLayoutBox &box = boxes.at(i: start + i);
243 qreal boxSize;
244
245 qreal desired;
246 if (isLargerThanMaximum) {
247 boxSize = box.q_maximumSize;
248 desired = rowInfo.boxes.value(i: start + i).q_maximumSize - boxSize;
249 } else {
250 boxSize = box.q_preferredSize;
251 desired = box.q_maximumSize - boxSize;
252 }
253 if (desired == 0.0) {
254 newSizes[i] = sizes[i];
255 factors[i] = 0.0;
256 } else {
257 Q_ASSERT(desired > 0.0);
258
259 int stretch = stretches[start + i];
260 if (sumStretches == 0) {
261 if (hasIgnoreFlag || sizes[i] == 0.0) {
262 factors[i] = (stretch < 0) ? 1.0 : 0.0;
263 } else {
264 factors[i] = (stretch < 0) ? sizes[i] : 0.0;
265 }
266 } else if (stretch == sumStretches) {
267 factors[i] = 1.0;
268 } else if (stretch <= 0) {
269 factors[i] = 0.0;
270 } else {
271 qreal ultimateSize;
272 qreal ultimateSumSizes;
273 qreal x = ((stretch * sumSizes)
274 - (sumStretches * boxSize))
275 / (sumStretches - stretch);
276 if (x >= 0.0) {
277 ultimateSize = boxSize + x;
278 ultimateSumSizes = sumSizes + x;
279 } else {
280 ultimateSize = boxSize;
281 ultimateSumSizes = (sumStretches * boxSize)
282 / stretch;
283 }
284
285 /*
286 We multiply these by 1.5 to give some space for a smooth transition
287 (at the expense of the stretch factors, which are not fully respected
288 during the transition).
289 */
290 ultimateSize = ultimateSize * 3 / 2;
291 ultimateSumSizes = ultimateSumSizes * 3 / 2;
292
293 qreal beta = ultimateSumSizes - sumSizes;
294 if (!beta) {
295 factors[i] = 1;
296 } else {
297 qreal alpha = qMin(a: sumCurrentAvailable, b: beta);
298 qreal ultimateFactor = (stretch * ultimateSumSizes / sumStretches)
299 - (boxSize);
300 qreal transitionalFactor = sumCurrentAvailable * (ultimateSize - boxSize) / beta;
301
302 factors[i] = ((alpha * ultimateFactor)
303 + ((beta - alpha) * transitionalFactor)) / beta;
304 }
305
306 }
307 sumFactors += factors[i];
308 if (desired < sumCurrentAvailable)
309 somethingHasAMaximumSize = true;
310
311 newSizes[i] = -1.0;
312 }
313 }
314
315 bool keepGoing = somethingHasAMaximumSize;
316 while (keepGoing) {
317 //sumCurrentAvailable is so large that something *might* reach its maximum size
318 keepGoing = false;
319
320 for (int i = 0; i < n; ++i) {
321 if (newSizes[i] >= 0.0)
322 continue;
323
324 const QList<QGridLayoutBox> &rBoxes = isLargerThanMaximum ? rowInfo.boxes : boxes;
325 const QGridLayoutBox &box = rBoxes.value(i: start + i);
326 qreal maxBoxSize = box.q_maximumSize;
327
328 if (snapToPixelGrid)
329 maxBoxSize = qMax(a: box.q_minimumSize, b: std::floor(x: maxBoxSize));
330
331 qreal avail = sumCurrentAvailable * factors[i] / sumFactors;
332 if (sizes[i] + avail >= maxBoxSize) {
333 newSizes[i] = maxBoxSize;
334 sumCurrentAvailable -= maxBoxSize - sizes[i];
335 sumFactors -= factors[i];
336 keepGoing = (sumCurrentAvailable > 0.0);
337 if (!keepGoing)
338 break;
339 }
340 }
341 }
342 for (int i = 0; i < n; ++i) {
343 if (newSizes[i] < 0.0) {
344 qreal delta = (sumFactors == 0.0) ? 0.0
345 : sumCurrentAvailable * factors[i] / sumFactors;
346 newSizes[i] = sizes[i] + delta;
347 }
348 }
349 }
350 }
351
352 if (sumAvailable > 0) {
353 qreal offset = 0;
354 for (int i = 0; i < n; ++i) {
355 qreal delta = newSizes[i] - sizes[i];
356 positions[i] += offset;
357 sizes[i] += delta;
358 offset += delta;
359 }
360
361#if 0 // some "pixel allocation"
362 int surplus = targetSize - (positions[n - 1] + sizes[n - 1]);
363 Q_ASSERT(surplus >= 0 && surplus <= n);
364
365 int prevSurplus = -1;
366 while (surplus > 0 && surplus != prevSurplus) {
367 prevSurplus = surplus;
368
369 int offset = 0;
370 for (int i = 0; i < n; ++i) {
371 const QGridLayoutBox &box = boxes.at(start + i);
372 int delta = (!ignore.testBit(start + i) && surplus > 0
373 && factors[i] > 0 && sizes[i] < box.q_maximumSize)
374 ? 1 : 0;
375
376 positions[i] += offset;
377 sizes[i] += delta;
378 offset += delta;
379 surplus -= delta;
380 }
381 }
382 Q_ASSERT(surplus == 0);
383#endif
384 }
385 if (snapToPixelGrid) {
386 for (int i = 0; i < n; ++i) {
387 const qreal oldpos = positions[i];
388 positions[i] = qround(f: oldpos);
389 const qreal delta = positions[i] - oldpos;
390 sizes[i] -= delta;
391 if (i > 0)
392 sizes[i - 1] += delta;
393 }
394
395 sizes[n - 1] = targetSize - positions[n - 1];
396 // This loop serves two purposes:
397 // 1. round off the small epsilons produced by the above loop.
398 // 2. avoid that the above loop didn't make the cell width smaller than its minimum constraint.
399 for (int i = 0; i < n; ++i) {
400 const QGridLayoutBox &box = boxes.at(i: start + i);
401 sizes[i] = qMax(a: box.q_minimumSize, b: qround(f: sizes[i]));
402 }
403 }
404
405 if (descents) {
406 for (int i = 0; i < n; ++i) {
407 if (ignore.testBit(i: start + i))
408 continue;
409 const QGridLayoutBox &box = boxes.at(i: start + i);
410 descents[i] = fixedDescent(descent: box.q_minimumDescent, ascent: box.q_minimumAscent, targetSize: sizes[i]);
411 }
412 }
413}
414
415QGridLayoutBox QGridLayoutRowData::totalBox(int start, int end) const
416{
417 QGridLayoutBox result;
418 if (start < end) {
419 result.q_maximumSize = 0.0;
420 qreal nextSpacing = 0.0;
421 for (int i = start; i < end; ++i) {
422 if (ignore.testBit(i))
423 continue;
424 result.add(other: boxes.at(i), stretch: stretches.at(i), spacing: nextSpacing);
425 nextSpacing = spacings.at(i);
426 }
427 }
428 return result;
429}
430
431void QGridLayoutRowData::stealBox(int start, int end, int which, qreal *positions, qreal *sizes)
432{
433 qreal offset = 0.0;
434 qreal nextSpacing = 0.0;
435
436 for (int i = start; i < end; ++i) {
437 qreal avail = 0.0;
438
439 if (!ignore.testBit(i)) {
440 const QGridLayoutBox &box = boxes.at(i);
441 avail = box.q_sizes(which);
442 offset += nextSpacing;
443 nextSpacing = spacings.at(i);
444 }
445
446 *positions++ = offset;
447 *sizes++ = avail;
448 offset += avail;
449 }
450}
451
452#ifdef QGRIDLAYOUTENGINE_DEBUG
453void QGridLayoutRowData::dump(int indent) const
454{
455 qDebug("%*sData", indent, "");
456
457 for (int i = 0; i < ignore.count(); ++i) {
458 qDebug("%*s Row %d (stretch %d, spacing %g)", indent, "", i, stretches.at(i),
459 spacings.at(i));
460 if (ignore.testBit(i))
461 qDebug("%*s Ignored", indent, "");
462 boxes.at(i).dump(indent + 2);
463 }
464
465 MultiCellMap::const_iterator it = multiCellMap.constBegin();
466 while (it != multiCellMap.constEnd()) {
467 qDebug("%*s Multi-cell entry <%d, %d> (stretch %d)", indent, "", it.key().first,
468 it.key().second, it.value().q_stretch);
469 it.value().q_box.dump(indent + 2);
470 }
471}
472#endif
473
474QGridLayoutItem::QGridLayoutItem(int row, int column, int rowSpan, int columnSpan,
475 Qt::Alignment alignment)
476 : q_firstRows{column, row},
477 q_rowSpans{columnSpan, rowSpan},
478 q_stretches{-1, -1},
479 q_alignment(alignment)
480{
481}
482
483int QGridLayoutItem::firstRow(Qt::Orientation orientation) const
484{
485 return q_firstRows[orientation];
486}
487
488int QGridLayoutItem::firstColumn(Qt::Orientation orientation) const
489{
490 return q_firstRows.transposed()[orientation];
491}
492
493int QGridLayoutItem::lastRow(Qt::Orientation orientation) const
494{
495 return firstRow(orientation) + rowSpan(orientation) - 1;
496}
497
498int QGridLayoutItem::lastColumn(Qt::Orientation orientation) const
499{
500 return firstColumn(orientation) + columnSpan(orientation) - 1;
501}
502
503int QGridLayoutItem::rowSpan(Qt::Orientation orientation) const
504{
505 return q_rowSpans[orientation];
506}
507
508int QGridLayoutItem::columnSpan(Qt::Orientation orientation) const
509{
510 return q_rowSpans.transposed()[orientation];
511}
512
513void QGridLayoutItem::setFirstRow(int row, Qt::Orientation orientation)
514{
515 q_firstRows[orientation] = row;
516}
517
518void QGridLayoutItem::setRowSpan(int rowSpan, Qt::Orientation orientation)
519{
520 q_rowSpans[orientation] = rowSpan;
521}
522
523int QGridLayoutItem::stretchFactor(Qt::Orientation orientation) const
524{
525 int stretch = q_stretches[orientation];
526 if (stretch >= 0)
527 return stretch;
528
529 QLayoutPolicy::Policy policy = sizePolicy(orientation);
530
531 if (policy & QLayoutPolicy::ExpandFlag) {
532 return 1;
533 } else if (policy & QLayoutPolicy::GrowFlag) {
534 return -1; // because we max it up
535 } else {
536 return 0;
537 }
538}
539
540void QGridLayoutItem::setStretchFactor(int stretch, Qt::Orientation orientation)
541{
542 Q_ASSERT(stretch >= 0); // ### deal with too big stretches
543 q_stretches[orientation] = stretch;
544}
545
546QLayoutPolicy::ControlTypes QGridLayoutItem::controlTypes(LayoutSide /*side*/) const
547{
548 return QLayoutPolicy::DefaultType;
549}
550
551QGridLayoutBox QGridLayoutItem::box(Qt::Orientation orientation, bool snapToPixelGrid, qreal constraint) const
552{
553 QGridLayoutBox result;
554 QLayoutPolicy::Policy policy = sizePolicy(orientation);
555
556 if (orientation == Qt::Horizontal) {
557 QSizeF constraintSize(-1.0, constraint);
558
559 result.q_preferredSize = sizeHint(which: Qt::PreferredSize, constraint: constraintSize).width();
560
561 if (policy & QLayoutPolicy::ShrinkFlag) {
562 result.q_minimumSize = sizeHint(which: Qt::MinimumSize, constraint: constraintSize).width();
563 } else {
564 result.q_minimumSize = result.q_preferredSize;
565 }
566 if (snapToPixelGrid)
567 result.q_minimumSize = qCeil(v: result.q_minimumSize);
568
569 if (policy & (QLayoutPolicy::GrowFlag | QLayoutPolicy::ExpandFlag)) {
570 result.q_maximumSize = sizeHint(which: Qt::MaximumSize, constraint: constraintSize).width();
571 } else {
572 result.q_maximumSize = result.q_preferredSize;
573 }
574 } else {
575 QSizeF constraintSize(constraint, -1.0);
576
577 result.q_preferredSize = sizeHint(which: Qt::PreferredSize, constraint: constraintSize).height();
578
579 if (policy & QLayoutPolicy::ShrinkFlag) {
580 result.q_minimumSize = sizeHint(which: Qt::MinimumSize, constraint: constraintSize).height();
581 } else {
582 result.q_minimumSize = result.q_preferredSize;
583 }
584 if (snapToPixelGrid)
585 result.q_minimumSize = qCeil(v: result.q_minimumSize);
586
587 if (policy & (QLayoutPolicy::GrowFlag | QLayoutPolicy::ExpandFlag)) {
588 result.q_maximumSize = sizeHint(which: Qt::MaximumSize, constraint: constraintSize).height();
589 } else {
590 result.q_maximumSize = result.q_preferredSize;
591 }
592
593 if (alignment() & Qt::AlignBaseline) {
594 result.q_minimumDescent = sizeHint(which: Qt::MinimumDescent, constraint: constraintSize).height();
595 if (result.q_minimumDescent != -1.0) {
596 const qreal minSizeHint = sizeHint(which: Qt::MinimumSize, constraint: constraintSize).height();
597 result.q_minimumDescent -= (minSizeHint - result.q_minimumSize);
598 result.q_minimumAscent = result.q_minimumSize - result.q_minimumDescent;
599 }
600 }
601 }
602 if (policy & QLayoutPolicy::IgnoreFlag)
603 result.q_preferredSize = result.q_minimumSize;
604
605 return result;
606}
607
608QRectF QGridLayoutItem::geometryWithin(qreal x, qreal y, qreal width, qreal height,
609 qreal rowDescent, Qt::Alignment align, bool snapToPixelGrid) const
610{
611 const qreal cellWidth = width;
612 const qreal cellHeight = height;
613
614 QSizeF size = effectiveMaxSize(constraint: QSizeF(-1,-1));
615 if (hasDynamicConstraint()) {
616 if (dynamicConstraintOrientation() == Qt::Vertical) {
617 if (size.width() > cellWidth)
618 size = effectiveMaxSize(constraint: QSizeF(cellWidth, -1));
619 } else if (size.height() > cellHeight) {
620 size = effectiveMaxSize(constraint: QSizeF(-1, cellHeight));
621 }
622 }
623 size = size.boundedTo(otherSize: QSizeF(cellWidth, cellHeight));
624 width = size.width();
625 height = size.height();
626
627 switch (align & Qt::AlignHorizontal_Mask) {
628 case Qt::AlignHCenter:
629 x += (cellWidth - width)/2;
630 break;
631 case Qt::AlignRight:
632 x += cellWidth - width;
633 break;
634 default:
635 break;
636 }
637
638 switch (align & Qt::AlignVertical_Mask) {
639 case Qt::AlignVCenter:
640 y += (cellHeight - height)/2;
641 break;
642 case Qt::AlignBottom:
643 y += cellHeight - height;
644 break;
645 case Qt::AlignBaseline: {
646 width = qMin(a: effectiveMaxSize(constraint: QSizeF(-1,-1)).width(), b: width);
647 QGridLayoutBox vBox = box(orientation: Qt::Vertical, snapToPixelGrid);
648 const qreal descent = vBox.q_minimumDescent;
649 const qreal ascent = vBox.q_minimumSize - descent;
650 y += (cellHeight - rowDescent - ascent);
651 height = ascent + descent;
652 break; }
653 default:
654 break;
655 }
656 return QRectF(x, y, width, height);
657}
658
659void QGridLayoutItem::transpose()
660{
661 q_firstRows.transpose();
662 q_rowSpans.transpose();
663 q_stretches.transpose();
664}
665
666void QGridLayoutItem::insertOrRemoveRows(int row, int delta, Qt::Orientation orientation)
667{
668 int oldFirstRow = firstRow(orientation);
669 if (oldFirstRow >= row) {
670 setFirstRow(row: oldFirstRow + delta, orientation);
671 } else if (lastRow(orientation) >= row) {
672 setRowSpan(rowSpan: rowSpan(orientation) + delta, orientation);
673 }
674}
675/*!
676 \internal
677 returns the effective maximumSize, will take the sizepolicy into
678 consideration. (i.e. if sizepolicy does not have QLayoutPolicy::Grow, then
679 maxSizeHint will be the preferredSize)
680 Note that effectiveSizeHint does not take sizePolicy into consideration,
681 (since it only evaluates the hints, as the name implies)
682*/
683QSizeF QGridLayoutItem::effectiveMaxSize(const QSizeF &constraint) const
684{
685 QSizeF size = constraint;
686 bool vGrow = (sizePolicy(orientation: Qt::Vertical) & QLayoutPolicy::GrowFlag) == QLayoutPolicy::GrowFlag;
687 bool hGrow = (sizePolicy(orientation: Qt::Horizontal) & QLayoutPolicy::GrowFlag) == QLayoutPolicy::GrowFlag;
688 if (!vGrow || !hGrow) {
689 QSizeF pref = sizeHint(which: Qt::PreferredSize, constraint);
690 if (!vGrow)
691 size.setHeight(pref.height());
692 if (!hGrow)
693 size.setWidth(pref.width());
694 }
695
696 if (!size.isValid()) {
697 QSizeF maxSize = sizeHint(which: Qt::MaximumSize, constraint: size);
698 if (size.width() == -1)
699 size.setWidth(maxSize.width());
700 if (size.height() == -1)
701 size.setHeight(maxSize.height());
702 }
703 return size;
704}
705
706#ifdef QGRIDLAYOUTENGINE_DEBUG
707void QGridLayoutItem::dump(int indent) const
708{
709 qDebug("%*s (%d, %d) %d x %d", indent, "", firstRow(), firstColumn(), //###
710 rowSpan(), columnSpan());
711
712 if (q_stretches[Qt::Horizontal] >= 0)
713 qDebug("%*s Horizontal stretch: %d", indent, "", q_stretches[Qt::Horizontal]);
714 if (q_stretches[Qt::Vertical] >= 0)
715 qDebug("%*s Vertical stretch: %d", indent, "", q_stretches[Qt::Vertical]);
716 if (q_alignment != 0)
717 qDebug("%*s Alignment: %x", indent, "", uint(q_alignment));
718 qDebug("%*s Horizontal size policy: %x Vertical size policy: %x",
719 indent, "", sizePolicy(Qt::Horizontal), sizePolicy(Qt::Vertical));
720}
721#endif
722
723void QGridLayoutRowInfo::insertOrRemoveRows(int row, int delta)
724{
725 count += delta;
726
727 insertOrRemoveItems(items&: stretches, index: row, delta);
728 insertOrRemoveItems(items&: spacings, index: row, delta);
729 insertOrRemoveItems(items&: alignments, index: row, delta);
730 insertOrRemoveItems(items&: boxes, index: row, delta);
731}
732
733#ifdef QGRIDLAYOUTENGINE_DEBUG
734void QGridLayoutRowInfo::dump(int indent) const
735{
736 qDebug("%*sInfo (count: %d)", indent, "", count);
737 for (int i = 0; i < count; ++i) {
738 QString message;
739
740 if (stretches.value(i).value() >= 0)
741 message += QString::fromLatin1(" stretch %1").arg(stretches.value(i).value());
742 if (spacings.value(i).value() >= 0.0)
743 message += QString::fromLatin1(" spacing %1").arg(spacings.value(i).value());
744 if (alignments.value(i) != 0)
745 message += QString::fromLatin1(" alignment %1").arg(int(alignments.value(i)), 16);
746
747 if (!message.isEmpty() || boxes.value(i) != QGridLayoutBox()) {
748 qDebug("%*s Row %d:%s", indent, "", i, qPrintable(message));
749 if (boxes.value(i) != QGridLayoutBox())
750 boxes.value(i).dump(indent + 1);
751 }
752 }
753}
754#endif
755
756QGridLayoutEngine::QGridLayoutEngine(Qt::Alignment defaultAlignment, bool snapToPixelGrid)
757{
758 m_visualDirection = Qt::LeftToRight;
759 m_defaultAlignment = defaultAlignment;
760 m_snapToPixelGrid = snapToPixelGrid;
761 m_uniformCellWidths = false;
762 m_uniformCellHeights = false;
763 invalidate();
764}
765
766int QGridLayoutEngine::rowCount(Qt::Orientation orientation) const
767{
768 return q_infos[orientation].count;
769}
770
771int QGridLayoutEngine::columnCount(Qt::Orientation orientation) const
772{
773 return q_infos.transposed()[orientation].count;
774}
775
776int QGridLayoutEngine::itemCount() const
777{
778 return q_items.size();
779}
780
781QGridLayoutItem *QGridLayoutEngine::itemAt(int index) const
782{
783 Q_ASSERT(index >= 0 && index < itemCount());
784 return q_items.at(i: index);
785}
786
787int QGridLayoutEngine::effectiveFirstRow(Qt::Orientation orientation) const
788{
789 ensureEffectiveFirstAndLastRows();
790 return q_cachedEffectiveFirstRows[orientation];
791}
792
793int QGridLayoutEngine::effectiveLastRow(Qt::Orientation orientation) const
794{
795 ensureEffectiveFirstAndLastRows();
796 return q_cachedEffectiveLastRows[orientation];
797}
798
799void QGridLayoutEngine::setSpacing(qreal spacing, Qt::Orientations orientations)
800{
801 if (orientations & Qt::Horizontal)
802 q_defaultSpacings[Qt::Horizontal].setUserValue(spacing);
803 if (orientations & Qt::Vertical)
804 q_defaultSpacings[Qt::Vertical].setUserValue(spacing);
805
806 invalidate();
807}
808
809qreal QGridLayoutEngine::spacing(Qt::Orientation orientation, const QAbstractLayoutStyleInfo *styleInfo) const
810{
811 if (!q_defaultSpacings[orientation].isUser()) {
812 qreal defaultSpacing = styleInfo->spacing(orientation);
813 q_defaultSpacings[orientation].setCachedValue(defaultSpacing);
814 }
815 return q_defaultSpacings[orientation].value();
816}
817
818void QGridLayoutEngine::setRowSpacing(int row, qreal spacing, Qt::Orientation orientation)
819{
820 Q_ASSERT(row >= 0);
821
822 QGridLayoutRowInfo &rowInfo = q_infos[orientation];
823 if (row >= rowInfo.spacings.size())
824 rowInfo.spacings.resize(size: row + 1);
825 if (spacing >= 0)
826 rowInfo.spacings[row].setUserValue(spacing);
827 else
828 rowInfo.spacings[row] = QLayoutParameter<qreal>();
829 invalidate();
830}
831
832qreal QGridLayoutEngine::rowSpacing(int row, Qt::Orientation orientation) const
833{
834 QLayoutParameter<qreal> spacing = q_infos[orientation].spacings.value(i: row);
835 if (!spacing.isDefault())
836 return spacing.value();
837 return q_defaultSpacings[orientation].value();
838}
839
840void QGridLayoutEngine::setRowStretchFactor(int row, int stretch, Qt::Orientation orientation)
841{
842 Q_ASSERT(row >= 0);
843 Q_ASSERT(stretch >= 0);
844
845 maybeExpandGrid(row, column: -1, orientation);
846
847 QGridLayoutRowInfo &rowInfo = q_infos[orientation];
848 if (row >= rowInfo.stretches.size())
849 rowInfo.stretches.resize(size: row + 1);
850 rowInfo.stretches[row].setUserValue(stretch);
851}
852
853int QGridLayoutEngine::rowStretchFactor(int row, Qt::Orientation orientation) const
854{
855 QStretchParameter stretch = q_infos[orientation].stretches.value(i: row);
856 if (!stretch.isDefault())
857 return stretch.value();
858 return 0;
859}
860
861void QGridLayoutEngine::setRowSizeHint(Qt::SizeHint which, int row, qreal size,
862 Qt::Orientation orientation)
863{
864 Q_ASSERT(row >= 0);
865 Q_ASSERT(size >= 0.0);
866
867 maybeExpandGrid(row, column: -1, orientation);
868
869 QGridLayoutRowInfo &rowInfo = q_infos[orientation];
870 if (row >= rowInfo.boxes.size())
871 rowInfo.boxes.resize(size: row + 1);
872 rowInfo.boxes[row].q_sizes(which) = size;
873}
874
875qreal QGridLayoutEngine::rowSizeHint(Qt::SizeHint which, int row, Qt::Orientation orientation) const
876{
877 return q_infos[orientation].boxes.value(i: row).q_sizes(which);
878}
879
880bool QGridLayoutEngine::uniformCellWidths() const
881{
882 return m_uniformCellWidths;
883}
884
885void QGridLayoutEngine::setUniformCellWidths(bool uniformCellWidths)
886{
887 if (m_uniformCellWidths == uniformCellWidths)
888 return;
889
890 m_uniformCellWidths = uniformCellWidths;
891 invalidate();
892}
893
894bool QGridLayoutEngine::uniformCellHeights() const
895{
896 return m_uniformCellHeights;
897}
898
899void QGridLayoutEngine::setUniformCellHeights(bool uniformCellHeights)
900{
901 if (m_uniformCellHeights == uniformCellHeights)
902 return;
903
904 m_uniformCellHeights = uniformCellHeights;
905 invalidate();
906}
907
908void QGridLayoutEngine::setRowAlignment(int row, Qt::Alignment alignment,
909 Qt::Orientation orientation)
910{
911 Q_ASSERT(row >= 0);
912
913 maybeExpandGrid(row, column: -1, orientation);
914
915 QGridLayoutRowInfo &rowInfo = q_infos[orientation];
916 if (row >= rowInfo.alignments.size())
917 rowInfo.alignments.resize(size: row + 1);
918 rowInfo.alignments[row] = alignment;
919}
920
921Qt::Alignment QGridLayoutEngine::rowAlignment(int row, Qt::Orientation orientation) const
922{
923 Q_ASSERT(row >= 0);
924 return q_infos[orientation].alignments.value(i: row);
925}
926
927Qt::Alignment QGridLayoutEngine::effectiveAlignment(const QGridLayoutItem *layoutItem) const
928{
929 Qt::Alignment align = layoutItem->alignment();
930 if (!(align & Qt::AlignVertical_Mask)) {
931 // no vertical alignment, respect the row alignment
932 int y = layoutItem->firstRow();
933 align |= (rowAlignment(row: y, orientation: Qt::Vertical) & Qt::AlignVertical_Mask);
934 if (!(align & Qt::AlignVertical_Mask))
935 align |= (m_defaultAlignment & Qt::AlignVertical_Mask);
936 }
937 if (!(align & Qt::AlignHorizontal_Mask)) {
938 // no horizontal alignment, respect the column alignment
939 int x = layoutItem->firstColumn();
940 align |= (rowAlignment(row: x, orientation: Qt::Horizontal) & Qt::AlignHorizontal_Mask);
941 }
942
943 return align;
944}
945
946/*!
947 \internal
948 The \a index is only used by QGraphicsLinearLayout to ensure that itemAt() reflects the order
949 of visual arrangement. Strictly speaking it does not have to, but most people expect it to.
950 (And if it didn't we would have to add itemArrangedAt(int index) or something..)
951 */
952void QGridLayoutEngine::insertItem(QGridLayoutItem *item, int index)
953{
954 maybeExpandGrid(row: item->lastRow(), column: item->lastColumn());
955
956 if (index < 0 || index >= q_items.size())
957 q_items.append(t: item);
958 else
959 q_items.insert(i: index, t: item);
960
961 for (int i = item->firstRow(); i <= item->lastRow(); ++i) {
962 for (int j = item->firstColumn(); j <= item->lastColumn(); ++j) {
963 if (itemAt(row: i, column: j))
964 qWarning(msg: "QGridLayoutEngine::addItem: Cell (%d, %d) already taken", i, j);
965 setItemAt(row: i, column: j, item);
966 }
967 }
968}
969
970void QGridLayoutEngine::addItem(QGridLayoutItem *item)
971{
972 insertItem(item, index: -1);
973}
974
975void QGridLayoutEngine::removeItem(QGridLayoutItem *item)
976{
977 Q_ASSERT(q_items.contains(item));
978
979 invalidate();
980
981 for (int i = item->firstRow(); i <= item->lastRow(); ++i) {
982 for (int j = item->firstColumn(); j <= item->lastColumn(); ++j) {
983 if (itemAt(row: i, column: j) == item)
984 setItemAt(row: i, column: j, item: nullptr);
985 }
986 }
987
988 q_items.removeAll(t: item);
989}
990
991
992QGridLayoutItem *QGridLayoutEngine::itemAt(int row, int column, Qt::Orientation orientation) const
993{
994 if (orientation == Qt::Horizontal)
995 qSwap(value1&: row, value2&: column);
996 if (uint(row) >= uint(rowCount()) || uint(column) >= uint(columnCount()))
997 return nullptr;
998 return q_grid.at(i: (row * internalGridColumnCount()) + column);
999}
1000
1001void QGridLayoutEngine::invalidate()
1002{
1003 q_cachedEffectiveFirstRows = {-1, -1};
1004 q_cachedEffectiveLastRows = {-1, -1};
1005
1006 q_totalBoxCachedConstraints = {NotCached, NotCached};
1007
1008 q_cachedSize = QSizeF();
1009 q_cachedConstraintOrientation = UnknownConstraint;
1010}
1011
1012static void visualRect(QRectF *geom, Qt::LayoutDirection dir, const QRectF &contentsRect)
1013{
1014 if (dir == Qt::RightToLeft)
1015 geom->moveRight(pos: contentsRect.right() - (geom->left() - contentsRect.left()));
1016}
1017
1018void QGridLayoutEngine::setGeometries(const QRectF &contentsGeometry, const QAbstractLayoutStyleInfo *styleInfo)
1019{
1020 if (rowCount() < 1 || columnCount() < 1)
1021 return;
1022
1023 ensureGeometries(size: contentsGeometry.size(), styleInfo);
1024
1025 for (int i = q_items.size() - 1; i >= 0; --i) {
1026 QGridLayoutItem *item = q_items.at(i);
1027
1028 qreal x = q_xx.at(i: item->firstColumn());
1029 qreal y = q_yy.at(i: item->firstRow());
1030 qreal width = q_widths.at(i: item->lastColumn());
1031 qreal height = q_heights.at(i: item->lastRow());
1032
1033 if (item->columnSpan() != 1)
1034 width += q_xx.at(i: item->lastColumn()) - x;
1035 if (item->rowSpan() != 1)
1036 height += q_yy.at(i: item->lastRow()) - y;
1037
1038 const Qt::Alignment align = effectiveAlignment(layoutItem: item);
1039 QRectF geom = item->geometryWithin(x: contentsGeometry.x() + x, y: contentsGeometry.y() + y,
1040 width, height, rowDescent: q_descents.at(i: item->lastRow()), align, snapToPixelGrid: m_snapToPixelGrid);
1041 if (m_snapToPixelGrid) {
1042 // x and y should already be rounded, but the call to geometryWithin() above might
1043 // result in a geom with x,y at half-pixels (due to centering within the cell)
1044 // QRectF may change the width as it wants to maintain the right edge. In this
1045 // case the width need to be preserved.
1046 geom.moveLeft(pos: qround(f: geom.x()));
1047 // Do not snap baseline aligned items, since that might cause the baselines to not be aligned.
1048 if (align != Qt::AlignBaseline)
1049 geom.moveTop(pos: qround(f: geom.y()));
1050 }
1051 visualRect(geom: &geom, dir: visualDirection(), contentsRect: contentsGeometry);
1052 item->setGeometry(geom);
1053 }
1054}
1055
1056// ### candidate for deletion
1057QRectF QGridLayoutEngine::cellRect(const QRectF &contentsGeometry, int row, int column, int rowSpan,
1058 int columnSpan, const QAbstractLayoutStyleInfo *styleInfo) const
1059{
1060 if (uint(row) >= uint(rowCount()) || uint(column) >= uint(columnCount())
1061 || rowSpan < 1 || columnSpan < 1)
1062 return QRectF();
1063
1064 ensureGeometries(size: contentsGeometry.size(), styleInfo);
1065
1066 int lastColumn = qMax(a: column + columnSpan, b: columnCount()) - 1;
1067 int lastRow = qMax(a: row + rowSpan, b: rowCount()) - 1;
1068
1069 qreal x = q_xx[column];
1070 qreal y = q_yy[row];
1071 qreal width = q_widths[lastColumn];
1072 qreal height = q_heights[lastRow];
1073
1074 if (columnSpan != 1)
1075 width += q_xx[lastColumn] - x;
1076 if (rowSpan != 1)
1077 height += q_yy[lastRow] - y;
1078
1079 return QRectF(contentsGeometry.x() + x, contentsGeometry.y() + y, width, height);
1080}
1081
1082QSizeF QGridLayoutEngine::sizeHint(Qt::SizeHint which, const QSizeF &constraint,
1083 const QAbstractLayoutStyleInfo *styleInfo) const
1084{
1085
1086
1087 if (hasDynamicConstraint() && rowCount() > 0 && columnCount() > 0) {
1088 QHVContainer<QGridLayoutBox> sizehint_totalBoxes;
1089 bool sizeHintCalculated = false;
1090 if (constraintOrientation() == Qt::Vertical) {
1091 //We have items whose height depends on their width
1092 if (constraint.width() >= 0) {
1093 ensureColumnAndRowData(rowData: &q_columnData, totalBox: &sizehint_totalBoxes[Qt::Horizontal], colPositions: nullptr, colSizes: nullptr, orientation: Qt::Horizontal, styleInfo);
1094 QList<qreal> sizehint_xx;
1095 QList<qreal> sizehint_widths;
1096
1097 sizehint_xx.resize(size: columnCount());
1098 sizehint_widths.resize(size: columnCount());
1099 qreal width = constraint.width();
1100 //Calculate column widths and positions, and put results in q_xx.data() and q_widths.data() so that we can use this information as
1101 //constraints to find the row heights
1102 q_columnData.calculateGeometries(start: 0, end: columnCount(), targetSize: width, positions: sizehint_xx.data(), sizes: sizehint_widths.data(),
1103 descents: nullptr, totalBox: sizehint_totalBoxes[Qt::Horizontal], rowInfo: q_infos[Qt::Horizontal], snapToPixelGrid: m_snapToPixelGrid);
1104 ensureColumnAndRowData(rowData: &q_rowData, totalBox: &sizehint_totalBoxes[Qt::Vertical], colPositions: sizehint_xx.data(), colSizes: sizehint_widths.data(), orientation: Qt::Vertical, styleInfo);
1105 sizeHintCalculated = true;
1106 }
1107 } else {
1108 if (constraint.height() >= 0) {
1109 //We have items whose width depends on their height
1110 ensureColumnAndRowData(rowData: &q_rowData, totalBox: &sizehint_totalBoxes[Qt::Vertical], colPositions: nullptr, colSizes: nullptr, orientation: Qt::Vertical, styleInfo);
1111 QList<qreal> sizehint_yy;
1112 QList<qreal> sizehint_heights;
1113
1114 sizehint_yy.resize(size: rowCount());
1115 sizehint_heights.resize(size: rowCount());
1116 qreal height = constraint.height();
1117 //Calculate row heights and positions, and put results in q_yy.data() and q_heights.data() so that we can use this information as
1118 //constraints to find the column widths
1119 q_rowData.calculateGeometries(start: 0, end: rowCount(), targetSize: height, positions: sizehint_yy.data(), sizes: sizehint_heights.data(),
1120 descents: nullptr, totalBox: sizehint_totalBoxes[Qt::Vertical], rowInfo: q_infos[Qt::Vertical], snapToPixelGrid: m_snapToPixelGrid);
1121 ensureColumnAndRowData(rowData: &q_columnData, totalBox: &sizehint_totalBoxes[Qt::Horizontal], colPositions: sizehint_yy.data(), colSizes: sizehint_heights.data(), orientation: Qt::Horizontal, styleInfo);
1122 sizeHintCalculated = true;
1123 }
1124 }
1125 if (sizeHintCalculated)
1126 return QSizeF{sizehint_totalBoxes[Qt::Horizontal].q_sizes(which),
1127 sizehint_totalBoxes[Qt::Vertical].q_sizes(which)};
1128 }
1129
1130 //No items with height for width, so it doesn't matter which order we do these in
1131 ensureColumnAndRowData(rowData: &q_columnData, totalBox: &q_totalBoxes[Qt::Horizontal], colPositions: nullptr, colSizes: nullptr, orientation: Qt::Horizontal, styleInfo);
1132 ensureColumnAndRowData(rowData: &q_rowData, totalBox: &q_totalBoxes[Qt::Vertical], colPositions: nullptr, colSizes: nullptr, orientation: Qt::Vertical, styleInfo);
1133 return QSizeF(q_totalBoxes[Qt::Horizontal].q_sizes(which), q_totalBoxes[Qt::Vertical].q_sizes(which));
1134}
1135
1136QLayoutPolicy::ControlTypes QGridLayoutEngine::controlTypes(LayoutSide side) const
1137{
1138 Qt::Orientation orientation = (side == Top || side == Bottom) ? Qt::Vertical : Qt::Horizontal;
1139 int row = (side == Top || side == Left) ? effectiveFirstRow(orientation)
1140 : effectiveLastRow(orientation);
1141 QLayoutPolicy::ControlTypes result;
1142
1143 for (int column = columnCount(orientation) - 1; column >= 0; --column) {
1144 if (QGridLayoutItem *item = itemAt(row, column, orientation))
1145 result |= item->controlTypes(side);
1146 }
1147 return result;
1148}
1149
1150void QGridLayoutEngine::transpose()
1151{
1152 invalidate();
1153
1154 for (int i = q_items.size() - 1; i >= 0; --i)
1155 q_items.at(i)->transpose();
1156
1157 q_defaultSpacings.transpose();
1158 q_infos.transpose();
1159
1160 regenerateGrid();
1161}
1162
1163void QGridLayoutEngine::setVisualDirection(Qt::LayoutDirection direction)
1164{
1165 m_visualDirection = direction;
1166}
1167
1168Qt::LayoutDirection QGridLayoutEngine::visualDirection() const
1169{
1170 return m_visualDirection;
1171}
1172
1173#ifdef QGRIDLAYOUTENGINE_DEBUG
1174void QGridLayoutEngine::dump(int indent) const
1175{
1176 qDebug("%*sEngine", indent, "");
1177
1178 qDebug("%*s Items (%d)", indent, "", q_items.count());
1179 int i;
1180 for (i = 0; i < q_items.count(); ++i)
1181 q_items.at(i)->dump(indent + 2);
1182
1183 qDebug("%*s Grid (%d x %d)", indent, "", internalGridRowCount(),
1184 internalGridColumnCount());
1185 for (int row = 0; row < internalGridRowCount(); ++row) {
1186 QString message = "[ "_L1;
1187 for (int column = 0; column < internalGridColumnCount(); ++column) {
1188 message += QString::number(q_items.indexOf(itemAt(row, column))).rightJustified(3);
1189 message += u' ';
1190 }
1191 message += u']';
1192 qDebug("%*s %s", indent, "", qPrintable(message));
1193 }
1194
1195 if (q_defaultSpacings[Qt::Horizontal].value() >= 0.0 || q_defaultSpacings[Qt::Vertical].value() >= 0.0)
1196 qDebug("%*s Default spacings: %g %g", indent, "",
1197 q_defaultSpacings[Qt::Horizontal].value(),
1198 q_defaultSpacings[Qt::Vertical].value());
1199
1200 qDebug("%*s Column and row info", indent, "");
1201 q_infos[Qt::Horizontal].dump(indent + 2);
1202 q_infos[Qt::Vertical].dump(indent + 2);
1203
1204 qDebug("%*s Column and row data", indent, "");
1205 q_columnData.dump(indent + 2);
1206 q_rowData.dump(indent + 2);
1207
1208 qDebug("%*s Geometries output", indent, "");
1209 QList<qreal> *cellPos = &q_yy;
1210 for (int pass = 0; pass < 2; ++pass) {
1211 QString message;
1212 for (i = 0; i < cellPos->count(); ++i) {
1213 message += (message.isEmpty() ? "["_L1 : ", "_L1);
1214 message += QString::number(cellPos->at(i));
1215 }
1216 message += u']';
1217 qDebug("%*s %s %s", indent, "", (pass == 0 ? "rows:" : "columns:"), qPrintable(message));
1218 cellPos = &q_xx;
1219 }
1220}
1221#endif
1222
1223void QGridLayoutEngine::maybeExpandGrid(int row, int column, Qt::Orientation orientation)
1224{
1225 invalidate(); // ### move out of here?
1226
1227 if (orientation == Qt::Horizontal)
1228 qSwap(value1&: row, value2&: column);
1229
1230 if (row < rowCount() && column < columnCount())
1231 return;
1232
1233 int oldGridRowCount = internalGridRowCount();
1234 int oldGridColumnCount = internalGridColumnCount();
1235
1236 q_infos[Qt::Vertical].count = qMax(a: row + 1, b: rowCount());
1237 q_infos[Qt::Horizontal].count = qMax(a: column + 1, b: columnCount());
1238
1239 int newGridRowCount = internalGridRowCount();
1240 int newGridColumnCount = internalGridColumnCount();
1241
1242 int newGridSize = newGridRowCount * newGridColumnCount;
1243 if (newGridSize != q_grid.size()) {
1244 q_grid.resize(size: newGridSize);
1245
1246 if (newGridColumnCount != oldGridColumnCount) {
1247 for (int i = oldGridRowCount - 1; i >= 1; --i) {
1248 for (int j = oldGridColumnCount - 1; j >= 0; --j) {
1249 int oldIndex = (i * oldGridColumnCount) + j;
1250 int newIndex = (i * newGridColumnCount) + j;
1251
1252 Q_ASSERT(newIndex > oldIndex);
1253 q_grid[newIndex] = q_grid[oldIndex];
1254 q_grid[oldIndex] = nullptr;
1255 }
1256 }
1257 }
1258 }
1259}
1260
1261void QGridLayoutEngine::regenerateGrid()
1262{
1263 q_grid.fill(t: nullptr);
1264
1265 for (int i = q_items.size() - 1; i >= 0; --i) {
1266 QGridLayoutItem *item = q_items.at(i);
1267
1268 for (int j = item->firstRow(); j <= item->lastRow(); ++j) {
1269 for (int k = item->firstColumn(); k <= item->lastColumn(); ++k) {
1270 setItemAt(row: j, column: k, item);
1271 }
1272 }
1273 }
1274}
1275
1276void QGridLayoutEngine::setItemAt(int row, int column, QGridLayoutItem *item)
1277{
1278 Q_ASSERT(row >= 0 && row < rowCount());
1279 Q_ASSERT(column >= 0 && column < columnCount());
1280 q_grid[(row * internalGridColumnCount()) + column] = item;
1281}
1282
1283void QGridLayoutEngine::insertOrRemoveRows(int row, int delta, Qt::Orientation orientation)
1284{
1285 int oldRowCount = rowCount(orientation);
1286 Q_ASSERT(uint(row) <= uint(oldRowCount));
1287
1288 invalidate();
1289
1290 // appending rows (or columns) is easy
1291 if (row == oldRowCount && delta > 0) {
1292 maybeExpandGrid(row: oldRowCount + delta - 1, column: -1, orientation);
1293 return;
1294 }
1295
1296 q_infos[orientation].insertOrRemoveRows(row, delta);
1297
1298 for (int i = q_items.size() - 1; i >= 0; --i)
1299 q_items.at(i)->insertOrRemoveRows(row, delta, orientation);
1300
1301 q_grid.resize(size: internalGridRowCount() * internalGridColumnCount());
1302 regenerateGrid();
1303}
1304
1305void QGridLayoutEngine::fillRowData(QGridLayoutRowData *rowData,
1306 const qreal *colPositions, const qreal *colSizes,
1307 Qt::Orientation orientation,
1308 const QAbstractLayoutStyleInfo *styleInfo) const
1309{
1310 const int ButtonMask = QLayoutPolicy::ButtonBox | QLayoutPolicy::PushButton;
1311 const QGridLayoutRowInfo &rowInfo = q_infos[orientation];
1312 const QGridLayoutRowInfo &columnInfo = q_infos.other(o: orientation);
1313 LayoutSide top = (orientation == Qt::Vertical) ? Top : Left;
1314 LayoutSide bottom = (orientation == Qt::Vertical) ? Bottom : Right;
1315
1316 const QLayoutParameter<qreal> &defaultSpacing = q_defaultSpacings[orientation];
1317 qreal innerSpacing = styleInfo->spacing(orientation);
1318 if (innerSpacing >= 0.0)
1319 defaultSpacing.setCachedValue(innerSpacing);
1320
1321 for (int row = 0; row < rowInfo.count; ++row) {
1322 bool rowIsEmpty = true;
1323 bool rowIsIdenticalToPrevious = (row > 0);
1324
1325 for (int column = 0; column < columnInfo.count; ++column) {
1326 QGridLayoutItem *item = itemAt(row, column, orientation);
1327
1328 if (rowIsIdenticalToPrevious && item != itemAt(row: row - 1, column, orientation))
1329 rowIsIdenticalToPrevious = false;
1330
1331 if (item && !item->isEmpty())
1332 rowIsEmpty = false;
1333 }
1334
1335 if ((rowIsEmpty || rowIsIdenticalToPrevious)
1336 && rowInfo.spacings.value(i: row).isDefault()
1337 && rowInfo.stretches.value(i: row).isDefault()
1338 && rowInfo.boxes.value(i: row) == QGridLayoutBox())
1339 rowData->ignore.setBit(i: row, val: true);
1340
1341 if (rowInfo.spacings.value(i: row).isUser()) {
1342 rowData->spacings[row] = rowInfo.spacings.at(i: row).value();
1343 } else if (!defaultSpacing.isDefault()) {
1344 rowData->spacings[row] = defaultSpacing.value();
1345 }
1346
1347 rowData->stretches[row] = rowInfo.stretches.value(i: row).value();
1348 }
1349
1350 struct RowAdHocData {
1351 int q_row;
1352 unsigned int q_hasButtons : 8;
1353 unsigned int q_hasNonButtons : 8;
1354
1355 inline RowAdHocData() : q_row(-1), q_hasButtons(false), q_hasNonButtons(false) {}
1356 inline void init(int row) {
1357 this->q_row = row;
1358 q_hasButtons = false;
1359 q_hasNonButtons = false;
1360 }
1361 inline bool hasOnlyButtons() const { return q_hasButtons && !q_hasNonButtons; }
1362 inline bool hasOnlyNonButtons() const { return q_hasNonButtons && !q_hasButtons; }
1363 };
1364 RowAdHocData lastRowAdHocData;
1365 RowAdHocData nextToLastRowAdHocData;
1366 RowAdHocData nextToNextToLastRowAdHocData;
1367
1368 rowData->hasIgnoreFlag = false;
1369 for (int row = 0; row < rowInfo.count; ++row) {
1370 if (rowData->ignore.testBit(i: row))
1371 continue;
1372
1373 QGridLayoutBox &rowBox = rowData->boxes[row];
1374 if (styleInfo->isWindow()) {
1375 nextToNextToLastRowAdHocData = nextToLastRowAdHocData;
1376 nextToLastRowAdHocData = lastRowAdHocData;
1377 lastRowAdHocData.init(row);
1378 }
1379
1380 bool userRowStretch = rowInfo.stretches.value(i: row).isUser();
1381 int &rowStretch = rowData->stretches[row];
1382
1383 bool hasIgnoreFlag = true;
1384 for (int column = 0; column < columnInfo.count; ++column) {
1385 QGridLayoutItem *item = itemAt(row, column, orientation);
1386 if (item) {
1387 int itemRow = item->firstRow(orientation);
1388 int itemColumn = item->firstColumn(orientation);
1389
1390 if (itemRow == row && itemColumn == column) {
1391 int itemStretch = item->stretchFactor(orientation);
1392 if (!(item->sizePolicy(orientation) & QLayoutPolicy::IgnoreFlag))
1393 hasIgnoreFlag = false;
1394 int itemRowSpan = item->rowSpan(orientation);
1395
1396 int effectiveRowSpan = 1;
1397 for (int i = 1; i < itemRowSpan; ++i) {
1398 if (!rowData->ignore.testBit(i: i + itemRow))
1399 ++effectiveRowSpan;
1400 }
1401
1402 QGridLayoutBox *box;
1403 if (effectiveRowSpan == 1) {
1404 box = &rowBox;
1405 if (!userRowStretch && itemStretch != 0)
1406 rowStretch = qMax(a: rowStretch, b: itemStretch);
1407 } else {
1408 QGridLayoutMultiCellData &multiCell =
1409 rowData->multiCellMap[qMakePair(value1&: row, value2&: itemRowSpan)];
1410 box = &multiCell.q_box;
1411 multiCell.q_stretch = itemStretch;
1412 }
1413 // Items with constraints need to be passed the constraint
1414 if (colSizes && colPositions && item->hasDynamicConstraint() && orientation == item->dynamicConstraintOrientation()) {
1415 /* Get the width of the item by summing up the widths of the columns that it spans.
1416 * We need to have already calculated the widths of the columns by calling
1417 * q_columns->calculateGeometries() before hand and passing the value in the colSizes
1418 * and colPositions parameters.
1419 * The variable name is still colSizes even when it actually has the row sizes
1420 */
1421 qreal length = colSizes[item->lastColumn(orientation)];
1422 if (item->columnSpan(orientation) != 1)
1423 length += colPositions[item->lastColumn(orientation)] - colPositions[item->firstColumn(orientation)];
1424 box->combine(other: item->box(orientation, snapToPixelGrid: m_snapToPixelGrid, constraint: length));
1425 } else {
1426 box->combine(other: item->box(orientation, snapToPixelGrid: m_snapToPixelGrid));
1427 }
1428
1429 if (effectiveRowSpan == 1) {
1430 QLayoutPolicy::ControlTypes controls = item->controlTypes(top);
1431 if (controls & ButtonMask)
1432 lastRowAdHocData.q_hasButtons = true;
1433 if (controls & ~ButtonMask)
1434 lastRowAdHocData.q_hasNonButtons = true;
1435 }
1436 }
1437 }
1438 }
1439 if (row < rowInfo.boxes.size()) {
1440 QGridLayoutBox rowBoxInfo = rowInfo.boxes.at(i: row);
1441 rowBoxInfo.normalize();
1442 rowBox.q_minimumSize = qMax(a: rowBox.q_minimumSize, b: rowBoxInfo.q_minimumSize);
1443 rowBox.q_maximumSize = qMax(a: rowBox.q_minimumSize,
1444 b: (rowBoxInfo.q_maximumSize != FLT_MAX ?
1445 rowBoxInfo.q_maximumSize : rowBox.q_maximumSize));
1446 rowBox.q_preferredSize = qBound(min: rowBox.q_minimumSize,
1447 val: qMax(a: rowBox.q_preferredSize, b: rowBoxInfo.q_preferredSize),
1448 max: rowBox.q_maximumSize);
1449 }
1450 if (hasIgnoreFlag)
1451 rowData->hasIgnoreFlag = true;
1452 }
1453
1454 /*
1455 Heuristic: Detect button boxes that don't use QLayoutPolicy::ButtonBox.
1456 This is somewhat ad hoc but it usually does the trick.
1457 */
1458 bool lastRowIsButtonBox = (lastRowAdHocData.hasOnlyButtons()
1459 && nextToLastRowAdHocData.hasOnlyNonButtons());
1460 bool lastTwoRowsIsButtonBox = (lastRowAdHocData.hasOnlyButtons()
1461 && nextToLastRowAdHocData.hasOnlyButtons()
1462 && nextToNextToLastRowAdHocData.hasOnlyNonButtons()
1463 && orientation == Qt::Vertical);
1464
1465 if (defaultSpacing.isDefault()) {
1466 int prevRow = -1;
1467 for (int row = 0; row < rowInfo.count; ++row) {
1468 if (rowData->ignore.testBit(i: row))
1469 continue;
1470
1471 if (prevRow != -1 && !rowInfo.spacings.value(i: prevRow).isUser()) {
1472 qreal &rowSpacing = rowData->spacings[prevRow];
1473 for (int column = 0; column < columnInfo.count; ++column) {
1474 QGridLayoutItem *item1 = itemAt(row: prevRow, column, orientation);
1475 QGridLayoutItem *item2 = itemAt(row, column, orientation);
1476
1477 if (item1 && item2 && item1 != item2) {
1478 QLayoutPolicy::ControlTypes controls1 = item1->controlTypes(bottom);
1479 QLayoutPolicy::ControlTypes controls2 = item2->controlTypes(top);
1480
1481 if (controls2 & QLayoutPolicy::PushButton) {
1482 if ((row == nextToLastRowAdHocData.q_row && lastTwoRowsIsButtonBox)
1483 || (row == lastRowAdHocData.q_row && lastRowIsButtonBox)) {
1484 controls2 &= ~QLayoutPolicy::PushButton;
1485 controls2 |= QLayoutPolicy::ButtonBox;
1486 }
1487 }
1488
1489 qreal spacing = styleInfo->combinedLayoutSpacing(controls1, controls2,
1490 orientation);
1491 if (orientation == Qt::Horizontal) {
1492 qreal width1 = rowData->boxes.at(i: prevRow).q_minimumSize;
1493 qreal width2 = rowData->boxes.at(i: row).q_minimumSize;
1494 QRectF rect1 = item1->geometryWithin(x: 0.0, y: 0.0, width: width1, FLT_MAX, rowDescent: -1.0, align: effectiveAlignment(layoutItem: item1), snapToPixelGrid: m_snapToPixelGrid);
1495 QRectF rect2 = item2->geometryWithin(x: 0.0, y: 0.0, width: width2, FLT_MAX, rowDescent: -1.0, align: effectiveAlignment(layoutItem: item2), snapToPixelGrid: m_snapToPixelGrid);
1496 spacing -= (width1 - (rect1.x() + rect1.width())) + rect2.x();
1497 } else {
1498 const QGridLayoutBox &box1 = rowData->boxes.at(i: prevRow);
1499 const QGridLayoutBox &box2 = rowData->boxes.at(i: row);
1500 qreal height1 = box1.q_minimumSize;
1501 qreal height2 = box2.q_minimumSize;
1502 qreal rowDescent1 = fixedDescent(descent: box1.q_minimumDescent,
1503 ascent: box1.q_minimumAscent, targetSize: height1);
1504 qreal rowDescent2 = fixedDescent(descent: box2.q_minimumDescent,
1505 ascent: box2.q_minimumAscent, targetSize: height2);
1506 QRectF rect1 = item1->geometryWithin(x: 0.0, y: 0.0, FLT_MAX, height: height1,
1507 rowDescent: rowDescent1, align: effectiveAlignment(layoutItem: item1), snapToPixelGrid: m_snapToPixelGrid);
1508 QRectF rect2 = item2->geometryWithin(x: 0.0, y: 0.0, FLT_MAX, height: height2,
1509 rowDescent: rowDescent2, align: effectiveAlignment(layoutItem: item2), snapToPixelGrid: m_snapToPixelGrid);
1510 spacing -= (height1 - (rect1.y() + rect1.height())) + rect2.y();
1511 }
1512 rowSpacing = qMax(a: spacing, b: rowSpacing);
1513 }
1514 }
1515 }
1516 prevRow = row;
1517 }
1518 } else if (lastRowIsButtonBox || lastTwoRowsIsButtonBox) {
1519 /*
1520 Even for styles that define a uniform spacing, we cheat a
1521 bit and use the window margin as the spacing. This
1522 significantly improves the look of dialogs.
1523 */
1524 int prevRow = lastRowIsButtonBox ? nextToLastRowAdHocData.q_row
1525 : nextToNextToLastRowAdHocData.q_row;
1526 if (!defaultSpacing.isUser() && !rowInfo.spacings.value(i: prevRow).isUser()) {
1527 qreal windowMargin = styleInfo->windowMargin(orientation);
1528 qreal &rowSpacing = rowData->spacings[prevRow];
1529 rowSpacing = qMax(a: windowMargin, b: rowSpacing);
1530 }
1531 }
1532
1533 if (rowData->boxes.size() > 1 &&
1534 ((orientation == Qt::Horizontal && m_uniformCellWidths) ||
1535 (orientation == Qt::Vertical && m_uniformCellHeights))) {
1536 qreal averagePreferredSize = 0.;
1537 qreal minimumMaximumSize = std::numeric_limits<qreal>::max();
1538 qreal maximumMinimumSize = 0.;
1539 for (const auto &box : rowData->boxes) {
1540 averagePreferredSize += box.q_preferredSize;
1541 minimumMaximumSize = qMin(a: minimumMaximumSize, b: box.q_maximumSize);
1542 maximumMinimumSize = qMax(a: maximumMinimumSize, b: box.q_minimumSize);
1543 }
1544 averagePreferredSize /= rowData->boxes.size();
1545 minimumMaximumSize = qMax(a: minimumMaximumSize, b: maximumMinimumSize);
1546 averagePreferredSize = qBound(min: maximumMinimumSize, val: averagePreferredSize, max: minimumMaximumSize);
1547 for (auto &box : rowData->boxes) {
1548 box.q_preferredSize = averagePreferredSize;
1549 box.q_minimumSize = maximumMinimumSize;
1550 box.q_maximumSize = minimumMaximumSize;
1551 }
1552 }
1553}
1554
1555void QGridLayoutEngine::ensureEffectiveFirstAndLastRows() const
1556{
1557 if (q_cachedEffectiveFirstRows[Qt::Horizontal] == -1 && !q_items.isEmpty()) {
1558 int rowCount = this->rowCount();
1559 int columnCount = this->columnCount();
1560
1561 q_cachedEffectiveFirstRows = {columnCount, rowCount};
1562 q_cachedEffectiveLastRows = {-1, -1};
1563
1564 for (int i = q_items.size() - 1; i >= 0; --i) {
1565 const QGridLayoutItem *item = q_items.at(i);
1566
1567 for (Qt::Orientation o : {Qt::Horizontal, Qt::Vertical}) {
1568 if (item->firstRow(orientation: o) < q_cachedEffectiveFirstRows[o])
1569 q_cachedEffectiveFirstRows[o] = item->firstRow(orientation: o);
1570 if (item->lastRow(orientation: o) > q_cachedEffectiveLastRows[o])
1571 q_cachedEffectiveLastRows[o] = item->lastRow(orientation: o);
1572 }
1573 }
1574 }
1575}
1576
1577void QGridLayoutEngine::ensureColumnAndRowData(QGridLayoutRowData *rowData, QGridLayoutBox *totalBox,
1578 const qreal *colPositions, const qreal *colSizes,
1579 Qt::Orientation orientation,
1580 const QAbstractLayoutStyleInfo *styleInfo) const
1581{
1582 const int cc = columnCount(orientation);
1583
1584 const qreal constraint = (colPositions && colSizes && hasDynamicConstraint()) ? (colPositions[cc - 1] + colSizes[cc - 1]) : qreal(CachedWithNoConstraint);
1585 qreal &cachedConstraint = q_totalBoxCachedConstraints[orientation];
1586 if (cachedConstraint == constraint) {
1587 if (totalBox != &q_totalBoxes[orientation])
1588 *totalBox = q_totalBoxes[orientation];
1589 return;
1590 }
1591 rowData->reset(count: rowCount(orientation));
1592 fillRowData(rowData, colPositions, colSizes, orientation, styleInfo);
1593 const QGridLayoutRowInfo &rowInfo = q_infos[orientation];
1594 rowData->distributeMultiCells(rowInfo, snapToPixelGrid: m_snapToPixelGrid);
1595 *totalBox = rowData->totalBox(start: 0, end: rowCount(orientation));
1596
1597 if (totalBox != &q_totalBoxes[orientation])
1598 q_totalBoxes[orientation] = *totalBox;
1599
1600 cachedConstraint = constraint;
1601}
1602
1603/**
1604 returns false if the layout has contradicting constraints (i.e. some items with a horizontal
1605 constraint and other items with a vertical constraint)
1606 */
1607bool QGridLayoutEngine::ensureDynamicConstraint() const
1608{
1609 if (q_cachedConstraintOrientation == UnknownConstraint) {
1610 for (int i = q_items.size() - 1; i >= 0; --i) {
1611 QGridLayoutItem *item = q_items.at(i);
1612 if (item->hasDynamicConstraint()) {
1613 Qt::Orientation itemConstraintOrientation = item->dynamicConstraintOrientation();
1614 if (q_cachedConstraintOrientation == UnknownConstraint) {
1615 q_cachedConstraintOrientation = itemConstraintOrientation;
1616 } else if (q_cachedConstraintOrientation != itemConstraintOrientation) {
1617 q_cachedConstraintOrientation = UnfeasibleConstraint;
1618 qWarning(msg: "QGridLayoutEngine: Unfeasible, cannot mix horizontal and"
1619 " vertical constraint in the same layout");
1620 return false;
1621 }
1622 }
1623 }
1624 if (q_cachedConstraintOrientation == UnknownConstraint)
1625 q_cachedConstraintOrientation = NoConstraint;
1626 }
1627 return true;
1628}
1629
1630bool QGridLayoutEngine::hasDynamicConstraint() const
1631{
1632 if (!ensureDynamicConstraint())
1633 return false;
1634 return q_cachedConstraintOrientation != NoConstraint;
1635}
1636
1637/*
1638 * return value is only valid if hasConstraint() returns \c true
1639 */
1640Qt::Orientation QGridLayoutEngine::constraintOrientation() const
1641{
1642 (void)ensureDynamicConstraint();
1643 return (Qt::Orientation)q_cachedConstraintOrientation;
1644}
1645
1646void QGridLayoutEngine::ensureGeometries(const QSizeF &size,
1647 const QAbstractLayoutStyleInfo *styleInfo) const
1648{
1649 if (q_cachedSize == size)
1650 return;
1651
1652 q_cachedSize = size;
1653
1654 q_xx.resize(size: columnCount());
1655 q_widths.resize(size: columnCount());
1656 q_yy.resize(size: rowCount());
1657 q_heights.resize(size: rowCount());
1658 q_descents.resize(size: rowCount());
1659
1660 if (constraintOrientation() != Qt::Horizontal) {
1661 //We might have items whose height depends on their width (HFW)
1662 ensureColumnAndRowData(rowData: &q_columnData, totalBox: &q_totalBoxes[Qt::Horizontal], colPositions: nullptr, colSizes: nullptr, orientation: Qt::Horizontal, styleInfo);
1663 //Calculate column widths and positions, and put results in q_xx.data() and q_widths.data() so that we can use this information as
1664 //constraints to find the row heights
1665 q_columnData.calculateGeometries(start: 0, end: columnCount(), targetSize: size.width(), positions: q_xx.data(), sizes: q_widths.data(),
1666 descents: nullptr, totalBox: q_totalBoxes[Qt::Horizontal], rowInfo: q_infos[Qt::Horizontal], snapToPixelGrid: m_snapToPixelGrid);
1667 ensureColumnAndRowData(rowData: &q_rowData, totalBox: &q_totalBoxes[Qt::Vertical], colPositions: q_xx.data(), colSizes: q_widths.data(), orientation: Qt::Vertical, styleInfo);
1668 //Calculate row heights and positions, and put results in q_yy.data() and q_heights.data()
1669 q_rowData.calculateGeometries(start: 0, end: rowCount(), targetSize: size.height(), positions: q_yy.data(), sizes: q_heights.data(),
1670 descents: q_descents.data(), totalBox: q_totalBoxes[Qt::Vertical], rowInfo: q_infos[Qt::Vertical], snapToPixelGrid: m_snapToPixelGrid);
1671 } else {
1672 //We have items whose width depends on their height (WFH)
1673 ensureColumnAndRowData(rowData: &q_rowData, totalBox: &q_totalBoxes[Qt::Vertical], colPositions: nullptr, colSizes: nullptr, orientation: Qt::Vertical, styleInfo);
1674 //Calculate row heights and positions, and put results in q_yy.data() and q_heights.data() so that we can use this information as
1675 //constraints to find the column widths
1676 q_rowData.calculateGeometries(start: 0, end: rowCount(), targetSize: size.height(), positions: q_yy.data(), sizes: q_heights.data(),
1677 descents: q_descents.data(), totalBox: q_totalBoxes[Qt::Vertical], rowInfo: q_infos[Qt::Vertical], snapToPixelGrid: m_snapToPixelGrid);
1678 ensureColumnAndRowData(rowData: &q_columnData, totalBox: &q_totalBoxes[Qt::Horizontal], colPositions: q_yy.data(), colSizes: q_heights.data(), orientation: Qt::Horizontal, styleInfo);
1679 //Calculate row heights and positions, and put results in q_yy.data() and q_heights.data()
1680 q_columnData.calculateGeometries(start: 0, end: columnCount(), targetSize: size.width(), positions: q_xx.data(), sizes: q_widths.data(),
1681 descents: nullptr, totalBox: q_totalBoxes[Qt::Horizontal], rowInfo: q_infos[Qt::Horizontal], snapToPixelGrid: m_snapToPixelGrid);
1682 }
1683}
1684
1685QT_END_NAMESPACE
1686

source code of qtbase/src/gui/util/qgridlayoutengine.cpp