From 604c51f1fc5c79b7fad12cda911b06b9e6e5005f Mon Sep 17 00:00:00 2001 From: John Tapsell Date: Mon, 15 Nov 2010 12:55:06 +0100 Subject: Fix item alignment in layouts bigger than the items thay they contain MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Task-number: QTBUG-13551 Task-number: QTBUG-7756 Merge-request: 894 Reviewed-by: Jan-Arve Sæther --- src/gui/graphicsview/qgridlayoutengine.cpp | 92 ++++++++++++++-------- src/gui/graphicsview/qgridlayoutengine_p.h | 7 +- .../tst_qgraphicsgridlayout.cpp | 70 +++++++++++++--- .../tst_qgraphicslinearlayout.cpp | 85 ++++++++++++++++++++ 4 files changed, 208 insertions(+), 46 deletions(-) diff --git a/src/gui/graphicsview/qgridlayoutengine.cpp b/src/gui/graphicsview/qgridlayoutengine.cpp index b7fe5a0..f1055ba 100644 --- a/src/gui/graphicsview/qgridlayoutengine.cpp +++ b/src/gui/graphicsview/qgridlayoutengine.cpp @@ -166,7 +166,7 @@ void QGridLayoutRowData::reset(int count) hasIgnoreFlag = false; } -void QGridLayoutRowData::distributeMultiCells() +void QGridLayoutRowData::distributeMultiCells(const QGridLayoutRowInfo &rowInfo) { MultiCellMap::const_iterator i = multiCellMap.constBegin(); for (; i != multiCellMap.constEnd(); ++i) { @@ -185,7 +185,7 @@ void QGridLayoutRowData::distributeMultiCells() qreal extra = compare(box, totalBox, j); if (extra > 0.0) { calculateGeometries(start, end, box.q_sizes(j), dummy.data(), newSizes.data(), - 0, totalBox); + 0, totalBox, rowInfo); for (int k = 0; k < span; ++k) extras[k].q_sizes(j) = newSizes[k]; @@ -202,11 +202,12 @@ void QGridLayoutRowData::distributeMultiCells() void QGridLayoutRowData::calculateGeometries(int start, int end, qreal targetSize, qreal *positions, qreal *sizes, qreal *descents, - const QGridLayoutBox &totalBox) + const QGridLayoutBox &totalBox, + const QGridLayoutRowInfo &rowInfo) { Q_ASSERT(end > start); - targetSize = qBound(totalBox.q_minimumSize, targetSize, totalBox.q_maximumSize); + targetSize = qMax(totalBox.q_minimumSize, targetSize); int n = end - start; QVarLengthArray newSizes(n); @@ -246,16 +247,22 @@ void QGridLayoutRowData::calculateGeometries(int start, int end, qreal targetSiz } } } else { - stealBox(start, end, PreferredSize, positions, sizes); + bool isLargerThanMaximum = (targetSize > totalBox.q_maximumSize); + if (isLargerThanMaximum) { + stealBox(start, end, MaximumSize, positions, sizes); + sumAvailable = targetSize - totalBox.q_maximumSize; + } else { + stealBox(start, end, PreferredSize, positions, sizes); + sumAvailable = targetSize - totalBox.q_preferredSize; + } - sumAvailable = targetSize - totalBox.q_preferredSize; if (sumAvailable > 0.0) { qreal sumCurrentAvailable = sumAvailable; bool somethingHasAMaximumSize = false; - qreal sumPreferredSizes = 0.0; + qreal sumSizes = 0.0; for (int i = 0; i < n; ++i) - sumPreferredSizes += sizes[i]; + sumSizes += sizes[i]; for (int i = 0; i < n; ++i) { if (ignore.testBit(start + i)) { @@ -265,7 +272,16 @@ void QGridLayoutRowData::calculateGeometries(int start, int end, qreal targetSiz } const QGridLayoutBox &box = boxes.at(start + i); - qreal desired = box.q_maximumSize - box.q_preferredSize; + qreal boxSize; + + qreal desired; + if (isLargerThanMaximum) { + boxSize = box.q_maximumSize; + desired = rowInfo.boxes.value(start + i).q_maximumSize - boxSize; + } else { + boxSize = box.q_preferredSize; + desired = box.q_maximumSize - boxSize; + } if (desired == 0.0) { newSizes[i] = sizes[i]; factors[i] = 0.0; @@ -284,17 +300,17 @@ void QGridLayoutRowData::calculateGeometries(int start, int end, qreal targetSiz } else if (stretch <= 0) { factors[i] = 0.0; } else { - qreal ultimatePreferredSize; - qreal ultimateSumPreferredSizes; - qreal x = ((stretch * sumPreferredSizes) - - (sumStretches * box.q_preferredSize)) + qreal ultimateSize; + qreal ultimateSumSizes; + qreal x = ((stretch * sumSizes) + - (sumStretches * boxSize)) / (sumStretches - stretch); if (x >= 0.0) { - ultimatePreferredSize = box.q_preferredSize + x; - ultimateSumPreferredSizes = sumPreferredSizes + x; + ultimateSize = boxSize + x; + ultimateSumSizes = sumSizes + x; } else { - ultimatePreferredSize = box.q_preferredSize; - ultimateSumPreferredSizes = (sumStretches * box.q_preferredSize) + ultimateSize = boxSize; + ultimateSumSizes = (sumStretches * boxSize) / stretch; } @@ -303,17 +319,17 @@ void QGridLayoutRowData::calculateGeometries(int start, int end, qreal targetSiz (at the expense of the stretch factors, which are not fully respected during the transition). */ - ultimatePreferredSize = ultimatePreferredSize * 3 / 2; - ultimateSumPreferredSizes = ultimateSumPreferredSizes * 3 / 2; + ultimateSize = ultimateSize * 3 / 2; + ultimateSumSizes = ultimateSumSizes * 3 / 2; - qreal beta = ultimateSumPreferredSizes - sumPreferredSizes; + qreal beta = ultimateSumSizes - sumSizes; if (!beta) { factors[i] = 1; } else { qreal alpha = qMin(sumCurrentAvailable, beta); - qreal ultimateFactor = (stretch * ultimateSumPreferredSizes / sumStretches) - - (box.q_preferredSize); - qreal transitionalFactor = sumCurrentAvailable * (ultimatePreferredSize - box.q_preferredSize) / beta; + qreal ultimateFactor = (stretch * ultimateSumSizes / sumStretches) + - (boxSize); + qreal transitionalFactor = sumCurrentAvailable * (ultimateSize - boxSize) / beta; factors[i] = ((alpha * ultimateFactor) + ((beta - alpha) * transitionalFactor)) / beta; @@ -336,11 +352,16 @@ void QGridLayoutRowData::calculateGeometries(int start, int end, qreal targetSiz if (newSizes[i] >= 0.0) continue; - const QGridLayoutBox &box = boxes.at(start + i); + qreal maxBoxSize; + if (isLargerThanMaximum) + maxBoxSize = rowInfo.boxes.value(start + i).q_maximumSize; + else + maxBoxSize = boxes.at(start + i).q_maximumSize; + qreal avail = sumCurrentAvailable * factors[i] / sumFactors; - if (sizes[i] + avail >= box.q_maximumSize) { - newSizes[i] = box.q_maximumSize; - sumCurrentAvailable -= box.q_maximumSize - sizes[i]; + if (sizes[i] + avail >= maxBoxSize) { + newSizes[i] = maxBoxSize; + sumCurrentAvailable -= maxBoxSize - sizes[i]; sumFactors -= factors[i]; keepGoing = (sumCurrentAvailable > 0.0); if (!keepGoing) @@ -1126,7 +1147,7 @@ QSizeF QGridLayoutEngine::sizeHint(const QLayoutStyleInfo &styleInfo, Qt::SizeHi //Calculate column widths and positions, and put results in q_xx.data() and q_widths.data() so that we can use this information as //constraints to find the row heights q_columnData.calculateGeometries(0, columnCount(), width, sizehint_xx.data(), sizehint_widths.data(), - 0, sizehint_totalBoxes[Hor]); + 0, sizehint_totalBoxes[Hor], q_infos[Hor]); ensureColumnAndRowData(&q_rowData, &sizehint_totalBoxes[Ver], styleInfo, sizehint_xx.data(), sizehint_widths.data(), Qt::Vertical); sizeHintCalculated = true; } @@ -1143,7 +1164,7 @@ QSizeF QGridLayoutEngine::sizeHint(const QLayoutStyleInfo &styleInfo, Qt::SizeHi //Calculate row heights and positions, and put results in q_yy.data() and q_heights.data() so that we can use this information as //constraints to find the column widths q_rowData.calculateGeometries(0, rowCount(), height, sizehint_yy.data(), sizehint_heights.data(), - 0, sizehint_totalBoxes[Ver]); + 0, sizehint_totalBoxes[Ver], q_infos[Ver]); ensureColumnAndRowData(&q_columnData, &sizehint_totalBoxes[Hor], styleInfo, sizehint_yy.data(), sizehint_heights.data(), Qt::Vertical); sizeHintCalculated = true; } @@ -1159,7 +1180,7 @@ QSizeF QGridLayoutEngine::sizeHint(const QLayoutStyleInfo &styleInfo, Qt::SizeHi sizehint_totalBoxes[Hor] = q_totalBoxes[Hor]; sizehint_totalBoxes[Ver] = q_totalBoxes[Ver]; } - } + } switch (which) { case Qt::MinimumSize: @@ -1617,7 +1638,8 @@ void QGridLayoutEngine::ensureColumnAndRowData(QGridLayoutRowData *rowData, QGri { rowData->reset(rowCount(orientation)); fillRowData(rowData, styleInfo, colPositions, colSizes, orientation); - rowData->distributeMultiCells(); + const QGridLayoutRowInfo &rowInfo = q_infos[orientation == Qt::Vertical]; + rowData->distributeMultiCells(rowInfo); *totalBox = rowData->totalBox(0, rowCount(orientation)); //We have items whose width depends on their height } @@ -1686,22 +1708,22 @@ void QGridLayoutEngine::ensureGeometries(const QLayoutStyleInfo &styleInfo, //Calculate column widths and positions, and put results in q_xx.data() and q_widths.data() so that we can use this information as //constraints to find the row heights q_columnData.calculateGeometries(0, columnCount(), size.width(), q_xx.data(), q_widths.data(), - 0, q_totalBoxes[Hor]); + 0, q_totalBoxes[Hor], q_infos[Hor] ); ensureColumnAndRowData(&q_rowData, &q_totalBoxes[Ver], styleInfo, q_xx.data(), q_widths.data(), Qt::Vertical); //Calculate row heights and positions, and put results in q_yy.data() and q_heights.data() q_rowData.calculateGeometries(0, rowCount(), size.height(), q_yy.data(), q_heights.data(), - q_descents.data(), q_totalBoxes[Ver]); + q_descents.data(), q_totalBoxes[Ver], q_infos[Ver]); } else { //We have items whose height depends on their width ensureColumnAndRowData(&q_rowData, &q_totalBoxes[Ver], styleInfo, NULL, NULL, Qt::Vertical); //Calculate row heights and positions, and put results in q_yy.data() and q_heights.data() so that we can use this information as //constraints to find the column widths q_rowData.calculateGeometries(0, rowCount(), size.height(), q_yy.data(), q_heights.data(), - q_descents.data(), q_totalBoxes[Ver]); + q_descents.data(), q_totalBoxes[Ver], q_infos[Ver]); ensureColumnAndRowData(&q_columnData, &q_totalBoxes[Hor], styleInfo, q_yy.data(), q_heights.data(), Qt::Horizontal); //Calculate row heights and positions, and put results in q_yy.data() and q_heights.data() q_columnData.calculateGeometries(0, columnCount(), size.width(), q_xx.data(), q_widths.data(), - 0, q_totalBoxes[Hor]); + 0, q_totalBoxes[Hor], q_infos[Hor]); } } diff --git a/src/gui/graphicsview/qgridlayoutengine_p.h b/src/gui/graphicsview/qgridlayoutengine_p.h index 02c74b0..44efbba 100644 --- a/src/gui/graphicsview/qgridlayoutengine_p.h +++ b/src/gui/graphicsview/qgridlayoutengine_p.h @@ -224,13 +224,16 @@ public: typedef QMap, QGridLayoutMultiCellData> MultiCellMap; +class QGridLayoutRowInfo; + class QGridLayoutRowData { public: void reset(int count); - void distributeMultiCells(); + void distributeMultiCells(const QGridLayoutRowInfo &rowInfo); void calculateGeometries(int start, int end, qreal targetSize, qreal *positions, qreal *sizes, - qreal *descents, const QGridLayoutBox &totalBox); + qreal *descents, const QGridLayoutBox &totalBox, + const QGridLayoutRowInfo &rowInfo); QGridLayoutBox totalBox(int start, int end) const; void stealBox(int start, int end, int which, qreal *positions, qreal *sizes); diff --git a/tests/auto/qgraphicsgridlayout/tst_qgraphicsgridlayout.cpp b/tests/auto/qgraphicsgridlayout/tst_qgraphicsgridlayout.cpp index 7170059..248778f 100644 --- a/tests/auto/qgraphicsgridlayout/tst_qgraphicsgridlayout.cpp +++ b/tests/auto/qgraphicsgridlayout/tst_qgraphicsgridlayout.cpp @@ -700,6 +700,10 @@ void tst_QGraphicsGridLayout::columnMaximumWidth() layout->setContentsMargins(0, 0, 0, 0); layout->setSpacing(0); + QCOMPARE(layout->minimumSize(), QSizeF(10+10+10, 10+10)); + QCOMPARE(layout->preferredSize(), QSizeF(25+25+25, 25+25)); + QCOMPARE(layout->maximumSize(), QSizeF(50+50+50, 50+50)); + // should at least be a very large number QVERIFY(layout->columnMaximumWidth(0) >= 10000); QCOMPARE(layout->columnMaximumWidth(0), layout->columnMaximumWidth(1)); @@ -707,17 +711,65 @@ void tst_QGraphicsGridLayout::columnMaximumWidth() layout->setColumnMaximumWidth(0, 20); layout->setColumnMaximumWidth(2, 60); - view.show(); - widget->show(); + QCOMPARE(layout->minimumSize(), QSizeF(10+10+10, 10+10)); + QCOMPARE(layout->preferredSize(), QSizeF(20+25+25, 25+25)); + QCOMPARE(layout->maximumSize(), QSizeF(20+50+60, 50+50)); + QCOMPARE(layout->maximumSize(), widget->maximumSize()); + widget->resize(widget->effectiveSizeHint(Qt::PreferredSize)); - QApplication::processEvents(); + layout->activate(); - QCOMPARE(layout->itemAt(0,0)->geometry().width(), 20.0); - QCOMPARE(layout->itemAt(1,0)->geometry().width(), 20.0); - QCOMPARE(layout->itemAt(0,1)->geometry().width(), 25.0); - QCOMPARE(layout->itemAt(1,1)->geometry().width(), 25.0); - QCOMPARE(layout->itemAt(0,2)->geometry().width(), 25.0); - QCOMPARE(layout->itemAt(1,2)->geometry().width(), 25.0); + QCOMPARE(layout->itemAt(0,0)->geometry(), QRectF(0, 0, 20, 25)); + QCOMPARE(layout->itemAt(1,0)->geometry(), QRectF(0, 25, 20, 25)); + QCOMPARE(layout->itemAt(0,1)->geometry(), QRectF(20, 0, 25, 25)); + QCOMPARE(layout->itemAt(1,1)->geometry(), QRectF(20, 25, 25, 25)); + QCOMPARE(layout->itemAt(0,2)->geometry(), QRectF(45, 0, 25, 25)); + QCOMPARE(layout->itemAt(1,2)->geometry(), QRectF(45, 25, 25, 25)); + + layout->setColumnAlignment(2, Qt::AlignCenter); //FIXME This shouldn't be needed because the documentation says that center is default + widget->resize(widget->effectiveSizeHint(Qt::MaximumSize)); + layout->activate(); + QCOMPARE(layout->geometry(), QRectF(0,0,20+50+60, 50+50)); + QCOMPARE(layout->itemAt(0,0)->geometry(), QRectF(0, 0, 20, 50)); + QCOMPARE(layout->itemAt(1,0)->geometry(), QRectF(0, 50, 20, 50)); + QCOMPARE(layout->itemAt(0,1)->geometry(), QRectF(20, 0, 50, 50)); + QCOMPARE(layout->itemAt(1,1)->geometry(), QRectF(20, 50, 50, 50)); + QCOMPARE(layout->itemAt(0,2)->geometry(), QRectF(75, 0, 50, 50)); + QCOMPARE(layout->itemAt(1,2)->geometry(), QRectF(75, 50, 50, 50)); + + for(int i = 0; i < layout->count(); i++) + layout->setAlignment(layout->itemAt(i), Qt::AlignRight | Qt::AlignBottom); + layout->activate(); + QCOMPARE(layout->itemAt(0,0)->geometry(), QRectF(0, 0, 20, 50)); + QCOMPARE(layout->itemAt(1,0)->geometry(), QRectF(0, 50, 20, 50)); + QCOMPARE(layout->itemAt(0,1)->geometry(), QRectF(20, 0, 50, 50)); + QCOMPARE(layout->itemAt(1,1)->geometry(), QRectF(20, 50, 50, 50)); + QCOMPARE(layout->itemAt(0,2)->geometry(), QRectF(80, 0, 50, 50)); + QCOMPARE(layout->itemAt(1,2)->geometry(), QRectF(80, 50, 50, 50)); + for(int i = 0; i < layout->count(); i++) + layout->setAlignment(layout->itemAt(i), Qt::AlignCenter); + + layout->setMaximumSize(layout->maximumSize() + QSizeF(60,60)); + widget->resize(widget->effectiveSizeHint(Qt::MaximumSize)); + layout->activate(); + + QCOMPARE(layout->itemAt(0,0)->geometry(), QRectF(0, 15, 20, 50)); + QCOMPARE(layout->itemAt(1,0)->geometry(), QRectF(0, 95, 20, 50)); + QCOMPARE(layout->itemAt(0,1)->geometry(), QRectF(20+30, 15, 50, 50)); + QCOMPARE(layout->itemAt(1,1)->geometry(), QRectF(20+30, 95, 50, 50)); + QCOMPARE(layout->itemAt(0,2)->geometry(), QRectF(20+60+50+5, 15, 50, 50)); + QCOMPARE(layout->itemAt(1,2)->geometry(), QRectF(20+60+50+5, 95, 50, 50)); + + layout->setMaximumSize(layout->preferredSize() + QSizeF(20,20)); + widget->resize(widget->effectiveSizeHint(Qt::MaximumSize)); + layout->activate(); + + QCOMPARE(layout->itemAt(0,0)->geometry(), QRectF(0, 0, 20, 35)); + QCOMPARE(layout->itemAt(1,0)->geometry(), QRectF(0, 35, 20, 35)); + QCOMPARE(layout->itemAt(0,1)->geometry(), QRectF(20, 0, 35, 35)); + QCOMPARE(layout->itemAt(1,1)->geometry(), QRectF(20, 35, 35, 35)); + QCOMPARE(layout->itemAt(0,2)->geometry(), QRectF(55, 0, 35, 35)); + QCOMPARE(layout->itemAt(1,2)->geometry(), QRectF(55, 35, 35, 35)); delete widget; } diff --git a/tests/auto/qgraphicslinearlayout/tst_qgraphicslinearlayout.cpp b/tests/auto/qgraphicslinearlayout/tst_qgraphicslinearlayout.cpp index 6107fa1..e271aee 100644 --- a/tests/auto/qgraphicslinearlayout/tst_qgraphicslinearlayout.cpp +++ b/tests/auto/qgraphicslinearlayout/tst_qgraphicslinearlayout.cpp @@ -103,6 +103,8 @@ private slots: void removeLayout(); void avoidRecursionInInsertItem(); void styleInfoLeak(); + void testAlignmentInLargerLayout(); + void testOffByOneInLargerLayout(); // Task specific tests void task218400_insertStretchCrash(); @@ -1465,6 +1467,89 @@ void tst_QGraphicsLinearLayout::task218400_insertStretchCrash() form->setLayout(layout); // crash } +void tst_QGraphicsLinearLayout::testAlignmentInLargerLayout() +{ + QGraphicsScene *scene = new QGraphicsScene; + QGraphicsWidget *form = new QGraphicsWidget; + scene->addItem(form); + QGraphicsLinearLayout *layout = new QGraphicsLinearLayout(Qt::Vertical, form); + layout->setSpacing(0); + layout->setContentsMargins(0,0,0,0); + + QGraphicsWidget *a = new QGraphicsWidget; + a->setMaximumSize(100,100); + layout->addItem(a); + + QCOMPARE(form->maximumSize(), QSizeF(100,100)); + QCOMPARE(layout->maximumSize(), QSizeF(100,100)); + layout->setMinimumSize(QSizeF(200,200)); + layout->setMaximumSize(QSizeF(200,200)); + + layout->setAlignment(a, Qt::AlignCenter); + layout->activate(); + QCOMPARE(a->geometry(), QRectF(50,50,100,100)); + + layout->setAlignment(a, Qt::AlignRight | Qt::AlignBottom); + layout->activate(); + QCOMPARE(a->geometry(), QRectF(100,100,100,100)); + + layout->setAlignment(a, Qt::AlignHCenter | Qt::AlignTop); + layout->activate(); + QCOMPARE(a->geometry(), QRectF(50,0,100,100)); + + QGraphicsWidget *b = new QGraphicsWidget; + b->setMaximumSize(100,100); + layout->addItem(b); + + layout->setAlignment(a, Qt::AlignCenter); + layout->setAlignment(b, Qt::AlignCenter); + layout->activate(); + QCOMPARE(a->geometry(), QRectF(50,0,100,100)); + QCOMPARE(b->geometry(), QRectF(50,100,100,100)); + + layout->setAlignment(a, Qt::AlignRight | Qt::AlignBottom); + layout->setAlignment(b, Qt::AlignLeft | Qt::AlignTop); + layout->activate(); + QCOMPARE(a->geometry(), QRectF(100,0,100,100)); + QCOMPARE(b->geometry(), QRectF(0,100,100,100)); +} + +void tst_QGraphicsLinearLayout::testOffByOneInLargerLayout() { + QGraphicsScene *scene = new QGraphicsScene; + QGraphicsWidget *form = new QGraphicsWidget; + scene->addItem(form); + QGraphicsLinearLayout *layout = new QGraphicsLinearLayout(Qt::Vertical, form); + layout->setSpacing(0); + layout->setContentsMargins(0,0,0,0); + + QGraphicsWidget *a = new QGraphicsWidget; + QGraphicsWidget *b = new QGraphicsWidget; + a->setMaximumSize(100,100); + b->setMaximumSize(100,100); + layout->addItem(a); + layout->addItem(b); + + layout->setAlignment(a, Qt::AlignRight | Qt::AlignBottom); + layout->setAlignment(b, Qt::AlignLeft | Qt::AlignTop); + layout->setMinimumSize(QSizeF(101,201)); + layout->setMaximumSize(QSizeF(101,201)); + layout->activate(); + QCOMPARE(a->geometry(), QRectF(1,0.5,100,100)); + QCOMPARE(b->geometry(), QRectF(0,100.5,100,100)); + + layout->setMinimumSize(QSizeF(100,200)); + layout->setMaximumSize(QSizeF(100,200)); + layout->activate(); + QCOMPARE(a->geometry(), QRectF(0,0,100,100)); + QCOMPARE(b->geometry(), QRectF(0,100,100,100)); + + layout->setMinimumSize(QSizeF(99,199)); + layout->setMaximumSize(QSizeF(99,199)); + layout->activate(); + QCOMPARE(a->geometry(), QRectF(0,0,99,99.5)); + QCOMPARE(b->geometry(), QRectF(0,99.5,99,99.5)); +} + QTEST_MAIN(tst_QGraphicsLinearLayout) #include "tst_qgraphicslinearlayout.moc" -- cgit v0.12