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

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