src/gui/graphicsview/qgridlayoutengine.cpp
changeset 37 758a864f9613
parent 18 2f34d5167611
--- a/src/gui/graphicsview/qgridlayoutengine.cpp	Fri Sep 17 08:34:18 2010 +0300
+++ b/src/gui/graphicsview/qgridlayoutengine.cpp	Mon Oct 04 01:19:32 2010 +0300
@@ -250,6 +250,7 @@
 
         sumAvailable = targetSize - totalBox.q_preferredSize;
         if (sumAvailable > 0.0) {
+            qreal sumCurrentAvailable = sumAvailable;
             bool somethingHasAMaximumSize = false;
 
             qreal sumPreferredSizes = 0.0;
@@ -308,12 +309,12 @@
                         qreal ultimateFactor = (stretch * ultimateSumPreferredSizes
                                                 / sumStretches)
                                                - (box.q_preferredSize);
-                        qreal transitionalFactor = sumAvailable
+                        qreal transitionalFactor = sumCurrentAvailable
                                                    * (ultimatePreferredSize - box.q_preferredSize)
                                                    / (ultimateSumPreferredSizes
                                                       - sumPreferredSizes);
 
-                        qreal alpha = qMin(sumAvailable,
+                        qreal alpha = qMin(sumCurrentAvailable,
                                            ultimateSumPreferredSizes - sumPreferredSizes);
                         qreal beta = ultimateSumPreferredSizes - sumPreferredSizes;
 
@@ -321,7 +322,7 @@
                                       + ((beta - alpha) * transitionalFactor)) / beta;
                     }
                     sumFactors += factors[i];
-                    if (desired < sumAvailable)
+                    if (desired < sumCurrentAvailable)
                         somethingHasAMaximumSize = true;
 
                     newSizes[i] = -1.0;
@@ -337,12 +338,12 @@
                         continue;
 
                     const QGridLayoutBox &box = boxes.at(start + i);
-                    qreal avail = sumAvailable * factors[i] / sumFactors;
+                    qreal avail = sumCurrentAvailable * factors[i] / sumFactors;
                     if (sizes[i] + avail >= box.q_maximumSize) {
                         newSizes[i] = box.q_maximumSize;
-                        sumAvailable -= box.q_maximumSize - sizes[i];
+                        sumCurrentAvailable -= box.q_maximumSize - sizes[i];
                         sumFactors -= factors[i];
-                        keepGoing = (sumAvailable > 0.0);
+                        keepGoing = (sumCurrentAvailable > 0.0);
                         if (!keepGoing)
                             break;
                     }
@@ -352,7 +353,7 @@
             for (int i = 0; i < n; ++i) {
                 if (newSizes[i] < 0.0) {
                     qreal delta = (sumFactors == 0.0) ? 0.0
-                                                      : sumAvailable * factors[i] / sumFactors;
+                                                      : sumCurrentAvailable * factors[i] / sumFactors;
                     newSizes[i] = sizes[i] + delta;
                 }
             }
@@ -545,6 +546,24 @@
                                            : sizePolicy.verticalPolicy();
 }
 
+/*
+  returns true if the size policy returns true for either hasHeightForWidth()
+  or hasWidthForHeight()
+ */
+bool QGridLayoutItem::hasDynamicConstraint() const
+{
+    return QGraphicsLayoutItemPrivate::get(q_layoutItem)->hasHeightForWidth()
+        || QGraphicsLayoutItemPrivate::get(q_layoutItem)->hasWidthForHeight();
+}
+
+Qt::Orientation QGridLayoutItem::dynamicConstraintOrientation() const
+{
+    if (QGraphicsLayoutItemPrivate::get(q_layoutItem)->hasHeightForWidth())
+        return Qt::Vertical;
+    else //if (QGraphicsLayoutItemPrivate::get(q_layoutItem)->hasWidthForHeight())
+        return Qt::Horizontal;
+}
+
 QSizePolicy::ControlTypes QGridLayoutItem::controlTypes(LayoutSide /* side */) const
 {
     return q_layoutItem->sizePolicy().controlType();
@@ -613,7 +632,14 @@
         qreal cellWidth = width;
         qreal cellHeight = height;
 
-        QSizeF size = effectiveMaxSize().boundedTo(QSizeF(cellWidth, cellHeight));
+        QSize constraint;
+        if (hasDynamicConstraint()) {
+            if (dynamicConstraintOrientation() == Qt::Vertical)
+                constraint.setWidth(cellWidth);
+            else
+                constraint.setHeight(cellHeight);
+        }
+        QSizeF size = effectiveMaxSize(constraint).boundedTo(QSizeF(cellWidth, cellHeight));
         width = size.width();
         height = size.height();
 
@@ -675,13 +701,13 @@
     Note that effectiveSizeHint does not take sizePolicy into consideration,
     (since it only evaluates the hints, as the name implies)
 */
-QSizeF QGridLayoutItem::effectiveMaxSize() const
+QSizeF QGridLayoutItem::effectiveMaxSize(const QSizeF &constraint) const
 {
-    QSizeF size;
+    QSizeF size = constraint;
     bool vGrow = (sizePolicy(Qt::Vertical) & QSizePolicy::GrowFlag) == QSizePolicy::GrowFlag;
     bool hGrow = (sizePolicy(Qt::Horizontal) & QSizePolicy::GrowFlag) == QSizePolicy::GrowFlag;
     if (!vGrow || !hGrow) {
-        QSizeF pref = layoutItem()->effectiveSizeHint(Qt::PreferredSize);
+        QSizeF pref = layoutItem()->effectiveSizeHint(Qt::PreferredSize, constraint);
         if (!vGrow)
             size.setHeight(pref.height());
         if (!hGrow)
@@ -689,7 +715,7 @@
     }
 
     if (!size.isValid()) {
-        QSizeF maxSize = layoutItem()->effectiveSizeHint(Qt::MaximumSize);
+        QSizeF maxSize = layoutItem()->effectiveSizeHint(Qt::MaximumSize, constraint);
         if (size.width() == -1)
             size.setWidth(maxSize.width());
         if (size.height() == -1)
@@ -1010,6 +1036,7 @@
     q_cachedEffectiveLastRows[Ver] = -1;
     q_cachedDataForStyleInfo.invalidate();
     q_cachedSize = QSizeF();
+    q_cachedConstraintOrientation = UnknownConstraint;
 }
 
 static void visualRect(QRectF *geom, Qt::LayoutDirection dir, const QRectF &contentsRect)
@@ -1074,10 +1101,13 @@
 }
 
 QSizeF QGridLayoutEngine::sizeHint(const QLayoutStyleInfo &styleInfo, Qt::SizeHint which,
-                                   const QSizeF & /* constraint */) const
+                                   const QSizeF &constraint) const
 {
     ensureColumnAndRowData(styleInfo);
 
+    if (hasDynamicConstraint())
+        return dynamicallyConstrainedSizeHint(which, constraint);
+
     switch (which) {
     case Qt::MinimumSize:
         return QSizeF(q_totalBoxes[Hor].q_minimumSize, q_totalBoxes[Ver].q_minimumSize);
@@ -1375,7 +1405,11 @@
                         box = &multiCell.q_box;
                         multiCell.q_stretch = itemStretch;
                     }
-                    box->combine(item->box(orientation));
+                    // Items with constraints are not included in the orientation that
+                    // they are constrained (since it depends on the hfw/constraint function).
+                    // They must be combined at a later stage.
+                    if (!item->hasDynamicConstraint() || orientation != item->dynamicConstraintOrientation())
+                        box->combine(item->box(orientation));
 
                     if (effectiveRowSpan == 1) {
                         QSizePolicy::ControlTypes controls = item->controlTypes(top);
@@ -1532,6 +1566,138 @@
     q_cachedDataForStyleInfo = styleInfo;
 }
 
+QSizeF QGridLayoutEngine::dynamicallyConstrainedSizeHint(Qt::SizeHint which,
+                                                         const QSizeF &constraint) const
+{
+    Q_ASSERT(hasDynamicConstraint());
+    if (constraint.width() < 0 && constraint.height() < 0) {
+        // Process the hfw / wfh items that we did not process in fillRowData()
+        const Qt::Orientation constraintOrient = constraintOrientation();
+
+        QGridLayoutRowData rowData = constraintOrient == Qt::Vertical ? q_rowData : q_columnData;
+        for (int i = q_items.count() - 1; i >= 0; --i) {
+            QGridLayoutItem *item = q_items.at(i);
+            if (item->hasDynamicConstraint()) {
+                QGridLayoutBox box = item->box(constraintOrient);
+                QGridLayoutBox &rowBox = rowData.boxes[item->firstRow(constraintOrient)];
+                rowBox.combine(box);
+            }
+        }
+
+        QGridLayoutBox totalBoxes[2];
+        if (constraintOrient == Qt::Vertical) {
+            totalBoxes[Hor] = q_columnData.totalBox(0, columnCount());
+            totalBoxes[Ver] = rowData.totalBox(0, rowCount());
+        } else {
+            totalBoxes[Hor] = rowData.totalBox(0, columnCount());
+            totalBoxes[Ver] = q_rowData.totalBox(0, rowCount());
+        }
+        return QSizeF(totalBoxes[Hor].q_sizes(which), totalBoxes[Ver].q_sizes(which));
+    }
+
+
+    Q_ASSERT(constraint.width() >= 0 || constraint.height() >= 0);
+    q_xx.resize(columnCount());
+    q_yy.resize(rowCount());
+    q_widths.resize(columnCount());
+    q_heights.resize(rowCount());
+    q_descents.resize(rowCount());
+
+
+    const Qt::Orientation orientation = constraintOrientation();
+    QGridLayoutRowData *colData;
+    QGridLayoutRowData constrainedRowData;
+    QGridLayoutBox *totalBox;
+    qreal *sizes;
+    qreal *pos;
+    qreal *descents;
+    qreal targetSize;
+    qreal cCount;
+    qreal rCount;
+
+    if (orientation == Qt::Vertical) {
+        // height for width
+        colData = &q_columnData;
+        totalBox = &q_totalBoxes[Hor];
+        sizes = q_widths.data();
+        pos = q_xx.data();
+        descents = 0;
+        targetSize = constraint.width();
+        cCount = columnCount();
+        rCount = rowCount();
+        constrainedRowData = q_rowData;
+    } else {
+        // width for height
+        colData = &q_rowData;
+        totalBox = &q_totalBoxes[Ver];
+        sizes = q_heights.data();
+        pos = q_yy.data();
+        descents = q_descents.data();
+        targetSize = constraint.height();
+        cCount = rowCount();
+        rCount = columnCount();
+        constrainedRowData = q_columnData;
+    }
+    colData->calculateGeometries(0, cCount, targetSize, pos, sizes, descents, *totalBox);
+    for (int i = q_items.count() - 1; i >= 0; --i) {
+        QGridLayoutItem *item = q_items.at(i);
+
+        if (item->hasDynamicConstraint()) {
+            const qreal size = sizes[item->firstColumn(orientation)];
+            QGridLayoutBox box = item->box(orientation, size);
+            QGridLayoutBox &rowBox = constrainedRowData.boxes[item->firstRow(orientation)];
+            rowBox.combine(box);
+        }
+    }
+    const qreal newSize = constrainedRowData.totalBox(0, rCount).q_sizes(which);
+
+    return (orientation == Qt::Vertical) ? QSizeF(targetSize, newSize) : QSizeF(newSize, targetSize);
+}
+
+
+/**
+   returns false if the layout has contradicting constraints (i.e. some items with a horizontal
+   constraint and other items with a vertical constraint)
+ */
+bool QGridLayoutEngine::ensureDynamicConstraint() const
+{
+    if (q_cachedConstraintOrientation == UnknownConstraint) {
+        for (int i = q_items.count() - 1; i >= 0; --i) {
+            QGridLayoutItem *item = q_items.at(i);
+            if (item->hasDynamicConstraint()) {
+                Qt::Orientation itemConstraintOrientation = item->dynamicConstraintOrientation();
+                if (q_cachedConstraintOrientation == UnknownConstraint) {
+                    q_cachedConstraintOrientation = itemConstraintOrientation;
+                } else if (q_cachedConstraintOrientation != itemConstraintOrientation) {
+                    q_cachedConstraintOrientation = UnfeasibleConstraint;
+                    qWarning("QGridLayoutEngine: Unfeasible, cannot mix horizontal and"
+                             " vertical constraint in the same layout");
+                    return false;
+                }
+            }
+        }
+        if (q_cachedConstraintOrientation == UnknownConstraint)
+            q_cachedConstraintOrientation = NoConstraint;
+    }
+    return true;
+}
+
+bool QGridLayoutEngine::hasDynamicConstraint() const
+{
+    if (!ensureDynamicConstraint())
+        return false;
+    return q_cachedConstraintOrientation != NoConstraint;
+}
+
+/*
+ * return value is only valid if hasConstraint() returns true
+ */
+Qt::Orientation QGridLayoutEngine::constraintOrientation() const
+{
+    (void)ensureDynamicConstraint();
+    return (Qt::Orientation)q_cachedConstraintOrientation;
+}
+
 void QGridLayoutEngine::ensureGeometries(const QLayoutStyleInfo &styleInfo,
                                          const QSizeF &size) const
 {
@@ -1544,10 +1710,74 @@
     q_widths.resize(columnCount());
     q_heights.resize(rowCount());
     q_descents.resize(rowCount());
-    q_columnData.calculateGeometries(0, columnCount(), size.width(), q_xx.data(), q_widths.data(),
-                                     0, q_totalBoxes[Hor]);
-    q_rowData.calculateGeometries(0, rowCount(), size.height(), q_yy.data(), q_heights.data(),
-                                  q_descents.data(), q_totalBoxes[Ver]);
+
+
+    Qt::Orientation orientation = Qt::Vertical;
+    if (hasDynamicConstraint())
+        orientation = constraintOrientation();
+
+    /*
+      In order to do hfw we need to first distribute the columns, then the rows.
+      In order to do wfh we need to first distribute the rows, then the columns.
+
+      If there is no constraint, the order of distributing the rows or columns first is irrelevant.
+      We choose horizontal just to keep the same behaviour as before (however, there shouldn't
+      be any behaviour difference).
+ */
+
+    QGridLayoutRowData *colData;
+    QGridLayoutRowData rowData;
+    qreal *widths;
+    qreal *heights;
+    qreal *xx;
+    qreal *yy;
+    qreal *xdescents = 0;
+    qreal *ydescents = 0;
+    qreal cCount;
+    qreal rCount;
+    QSizeF oSize = size;
+    if (orientation == Qt::Vertical) {
+        // height for width
+        colData = &q_columnData;
+        rowData = q_rowData;
+        widths = q_widths.data();
+        heights = q_heights.data();
+        xx = q_xx.data();
+        yy = q_yy.data();
+        cCount = columnCount();
+        rCount = rowCount();
+        ydescents = q_descents.data();
+    } else {
+        // width for height
+        colData = &q_rowData;
+        rowData = q_columnData;
+        widths = q_heights.data();
+        heights = q_widths.data();
+        xx = q_yy.data();
+        yy = q_xx.data();
+        cCount = rowCount();
+        rCount = columnCount();
+        xdescents = q_descents.data();
+        oSize.transpose();
+    }
+
+    colData->calculateGeometries(0, cCount, oSize.width(), xx, widths,
+                                 xdescents, q_totalBoxes[orientation == Qt::Horizontal]);
+    for (int i = q_items.count() - 1; i >= 0; --i) {
+        QGridLayoutItem *item = q_items.at(i);
+        const int col = item->firstColumn(orientation);
+        const int row = item->firstRow(orientation);
+        if (item->hasDynamicConstraint()) {
+            const qreal sz = widths[col];
+            QGridLayoutBox box = item->box(orientation, sz);
+            rowData.boxes[row].combine(box);
+        }
+    }
+
+    QGridLayoutBox &totalBox = q_totalBoxes[orientation == Qt::Vertical];
+    totalBox = rowData.totalBox(0, rCount);
+    rowData.calculateGeometries(0, rCount, oSize.height(), yy, heights,
+                                 ydescents, totalBox);
 
     q_cachedSize = size;
 }