diff options
-rw-r--r-- | src/gui/graphicsview/qgraphicslayoutitem.cpp | 70 | ||||
-rw-r--r-- | src/gui/kernel/qwidget_x11.cpp | 11 | ||||
-rw-r--r-- | tests/auto/qgraphicslayoutitem/tst_qgraphicslayoutitem.cpp | 108 |
3 files changed, 186 insertions, 3 deletions
diff --git a/src/gui/graphicsview/qgraphicslayoutitem.cpp b/src/gui/graphicsview/qgraphicslayoutitem.cpp index 0631df8..5a2d051 100644 --- a/src/gui/graphicsview/qgraphicslayoutitem.cpp +++ b/src/gui/graphicsview/qgraphicslayoutitem.cpp @@ -133,15 +133,81 @@ void QGraphicsLayoutItemPrivate::init() /*! \internal + + effectiveSizeHint has a quirky behavior, one of the quirkinesses is when the hfw function is + combined with user-specified min/max sizes. The input to hfw function (e.g width) must be within + the min/max width constraint, and the output must be within the min/max height. This sets up a + loose dependency between minimum width and maximum height (or minimum height, depending on the + type of hfw function). Note that its only the concrete subclass that implements that hfw + function that knows if this dependency means that the height will increase or decrease when the + width is increased. + + The application should try to ensure that the user-defined sizes are within the range so that + they don't conflict with the hfw function. + + Suppose, for instance that the hfw function is: + + height = 2000/width + + and the item has these user-defined sizes: + + min ( 5, 5) + pref(100, 10) + max (500,100) + + what is the return value if one calls item->effectiveSizeHint(Qt::MinimumSize, QSizeF(10, -1)); ? + The sizeHint() function would return QSizeF(10, 200), but it would be bounded down to 100 due + to the max value, so it would return (10, 100). This is not what the item expects, since it + really wants that its hfw is respected. If this is a label with wrapped text, this would most + likely lead to that some text is clipped. This is certainly not what the app developer wants. + Now, it would be better if the user changed those constraints to match the hfw function: + + min ( 20, 5) + pref(100, 10) + max (500,100) + + here, it says that the width cannot be smaller than 20. This is because if it becomes smaller + than 20 the result of the hfw function would violate the max height (100). + + However, there is a similar problem if the width passed to the hfw function reaches *max* width: + + the sizeHint() function would now return QSizeF(500, 4), but 4 is smaller than the minimum + height (5), so effectiveSizeHint() would return (500, 5), which would leave too much space. + In this case, setting the max width to 400 fixes the problem: + + min ( 20, 5) + pref(100, 10) + max (400,100) + + + The implementor of a hfw widget must be aware of this when sizeHint() is reimplemented, so that + the default min and max sizes works sensible. (unfortunately the implementor does not have the + control over user-set values). + */ QSizeF *QGraphicsLayoutItemPrivate::effectiveSizeHints(const QSizeF &constraint) const { Q_Q(const QGraphicsLayoutItem); QSizeF *sizeHintCache; const bool hasConstraint = constraint.width() >= 0 || constraint.height() >= 0; + QSizeF adjustedConstraint = constraint; if (hasConstraint) { if (!sizeHintWithConstraintCacheDirty && constraint == cachedConstraint) return cachedSizeHintsWithConstraints; + + const QSizeF *hintsWithoutConstraint = effectiveSizeHints(QSizeF(-1,-1)); + + if (adjustedConstraint.width() >= 0) + adjustedConstraint.setWidth( qBound( hintsWithoutConstraint[Qt::MinimumSize].width(), + adjustedConstraint.width(), + hintsWithoutConstraint[Qt::MaximumSize].width())); + if (adjustedConstraint.height() >= 0) + adjustedConstraint.setHeight( qBound( hintsWithoutConstraint[Qt::MinimumSize].height(), + adjustedConstraint.height(), + hintsWithoutConstraint[Qt::MaximumSize].height())); + + if (!sizeHintWithConstraintCacheDirty && adjustedConstraint == cachedConstraint) + return cachedSizeHintsWithConstraints; sizeHintCache = cachedSizeHintsWithConstraints; } else { if (!sizeHintCacheDirty) @@ -150,7 +216,7 @@ QSizeF *QGraphicsLayoutItemPrivate::effectiveSizeHints(const QSizeF &constraint) } for (int i = 0; i < Qt::NSizeHints; ++i) { - sizeHintCache[i] = constraint; + sizeHintCache[i] = adjustedConstraint; if (userSizeHints) combineSize(sizeHintCache[i], userSizeHints[i]); } @@ -185,7 +251,7 @@ QSizeF *QGraphicsLayoutItemPrivate::effectiveSizeHints(const QSizeF &constraint) // COMBINE_SIZE(descentS, q->sizeHint(Qt::MinimumDescent, constraint)); if (hasConstraint) { - cachedConstraint = constraint; + cachedConstraint = adjustedConstraint; sizeHintWithConstraintCacheDirty = false; } else { sizeHintCacheDirty = false; diff --git a/src/gui/kernel/qwidget_x11.cpp b/src/gui/kernel/qwidget_x11.cpp index f99cc2c..98c33da 100644 --- a/src/gui/kernel/qwidget_x11.cpp +++ b/src/gui/kernel/qwidget_x11.cpp @@ -2694,8 +2694,17 @@ void QWidgetPrivate::setConstraints_sys() #ifdef ALIEN_DEBUG qDebug() << "QWidgetPrivate::setConstraints_sys START" << q; #endif - if (q->testAttribute(Qt::WA_WState_Created)) + if (q->testAttribute(Qt::WA_WState_Created)) { do_size_hints(q, extra); + QtMWMHints mwmHints = GetMWMHints(X11->display, q->internalWinId()); + const bool wasFuncResize = mwmHints.functions & MWM_FUNC_RESIZE; + if (q->minimumSize() == q->maximumSize()) + mwmHints.functions &= ~MWM_FUNC_RESIZE; + else + mwmHints.functions |= MWM_FUNC_RESIZE; + if (wasFuncResize != (mwmHints.functions & MWM_FUNC_RESIZE)) + SetMWMHints(X11->display, q->internalWinId(), mwmHints); + } #ifdef ALIEN_DEBUG qDebug() << "QWidgetPrivate::setConstraints_sys END" << q; #endif diff --git a/tests/auto/qgraphicslayoutitem/tst_qgraphicslayoutitem.cpp b/tests/auto/qgraphicslayoutitem/tst_qgraphicslayoutitem.cpp index a8b6c08..34ca5d4 100644 --- a/tests/auto/qgraphicslayoutitem/tst_qgraphicslayoutitem.cpp +++ b/tests/auto/qgraphicslayoutitem/tst_qgraphicslayoutitem.cpp @@ -44,6 +44,7 @@ #include <qgraphicslayoutitem.h> #include <float.h> #include <limits.h> +#include <QtGui/qgraphicswidget.h> class tst_QGraphicsLayoutItem : public QObject { Q_OBJECT @@ -60,6 +61,8 @@ private slots: void contentsRect(); void effectiveSizeHint_data(); void effectiveSizeHint(); + void effectiveSizeHint2_data(); + void effectiveSizeHint2(); void getContentsMargins(); void isLayout_data(); void isLayout(); @@ -104,6 +107,40 @@ public: }; +class RectWidget : public QGraphicsWidget +{ +public: + RectWidget(QGraphicsItem *parent = 0) : QGraphicsWidget(parent), m_fnConstraint(fn2000_div_w) {} + + + QSizeF sizeHint(Qt::SizeHint which, const QSizeF &constraint = QSizeF()) const + { + if (constraint.width() < 0 && constraint.height() < 0 && m_sizeHints[which].isValid()) { + return m_sizeHints[which]; + } + if (m_fnConstraint) { + return m_fnConstraint(which, constraint); + } + return QGraphicsWidget::sizeHint(which, constraint); + } + + void setSizeHint(Qt::SizeHint which, const QSizeF &size) { + m_sizeHints[which] = size; + updateGeometry(); + } + + void setConstraintFunction(QSizeF (*fnConstraint)(Qt::SizeHint, const QSizeF &)) { + m_fnConstraint = fnConstraint; + } + + QSizeF m_sizeHints[Qt::NSizeHints]; + QSizeF (*m_fnConstraint)(Qt::SizeHint, const QSizeF &); + + static QSizeF fn2000_div_w(Qt::SizeHint /*which*/, const QSizeF &constraint = QSizeF()) { + return QSizeF(constraint.width(), 2000.0/constraint.width()); + } +}; + // This will be called before the first test function is executed. // It is only called once. void tst_QGraphicsLayoutItem::initTestCase() @@ -183,6 +220,77 @@ void tst_QGraphicsLayoutItem::effectiveSizeHint() QCOMPARE(r.height(), constraint.height()); } + +void tst_QGraphicsLayoutItem::effectiveSizeHint2_data() +{ + QTest::addColumn<QSizeF>("minimumSize"); + QTest::addColumn<QSizeF>("preferredSize"); + QTest::addColumn<QSizeF>("maximumSize"); + QTest::addColumn<QSizeF>("minimumSizeHint"); + QTest::addColumn<QSizeF>("preferredSizeHint"); + QTest::addColumn<QSizeF>("maximumSizeHint"); + + QTest::addColumn<QSizeF>("inputConstraint"); + QTest::addColumn<QSizeF>("expectedMinimumESH"); + QTest::addColumn<QSizeF>("expectedPreferredESH"); + QTest::addColumn<QSizeF>("expectedMaximumESH"); + + QTest::newRow("P1-a") + << QSizeF( 6, 4) << QSizeF( 60, 40) << QSizeF( 600, 400) + << QSizeF( -1, -1) << QSizeF( -1, -1) << QSizeF( -1, -1) + << QSizeF(-1, -1) + << QSizeF(6, 4) << QSizeF( 60, 40) << QSizeF(600, 400); + + QTest::newRow("P1-hfw-1") + << QSizeF( -1, -1) << QSizeF( -1, -1) << QSizeF( -1, -1) + << QSizeF( 6, 4) << QSizeF( 60, 40) << QSizeF(600, 400) + << QSizeF(200, -1) + << QSizeF(200, 10) << QSizeF(200, 10) << QSizeF(200, 10); + + QTest::newRow("P1-hfw-2") + << QSizeF( 6, -1) << QSizeF( 60, -1) << QSizeF(600, -1) + << QSizeF( -1, -1) << QSizeF( -1, -1) << QSizeF( -1, -1) + << QSizeF(200, -1) + << QSizeF(200, 10) << QSizeF(200, 10) << QSizeF(200, 10); + + // constraint is bigger than max width + QTest::newRow("P1-hfw-3") + << QSizeF( 5, -1) << QSizeF( 50, -1) << QSizeF(500, -1) + << QSizeF( -1, -1) << QSizeF( -1, -1) << QSizeF( -1, -1) + << QSizeF(600, -1) + << QSizeF(500, 4) << QSizeF(500, 4) << QSizeF(500, 4); + +} + +void tst_QGraphicsLayoutItem::effectiveSizeHint2() +{ + QFETCH(QSizeF, minimumSize); + QFETCH(QSizeF, preferredSize); + QFETCH(QSizeF, maximumSize); + QFETCH(QSizeF, minimumSizeHint); + QFETCH(QSizeF, preferredSizeHint); + QFETCH(QSizeF, maximumSizeHint); + + QFETCH(QSizeF, inputConstraint); + QFETCH(QSizeF, expectedMinimumESH); + QFETCH(QSizeF, expectedPreferredESH); + QFETCH(QSizeF, expectedMaximumESH); + + RectWidget *item = new RectWidget; + item->setMinimumSize(minimumSize); + item->setPreferredSize(preferredSize); + item->setMaximumSize(maximumSize); + item->setSizeHint(Qt::MinimumSize, minimumSizeHint); + item->setSizeHint(Qt::PreferredSize, preferredSizeHint); + item->setSizeHint(Qt::MaximumSize, maximumSizeHint); + + QCOMPARE(item->effectiveSizeHint(Qt::MinimumSize, inputConstraint), expectedMinimumESH); + QCOMPARE(item->effectiveSizeHint(Qt::PreferredSize, inputConstraint), expectedPreferredESH); + QCOMPARE(item->effectiveSizeHint(Qt::MaximumSize, inputConstraint), expectedMaximumESH); + +} + + // void getContentsMargins(qreal* left, qreal* top, qreal* right, qreal* bottom) const public void tst_QGraphicsLayoutItem::getContentsMargins() { |