diff --git a/src/plugins/platforms/windows/qwindowscontext.cpp b/src/plugins/platforms/windows/qwindowscontext.cpp index 28269e8653..40aa446a66 100644 --- a/src/plugins/platforms/windows/qwindowscontext.cpp +++ b/src/plugins/platforms/windows/qwindowscontext.cpp @@ -783,7 +783,7 @@ bool QWindowsContext::windowsProc(HWND hwnd, UINT message, d->m_creationContext->obtainedGeometry.moveTo(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); return true; case QtWindows::CalculateSize: - return false; + return QWindowsGeometryHint::handleCalculateSize(d->m_creationContext->customMargins, msg, result); default: break; } @@ -818,12 +818,7 @@ bool QWindowsContext::windowsProc(HWND hwnd, UINT message, platformWindow->getSizeHints(reinterpret_cast(lParam)); return true;// maybe available on some SDKs revisit WM_NCCALCSIZE case QtWindows::CalculateSize: - // NCCALCSIZE_PARAMS structure if wParam==TRUE - if (wParam && QWindowsContext::verboseWindows) { - const NCCALCSIZE_PARAMS *ncp = reinterpret_cast(lParam); - qDebug() << platformWindow->window() << *ncp; - } - break; + return QWindowsGeometryHint::handleCalculateSize(platformWindow->customMargins(), msg, result); #endif case QtWindows::ExposeEvent: return platformWindow->handleWmPaint(hwnd, message, wParam, lParam); diff --git a/src/plugins/platforms/windows/qwindowsintegration.cpp b/src/plugins/platforms/windows/qwindowsintegration.cpp index f5ad442e68..30d399f556 100644 --- a/src/plugins/platforms/windows/qwindowsintegration.cpp +++ b/src/plugins/platforms/windows/qwindowsintegration.cpp @@ -75,6 +75,7 @@ #include #include +#include QT_BEGIN_NAMESPACE @@ -109,6 +110,11 @@ public: void *eventProc) const; bool asyncExpose() const; void setAsyncExpose(bool value); + + QVariantMap windowProperties(QPlatformWindow *window) const; + QVariant windowProperty(QPlatformWindow *window, const QString &name) const; + QVariant windowProperty(QPlatformWindow *window, const QString &name, const QVariant &defaultValue) const; + void setWindowProperty(QPlatformWindow *window, const QString &name, const QVariant &value); }; void *QWindowsNativeInterface::nativeResourceForWindow(const QByteArray &resource, QWindow *window) @@ -145,6 +151,37 @@ void *QWindowsNativeInterface::nativeResourceForBackingStore(const QByteArray &r return 0; } +static const char customMarginPropertyC[] = "WindowsCustomMargins"; + +QVariant QWindowsNativeInterface::windowProperty(QPlatformWindow *window, const QString &name) const +{ + QWindowsWindow *platformWindow = static_cast(window); + if (name == QLatin1String(customMarginPropertyC)) + return qVariantFromValue(platformWindow->customMargins()); + return QVariant(); +} + +QVariant QWindowsNativeInterface::windowProperty(QPlatformWindow *window, const QString &name, const QVariant &defaultValue) const +{ + const QVariant result = windowProperty(window, name); + return result.isValid() ? result : defaultValue; +} + +void QWindowsNativeInterface::setWindowProperty(QPlatformWindow *window, const QString &name, const QVariant &value) +{ + QWindowsWindow *platformWindow = static_cast(window); + if (name == QLatin1String(customMarginPropertyC)) + platformWindow->setCustomMargins(qvariant_cast(value)); +} + +QVariantMap QWindowsNativeInterface::windowProperties(QPlatformWindow *window) const +{ + QVariantMap result; + const QString customMarginProperty = QLatin1String(customMarginPropertyC); + result.insert(customMarginProperty, windowProperty(window, customMarginProperty)); + return result; +} + #ifndef QT_NO_OPENGL void *QWindowsNativeInterface::nativeResourceForContext(const QByteArray &resource, QOpenGLContext *context) { @@ -356,6 +393,11 @@ QPlatformWindow *QWindowsIntegration::createPlatformWindow(QWindow *window) cons QWindowsWindow::WindowData requested; requested.flags = window->flags(); requested.geometry = window->geometry(); + // Apply custom margins (see QWindowsWindow::setCustomMargins())). + const QVariant customMarginsV = window->property("_q_windowsCustomMargins"); + if (customMarginsV.isValid()) + requested.customMargins = qvariant_cast(customMarginsV); + const QWindowsWindow::WindowData obtained = QWindowsWindow::WindowData::create(window, requested, window->title()); if (QWindowsContext::verboseIntegration || QWindowsContext::verboseWindows) diff --git a/src/plugins/platforms/windows/qwindowswindow.cpp b/src/plugins/platforms/windows/qwindowswindow.cpp index 97aeadacd3..50fd7a0792 100644 --- a/src/plugins/platforms/windows/qwindowswindow.cpp +++ b/src/plugins/platforms/windows/qwindowswindow.cpp @@ -274,7 +274,7 @@ struct WindowCreationData tool(false), embedded(false) {} void fromWindow(const QWindow *w, const Qt::WindowFlags flags, unsigned creationFlags = 0); - inline WindowData create(const QWindow *w, const QRect &geometry, QString title) const; + inline WindowData create(const QWindow *w, const WindowData &data, QString title) const; inline void applyWindowFlags(HWND hwnd) const; void initialize(HWND h, bool frameChange, qreal opacityLevel) const; @@ -416,7 +416,7 @@ void WindowCreationData::fromWindow(const QWindow *w, const Qt::WindowFlags flag } QWindowsWindow::WindowData - WindowCreationData::create(const QWindow *w, const QRect &geometry, QString title) const + WindowCreationData::create(const QWindow *w, const WindowData &data, QString title) const { typedef QSharedPointer QWindowCreationContextPtr; @@ -442,24 +442,25 @@ QWindowsWindow::WindowData const wchar_t *titleUtf16 = reinterpret_cast(title.utf16()); const wchar_t *classNameUtf16 = reinterpret_cast(windowClassName.utf16()); - // Capture events before CreateWindowEx() returns. - const QWindowCreationContextPtr context(new QWindowCreationContext(w, geometry, style, exStyle)); + // Capture events before CreateWindowEx() returns. The context is cleared in + // the QWindowsWindow constructor. + const QWindowCreationContextPtr context(new QWindowCreationContext(w, data.geometry, data.customMargins, style, exStyle)); QWindowsContext::instance()->setWindowCreationContext(context); if (QWindowsContext::verboseWindows) qDebug().nospace() << "CreateWindowEx: " << w << *this << " class=" <frameWidth << 'x' << context->frameHeight - << '+' << context->frameX << '+' << context->frameY; + << '+' << context->frameX << '+' << context->frameY + << " custom margins: " << context->customMargins; result.hwnd = CreateWindowEx(exStyle, classNameUtf16, titleUtf16, style, context->frameX, context->frameY, context->frameWidth, context->frameHeight, parentHandle, NULL, appinst, NULL); - QWindowsContext::instance()->setWindowCreationContext(QWindowCreationContextPtr()); if (QWindowsContext::verboseWindows) qDebug().nospace() << "CreateWindowEx: returns " << w << ' ' << result.hwnd << " obtained geometry: " @@ -473,6 +474,7 @@ QWindowsWindow::WindowData result.geometry = context->obtainedGeometry; result.frame = context->margins; result.embedded = embedded; + result.customMargins = context->customMargins; return result; } @@ -511,6 +513,8 @@ void WindowCreationData::initialize(HWND hwnd, bool frameChange, qreal opacityLe qWarning() << "QWidget: Incompatible window flags: the window can't be on top and on bottom at the same time"; } else if (flags & Qt::WindowStaysOnBottomHint) { SetWindowPos(hwnd, HWND_BOTTOM, 0, 0, 0, 0, swpFlags); + } else if (frameChange) { // Force WM_NCCALCSIZE with wParam=1 in case of custom margins. + SetWindowPos(hwnd, 0, 0, 0, 0, 0, swpFlags); } if (flags & (Qt::CustomizeWindowHint|Qt::WindowTitleHint)) { HMENU systemMenu = GetSystemMenu(hwnd, FALSE); @@ -571,6 +575,33 @@ QMargins QWindowsGeometryHint::frame(DWORD style, DWORD exStyle) return result; } +bool QWindowsGeometryHint::handleCalculateSize(const QMargins &customMargins, const MSG &msg, LRESULT *result) +{ +#ifndef Q_OS_WINCE + // NCCALCSIZE_PARAMS structure if wParam==TRUE + if (!msg.wParam || customMargins.isNull()) + return false; + *result = DefWindowProc(msg.hwnd, msg.message, msg.wParam, msg.lParam); + NCCALCSIZE_PARAMS *ncp = reinterpret_cast(msg.lParam); + const RECT oldClientArea = ncp->rgrc[0]; + ncp->rgrc[0].left += customMargins.left(); + ncp->rgrc[0].top += customMargins.top(); + ncp->rgrc[0].right -= customMargins.right(); + ncp->rgrc[0].bottom -= customMargins.bottom(); + result = 0; + if (QWindowsContext::verboseWindows) + qDebug() << __FUNCTION__ << oldClientArea << '+' << customMargins << "-->" + << ncp->rgrc[0] << ' ' << ncp->rgrc[1] << ' ' << ncp->rgrc[2] + << ' ' << ncp->lppos->cx << ',' << ncp->lppos->cy; + return true; +#else + Q_UNUSED(customMargins) + Q_UNUSED(msg) + Q_UNUSED(result) + return false; +#endif +} + #ifndef Q_OS_WINCE void QWindowsGeometryHint::applyToMinMaxInfo(HWND hwnd, MINMAXINFO *mmi) const { @@ -637,10 +668,11 @@ bool QWindowsGeometryHint::positionIncludesFrame(const QWindow *w) QWindowCreationContext::QWindowCreationContext(const QWindow *w, const QRect &geometry, + const QMargins &cm, DWORD style_, DWORD exStyle_) : geometryHint(w), style(style_), exStyle(exStyle_), requestedGeometry(geometry), obtainedGeometry(geometry), - margins(QWindowsGeometryHint::frame(style, exStyle)), + margins(QWindowsGeometryHint::frame(style, exStyle)), customMargins(cm), frameX(CW_USEDEFAULT), frameY(CW_USEDEFAULT), frameWidth(CW_USEDEFAULT), frameHeight(CW_USEDEFAULT) { @@ -651,14 +683,16 @@ QWindowCreationContext::QWindowCreationContext(const QWindow *w, if (geometry.isValid()) { frameX = geometry.x(); frameY = geometry.y(); - frameWidth = margins.left() + geometry.width() + margins.right(); - frameHeight = margins.top() + geometry.height() + margins.bottom(); + const QMargins effectiveMargins = margins + customMargins; + frameWidth = effectiveMargins.left() + geometry.width() + effectiveMargins.right(); + frameHeight = effectiveMargins.top() + geometry.height() + effectiveMargins.bottom(); const bool isDefaultPosition = !frameX && !frameY && w->isTopLevel(); if (!QWindowsGeometryHint::positionIncludesFrame(w) && !isDefaultPosition) { - frameX -= margins.left(); - frameY -= margins.top(); + frameX -= effectiveMargins.left(); + frameY -= effectiveMargins.top(); } } + if (QWindowsContext::verboseWindows) qDebug().nospace() << __FUNCTION__ << ' ' << w << geometry @@ -666,7 +700,8 @@ QWindowCreationContext::QWindowCreationContext(const QWindow *w, << " frame: " << frameWidth << 'x' << frameHeight << '+' << frameX << '+' << frameY << " min" << geometryHint.minimumSize - << " max" << geometryHint.maximumSize; + << " max" << geometryHint.maximumSize + << " custom margins " << customMargins; } /*! @@ -713,6 +748,8 @@ QWindowsWindow::QWindowsWindow(QWindow *aWindow, const WindowData &data) : { if (aWindow->surfaceType() == QWindow::OpenGLSurface) setFlag(OpenGLSurface); + // Clear the creation context as the window can be found in QWindowsContext's map. + QWindowsContext::instance()->setWindowCreationContext(QSharedPointer()); QWindowsContext::instance()->addWindow(m_data.hwnd, this); if (aWindow->isTopLevel()) { switch (aWindow->type()) { @@ -826,8 +863,9 @@ QWindowsWindow::WindowData { WindowCreationData creationData; creationData.fromWindow(w, parameters.flags); - WindowData result = creationData.create(w, parameters.geometry, title); - creationData.initialize(result.hwnd, false, 1); + WindowData result = creationData.create(w, parameters, title); + // Force WM_NCCALCSIZE (with wParam=1) via SWP_FRAMECHANGED for custom margin. + creationData.initialize(result.hwnd, !parameters.customMargins.isNull(), 1); return result; } @@ -1452,7 +1490,7 @@ QMargins QWindowsWindow::frameMargins() const m_data.frame = QWindowsGeometryHint::frame(style(), exStyle()); clearFlag(FrameDirty); } - return m_data.frame; + return m_data.frame + m_data.customMargins; } void QWindowsWindow::setOpacity(qreal level) @@ -1808,4 +1846,32 @@ void QWindowsWindow::setWindowIcon(const QIcon &icon) } } +/*! + \brief Sets custom margins to be added to the default margins determined by + the windows style in the handling of the WM_NCCALCSIZE message. + + This is currently used to give the Aero-style QWizard a smaller top margin. + The property can be set using QPlatformNativeInterface::setWindowProperty() or, + before platform window creation, by setting a dynamic property + on the QWindow (see QWindowsIntegration::createPlatformWindow()). +*/ + +void QWindowsWindow::setCustomMargins(const QMargins &newCustomMargins) +{ + if (newCustomMargins != m_data.customMargins) { + const QMargins oldCustomMargins = m_data.customMargins; + m_data.customMargins = newCustomMargins; + // Re-trigger WM_NCALCSIZE with wParam=1 by passing SWP_FRAMECHANGED + const QRect currentFrameGeometry = frameGeometry_sys(); + const QPoint topLeft = currentFrameGeometry.topLeft(); + QRect newFrame = currentFrameGeometry.marginsRemoved(oldCustomMargins) + m_data.customMargins; + newFrame.moveTo(topLeft); + setFlag(FrameDirty); + if (QWindowsContext::verboseWindows) + qDebug() << __FUNCTION__ << oldCustomMargins << "->" << newCustomMargins + << currentFrameGeometry << "->" << newFrame; + SetWindowPos(m_data.hwnd, 0, newFrame.x(), newFrame.y(), newFrame.width(), newFrame.height(), SWP_NOZORDER | SWP_FRAMECHANGED); + } +} + QT_END_NAMESPACE diff --git a/src/plugins/platforms/windows/qwindowswindow.h b/src/plugins/platforms/windows/qwindowswindow.h index 7af1e2f718..4e82938cf4 100644 --- a/src/plugins/platforms/windows/qwindowswindow.h +++ b/src/plugins/platforms/windows/qwindowswindow.h @@ -69,6 +69,7 @@ struct QWindowsGeometryHint QWindowsGeometryHint() {} explicit QWindowsGeometryHint(const QWindow *w); static QMargins frame(DWORD style, DWORD exStyle); + static bool handleCalculateSize(const QMargins &customMargins, const MSG &msg, LRESULT *result); #ifndef Q_OS_WINCE //MinMax maybe define struct if not available void applyToMinMaxInfo(DWORD style, DWORD exStyle, MINMAXINFO *mmi) const; void applyToMinMaxInfo(HWND hwnd, MINMAXINFO *mmi) const; @@ -89,6 +90,7 @@ struct QWindowsGeometryHint struct QWindowCreationContext { QWindowCreationContext(const QWindow *w, const QRect &r, + const QMargins &customMargins, DWORD style, DWORD exStyle); #ifndef Q_OS_WINCE //MinMax maybe define struct if not available void applyToMinMaxInfo(MINMAXINFO *mmi) const @@ -101,6 +103,7 @@ struct QWindowCreationContext QRect requestedGeometry; QRect obtainedGeometry; QMargins margins; + QMargins customMargins; // User-defined, additional frame for WM_NCCALCSIZE int frameX; // Passed on to CreateWindowEx(), including frame. int frameY; int frameWidth; @@ -137,6 +140,7 @@ public: Qt::WindowFlags flags; QRect geometry; QMargins frame; // Do not use directly for windows, see FrameDirty. + QMargins customMargins; // User-defined, additional frame for NCCALCSIZE HWND hwnd; bool embedded; @@ -190,6 +194,9 @@ public: void setFrameStrutEventsEnabled(bool enabled); bool frameStrutEventsEnabled() const { return testFlag(FrameStrutEventsEnabled); } + QMargins customMargins() const { return m_data.customMargins; } + void setCustomMargins(const QMargins &m); + #ifdef QT_OPENGL_ES_2 EGLSurface eglSurfaceHandle() const { return m_eglSurface;} EGLSurface ensureEglSurfaceHandle(const QWindowsEGLStaticContextPtr &staticContext, EGLConfig config); @@ -353,4 +360,6 @@ inline void QWindowsWindow::destroyIcon() QT_END_NAMESPACE +Q_DECLARE_METATYPE(QMargins) + #endif // QWINDOWSWINDOW_H diff --git a/src/widgets/dialogs/qwizard.cpp b/src/widgets/dialogs/qwizard.cpp index 241e9f678a..654ce9cc6b 100644 --- a/src/widgets/dialogs/qwizard.cpp +++ b/src/widgets/dialogs/qwizard.cpp @@ -1557,7 +1557,7 @@ void QWizardPrivate::handleAeroStyleChange() _q_updateButtonStates(); if (q->isVisible()) - vistaHelper->setWindowPosHack(); + vistaHelper->updateCustomMargins(); inHandleAeroStyleChange = false; } @@ -2922,6 +2922,10 @@ QWidget *QWizard::sideWidget() const void QWizard::setVisible(bool visible) { Q_D(QWizard); +#if !defined(QT_NO_STYLE_WINDOWSVISTA) + if (visible) + d->vistaHelper->updateCustomMargins(); +#endif if (visible) { if (d->current == -1) restart(); diff --git a/src/widgets/dialogs/qwizard_win.cpp b/src/widgets/dialogs/qwizard_win.cpp index c78202e866..e328a5f498 100644 --- a/src/widgets/dialogs/qwizard_win.cpp +++ b/src/widgets/dialogs/qwizard_win.cpp @@ -49,7 +49,9 @@ #include "qwizard.h" #include "qpaintengine.h" #include "qapplication.h" +#include #include +#include #include // Note, these tests are duplicates in qwindowsxpstyle_p.h. @@ -66,6 +68,8 @@ #include +Q_DECLARE_METATYPE(QMargins) + QT_BEGIN_NAMESPACE //DWM related @@ -265,6 +269,24 @@ QVistaHelper::~QVistaHelper() --instanceCount; } +void QVistaHelper::updateCustomMargins() +{ + if (QWindow *window = wizard->windowHandle()) { + // Reduce top frame to zero since we paint it ourselves. + const QMargins customMargins = vistaState() == VistaAero ? + QMargins(0, -titleBarSize(), 0, 0) : QMargins(); + const QVariant customMarginsV = qVariantFromValue(customMargins); + // The dynamic property takes effect when creating the platform window. + window->setProperty("_q_windowsCustomMargins", customMarginsV); + // If a platform window exists, change via native interface. + if (QPlatformWindow *platformWindow = window->handle()) { + QGuiApplication::platformNativeInterface()-> + setWindowProperty(platformWindow, QStringLiteral("WindowsCustomMargins"), + customMarginsV); + } + } +} + bool QVistaHelper::isCompositionEnabled() { bool value = is_vista; @@ -402,13 +424,6 @@ bool QVistaHelper::winEvent(MSG* msg, long* result) } break; } -// case WM_NCCALCSIZE: { #fixme: If the frame size is changed, it needs to be communicated to the QWindow. -// NCCALCSIZE_PARAMS* lpncsp = (NCCALCSIZE_PARAMS*)msg->lParam; -// *result = DefWindowProc(msg->hwnd, msg->message, msg->wParam, msg->lParam); -// lpncsp->rgrc[0].top -= (vistaState() == VistaAero ? titleBarSize() : 0); -// -// break; -// } default: LRESULT lResult; // Pass to DWM to handle @@ -449,38 +464,6 @@ void QVistaHelper::mouseEvent(QEvent *event) } } -// The following hack ensures that the titlebar is updated correctly -// when the wizard style changes to and from AeroStyle. Specifically, -// this function causes a Windows message of type WM_NCCALCSIZE to -// be triggered. -void QVistaHelper::setWindowPosHack() -{ - const int x = wizard->geometry().x(); // ignored by SWP_NOMOVE - const int y = wizard->geometry().y(); // ignored by SWP_NOMOVE - const int w = wizard->width(); - const int h = wizard->height(); - HWND handle = QApplicationPrivate::getHWNDForWidget(wizard); - SetWindowPos(handle, 0, x, y, w, h, SWP_NOMOVE | SWP_NOZORDER | SWP_FRAMECHANGED); -} - -// The following hack allows any QWidget subclass to access -// QWidgetPrivate::topData() without being declared as a -// friend by QWidget. -class QHackWidget : public QWidget -{ -public: - Q_DECLARE_PRIVATE(QWidget) - QTLWExtra* topData() { return d_func()->topData(); } -}; - -void QVistaHelper::collapseTopFrameStrut() -{ - QTLWExtra *top = ((QHackWidget *)wizard)->d_func()->topData(); - int x1, y1, x2, y2; - top->frameStrut.getCoords(&x1, &y1, &x2, &y2); - top->frameStrut.setCoords(x1, 0, x2, y2); -} - bool QVistaHelper::handleWinEvent(MSG *message, long *result) { if (message->message == WIZ_WM_THEMECHANGED || message->message == WIZ_WM_DWMCOMPOSITIONCHANGED) @@ -489,12 +472,8 @@ bool QVistaHelper::handleWinEvent(MSG *message, long *result) bool status = false; if (wizard->wizardStyle() == QWizard::AeroStyle && vistaState() == VistaAero) { status = winEvent(message, result); - if (message->message == WM_NCCALCSIZE) { -// if (status) #fixme -// collapseTopFrameStrut(); - } else if (message->message == WM_NCPAINT) { + if (message->message == WM_NCPAINT) wizard->update(); - } } return status; } diff --git a/src/widgets/dialogs/qwizard_win_p.h b/src/widgets/dialogs/qwizard_win_p.h index 8f1c0d6b01..610812dd60 100644 --- a/src/widgets/dialogs/qwizard_win_p.h +++ b/src/widgets/dialogs/qwizard_win_p.h @@ -87,6 +87,7 @@ public: QVistaHelper(QWizard *wizard); ~QVistaHelper(); enum TitleBarChangeType { NormalTitleBar, ExtendedTitleBar }; + void updateCustomMargins(); bool setDWMTitleBar(TitleBarChangeType type); void setTitleBarIconAndCaptionVisible(bool visible); void mouseEvent(QEvent *event); @@ -96,7 +97,6 @@ public: QVistaBackButton *backButton() const { return backButton_; } void disconnectBackButton() { if (backButton_) backButton_->disconnect(); } void hideBackButton() { if (backButton_) backButton_->hide(); } - void setWindowPosHack(); QColor basicWindowFrameColor(); enum VistaState { VistaAero, VistaBasic, Classic, Dirty }; static VistaState vistaState();