From a56acff4789521a7b6a6439dcb88e6d9ba4b7ada Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Arve=20S=C3=A6ther?= Date: Fri, 4 Sep 2009 12:37:20 +0200 Subject: Make sure that top-level windows respect hfw/wfh constraint. Note that the constraint is only enforced when resizing the window interactively. Calling setGeometry() will not try to enforce the constraint. See the graphicsview/flowlayout example for a "manual test". Task-number: 257455 --- src/gui/graphicsview/qgraphicswidget_p.cpp | 164 +++++++++++++++++---- src/gui/kernel/qsizepolicy.h | 12 ++ tests/auto/qgraphicswidget/tst_qgraphicswidget.cpp | 79 ++++++++++ 3 files changed, 230 insertions(+), 25 deletions(-) diff --git a/src/gui/graphicsview/qgraphicswidget_p.cpp b/src/gui/graphicsview/qgraphicswidget_p.cpp index 787bbb1..bf826a9 100644 --- a/src/gui/graphicsview/qgraphicswidget_p.cpp +++ b/src/gui/graphicsview/qgraphicswidget_p.cpp @@ -393,49 +393,162 @@ void QGraphicsWidgetPrivate::windowFrameMousePressEvent(QGraphicsSceneMouseEvent event->setAccepted(windowData->grabbedSection != Qt::NoSection); } +/*! + Used to calculate the + Precondition: + \a widget should support either hfw or wfh + + If \a heightForWidth is set to false, this function will query the width for height + instead. \a width will then be interpreted as height, \a minh and \a maxh will be interpreted + as minimum width and maximum width. + */ +static qreal minimumHeightForWidth(qreal width, qreal minh, qreal maxh, + const QGraphicsWidget *widget, + bool heightForWidth = true) +{ + qreal minimumHeightForWidth = -1; + const QSizePolicy sp = widget->layout() ? widget->layout()->sizePolicy() : widget->sizePolicy(); + const bool hasHFW = sp.hasHeightForWidth(); + if (hasHFW == heightForWidth) { + minimumHeightForWidth = hasHFW + ? widget->effectiveSizeHint(Qt::MinimumSize, QSizeF(width, -1)).height() + : widget->effectiveSizeHint(Qt::MinimumSize, QSizeF(-1, width)).width(); //"width" is here height! + } else { + // widthForHeight + const qreal constraint = width; + while (maxh - minh > 0.1) { + qreal middle = minh + (maxh - minh)/2; + // ### really bad, if we are a widget with a layout it will call + // layout->effectiveSizeHint(Qt::MiniumumSize), which again will call + // sizeHint three times because of how the cache works + qreal hfw = hasHFW + ? widget->effectiveSizeHint(Qt::MinimumSize, QSizeF(middle, -1)).height() + : widget->effectiveSizeHint(Qt::MinimumSize, QSizeF(-1, middle)).width(); + if (hfw > constraint) { + minh = middle; + } else if (hfw <= constraint) { + maxh = middle; + } + } + minimumHeightForWidth = maxh; + } + return minimumHeightForWidth; +} + +static qreal minimumWidthForHeight(qreal height, qreal minw, qreal maxw, + const QGraphicsWidget *widget) +{ + return minimumHeightForWidth(height, minw, maxw, widget, false); +} + +static QSizeF closestAcceptableSize(const QSizeF &proposed, + const QGraphicsWidget *widget) +{ + const QSizeF current = widget->size(); + + qreal minw = proposed.width(); + qreal maxw = current.width(); + qreal minh = proposed.height(); + qreal maxh = current.height(); + + qreal middlew = maxw; + qreal middleh = maxh; + qreal min_hfw; + min_hfw = minimumHeightForWidth(maxw, minh, maxh, widget); + + do { + if (maxw - minw < 0.1) { + // we still havent found anything, cut off binary search + minw = maxw; + minh = maxh; + } + middlew = minw + (maxw - minw)/2.0; + middleh = minh + (maxh - minh)/2.0; + + min_hfw = minimumHeightForWidth(middlew, minh, maxh, widget); + + if (min_hfw > middleh) { + minw = middlew; + minh = middleh; + } else if (min_hfw <= middleh) { + maxw = middlew; + maxh = middleh; + } + } while (maxw != minw); + + min_hfw = minimumHeightForWidth(middlew, minh, maxh, widget); + + QSizeF result; + if (min_hfw < maxh) { + result = QSizeF(middlew, min_hfw); + } else { + // Needed because of the cut-off we do above. + result = QSizeF(minimumWidthForHeight(maxh, proposed.width(), current.width(), widget), maxh); + } + return result; +} + static void _q_boundGeometryToSizeConstraints(const QRectF &startGeometry, QRectF *rect, Qt::WindowFrameSection section, - const QSizeF &min, const QSizeF &max) + const QSizeF &min, const QSizeF &max, + const QGraphicsWidget *widget) { - int height; - int width; + const QRectF proposedRect = *rect; + qreal width = qBound(min.width(), proposedRect.width(), max.width()); + qreal height = qBound(min.height(), proposedRect.height(), max.height()); + + QSizePolicy sp = widget->sizePolicy(); + if (const QGraphicsLayout *l = widget->layout()) { + sp = l->sizePolicy(); + } + const bool hasHFW = sp.hasHeightForWidth(); // || sp.hasWidthForHeight(); + + const bool widthChanged = proposedRect.width() < widget->size().width(); + const bool heightChanged = proposedRect.height() < widget->size().height(); + + if (hasHFW) { + if (widthChanged || heightChanged) { + const qreal minh = min.height(); + const qreal maxh = max.height(); + const qreal proposedHFW = minimumHeightForWidth(width, minh, maxh, widget); + if (proposedHFW > proposedRect.height()) { + QSizeF effectiveSize = closestAcceptableSize(QSizeF(width, height), widget); + width = effectiveSize.width(); + height = effectiveSize.height(); + } + } + } + switch (section) { case Qt::LeftSection: - width = qRound(qBound(min.width(), rect->width(), max.width())); - rect->setRect(startGeometry.right() - width, startGeometry.top(), - width, startGeometry.height()); + rect->setRect(startGeometry.right() - qRound(width), startGeometry.top(), + qRound(width), startGeometry.height()); break; case Qt::TopLeftSection: - width = qRound(qBound(min.width(), rect->width(), max.width())); - height = qRound(qBound(min.height(), rect->height(), max.height())); - rect->setRect(startGeometry.right() - width, startGeometry.bottom() - height, - width, height); + rect->setRect(startGeometry.right() - qRound(width), startGeometry.bottom() - qRound(height), + qRound(width), qRound(height)); break; case Qt::TopSection: - height = qRound(qBound(min.height(), rect->height(), max.height())); - rect->setRect(startGeometry.left(), startGeometry.bottom() - height, - startGeometry.width(), height); + rect->setRect(startGeometry.left(), startGeometry.bottom() - qRound(height), + startGeometry.width(), qRound(height)); break; case Qt::TopRightSection: - height = qRound(qBound(min.height(), rect->height(), max.height())); - rect->setTop(rect->bottom() - height); - rect->setWidth(qBound(min.width(), rect->width(), max.width())); + rect->setTop(rect->bottom() - qRound(height)); + rect->setWidth(qRound(width)); break; case Qt::RightSection: - rect->setWidth(qBound(min.width(), rect->width(), max.width())); + rect->setWidth(qRound(width)); break; case Qt::BottomRightSection: - rect->setWidth(qBound(min.width(), rect->width(), max.width())); - rect->setHeight(qBound(min.height(), rect->height(), max.height())); + rect->setWidth(qRound(width)); + rect->setHeight(qRound(height)); break; case Qt::BottomSection: - rect->setHeight(qBound(min.height(), rect->height(), max.height())); + rect->setHeight(qRound(height)); break; case Qt::BottomLeftSection: - height = qRound(qBound(min.height(), rect->height(), max.height())); - width = qRound(qBound(min.width(), rect->width(), max.width())); - rect->setRect(startGeometry.right() - width, startGeometry.top(), - width, height); + rect->setRect(startGeometry.right() - qRound(width), startGeometry.top(), + qRound(width), qRound(height)); break; default: break; @@ -506,7 +619,8 @@ void QGraphicsWidgetPrivate::windowFrameMouseMoveEvent(QGraphicsSceneMouseEvent _q_boundGeometryToSizeConstraints(windowData->startGeometry, &newGeometry, windowData->grabbedSection, q->effectiveSizeHint(Qt::MinimumSize), - q->effectiveSizeHint(Qt::MaximumSize)); + q->effectiveSizeHint(Qt::MaximumSize), + q); q->setGeometry(newGeometry); } } diff --git a/src/gui/kernel/qsizepolicy.h b/src/gui/kernel/qsizepolicy.h index 4261dda..eb13788 100644 --- a/src/gui/kernel/qsizepolicy.h +++ b/src/gui/kernel/qsizepolicy.h @@ -64,6 +64,7 @@ private: VMask = HMask << HSize, CTShift = 9, CTSize = 5, + WFHShift = CTShift + CTSize, CTMask = ((0x1 << CTSize) - 1) << CTShift, UnusedShift = CTShift + CTSize, UnusedSize = 2 @@ -199,6 +200,17 @@ private: QSizePolicy(int i) : data(i) { } quint32 data; +/* use bit flags instead, keep it here for improved readability for now + quint32 horzPolicy : 4; + quint32 vertPolicy : 4; + quint32 hfw : 1; + quint32 ctype : 5; + quint32 wfh : 1; + quint32 padding : 1; // we cannot use the highest bit + quint32 horStretch : 8; + quint32 verStretch : 8; +*/ + }; Q_DECLARE_OPERATORS_FOR_FLAGS(QSizePolicy::ControlTypes) diff --git a/tests/auto/qgraphicswidget/tst_qgraphicswidget.cpp b/tests/auto/qgraphicswidget/tst_qgraphicswidget.cpp index 03054f9..d1193bd 100644 --- a/tests/auto/qgraphicswidget/tst_qgraphicswidget.cpp +++ b/tests/auto/qgraphicswidget/tst_qgraphicswidget.cpp @@ -157,6 +157,7 @@ private slots: void shortcutsDeletion(); void painterStateProtectionOnWindowFrame(); void ensureClipping(); + void respectHFW(); // Task fixes void task236127_bspTreeIndexFails(); @@ -2543,6 +2544,84 @@ void tst_QGraphicsWidget::ensureClipping() QVERIFY(scene.drawnItems.contains(childitem)); } +class HFWWidget : public QGraphicsWidget +{ +public: + HFWWidget() : QGraphicsWidget(0, Qt::Window) + { + QSizePolicy sp; + sp.setHeightForWidth(true); + setSizePolicy(sp); + } + + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) + { + Q_UNUSED(option); + Q_UNUSED(widget); + qreal w = rect().width(); + QRectF box(0, 0, w, 2400/w); + painter->drawRoundRect(box); + painter->drawLine(box.topLeft(), box.bottomRight()); + painter->drawLine(box.bottomLeft(), box.topRight()); + } + +protected: + QSizeF sizeHint(Qt::SizeHint which, const QSizeF &constraint = QSizeF()) const + { + qreal w = constraint.width(); + switch (which) { + case Qt::MinimumSize: + if (w >= 0 && constraint.height() < 0) { + // keep the same area of 60x40 = 2400 + return QSizeF(w, 2400.0/w); + } else { + return QSizeF(10, 10); + } + break; + case Qt::PreferredSize: + return QSizeF(48.989794, 48.989794); + default: + break; + } + return QGraphicsWidget::sizeHint(which, constraint); + } +}; + +void tst_QGraphicsWidget::respectHFW() +{ + QGraphicsScene scene; + HFWWidget *window = new HFWWidget; + scene.addItem(window); + QGraphicsView *view = new QGraphicsView(&scene); + view->resize(400, 400); + view->setSceneRect(-100, -100, 300,300); + + view->show(); + window->setGeometry(0, 0, 70, 70); + + { // here we go - simulate a interactive resize of the window + QTest::qWait(200); + QTest::mouseMove(view, view->mapFromScene(71, 71)); // bottom right corner + QTest::qWait(200); + + QTest::mousePress(view->viewport(), Qt::LeftButton, 0, view->mapFromScene(71, 71), 200); + view->grabMouse(); + // move both mouse cursor and set correct event in order to emulate resize + QTest::mouseMove(view->viewport(), view->mapFromScene(60, 30), 200); + QMouseEvent e = QMouseEvent(QEvent::MouseMove, + view->mapFromScene(60, 20), + Qt::NoButton, + Qt::LeftButton, + Qt::NoModifier); + QApplication::sendEvent(view->viewport(), &e); + view->releaseMouse(); + } + QTest::qWait(200); + const QSizeF winSize = window->size(); + qreal minHFW = window->effectiveSizeHint(Qt::MinimumSize, QSizeF(winSize.width(), -1)).height(); + QVERIFY(qAbs(minHFW - winSize.height()) < 1); +} + QTEST_MAIN(tst_QGraphicsWidget) #include "tst_qgraphicswidget.moc" -- cgit v0.12