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 
48 QT_BEGIN_NAMESPACE
49 
50 template <typename T>
insertOrRemoveItems(QVector<T> & items,int index,int delta)51 static 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(-delta, count - index));
59         }
60     }
61 }
62 
growthFactorBelowPreferredSize(qreal desired,qreal sumAvailable,qreal sumDesired)63 static qreal growthFactorBelowPreferredSize(qreal desired, qreal sumAvailable, qreal sumDesired)
64 {
65     Q_ASSERT(sumDesired != 0.0);
66     return desired * qPow(sumAvailable / sumDesired, desired / sumDesired);
67 }
68 
fixedDescent(qreal descent,qreal ascent,qreal targetSize)69 static 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 
compare(const QGridLayoutBox & box1,const QGridLayoutBox & box2,int which)82 static 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 
add(const QGridLayoutBox & other,int stretch,qreal spacing)94 void 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 
combine(const QGridLayoutBox & other)103 void QGridLayoutBox::combine(const QGridLayoutBox &other)
104 {
105     q_minimumDescent = qMax(q_minimumDescent, other.q_minimumDescent);
106     q_minimumAscent = qMax(q_minimumAscent, other.q_minimumAscent);
107 
108     q_minimumSize = qMax(q_minimumAscent + q_minimumDescent,
109                          qMax(q_minimumSize, 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(q_maximumSize, other.q_maximumSize);
117 
118     q_maximumSize = qMax(q_minimumSize, maxMax);
119     q_preferredSize = qBound(q_minimumSize, qMax(q_preferredSize, other.q_preferredSize),
120                              q_maximumSize);
121 }
122 
normalize()123 void QGridLayoutBox::normalize()
124 {
125     q_maximumSize = qMax(qreal(0.0), q_maximumSize);
126     q_minimumSize = qBound(qreal(0.0), q_minimumSize, q_maximumSize);
127     q_preferredSize = qBound(q_minimumSize, q_preferredSize, q_maximumSize);
128     q_minimumDescent = qMin(q_minimumDescent, q_minimumSize);
129 
130     Q_ASSERT((q_minimumDescent < 0.0) == (q_minimumAscent < 0.0));
131 }
132 
133 #ifdef QGRIDLAYOUTENGINE_DEBUG
dump(int indent) const134 void 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 
operator ==(const QGridLayoutBox & box1,const QGridLayoutBox & box2)141 bool operator==(const QGridLayoutBox &box1, const QGridLayoutBox &box2)
142 {
143     for (int i = 0; i < NSizes; ++i) {
144         if (box1.q_sizes(i) != box2.q_sizes(i))
145             return false;
146     }
147     return box1.q_minimumDescent == box2.q_minimumDescent
148            && box1.q_minimumAscent == box2.q_minimumAscent;
149 }
150 
reset(int count)151 void QGridLayoutRowData::reset(int count)
152 {
153     ignore.fill(false, count);
154     boxes.fill(QGridLayoutBox(), count);
155     multiCellMap.clear();
156     stretches.fill(0, count);
157     spacings.fill(0.0, count);
158     hasIgnoreFlag = false;
159 }
160 
distributeMultiCells(const QGridLayoutRowInfo & rowInfo,bool snapToPixelGrid)161 void 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(box, totalBox, j);
178             if (extra > 0.0) {
179                 calculateGeometries(start, end, box.q_sizes(j), dummy.data(), newSizes.data(),
180                                     nullptr, totalBox, rowInfo, snapToPixelGrid);
181 
182                 for (int k = 0; k < span; ++k)
183                     extras[k].q_sizes(j) = newSizes[k];
184             }
185         }
186 
187         for (int k = 0; k < span; ++k) {
188             boxes[start + k].combine(extras[k]);
189             if (stretch != 0)
190                 stretches[start + k] = qMax(stretches[start + k], stretch);
191         }
192     }
193     multiCellMap.clear();
194 }
195 namespace {
196 
197 // does not return int
qround(qreal f)198 static inline qreal qround(qreal f)
199 {
200     return std::floor(f + qreal(0.5));
201 }
202 
203 }
calculateGeometries(int start,int end,qreal targetSize,qreal * positions,qreal * sizes,qreal * descents,const QGridLayoutBox & totalBox,const QGridLayoutRowInfo & rowInfo,bool snapToPixelGrid)204 void 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(totalBox.q_minimumSize, 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(start + i);
222         if (stretch > 0)
223             sumStretches += stretch;
224     }
225 
226     if (targetSize < totalBox.q_preferredSize) {
227         stealBox(start, end, 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(start + i)) {
235                     factors[i] = 0.0;
236                     continue;
237                 }
238 
239                 const QGridLayoutBox &box = boxes.at(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, MaximumSize, positions, sizes);
255             sumAvailable = targetSize - totalBox.q_maximumSize;
256         } else {
257             stealBox(start, end, 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(start + i)) {
271                     newSizes[i] = 0.0;
272                     factors[i] = 0.0;
273                     continue;
274                 }
275 
276                 const QGridLayoutBox &box = boxes.at(start + i);
277                 qreal boxSize;
278 
279                 qreal desired;
280                 if (isLargerThanMaximum) {
281                     boxSize = box.q_maximumSize;
282                     desired = rowInfo.boxes.value(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(sumCurrentAvailable, 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(start + i);
360                     qreal maxBoxSize = box.q_maximumSize;
361 
362                     if (snapToPixelGrid)
363                         maxBoxSize = qMax(box.q_minimumSize, std::floor(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(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(start + i);
435             sizes[i] = qMax(box.q_minimumSize, qround(sizes[i]));
436         }
437     }
438 
439     if (descents) {
440         for (int i = 0; i < n; ++i) {
441             if (ignore.testBit(start + i))
442                 continue;
443             const QGridLayoutBox &box = boxes.at(start + i);
444             descents[i] = fixedDescent(box.q_minimumDescent, box.q_minimumAscent, sizes[i]);
445         }
446     }
447 }
448 
totalBox(int start,int end) const449 QGridLayoutBox 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(boxes.at(i), stretches.at(i), nextSpacing);
459             nextSpacing = spacings.at(i);
460         }
461     }
462     return result;
463 }
464 
stealBox(int start,int end,int which,qreal * positions,qreal * sizes)465 void 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
dump(int indent) const487 void 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 
QGridLayoutItem(int row,int column,int rowSpan,int columnSpan,Qt::Alignment alignment)508 QGridLayoutItem::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 
firstRow(Qt::Orientation orientation) const520 int QGridLayoutItem::firstRow(Qt::Orientation orientation) const
521 {
522     return q_firstRows[orientation == Qt::Vertical];
523 }
524 
firstColumn(Qt::Orientation orientation) const525 int QGridLayoutItem::firstColumn(Qt::Orientation orientation) const
526 {
527     return q_firstRows[orientation == Qt::Horizontal];
528 }
529 
lastRow(Qt::Orientation orientation) const530 int QGridLayoutItem::lastRow(Qt::Orientation orientation) const
531 {
532     return firstRow(orientation) + rowSpan(orientation) - 1;
533 }
534 
lastColumn(Qt::Orientation orientation) const535 int QGridLayoutItem::lastColumn(Qt::Orientation orientation) const
536 {
537     return firstColumn(orientation) + columnSpan(orientation) - 1;
538 }
539 
rowSpan(Qt::Orientation orientation) const540 int QGridLayoutItem::rowSpan(Qt::Orientation orientation) const
541 {
542     return q_rowSpans[orientation == Qt::Vertical];
543 }
544 
columnSpan(Qt::Orientation orientation) const545 int QGridLayoutItem::columnSpan(Qt::Orientation orientation) const
546 {
547     return q_rowSpans[orientation == Qt::Horizontal];
548 }
549 
setFirstRow(int row,Qt::Orientation orientation)550 void QGridLayoutItem::setFirstRow(int row, Qt::Orientation orientation)
551 {
552     q_firstRows[orientation == Qt::Vertical] = row;
553 }
554 
setRowSpan(int rowSpan,Qt::Orientation orientation)555 void QGridLayoutItem::setRowSpan(int rowSpan, Qt::Orientation orientation)
556 {
557     q_rowSpans[orientation == Qt::Vertical] = rowSpan;
558 }
559 
stretchFactor(Qt::Orientation orientation) const560 int 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 
setStretchFactor(int stretch,Qt::Orientation orientation)577 void 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 
controlTypes(LayoutSide) const583 QLayoutPolicy::ControlTypes QGridLayoutItem::controlTypes(LayoutSide /*side*/) const
584 {
585     return QLayoutPolicy::DefaultType;
586 }
587 
box(Qt::Orientation orientation,bool snapToPixelGrid,qreal constraint) const588 QGridLayoutBox 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(Qt::PreferredSize, constraintSize).width();
597 
598         if (policy & QLayoutPolicy::ShrinkFlag) {
599             result.q_minimumSize = sizeHint(Qt::MinimumSize, constraintSize).width();
600         } else {
601             result.q_minimumSize = result.q_preferredSize;
602         }
603         if (snapToPixelGrid)
604             result.q_minimumSize = qCeil(result.q_minimumSize);
605 
606         if (policy & (QLayoutPolicy::GrowFlag | QLayoutPolicy::ExpandFlag)) {
607             result.q_maximumSize = sizeHint(Qt::MaximumSize, 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(Qt::PreferredSize, constraintSize).height();
615 
616         if (policy & QLayoutPolicy::ShrinkFlag) {
617             result.q_minimumSize = sizeHint(Qt::MinimumSize, constraintSize).height();
618         } else {
619             result.q_minimumSize = result.q_preferredSize;
620         }
621         if (snapToPixelGrid)
622             result.q_minimumSize = qCeil(result.q_minimumSize);
623 
624         if (policy & (QLayoutPolicy::GrowFlag | QLayoutPolicy::ExpandFlag)) {
625             result.q_maximumSize = sizeHint(Qt::MaximumSize, constraintSize).height();
626         } else {
627             result.q_maximumSize = result.q_preferredSize;
628         }
629 
630         if (alignment() & Qt::AlignBaseline) {
631             result.q_minimumDescent = sizeHint(Qt::MinimumDescent, constraintSize).height();
632             if (result.q_minimumDescent != -1.0) {
633                 const qreal minSizeHint = sizeHint(Qt::MinimumSize, 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 
geometryWithin(qreal x,qreal y,qreal width,qreal height,qreal rowDescent,Qt::Alignment align,bool snapToPixelGrid) const645 QRectF 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(QSizeF(-1,-1));
652     if (hasDynamicConstraint()) {
653         if (dynamicConstraintOrientation() == Qt::Vertical) {
654            if (size.width() > cellWidth)
655                size = effectiveMaxSize(QSizeF(cellWidth, -1));
656         } else if (size.height() > cellHeight) {
657             size = effectiveMaxSize(QSizeF(-1, cellHeight));
658         }
659     }
660     size = size.boundedTo(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(effectiveMaxSize(QSizeF(-1,-1)).width(), width);
684         QGridLayoutBox vBox = box(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 
transpose()696 void QGridLayoutItem::transpose()
697 {
698     qSwap(q_firstRows[Hor], q_firstRows[Ver]);
699     qSwap(q_rowSpans[Hor], q_rowSpans[Ver]);
700     qSwap(q_stretches[Hor], q_stretches[Ver]);
701 }
702 
insertOrRemoveRows(int row,int delta,Qt::Orientation orientation)703 void QGridLayoutItem::insertOrRemoveRows(int row, int delta, Qt::Orientation orientation)
704 {
705     int oldFirstRow = firstRow(orientation);
706     if (oldFirstRow >= row) {
707         setFirstRow(oldFirstRow + delta, orientation);
708     } else if (lastRow(orientation) >= row) {
709         setRowSpan(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 */
effectiveMaxSize(const QSizeF & constraint) const720 QSizeF QGridLayoutItem::effectiveMaxSize(const QSizeF &constraint) const
721 {
722     QSizeF size = constraint;
723     bool vGrow = (sizePolicy(Qt::Vertical) & QLayoutPolicy::GrowFlag) == QLayoutPolicy::GrowFlag;
724     bool hGrow = (sizePolicy(Qt::Horizontal) & QLayoutPolicy::GrowFlag) == QLayoutPolicy::GrowFlag;
725     if (!vGrow || !hGrow) {
726         QSizeF pref = sizeHint(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(Qt::MaximumSize, 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
dump(int indent) const744 void 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 
insertOrRemoveRows(int row,int delta)760 void QGridLayoutRowInfo::insertOrRemoveRows(int row, int delta)
761 {
762     count += delta;
763 
764     insertOrRemoveItems(stretches, row, delta);
765     insertOrRemoveItems(spacings, row, delta);
766     insertOrRemoveItems(alignments, row, delta);
767     insertOrRemoveItems(boxes, row, delta);
768 }
769 
770 #ifdef QGRIDLAYOUTENGINE_DEBUG
dump(int indent) const771 void 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 
QGridLayoutEngine(Qt::Alignment defaultAlignment,bool snapToPixelGrid)793 QGridLayoutEngine::QGridLayoutEngine(Qt::Alignment defaultAlignment, bool snapToPixelGrid)
794 {
795     m_visualDirection = Qt::LeftToRight;
796     m_defaultAlignment = defaultAlignment;
797     m_snapToPixelGrid = snapToPixelGrid;
798     invalidate();
799 }
800 
rowCount(Qt::Orientation orientation) const801 int QGridLayoutEngine::rowCount(Qt::Orientation orientation) const
802 {
803     return q_infos[orientation == Qt::Vertical].count;
804 }
805 
columnCount(Qt::Orientation orientation) const806 int QGridLayoutEngine::columnCount(Qt::Orientation orientation) const
807 {
808     return q_infos[orientation == Qt::Horizontal].count;
809 }
810 
itemCount() const811 int QGridLayoutEngine::itemCount() const
812 {
813     return q_items.count();
814 }
815 
itemAt(int index) const816 QGridLayoutItem *QGridLayoutEngine::itemAt(int index) const
817 {
818     Q_ASSERT(index >= 0 && index < itemCount());
819     return q_items.at(index);
820 }
821 
effectiveFirstRow(Qt::Orientation orientation) const822 int QGridLayoutEngine::effectiveFirstRow(Qt::Orientation orientation) const
823 {
824     ensureEffectiveFirstAndLastRows();
825     return q_cachedEffectiveFirstRows[orientation == Qt::Vertical];
826 }
827 
effectiveLastRow(Qt::Orientation orientation) const828 int QGridLayoutEngine::effectiveLastRow(Qt::Orientation orientation) const
829 {
830     ensureEffectiveFirstAndLastRows();
831     return q_cachedEffectiveLastRows[orientation == Qt::Vertical];
832 }
833 
setSpacing(qreal spacing,Qt::Orientations orientations)834 void 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 
spacing(Qt::Orientation orientation,const QAbstractLayoutStyleInfo * styleInfo) const844 qreal 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 
setRowSpacing(int row,qreal spacing,Qt::Orientation orientation)853 void 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(row + 1);
860     if (spacing >= 0)
861         rowInfo.spacings[row].setUserValue(spacing);
862     else
863         rowInfo.spacings[row] = QLayoutParameter<qreal>();
864     invalidate();
865 }
866 
rowSpacing(int row,Qt::Orientation orientation) const867 qreal QGridLayoutEngine::rowSpacing(int row, Qt::Orientation orientation) const
868 {
869     QLayoutParameter<qreal> spacing = q_infos[orientation == Qt::Vertical].spacings.value(row);
870     if (!spacing.isDefault())
871         return spacing.value();
872     return q_defaultSpacings[orientation == Qt::Vertical].value();
873 }
874 
setRowStretchFactor(int row,int stretch,Qt::Orientation orientation)875 void QGridLayoutEngine::setRowStretchFactor(int row, int stretch, Qt::Orientation orientation)
876 {
877     Q_ASSERT(row >= 0);
878     Q_ASSERT(stretch >= 0);
879 
880     maybeExpandGrid(row, -1, orientation);
881 
882     QGridLayoutRowInfo &rowInfo = q_infos[orientation == Qt::Vertical];
883     if (row >= rowInfo.stretches.count())
884         rowInfo.stretches.resize(row + 1);
885     rowInfo.stretches[row].setUserValue(stretch);
886 }
887 
rowStretchFactor(int row,Qt::Orientation orientation) const888 int QGridLayoutEngine::rowStretchFactor(int row, Qt::Orientation orientation) const
889 {
890     QStretchParameter stretch = q_infos[orientation == Qt::Vertical].stretches.value(row);
891     if (!stretch.isDefault())
892         return stretch.value();
893     return 0;
894 }
895 
setRowSizeHint(Qt::SizeHint which,int row,qreal size,Qt::Orientation orientation)896 void 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, -1, orientation);
903 
904     QGridLayoutRowInfo &rowInfo = q_infos[orientation == Qt::Vertical];
905     if (row >= rowInfo.boxes.count())
906         rowInfo.boxes.resize(row + 1);
907     rowInfo.boxes[row].q_sizes(which) = size;
908 }
909 
rowSizeHint(Qt::SizeHint which,int row,Qt::Orientation orientation) const910 qreal QGridLayoutEngine::rowSizeHint(Qt::SizeHint which, int row, Qt::Orientation orientation) const
911 {
912     return q_infos[orientation == Qt::Vertical].boxes.value(row).q_sizes(which);
913 }
914 
setRowAlignment(int row,Qt::Alignment alignment,Qt::Orientation orientation)915 void QGridLayoutEngine::setRowAlignment(int row, Qt::Alignment alignment,
916                                         Qt::Orientation orientation)
917 {
918     Q_ASSERT(row >= 0);
919 
920     maybeExpandGrid(row, -1, orientation);
921 
922     QGridLayoutRowInfo &rowInfo = q_infos[orientation == Qt::Vertical];
923     if (row >= rowInfo.alignments.count())
924         rowInfo.alignments.resize(row + 1);
925     rowInfo.alignments[row] = alignment;
926 }
927 
rowAlignment(int row,Qt::Orientation orientation) const928 Qt::Alignment QGridLayoutEngine::rowAlignment(int row, Qt::Orientation orientation) const
929 {
930     Q_ASSERT(row >= 0);
931     return q_infos[orientation == Qt::Vertical].alignments.value(row);
932 }
933 
effectiveAlignment(const QGridLayoutItem * layoutItem) const934 Qt::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(y, 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(x, 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  */
insertItem(QGridLayoutItem * item,int index)959 void QGridLayoutEngine::insertItem(QGridLayoutItem *item, int index)
960 {
961     maybeExpandGrid(item->lastRow(), item->lastColumn());
962 
963     if (index == -1)
964         q_items.append(item);
965     else
966         q_items.insert(index, 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(i, j))
971                 qWarning("QGridLayoutEngine::addItem: Cell (%d, %d) already taken", i, j);
972             setItemAt(i, j, item);
973         }
974     }
975 }
976 
addItem(QGridLayoutItem * item)977 void QGridLayoutEngine::addItem(QGridLayoutItem *item)
978 {
979     insertItem(item, -1);
980 }
981 
removeItem(QGridLayoutItem * item)982 void 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(i, j) == item)
991                 setItemAt(i, j, nullptr);
992         }
993     }
994 
995     q_items.removeAll(item);
996 }
997 
998 
itemAt(int row,int column,Qt::Orientation orientation) const999 QGridLayoutItem *QGridLayoutEngine::itemAt(int row, int column, Qt::Orientation orientation) const
1000 {
1001     if (orientation == Qt::Horizontal)
1002         qSwap(row, column);
1003     if (uint(row) >= uint(rowCount()) || uint(column) >= uint(columnCount()))
1004         return nullptr;
1005     return q_grid.at((row * internalGridColumnCount()) + column);
1006 }
1007 
invalidate()1008 void 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 
visualRect(QRectF * geom,Qt::LayoutDirection dir,const QRectF & contentsRect)1022 static void visualRect(QRectF *geom, Qt::LayoutDirection dir, const QRectF &contentsRect)
1023 {
1024     if (dir == Qt::RightToLeft)
1025         geom->moveRight(contentsRect.right() - (geom->left() - contentsRect.left()));
1026 }
1027 
setGeometries(const QRectF & contentsGeometry,const QAbstractLayoutStyleInfo * styleInfo)1028 void QGridLayoutEngine::setGeometries(const QRectF &contentsGeometry, const QAbstractLayoutStyleInfo *styleInfo)
1029 {
1030     if (rowCount() < 1 || columnCount() < 1)
1031         return;
1032 
1033     ensureGeometries(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(item->firstColumn());
1039         qreal y = q_yy.at(item->firstRow());
1040         qreal width = q_widths.at(item->lastColumn());
1041         qreal height = q_heights.at(item->lastRow());
1042 
1043         if (item->columnSpan() != 1)
1044             width += q_xx.at(item->lastColumn()) - x;
1045         if (item->rowSpan() != 1)
1046             height += q_yy.at(item->lastRow()) - y;
1047 
1048         const Qt::Alignment align = effectiveAlignment(item);
1049         QRectF geom = item->geometryWithin(contentsGeometry.x() + x, contentsGeometry.y() + y,
1050                                                width, height, q_descents.at(item->lastRow()), align, 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(qround(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(qround(geom.y()));
1060         }
1061         visualRect(&geom, visualDirection(), contentsGeometry);
1062         item->setGeometry(geom);
1063     }
1064 }
1065 
1066 // ### candidate for deletion
cellRect(const QRectF & contentsGeometry,int row,int column,int rowSpan,int columnSpan,const QAbstractLayoutStyleInfo * styleInfo) const1067 QRectF 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(contentsGeometry.size(), styleInfo);
1075 
1076     int lastColumn = qMax(column + columnSpan, columnCount()) - 1;
1077     int lastRow = qMax(row + rowSpan, 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 
sizeHint(Qt::SizeHint which,const QSizeF & constraint,const QAbstractLayoutStyleInfo * styleInfo) const1092 QSizeF 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(&q_columnData, &sizehint_totalBoxes[Hor], nullptr, nullptr, Qt::Horizontal, styleInfo);
1104                 QVector<qreal> sizehint_xx;
1105                 QVector<qreal> sizehint_widths;
1106 
1107                 sizehint_xx.resize(columnCount());
1108                 sizehint_widths.resize(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(0, columnCount(), width, sizehint_xx.data(), sizehint_widths.data(),
1113                         nullptr, sizehint_totalBoxes[Hor], q_infos[Hor], m_snapToPixelGrid);
1114                 ensureColumnAndRowData(&q_rowData, &sizehint_totalBoxes[Ver], sizehint_xx.data(), sizehint_widths.data(), 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(&q_rowData, &sizehint_totalBoxes[Ver], nullptr, nullptr, Qt::Vertical, styleInfo);
1121                 QVector<qreal> sizehint_yy;
1122                 QVector<qreal> sizehint_heights;
1123 
1124                 sizehint_yy.resize(rowCount());
1125                 sizehint_heights.resize(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(0, rowCount(), height, sizehint_yy.data(), sizehint_heights.data(),
1130                         nullptr, sizehint_totalBoxes[Ver], q_infos[Ver], m_snapToPixelGrid);
1131                 ensureColumnAndRowData(&q_columnData, &sizehint_totalBoxes[Hor], sizehint_yy.data(), sizehint_heights.data(), 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(&q_columnData, &q_totalBoxes[Hor], nullptr, nullptr, Qt::Horizontal, styleInfo);
1141     ensureColumnAndRowData(&q_rowData, &q_totalBoxes[Ver], nullptr, nullptr, Qt::Vertical, styleInfo);
1142     return QSizeF(q_totalBoxes[Hor].q_sizes(which), q_totalBoxes[Ver].q_sizes(which));
1143 }
1144 
controlTypes(LayoutSide side) const1145 QLayoutPolicy::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 
transpose()1159 void 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(q_defaultSpacings[Hor], q_defaultSpacings[Ver]);
1167     qSwap(q_infos[Hor], q_infos[Ver]);
1168 
1169     regenerateGrid();
1170 }
1171 
setVisualDirection(Qt::LayoutDirection direction)1172 void QGridLayoutEngine::setVisualDirection(Qt::LayoutDirection direction)
1173 {
1174     m_visualDirection = direction;
1175 }
1176 
visualDirection() const1177 Qt::LayoutDirection QGridLayoutEngine::visualDirection() const
1178 {
1179     return m_visualDirection;
1180 }
1181 
1182 #ifdef QGRIDLAYOUTENGINE_DEBUG
dump(int indent) const1183 void 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 
maybeExpandGrid(int row,int column,Qt::Orientation orientation)1231 void QGridLayoutEngine::maybeExpandGrid(int row, int column, Qt::Orientation orientation)
1232 {
1233     invalidate();   // ### move out of here?
1234 
1235     if (orientation == Qt::Horizontal)
1236         qSwap(row, 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(row + 1, rowCount());
1245     q_infos[Hor].count = qMax(column + 1, 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(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 
regenerateGrid()1269 void QGridLayoutEngine::regenerateGrid()
1270 {
1271     q_grid.fill(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(j, k, item);
1279             }
1280         }
1281     }
1282 }
1283 
setItemAt(int row,int column,QGridLayoutItem * item)1284 void 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 
insertOrRemoveRows(int row,int delta,Qt::Orientation orientation)1291 void 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(oldRowCount + delta - 1, -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(internalGridRowCount() * internalGridColumnCount());
1310     regenerateGrid();
1311 }
1312 
fillRowData(QGridLayoutRowData * rowData,const qreal * colPositions,const qreal * colSizes,Qt::Orientation orientation,const QAbstractLayoutStyleInfo * styleInfo) const1313 void 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 - 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(row).isDefault()
1345                 && rowInfo.stretches.value(row).isDefault()
1346                 && rowInfo.boxes.value(row) == QGridLayoutBox())
1347             rowData->ignore.setBit(row, true);
1348 
1349         if (rowInfo.spacings.value(row).isUser()) {
1350             rowData->spacings[row] = rowInfo.spacings.at(row).value();
1351         } else if (!defaultSpacing.isDefault()) {
1352             rowData->spacings[row] = defaultSpacing.value();
1353         }
1354 
1355         rowData->stretches[row] = rowInfo.stretches.value(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(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(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 + itemRow))
1407                             ++effectiveRowSpan;
1408                     }
1409 
1410                     QGridLayoutBox *box;
1411                     if (effectiveRowSpan == 1) {
1412                         box = &rowBox;
1413                         if (!userRowStretch && itemStretch != 0)
1414                             rowStretch = qMax(rowStretch, itemStretch);
1415                     } else {
1416                         QGridLayoutMultiCellData &multiCell =
1417                                 rowData->multiCellMap[qMakePair(row, 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(item->box(orientation, m_snapToPixelGrid, length));
1433                     } else {
1434                         box->combine(item->box(orientation, 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(row);
1449             rowBoxInfo.normalize();
1450             rowBox.q_minimumSize = qMax(rowBox.q_minimumSize, rowBoxInfo.q_minimumSize);
1451             rowBox.q_maximumSize = qMax(rowBox.q_minimumSize,
1452                                         (rowBoxInfo.q_maximumSize != FLT_MAX ?
1453                                         rowBoxInfo.q_maximumSize : rowBox.q_maximumSize));
1454             rowBox.q_preferredSize = qBound(rowBox.q_minimumSize,
1455                                             qMax(rowBox.q_preferredSize, rowBoxInfo.q_preferredSize),
1456                                             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(row))
1477                 continue;
1478 
1479             if (prevRow != -1 && !rowInfo.spacings.value(prevRow).isUser()) {
1480                 qreal &rowSpacing = rowData->spacings[prevRow];
1481                 for (int column = 0; column < columnInfo.count; ++column) {
1482                     QGridLayoutItem *item1 = itemAt(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(prevRow).q_minimumSize;
1501                             qreal width2 = rowData->boxes.at(row).q_minimumSize;
1502                             QRectF rect1 = item1->geometryWithin(0.0, 0.0, width1, FLT_MAX, -1.0, effectiveAlignment(item1), m_snapToPixelGrid);
1503                             QRectF rect2 = item2->geometryWithin(0.0, 0.0, width2, FLT_MAX, -1.0, effectiveAlignment(item2), m_snapToPixelGrid);
1504                             spacing -= (width1 - (rect1.x() + rect1.width())) + rect2.x();
1505                         } else {
1506                             const QGridLayoutBox &box1 = rowData->boxes.at(prevRow);
1507                             const QGridLayoutBox &box2 = rowData->boxes.at(row);
1508                             qreal height1 = box1.q_minimumSize;
1509                             qreal height2 = box2.q_minimumSize;
1510                             qreal rowDescent1 = fixedDescent(box1.q_minimumDescent,
1511                                                              box1.q_minimumAscent, height1);
1512                             qreal rowDescent2 = fixedDescent(box2.q_minimumDescent,
1513                                                              box2.q_minimumAscent, height2);
1514                             QRectF rect1 = item1->geometryWithin(0.0, 0.0, FLT_MAX, height1,
1515                                                                  rowDescent1, effectiveAlignment(item1), m_snapToPixelGrid);
1516                             QRectF rect2 = item2->geometryWithin(0.0, 0.0, FLT_MAX, height2,
1517                                                                  rowDescent2, effectiveAlignment(item2), m_snapToPixelGrid);
1518                             spacing -= (height1 - (rect1.y() + rect1.height())) + rect2.y();
1519                         }
1520                         rowSpacing = qMax(spacing, 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(prevRow).isUser()) {
1535             qreal windowMargin = styleInfo->windowMargin(orientation);
1536             qreal &rowSpacing = rowData->spacings[prevRow];
1537             rowSpacing = qMax(windowMargin, rowSpacing);
1538         }
1539     }
1540 }
1541 
ensureEffectiveFirstAndLastRows() const1542 void 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 
ensureColumnAndRowData(QGridLayoutRowData * rowData,QGridLayoutBox * totalBox,const qreal * colPositions,const qreal * colSizes,Qt::Orientation orientation,const QAbstractLayoutStyleInfo * styleInfo) const1567 void 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(rowCount(orientation));
1583     fillRowData(rowData, colPositions, colSizes, orientation, styleInfo);
1584     const QGridLayoutRowInfo &rowInfo = q_infos[orientation == Qt::Vertical];
1585     rowData->distributeMultiCells(rowInfo, m_snapToPixelGrid);
1586     *totalBox = rowData->totalBox(0, 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  */
ensureDynamicConstraint() const1598 bool 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("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 
hasDynamicConstraint() const1621 bool 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  */
constraintOrientation() const1631 Qt::Orientation QGridLayoutEngine::constraintOrientation() const
1632 {
1633     (void)ensureDynamicConstraint();
1634     return (Qt::Orientation)q_cachedConstraintOrientation;
1635 }
1636 
ensureGeometries(const QSizeF & size,const QAbstractLayoutStyleInfo * styleInfo) const1637 void 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(columnCount());
1646     q_widths.resize(columnCount());
1647     q_yy.resize(rowCount());
1648     q_heights.resize(rowCount());
1649     q_descents.resize(rowCount());
1650 
1651     if (constraintOrientation() != Qt::Horizontal) {
1652         //We might have items whose height depends on their width (HFW)
1653         ensureColumnAndRowData(&q_columnData, &q_totalBoxes[Hor], nullptr, nullptr, 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(0, columnCount(), size.width(), q_xx.data(), q_widths.data(),
1657                 nullptr, q_totalBoxes[Hor], q_infos[Hor], m_snapToPixelGrid);
1658         ensureColumnAndRowData(&q_rowData, &q_totalBoxes[Ver], q_xx.data(), q_widths.data(), Qt::Vertical, styleInfo);
1659         //Calculate row heights and positions, and put results in q_yy.data() and q_heights.data()
1660         q_rowData.calculateGeometries(0, rowCount(), size.height(), q_yy.data(), q_heights.data(),
1661                 q_descents.data(), q_totalBoxes[Ver], q_infos[Ver], m_snapToPixelGrid);
1662     } else {
1663         //We have items whose width depends on their height (WFH)
1664         ensureColumnAndRowData(&q_rowData, &q_totalBoxes[Ver], nullptr, nullptr, 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(0, rowCount(), size.height(), q_yy.data(), q_heights.data(),
1668                 q_descents.data(), q_totalBoxes[Ver], q_infos[Ver], m_snapToPixelGrid);
1669         ensureColumnAndRowData(&q_columnData, &q_totalBoxes[Hor], q_yy.data(), q_heights.data(), Qt::Horizontal, styleInfo);
1670         //Calculate row heights and positions, and put results in q_yy.data() and q_heights.data()
1671         q_columnData.calculateGeometries(0, columnCount(), size.width(), q_xx.data(), q_widths.data(),
1672                 nullptr, q_totalBoxes[Hor], q_infos[Hor], m_snapToPixelGrid);
1673     }
1674 }
1675 
1676 QT_END_NAMESPACE
1677