From cb679241b1df9117ef50664dcf5549341bac8c3e Mon Sep 17 00:00:00 2001
From: Friedemann Kleint <Friedemann.Kleint@theqtcompany.com>
Date: Tue, 28 Oct 2014 14:20:46 +0100
Subject: [PATCH] Implement heightForWidth().
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Add a virtual function QWindowPrivate::closestAcceptableGeometry()
which is called from the platform plugin.

Task-number: QTBUG-36220
Task-number: QTBUG-36318
Change-Id: I2b3d205e2c75f1d4dd2ba1d333b0d89bc0fcf13a
Reviewed-by: Jan Arve Sæther <jan-arve.saether@theqtcompany.com>
---
 src/gui/kernel/qwindow.cpp                    |  9 +++++
 src/gui/kernel/qwindow_p.h                    |  1 +
 .../platforms/windows/qtwindowsglobal.h       |  7 +++-
 .../platforms/windows/qwindowscontext.cpp     |  2 +
 .../platforms/windows/qwindowswindow.cpp      | 37 +++++++++++++++++++
 .../platforms/windows/qwindowswindow.h        |  1 +
 src/widgets/kernel/qwidgetwindow.cpp          | 31 ++++++++++++++++
 7 files changed, 87 insertions(+), 1 deletion(-)

diff --git a/src/gui/kernel/qwindow.cpp b/src/gui/kernel/qwindow.cpp
index c6dd0955aac..c5d88b198be 100644
--- a/src/gui/kernel/qwindow.cpp
+++ b/src/gui/kernel/qwindow.cpp
@@ -411,6 +411,15 @@ void QWindowPrivate::clearFocusObject()
 {
 }
 
+// Allows for manipulating the suggested geometry before a resize/move
+// event in derived classes for platforms that support it, for example to
+// implement heightForWidth().
+QRectF QWindowPrivate::closestAcceptableGeometry(const QRectF &rect) const
+{
+    Q_UNUSED(rect)
+    return QRectF();
+}
+
 /*!
     Sets the \a surfaceType of the window.
 
diff --git a/src/gui/kernel/qwindow_p.h b/src/gui/kernel/qwindow_p.h
index 46dc2e463c7..bc5dfa48767 100644
--- a/src/gui/kernel/qwindow_p.h
+++ b/src/gui/kernel/qwindow_p.h
@@ -132,6 +132,7 @@ public:
     void emitScreenChangedRecursion(QScreen *newScreen);
 
     virtual void clearFocusObject();
+    virtual QRectF closestAcceptableGeometry(const QRectF &rect) const;
 
     QWindow::SurfaceType surfaceType;
     Qt::WindowFlags windowFlags;
diff --git a/src/plugins/platforms/windows/qtwindowsglobal.h b/src/plugins/platforms/windows/qtwindowsglobal.h
index 99c97d156f3..90e6d6ab9d7 100644
--- a/src/plugins/platforms/windows/qtwindowsglobal.h
+++ b/src/plugins/platforms/windows/qtwindowsglobal.h
@@ -72,7 +72,8 @@ enum WindowsEventType // Simplify event types
     ShowEventOnParentRestoring = WindowEventFlag + 20,
     HideEvent = WindowEventFlag + 8,
     DestroyEvent = WindowEventFlag + 9,
-    MoveEvent = WindowEventFlag + 10,
+    GeometryChangingEvent = WindowEventFlag + 10,
+    MoveEvent = WindowEventFlag + 11,
     ResizeEvent = WindowEventFlag + 12,
     QuerySizeHints = WindowEventFlag + 15,
     CalculateSize = WindowEventFlag + 16,
@@ -146,6 +147,10 @@ inline QtWindows::WindowsEventType windowsEventType(UINT message, WPARAM wParamI
     case WM_MOUSEWHEEL:
     case WM_MOUSEHWHEEL:
         return QtWindows::MouseWheelEvent;
+#ifndef Q_OS_WINCE
+    case WM_WINDOWPOSCHANGING:
+        return QtWindows::GeometryChangingEvent;
+#endif
     case WM_MOVE:
         return QtWindows::MoveEvent;
     case WM_SHOWWINDOW:
diff --git a/src/plugins/platforms/windows/qwindowscontext.cpp b/src/plugins/platforms/windows/qwindowscontext.cpp
index 22a4dbb09f3..4f1a1a375ff 100644
--- a/src/plugins/platforms/windows/qwindowscontext.cpp
+++ b/src/plugins/platforms/windows/qwindowscontext.cpp
@@ -995,6 +995,8 @@ bool QWindowsContext::windowsProc(HWND hwnd, UINT message,
         return QWindowsGeometryHint::handleCalculateSize(platformWindow->customMargins(), msg, result);
     case QtWindows::NonClientHitTest:
         return platformWindow->handleNonClientHitTest(QPoint(msg.pt.x, msg.pt.y), result);
+    case QtWindows::GeometryChangingEvent:
+        return platformWindow->QWindowsWindow::handleGeometryChanging(&msg);
 #endif // !Q_OS_WINCE
     case QtWindows::ExposeEvent:
         return platformWindow->handleWmPaint(hwnd, message, wParam, lParam);
diff --git a/src/plugins/platforms/windows/qwindowswindow.cpp b/src/plugins/platforms/windows/qwindowswindow.cpp
index 8a807293542..0b4bb9b09d6 100644
--- a/src/plugins/platforms/windows/qwindowswindow.cpp
+++ b/src/plugins/platforms/windows/qwindowswindow.cpp
@@ -1783,6 +1783,43 @@ void QWindowsWindow::propagateSizeHints()
     qCDebug(lcQpaWindows) << __FUNCTION__ << this << window();
 }
 
+bool QWindowsWindow::handleGeometryChanging(MSG *message) const
+{
+#ifndef Q_OS_WINCE
+    QWindow *qWin = window();
+    if (!qWin->isTopLevel())
+        return false;
+    WINDOWPOS *windowPos = reinterpret_cast<WINDOWPOS *>(message->lParam);
+    if ((windowPos->flags & (SWP_NOCOPYBITS | SWP_NOSIZE)))
+        return false;
+    const QMargins marginsDp = frameMarginsDp();
+    const QRect suggestedFrameGeometryDp(windowPos->x, windowPos->y,
+                                         windowPos->cx, windowPos->cy);
+    const qreal factor = QWindowsScaling::factor();
+    const QRect suggestedGeometryDp = suggestedFrameGeometryDp - marginsDp;
+    const QRectF suggestedGeometry = QRectF(QPointF(suggestedGeometryDp.topLeft()) / factor,
+                                            QSizeF(suggestedGeometryDp.size()) / factor);
+    const QRectF correctedGeometryF =
+        qt_window_private(qWin)->closestAcceptableGeometry(suggestedGeometry);
+    if (!correctedGeometryF.isValid())
+        return false;
+    const QRect correctedFrameGeometryDp
+        = QRectF(correctedGeometryF.topLeft() * factor,
+                 correctedGeometryF.size() * factor).toRect()
+          + marginsDp;
+    if (correctedFrameGeometryDp == suggestedFrameGeometryDp)
+        return false;
+    windowPos->x = correctedFrameGeometryDp.left();
+    windowPos->y = correctedFrameGeometryDp.top();
+    windowPos->cx = correctedFrameGeometryDp.width();
+    windowPos->cy = correctedFrameGeometryDp.height();
+    return true;
+#else // !Q_OS_WINCE
+    Q_UNUSED(message)
+    return false;
+#endif
+}
+
 QMargins QWindowsWindow::frameMarginsDp() const
 {
     // Frames are invalidated by style changes (window state, flags).
diff --git a/src/plugins/platforms/windows/qwindowswindow.h b/src/plugins/platforms/windows/qwindowswindow.h
index edba992b198..a63a9f56e39 100644
--- a/src/plugins/platforms/windows/qwindowswindow.h
+++ b/src/plugins/platforms/windows/qwindowswindow.h
@@ -183,6 +183,7 @@ public:
     void windowEvent(QEvent *event);
 
     void propagateSizeHints() Q_DECL_OVERRIDE;
+    bool handleGeometryChanging(MSG *message) const;
     QMargins frameMarginsDp() const;
     QMargins frameMargins() const Q_DECL_OVERRIDE { return frameMarginsDp() / QWindowsScaling::factor(); }
 
diff --git a/src/widgets/kernel/qwidgetwindow.cpp b/src/widgets/kernel/qwidgetwindow.cpp
index e455b772fbd..463eea4ddc0 100644
--- a/src/widgets/kernel/qwidgetwindow.cpp
+++ b/src/widgets/kernel/qwidgetwindow.cpp
@@ -33,6 +33,7 @@
 
 #include "private/qwindow_p.h"
 #include "qwidgetwindow_p.h"
+#include "qlayout.h"
 
 #include "private/qwidget_p.h"
 #include "private/qapplication_p.h"
@@ -79,8 +80,38 @@ public:
             widget->focusWidget()->clearFocus();
     }
 
+    QRectF closestAcceptableGeometry(const QRectF &rect) const Q_DECL_OVERRIDE;
 };
 
+QRectF QWidgetWindowPrivate::closestAcceptableGeometry(const QRectF &rect) const
+{
+    Q_Q(const QWidgetWindow);
+    const QWidget *widget = q->widget();
+    if (!widget->isWindow() || !widget->hasHeightForWidth())
+        return QRect();
+    const QSize oldSize = rect.size().toSize();
+    const QSize newSize = QLayout::closestAcceptableSize(widget, oldSize);
+    if (newSize == oldSize)
+        return QRectF();
+    const int dw = newSize.width() - oldSize.width();
+    const int dh = newSize.height() - oldSize.height();
+    QRectF result = rect;
+    const QRectF currentGeometry(widget->geometry());
+    const qreal topOffset = result.top() - currentGeometry.top();
+    const qreal bottomOffset = result.bottom() - currentGeometry.bottom();
+    if (qAbs(topOffset) > qAbs(bottomOffset))
+        result.setTop(result.top() - dh); // top edge drag
+    else
+        result.setBottom(result.bottom() + dh); // bottom edge drag
+    const qreal leftOffset = result.left() - currentGeometry.left();
+    const qreal rightOffset = result.right() - currentGeometry.right();
+    if (qAbs(leftOffset) > qAbs(rightOffset))
+        result.setLeft(result.left() - dw); // left edge drag
+    else
+        result.setRight(result.right() + dw); // right edge drag
+    return result;
+}
+
 QWidgetWindow::QWidgetWindow(QWidget *widget)
     : QWindow(*new QWidgetWindowPrivate(), 0)
     , m_widget(widget)
-- 
GitLab