From d22c8c60ffd986cc46d1f1cab878d60b03b5d4ea Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan-Arve=20S=C3=A6ther?= <jan-arve.saether@nokia.com>
Date: Fri, 9 Apr 2010 14:48:41 +0200
Subject: Implement heightForWidth support for QTabWidget and QStackedLayout.

In order to add the support we also had to add hasHeightForWidth to
QWidget that calls the new virtual hasHeightForWidth() in QWidgetPrivate

Task-number: QTBUG-7792
Reviewed-by: Paul
---
 src/gui/kernel/qlayoutitem.cpp           |  4 +--
 src/gui/kernel/qstackedlayout.cpp        | 26 +++++++++++++++++
 src/gui/kernel/qstackedlayout.h          |  2 ++
 src/gui/kernel/qwidget.cpp               | 17 +++++++++++
 src/gui/kernel/qwidget.h                 |  1 +
 src/gui/kernel/qwidget_p.h               |  1 +
 src/gui/widgets/qsizegrip.cpp            | 17 +++--------
 src/gui/widgets/qtabwidget.cpp           | 49 ++++++++++++++++++++++++++++++++
 src/gui/widgets/qtabwidget.h             |  1 +
 tests/auto/qtabwidget/tst_qtabwidget.cpp | 47 ++++++++++++++++++++++++++++++
 10 files changed, 149 insertions(+), 16 deletions(-)

diff --git a/src/gui/kernel/qlayoutitem.cpp b/src/gui/kernel/qlayoutitem.cpp
index 6a91d95..e615b2d 100644
--- a/src/gui/kernel/qlayoutitem.cpp
+++ b/src/gui/kernel/qlayoutitem.cpp
@@ -516,9 +516,7 @@ bool QWidgetItem::hasHeightForWidth() const
 {
     if (isEmpty())
         return false;
-    if (wid->layout())
-        return wid->layout()->hasHeightForWidth();
-    return wid->sizePolicy().hasHeightForWidth();
+    return wid->hasHeightForWidth();
 }
 
 /*!
diff --git a/src/gui/kernel/qstackedlayout.cpp b/src/gui/kernel/qstackedlayout.cpp
index 7559066..4b49638 100644
--- a/src/gui/kernel/qstackedlayout.cpp
+++ b/src/gui/kernel/qstackedlayout.cpp
@@ -475,6 +475,32 @@ void QStackedLayout::setGeometry(const QRect &rect)
     }
 }
 
+bool QStackedLayout::hasHeightForWidth() const
+{
+    const int n = count();
+
+    for (int i = 0; i < n; ++i) {
+        if (QLayoutItem *item = itemAt(i)) {
+            if (item->hasHeightForWidth())
+                return true;
+        }
+    }
+    return false;
+}
+
+int QStackedLayout::heightForWidth(int width) const
+{
+    const int n = count();
+
+    int hfw = 0;
+    for (int i = 0; i < n; ++i) {
+        if (QLayoutItem *item = itemAt(i)) {
+            hfw = qMax(hfw, item->heightForWidth(width));
+        }
+    }
+    return hfw;
+}
+
 /*!
     \enum QStackedLayout::StackingMode
     \since 4.4
diff --git a/src/gui/kernel/qstackedlayout.h b/src/gui/kernel/qstackedlayout.h
index c069149..842b62b 100644
--- a/src/gui/kernel/qstackedlayout.h
+++ b/src/gui/kernel/qstackedlayout.h
@@ -95,6 +95,8 @@ public:
     QLayoutItem *itemAt(int) const;
     QLayoutItem *takeAt(int);
     void setGeometry(const QRect &rect);
+    bool hasHeightForWidth() const;
+    int heightForWidth(int width) const;
 
 Q_SIGNALS:
     void widgetRemoved(int index);
diff --git a/src/gui/kernel/qwidget.cpp b/src/gui/kernel/qwidget.cpp
index e88026c..7a8b700 100644
--- a/src/gui/kernel/qwidget.cpp
+++ b/src/gui/kernel/qwidget.cpp
@@ -3816,6 +3816,11 @@ void QWidget::setMaximumSize(int maxw, int maxh)
     d->updateGeometry_helper(d->extra->minw == d->extra->maxw && d->extra->minh == d->extra->maxh);
 }
 
+bool QWidgetPrivate::hasHeightForWidth() const
+{
+    return layout ? layout->hasHeightForWidth() : size_policy.hasHeightForWidth();
+}
+
 /*!
     \overload
 
@@ -7961,6 +7966,18 @@ QSize QWidget::minimumSizeHint() const
     return QSize(-1, -1);
 }
 
+/*!
+    \internal
+    This is a bit hackish, but ideally this would have been a virtual
+    function so that subclasses could reimplement their own function.
+    Instead we add a virtual function to QWidgetPrivate.
+*/
+bool QWidget::hasHeightForWidth() const
+{
+    Q_D(const QWidget);
+    return d->hasHeightForWidth();
+}
+
 
 /*!
     \fn QWidget *QWidget::parentWidget() const
diff --git a/src/gui/kernel/qwidget.h b/src/gui/kernel/qwidget.h
index e12148b..6e5de7d 100644
--- a/src/gui/kernel/qwidget.h
+++ b/src/gui/kernel/qwidget.h
@@ -524,6 +524,7 @@ public:
 
     virtual QSize sizeHint() const;
     virtual QSize minimumSizeHint() const;
+    bool hasHeightForWidth() const;
 
     QSizePolicy sizePolicy() const;
     void setSizePolicy(QSizePolicy);
diff --git a/src/gui/kernel/qwidget_p.h b/src/gui/kernel/qwidget_p.h
index 89ea256..05a859c 100644
--- a/src/gui/kernel/qwidget_p.h
+++ b/src/gui/kernel/qwidget_p.h
@@ -493,6 +493,7 @@ public:
 
     bool setMinimumSize_helper(int &minw, int &minh);
     bool setMaximumSize_helper(int &maxw, int &maxh);
+    virtual bool hasHeightForWidth() const;
     void setConstraints_sys();
     QWidget *childAt_helper(const QPoint &, bool) const;
     void updateGeometry_helper(bool forceUpdate);
diff --git a/src/gui/widgets/qsizegrip.cpp b/src/gui/widgets/qsizegrip.cpp
index c9d613a..40f3129 100644
--- a/src/gui/widgets/qsizegrip.cpp
+++ b/src/gui/widgets/qsizegrip.cpp
@@ -78,15 +78,6 @@ static QWidget *qt_sizegrip_topLevelWidget(QWidget* w)
     return w;
 }
 
-static inline bool hasHeightForWidth(QWidget *widget)
-{
-    if (!widget)
-        return false;
-    if (QLayout *layout = widget->layout())
-        return layout->hasHeightForWidth();
-    return widget->sizePolicy().hasHeightForWidth();
-}
-
 class QSizeGripPrivate : public QWidgetPrivate
 {
     Q_DECLARE_PUBLIC(QSizeGrip)
@@ -318,7 +309,7 @@ void QSizeGrip::mousePressEvent(QMouseEvent * e)
 #ifdef Q_WS_X11
     // Use a native X11 sizegrip for "real" top-level windows if supported.
     if (tlw->isWindow() && X11->isSupportedByWM(ATOM(_NET_WM_MOVERESIZE))
-        && !tlw->testAttribute(Qt::WA_DontShowOnScreen) && !hasHeightForWidth(tlw)) {
+        && !tlw->testAttribute(Qt::WA_DontShowOnScreen) && !tlw->hasHeightForWidth()) {
         XEvent xev;
         xev.xclient.type = ClientMessage;
         xev.xclient.message_type = ATOM(_NET_WM_MOVERESIZE);
@@ -340,7 +331,7 @@ void QSizeGrip::mousePressEvent(QMouseEvent * e)
     }
 #endif // Q_WS_X11
 #ifdef Q_WS_WIN
-    if (tlw->isWindow() && !tlw->testAttribute(Qt::WA_DontShowOnScreen) && !hasHeightForWidth(tlw)) {
+    if (tlw->isWindow() && !tlw->testAttribute(Qt::WA_DontShowOnScreen) && !tlw->hasHeightForWidth()) {
         uint orientation = 0;
         if (d->atBottom())
             orientation = d->atLeft() ? SZ_SIZEBOTTOMLEFT : SZ_SIZEBOTTOMRIGHT;
@@ -429,12 +420,12 @@ void QSizeGrip::mouseMoveEvent(QMouseEvent * e)
 
 #ifdef Q_WS_X11
     if (tlw->isWindow() && X11->isSupportedByWM(ATOM(_NET_WM_MOVERESIZE))
-        && tlw->isTopLevel() && !tlw->testAttribute(Qt::WA_DontShowOnScreen) && !hasHeightForWidth(tlw))
+        && tlw->isTopLevel() && !tlw->testAttribute(Qt::WA_DontShowOnScreen) && !tlw->hasHeightForWidth())
         return;
 #endif
 #ifdef Q_WS_WIN
     if (tlw->isWindow() && GetSystemMenu(tlw->winId(), FALSE) != 0 && internalWinId()
-        && !tlw->testAttribute(Qt::WA_DontShowOnScreen) && !hasHeightForWidth(tlw)) {
+        && !tlw->testAttribute(Qt::WA_DontShowOnScreen) && !tlw->hasHeightForWidth()) {
         MSG msg;
         while(PeekMessage(&msg, winId(), WM_MOUSEMOVE, WM_MOUSEMOVE, PM_REMOVE));
         return;
diff --git a/src/gui/widgets/qtabwidget.cpp b/src/gui/widgets/qtabwidget.cpp
index 047a905..fb7ca64 100644
--- a/src/gui/widgets/qtabwidget.cpp
+++ b/src/gui/widgets/qtabwidget.cpp
@@ -195,6 +195,7 @@ public:
     void _q_removeTab(int);
     void _q_tabMoved(int from, int to);
     void init();
+    bool hasHeightForWidth() const;
 
     QTabBar *tabs;
     QStackedWidget *stack;
@@ -871,6 +872,46 @@ QSize QTabWidget::minimumSizeHint() const
                     .expandedTo(QApplication::globalStrut());
 }
 
+int QTabWidget::heightForWidth(int width) const
+{
+    Q_D(const QTabWidget);
+    QStyleOption opt(0);
+    opt.init(this);
+    opt.state = QStyle::State_None;
+
+    QSize zero(0,0);
+    const QSize padding = style()->sizeFromContents(QStyle::CT_TabWidget, &opt, zero, this)
+                                  .expandedTo(QApplication::globalStrut());
+
+    QSize lc(0, 0), rc(0, 0);
+    if (d->leftCornerWidget)
+        lc = d->leftCornerWidget->sizeHint();
+    if(d->rightCornerWidget)
+        rc = d->rightCornerWidget->sizeHint();
+    if (!d->dirty) {
+        QTabWidget *that = (QTabWidget*)this;
+        that->setUpLayout(true);
+    }
+    QSize t(d->tabs->sizeHint());
+
+    if(usesScrollButtons())
+        t = t.boundedTo(QSize(200,200));
+    else
+        t = t.boundedTo(QApplication::desktop()->size());
+
+    const bool tabIsHorizontal = (d->pos == North || d->pos == South);
+    const int contentsWidth = width - padding.width();
+    int stackWidth = contentsWidth;
+    if (!tabIsHorizontal)
+        stackWidth -= qMax(t.width(), qMax(lc.width(), rc.width()));
+
+    int stackHeight = d->stack->heightForWidth(stackWidth);
+    QSize s(stackWidth, stackHeight);
+
+    QSize contentSize = basicSize(tabIsHorizontal, lc, rc, s, t);
+    return (contentSize + padding).expandedTo(QApplication::globalStrut()).height();
+}
+
 /*!
     \reimp
  */
@@ -903,6 +944,14 @@ void QTabWidgetPrivate::updateTabBarPosition()
     q->setUpLayout();
 }
 
+bool QTabWidgetPrivate::hasHeightForWidth() const
+{
+    bool has = size_policy.hasHeightForWidth();
+    if (!has && stack)
+        has = stack->hasHeightForWidth();
+    return has;
+}
+
 /*!
     \property QTabWidget::tabPosition
     \brief the position of the tabs in this tab widget
diff --git a/src/gui/widgets/qtabwidget.h b/src/gui/widgets/qtabwidget.h
index 68200c8..ee50655 100644
--- a/src/gui/widgets/qtabwidget.h
+++ b/src/gui/widgets/qtabwidget.h
@@ -129,6 +129,7 @@ public:
 
     QSize sizeHint() const;
     QSize minimumSizeHint() const;
+    int heightForWidth(int width) const;
 
     void setCornerWidget(QWidget * w, Qt::Corner corner = Qt::TopRightCorner);
     QWidget * cornerWidget(Qt::Corner corner = Qt::TopRightCorner) const;
diff --git a/tests/auto/qtabwidget/tst_qtabwidget.cpp b/tests/auto/qtabwidget/tst_qtabwidget.cpp
index 4491fb3..204c27a 100644
--- a/tests/auto/qtabwidget/tst_qtabwidget.cpp
+++ b/tests/auto/qtabwidget/tst_qtabwidget.cpp
@@ -45,6 +45,7 @@
 #include <qdebug.h>
 #include <qapplication.h>
 #include <qlabel.h>
+#include <qboxlayout.h>
 
 //TESTED_CLASS=
 //TESTED_FILES=
@@ -120,6 +121,8 @@ class tst_QTabWidget:public QObject {
     void clear();
     void keyboardNavigation();
     void paintEventCount();
+    void heightForWidth();
+    void heightForWidth_data();
 
   private:
     int addPage();
@@ -621,6 +624,50 @@ void tst_QTabWidget::paintEventCount()
     QCOMPARE(tab2->count, 1);
 }
 
+void tst_QTabWidget::heightForWidth_data()
+{
+    QTest::addColumn<int>("tabPosition");
+    QTest::newRow("West") << int(QTabWidget::West);
+    QTest::newRow("North") << int(QTabWidget::North);
+    QTest::newRow("East") << int(QTabWidget::East);
+    QTest::newRow("South") << int(QTabWidget::South);
+}
+
+void tst_QTabWidget::heightForWidth()
+{
+    QFETCH(int, tabPosition);
+
+    QWidget *window = new QWidget;
+    QVBoxLayout *lay = new QVBoxLayout(window);
+    lay->setMargin(0);
+    lay->setSpacing(0);
+    QTabWidget *tabWid = new QTabWidget(window);
+    QWidget *w = new QWidget;
+    tabWid->addTab(w, QLatin1String("HFW page"));
+    tabWid->setTabPosition(QTabWidget::TabPosition(tabPosition));
+    QVBoxLayout *lay2 = new QVBoxLayout(w);
+    QLabel *label = new QLabel("Label with wordwrap turned on makes it trade height for width."
+                               " Make it a really long text so that it spans on several lines"
+                               " when the label is on its narrowest."
+                               " I don't like to repeat myself."
+                               " I don't like to repeat myself."
+                               " I don't like to repeat myself."
+                               " I don't like to repeat myself."
+                               );
+    label->setWordWrap(true);
+    lay2->addWidget(label);
+    lay2->setMargin(0);
+
+    lay->addWidget(tabWid);
+    int h = window->heightForWidth(160);
+    window->resize(160, h);
+    window->show();
+
+    QTest::qWaitForWindowShown(window);
+    QVERIFY(label->height() >= label->heightForWidth(label->width()));
+
+    delete window;
+}
 
 QTEST_MAIN(tst_QTabWidget)
 #include "tst_qtabwidget.moc"
-- 
cgit v0.12