1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the QtWidgets module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 
40 #include <QtWidgets/private/qtwidgetsglobal_p.h>
41 
42 #if QT_CONFIG(style_windowsvista)
43 
44 #include "qwizard_win_p.h"
45 #include <private/qapplication_p.h>
46 #include <qpa/qplatformnativeinterface.h>
47 #include "qwizard.h"
48 #include "qpaintengine.h"
49 #include "qapplication.h"
50 #include <QtCore/QOperatingSystemVersion>
51 #include <QtCore/QVariant>
52 #include <QtCore/QDebug>
53 #include <QtGui/QMouseEvent>
54 #include <QtGui/QWindow>
55 #include <QtWidgets/QDesktopWidget>
56 
57 #include <uxtheme.h>
58 #include <vssym32.h>
59 #include <dwmapi.h>
60 
61 Q_DECLARE_METATYPE(QMargins)
62 
63 #ifndef WM_DWMCOMPOSITIONCHANGED
64 #  define WM_DWMCOMPOSITIONCHANGED 0x031E
65 #endif
66 
67 QT_BEGIN_NAMESPACE
68 
69 int QVistaHelper::instanceCount = 0;
70 int QVistaHelper::m_devicePixelRatio = 1;
71 QVistaHelper::VistaState QVistaHelper::cachedVistaState = QVistaHelper::Dirty;
72 
73 /******************************************************************************
74 ** QVistaBackButton
75 */
76 
QVistaBackButton(QWidget * widget)77 QVistaBackButton::QVistaBackButton(QWidget *widget)
78     : QAbstractButton(widget)
79 {
80     setFocusPolicy(Qt::NoFocus);
81     // Native dialogs use ALT-Left even in RTL mode, so do the same, even if it might be counter-intuitive.
82 #if QT_CONFIG(shortcut)
83     setShortcut(QKeySequence(Qt::ALT | Qt::Key_Left));
84 #endif
85 }
86 
sizeHint() const87 QSize QVistaBackButton::sizeHint() const
88 {
89     ensurePolished();
90     int size = int(QStyleHelper::dpiScaled(32, this));
91     int width = size, height = size;
92     return QSize(width, height);
93 }
94 
enterEvent(QEvent * event)95 void QVistaBackButton::enterEvent(QEvent *event)
96 {
97     if (isEnabled())
98         update();
99     QAbstractButton::enterEvent(event);
100 }
101 
leaveEvent(QEvent * event)102 void QVistaBackButton::leaveEvent(QEvent *event)
103 {
104     if (isEnabled())
105         update();
106     QAbstractButton::leaveEvent(event);
107 }
108 
paintEvent(QPaintEvent *)109 void QVistaBackButton::paintEvent(QPaintEvent *)
110 {
111     QPainter p(this);
112     QRect r = rect();
113     const HANDLE theme = OpenThemeData(0, L"Navigation");
114     //RECT rect;
115     QPoint origin;
116     const HDC hdc = QVistaHelper::backingStoreDC(parentWidget(), &origin);
117     RECT clipRect;
118     int xoffset = origin.x() + QWidget::mapToParent(r.topLeft()).x() - 1;
119     int yoffset = origin.y() + QWidget::mapToParent(r.topLeft()).y() - 1;
120     const int dpr = devicePixelRatio();
121     const QRect rDp = QRect(r.topLeft() * dpr, r.size() * dpr);
122     const int xoffsetDp = xoffset * dpr;
123     const int yoffsetDp = yoffset * dpr;
124 
125     clipRect.top = rDp.top() + yoffsetDp;
126     clipRect.bottom = rDp.bottom() + yoffsetDp;
127     clipRect.left = rDp.left() + xoffsetDp;
128     clipRect.right = rDp.right()  + xoffsetDp;
129 
130     int state = NAV_BB_NORMAL;
131     if (!isEnabled())
132         state = NAV_BB_DISABLED;
133     else if (isDown())
134         state = NAV_BB_PRESSED;
135     else if (underMouse())
136         state = NAV_BB_HOT;
137 
138     DrawThemeBackground(theme, hdc,
139                         layoutDirection() == Qt::LeftToRight ? NAV_BACKBUTTON : NAV_FORWARDBUTTON,
140                         state, &clipRect, &clipRect);
141 }
142 
143 /******************************************************************************
144 ** QVistaHelper
145 */
146 
QVistaHelper(QWizard * wizard)147 QVistaHelper::QVistaHelper(QWizard *wizard)
148     : QObject(wizard)
149     , pressed(false)
150     , wizard(wizard)
151     , backButton_(0)
152 {
153     QVistaHelper::m_devicePixelRatio = wizard->devicePixelRatio();
154     if (instanceCount++ == 0)
155         cachedVistaState = Dirty;
156     backButton_ = new QVistaBackButton(wizard);
157     backButton_->hide();
158 
159     iconSpacing = QStyleHelper::dpiScaled(7, wizard);
160 }
161 
~QVistaHelper()162 QVistaHelper::~QVistaHelper()
163 {
164     --instanceCount;
165 }
166 
updateCustomMargins(bool vistaMargins)167 void QVistaHelper::updateCustomMargins(bool vistaMargins)
168 {
169     if (QWindow *window = wizard->windowHandle()) {
170         // Reduce top frame to zero since we paint it ourselves. Use
171         // device pixel to avoid rounding errors.
172         const QMargins customMarginsDp = vistaMargins
173             ? QMargins(0, -titleBarSizeDp(), 0, 0)
174             : QMargins();
175         const QVariant customMarginsV = QVariant::fromValue(customMarginsDp);
176         // The dynamic property takes effect when creating the platform window.
177         window->setProperty("_q_windowsCustomMargins", customMarginsV);
178         // If a platform window exists, change via native interface.
179         if (QPlatformWindow *platformWindow = window->handle()) {
180             QGuiApplication::platformNativeInterface()->
181                 setWindowProperty(platformWindow, QStringLiteral("WindowsCustomMargins"),
182                                   customMarginsV);
183         }
184     }
185 }
186 
isCompositionEnabled()187 bool QVistaHelper::isCompositionEnabled()
188 {
189     BOOL bEnabled;
190     return SUCCEEDED(DwmIsCompositionEnabled(&bEnabled)) && bEnabled;
191 }
192 
isThemeActive()193 bool QVistaHelper::isThemeActive()
194 {
195     return IsThemeActive();
196 }
197 
vistaState()198 QVistaHelper::VistaState QVistaHelper::vistaState()
199 {
200     if (instanceCount == 0 || cachedVistaState == Dirty)
201         cachedVistaState =
202             isCompositionEnabled() ? VistaAero : isThemeActive() ? VistaBasic : Classic;
203     return cachedVistaState;
204 }
205 
disconnectBackButton()206 void QVistaHelper::disconnectBackButton()
207 {
208     if (backButton_) // Leave QStyleSheetStyle's connections on destroyed() intact.
209         backButton_->disconnect(SIGNAL(clicked()));
210 }
211 
basicWindowFrameColor()212 QColor QVistaHelper::basicWindowFrameColor()
213 {
214     DWORD rgb;
215     const HANDLE hTheme = OpenThemeData(GetDesktopWindow(), L"WINDOW");
216     GetThemeColor(hTheme, WP_CAPTION, CS_ACTIVE,
217                   wizard->isActiveWindow() ? TMT_FILLCOLORHINT : TMT_BORDERCOLORHINT, &rgb);
218     BYTE r = GetRValue(rgb);
219     BYTE g = GetGValue(rgb);
220     BYTE b = GetBValue(rgb);
221     return QColor(r, g, b);
222 }
223 
setDWMTitleBar(TitleBarChangeType type)224 bool QVistaHelper::setDWMTitleBar(TitleBarChangeType type)
225 {
226     bool value = false;
227     if (vistaState() == VistaAero) {
228         MARGINS mar = {0, 0, 0, 0};
229         if (type == NormalTitleBar)
230             mar.cyTopHeight = 0;
231         else
232             mar.cyTopHeight = (titleBarSize() + topOffset(wizard)) * QVistaHelper::m_devicePixelRatio;
233         if (const HWND wizardHandle = wizardHWND())
234             if (SUCCEEDED(DwmExtendFrameIntoClientArea(wizardHandle, &mar)))
235                 value = true;
236     }
237     return value;
238 }
239 
240 Q_GUI_EXPORT HICON qt_pixmapToWinHICON(const QPixmap &);
241 
getCaptionLogFont(HANDLE hTheme)242 static LOGFONT getCaptionLogFont(HANDLE hTheme)
243 {
244     LOGFONT result = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, { 0 } };
245 
246     if (!hTheme || FAILED(GetThemeSysFont(hTheme, TMT_CAPTIONFONT, &result))) {
247         NONCLIENTMETRICS ncm;
248         ncm.cbSize = sizeof(NONCLIENTMETRICS);
249         SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICS), &ncm, false);
250         result = ncm.lfMessageFont;
251     }
252     return result;
253 }
254 
getCaptionQFont(int dpi,QFont * result)255 static bool getCaptionQFont(int dpi, QFont *result)
256 {
257     const HANDLE hTheme = OpenThemeData(GetDesktopWindow(), L"WINDOW");
258     if (!hTheme)
259         return false;
260     // Call into QWindowsNativeInterface to convert the LOGFONT into a QFont.
261     const LOGFONT logFont = getCaptionLogFont(hTheme);
262     QPlatformNativeInterface *ni = QGuiApplication::platformNativeInterface();
263     return ni && QMetaObject::invokeMethod(ni, "logFontToQFont", Qt::DirectConnection,
264                                            Q_RETURN_ARG(QFont, *result),
265                                            Q_ARG(const void*, &logFont),
266                                            Q_ARG(int, dpi));
267 }
268 
drawTitleBar(QPainter * painter)269 void QVistaHelper::drawTitleBar(QPainter *painter)
270 {
271     Q_ASSERT(backButton_);
272     QPoint origin;
273     const bool isWindow = wizard->isWindow();
274     const HDC hdc = QVistaHelper::backingStoreDC(wizard, &origin);
275 
276     if (vistaState() == VistaAero && isWindow)
277         drawBlackRect(QRect(0, 0, wizard->width(),
278                             titleBarSize() + topOffset(wizard)), hdc);
279     // The button is positioned in QWizardPrivate::handleAeroStyleChange(),
280     // all calculation is relative to it.
281     const int btnTop = backButton_->mapToParent(QPoint()).y();
282     const int btnHeight = backButton_->size().height();
283     const int verticalCenter = (btnTop + btnHeight / 2) - 1;
284 
285     const QString text = wizard->window()->windowTitle();
286     QFont font;
287     if (!isWindow || !getCaptionQFont(wizard->logicalDpiY() * wizard->devicePixelRatio(), &font))
288         font = QApplication::font("QMdiSubWindowTitleBar");
289     const QFontMetrics fontMetrics(font);
290     const QRect brect = fontMetrics.boundingRect(text);
291     int textHeight = brect.height();
292     int textWidth = brect.width();
293     int glowOffset = 0;
294 
295     if (vistaState() == VistaAero) {
296         glowOffset = glowSize(wizard);
297         textHeight += 2 * glowOffset;
298         textWidth += 2 * glowOffset;
299     }
300 
301     const int titleLeft = (wizard->layoutDirection() == Qt::LeftToRight
302                            ? titleOffset() - glowOffset
303                            : wizard->width() - titleOffset() - textWidth + glowOffset);
304 
305     const QRect textRectangle(titleLeft, verticalCenter - textHeight / 2, textWidth, textHeight);
306     if (isWindow) {
307         drawTitleText(painter, text, textRectangle, hdc);
308     } else {
309         painter->save();
310         painter->setFont(font);
311         painter->drawText(textRectangle, Qt::AlignVCenter | Qt::AlignHCenter, text);
312         painter->restore();
313     }
314 
315     const QIcon windowIcon = wizard->windowIcon();
316     if (!windowIcon.isNull()) {
317         const int size = QVistaHelper::iconSize(wizard);
318         const int iconLeft = (wizard->layoutDirection() == Qt::LeftToRight
319                               ? leftMargin(wizard)
320                               : wizard->width() - leftMargin(wizard) - size);
321 
322         const QPoint pos(origin.x() + iconLeft, origin.y() + verticalCenter - size / 2);
323         const QPoint posDp = pos * QVistaHelper::m_devicePixelRatio;
324         const HICON hIcon = qt_pixmapToWinHICON(windowIcon.pixmap(size * QVistaHelper::m_devicePixelRatio));
325         DrawIconEx(hdc, posDp.x(), posDp.y(), hIcon, 0, 0, 0, NULL, DI_NORMAL | DI_COMPAT);
326         DestroyIcon(hIcon);
327     }
328 }
329 
setTitleBarIconAndCaptionVisible(bool visible)330 void QVistaHelper::setTitleBarIconAndCaptionVisible(bool visible)
331 {
332     WTA_OPTIONS opt;
333     opt.dwFlags = WTNCA_NODRAWICON | WTNCA_NODRAWCAPTION;
334     if (visible)
335         opt.dwMask = 0;
336     else
337         opt.dwMask = WTNCA_NODRAWICON | WTNCA_NODRAWCAPTION;
338     if (const HWND handle = wizardHWND())
339         SetWindowThemeAttribute(handle, WTA_NONCLIENT, &opt, sizeof(WTA_OPTIONS));
340 }
341 
342 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
winEvent(MSG * msg,qintptr * result)343 bool QVistaHelper::winEvent(MSG* msg, qintptr *result)
344 #else
345 bool QVistaHelper::winEvent(MSG* msg, long* result)
346 #endif
347 {
348     switch (msg->message) {
349     case WM_NCHITTEST: {
350         LRESULT lResult;
351         // Perform hit testing using DWM
352         if (DwmDefWindowProc(msg->hwnd, msg->message, msg->wParam, msg->lParam, &lResult)) {
353             // DWM returned a hit, no further processing necessary
354             *result = lResult;
355         } else {
356             // DWM didn't return a hit, process using DefWindowProc
357             lResult = DefWindowProc(msg->hwnd, msg->message, msg->wParam, msg->lParam);
358             // If DefWindowProc returns a window caption button, just return HTCLIENT (client area).
359             // This avoid unnecessary hits to Windows NT style caption buttons which aren't visible but are
360             // located just under the Aero style window close button.
361             if (lResult == HTCLOSE || lResult == HTMAXBUTTON || lResult == HTMINBUTTON || lResult == HTHELP)
362                 *result = HTCLIENT;
363             else
364                 *result = lResult;
365         }
366         break;
367     }
368     default:
369         LRESULT lResult;
370         // Pass to DWM to handle
371         if (DwmDefWindowProc(msg->hwnd, msg->message, msg->wParam, msg->lParam, &lResult))
372             *result = lResult;
373         // If the message wasn't handled by DWM, continue processing it as normal
374         else
375             return false;
376     }
377 
378     return true;
379 }
380 
setMouseCursor(QPoint pos)381 void QVistaHelper::setMouseCursor(QPoint pos)
382 {
383 #ifndef QT_NO_CURSOR
384     if (rtTop.contains(pos))
385         wizard->setCursor(Qt::SizeVerCursor);
386     else
387         wizard->setCursor(Qt::ArrowCursor);
388 #endif
389 }
390 
mouseEvent(QEvent * event)391 void QVistaHelper::mouseEvent(QEvent *event)
392 {
393     switch (event->type()) {
394     case QEvent::MouseMove:
395         mouseMoveEvent(static_cast<QMouseEvent *>(event));
396         break;
397     case QEvent::MouseButtonPress:
398         mousePressEvent(static_cast<QMouseEvent *>(event));
399         break;
400     case QEvent::MouseButtonRelease:
401         mouseReleaseEvent(static_cast<QMouseEvent *>(event));
402         break;
403     default:
404         break;
405     }
406 }
407 
408 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
handleWinEvent(MSG * message,qintptr * result)409 bool QVistaHelper::handleWinEvent(MSG *message, qintptr *result)
410 #else
411 bool QVistaHelper::handleWinEvent(MSG *message, long *result)
412 #endif
413 {
414     if (message->message == WM_THEMECHANGED || message->message == WM_DWMCOMPOSITIONCHANGED)
415         cachedVistaState = Dirty;
416 
417     bool status = false;
418     if (wizard->wizardStyle() == QWizard::AeroStyle && vistaState() == VistaAero) {
419         status = winEvent(message, result);
420         if (message->message == WM_NCPAINT)
421             wizard->update();
422     }
423     return status;
424 }
425 
resizeEvent(QResizeEvent * event)426 void QVistaHelper::resizeEvent(QResizeEvent * event)
427 {
428     Q_UNUSED(event);
429     rtTop = QRect (0, 0, wizard->width(), frameSize());
430     int height = captionSize() + topOffset(wizard);
431     if (vistaState() == VistaBasic)
432         height -= titleBarSize();
433     rtTitle = QRect (0, frameSize(), wizard->width(), height);
434 }
435 
paintEvent(QPaintEvent * event)436 void QVistaHelper::paintEvent(QPaintEvent *event)
437 {
438     Q_UNUSED(event);
439     QPainter painter(wizard);
440     drawTitleBar(&painter);
441 }
442 
mouseMoveEvent(QMouseEvent * event)443 void QVistaHelper::mouseMoveEvent(QMouseEvent *event)
444 {
445     if (wizard->windowState() & Qt::WindowMaximized) {
446         event->ignore();
447         return;
448     }
449 
450     QRect rect = wizard->geometry();
451     if (pressed) {
452         switch (change) {
453         case resizeTop:
454             {
455                 const int dy = event->pos().y() - pressedPos.y();
456                 if ((dy > 0 && rect.height() > wizard->minimumHeight())
457                     || (dy < 0 && rect.height() < wizard->maximumHeight()))
458                     rect.setTop(rect.top() + dy);
459             }
460             break;
461         case movePosition: {
462             QPoint newPos = event->pos() - pressedPos;
463             rect.moveLeft(rect.left() + newPos.x());
464             rect.moveTop(rect.top() + newPos.y());
465             break; }
466         default:
467             break;
468         }
469         wizard->setGeometry(rect);
470 
471     } else if (vistaState() == VistaAero) {
472         setMouseCursor(event->pos());
473     }
474     event->ignore();
475 }
476 
mousePressEvent(QMouseEvent * event)477 void QVistaHelper::mousePressEvent(QMouseEvent *event)
478 {
479     change = noChange;
480 
481     if (event->button() != Qt::LeftButton || wizard->windowState() & Qt::WindowMaximized) {
482         event->ignore();
483         return;
484     }
485 
486     if (rtTitle.contains(event->pos())) {
487         change = movePosition;
488     } else if (rtTop.contains(event->pos()))
489         change = (vistaState() == VistaAero) ? resizeTop : movePosition;
490 
491     if (change != noChange) {
492         if (vistaState() == VistaAero)
493             setMouseCursor(event->pos());
494         pressed = true;
495         pressedPos = event->pos();
496     } else {
497         event->ignore();
498     }
499 }
500 
mouseReleaseEvent(QMouseEvent * event)501 void QVistaHelper::mouseReleaseEvent(QMouseEvent *event)
502 {
503     change = noChange;
504     if (pressed) {
505         pressed = false;
506         wizard->releaseMouse();
507         if (vistaState() == VistaAero)
508             setMouseCursor(event->pos());
509     }
510     event->ignore();
511 }
512 
eventFilter(QObject * obj,QEvent * event)513 bool QVistaHelper::eventFilter(QObject *obj, QEvent *event)
514 {
515     if (obj != wizard)
516         return QObject::eventFilter(obj, event);
517 
518     if (event->type() == QEvent::MouseMove) {
519         QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event);
520 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
521         qintptr result;
522 #else
523         long result;
524 #endif
525         MSG msg;
526         msg.message = WM_NCHITTEST;
527         msg.wParam  = 0;
528         msg.lParam = MAKELPARAM(mouseEvent->globalX(), mouseEvent->globalY());
529         msg.hwnd = wizardHWND();
530         winEvent(&msg, &result);
531         msg.wParam = result;
532         msg.message = WM_NCMOUSEMOVE;
533         winEvent(&msg, &result);
534      } else if (event->type() == QEvent::MouseButtonPress) {
535         QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event);
536 
537         if (mouseEvent->button() == Qt::LeftButton) {
538 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
539             qintptr result;
540 #else
541             long result;
542 #endif
543             MSG msg;
544             msg.message = WM_NCHITTEST;
545             msg.wParam  = 0;
546             msg.lParam = MAKELPARAM(mouseEvent->globalX(), mouseEvent->globalY());
547             msg.hwnd = wizardHWND();
548             winEvent(&msg, &result);
549             msg.wParam = result;
550             msg.message = WM_NCLBUTTONDOWN;
551             winEvent(&msg, &result);
552         }
553      } else if (event->type() == QEvent::MouseButtonRelease) {
554         QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event);
555 
556         if (mouseEvent->button() == Qt::LeftButton) {
557 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
558             qintptr result;
559 #else
560             long result;
561 #endif
562             MSG msg;
563             msg.message = WM_NCHITTEST;
564             msg.wParam  = 0;
565             msg.lParam = MAKELPARAM(mouseEvent->globalX(), mouseEvent->globalY());
566             msg.hwnd = wizardHWND();
567             winEvent(&msg, &result);
568             msg.wParam = result;
569             msg.message = WM_NCLBUTTONUP;
570             winEvent(&msg, &result);
571         }
572      }
573 
574      return false;
575 }
576 
577 // Return a HDC for the wizard along with the transformation if the
578 // wizard is a child window.
backingStoreDC(const QWidget * wizard,QPoint * offset)579 HDC QVistaHelper::backingStoreDC(const QWidget *wizard, QPoint *offset)
580 {
581     HDC hdc = static_cast<HDC>(QGuiApplication::platformNativeInterface()->nativeResourceForBackingStore(QByteArrayLiteral("getDC"), wizard->backingStore()));
582     *offset = QPoint(0, 0);
583     if (!wizard->windowHandle())
584         if (QWidget *nativeParent = wizard->nativeParentWidget())
585             *offset = wizard->mapTo(nativeParent, *offset);
586     return hdc;
587 }
588 
wizardHWND() const589 HWND QVistaHelper::wizardHWND() const
590 {
591     // Obtain the HWND if the wizard is a top-level window.
592     // Do not use winId() as this enforces native children of the parent
593     // widget when called before show() as happens when calling setWizardStyle().
594     if (QWindow *window = wizard->windowHandle())
595         if (window->handle())
596             if (void *vHwnd = QGuiApplication::platformNativeInterface()->nativeResourceForWindow(QByteArrayLiteral("handle"), window))
597                 return static_cast<HWND>(vHwnd);
598     qWarning().nospace() << "Failed to obtain HWND for wizard.";
599     return 0;
600 }
601 
drawTitleText(QPainter * painter,const QString & text,const QRect & rect,HDC hdc)602 bool QVistaHelper::drawTitleText(QPainter *painter, const QString &text, const QRect &rect, HDC hdc)
603 {
604     bool value = false;
605     if (vistaState() == VistaAero) {
606         const QRect rectDp = QRect(rect.topLeft() * QVistaHelper::m_devicePixelRatio,
607                                    rect.size() * QVistaHelper::m_devicePixelRatio);
608         const HANDLE hTheme = OpenThemeData(GetDesktopWindow(), L"WINDOW");
609         if (!hTheme) return false;
610         // Set up a memory DC and bitmap that we'll draw into
611         HDC dcMem;
612         HBITMAP bmp;
613         BITMAPINFO dib;
614         ZeroMemory(&dib, sizeof(dib));
615         dcMem = CreateCompatibleDC(hdc);
616 
617         dib.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
618         dib.bmiHeader.biWidth = rectDp.width();
619         dib.bmiHeader.biHeight = -rectDp.height();
620         dib.bmiHeader.biPlanes = 1;
621         dib.bmiHeader.biBitCount = 32;
622         dib.bmiHeader.biCompression = BI_RGB;
623 
624         bmp = CreateDIBSection(hdc, &dib, DIB_RGB_COLORS, NULL, NULL, 0);
625 
626         // Set up the DC
627         const LOGFONT captionLogFont = getCaptionLogFont(hTheme);
628         const HFONT hCaptionFont = CreateFontIndirect(&captionLogFont);
629         auto hOldBmp = reinterpret_cast<HBITMAP>(SelectObject(dcMem, (HGDIOBJ) bmp));
630         auto hOldFont = reinterpret_cast<HFONT>(SelectObject(dcMem, (HGDIOBJ) hCaptionFont));
631 
632         // Draw the text!
633         DTTOPTS dto;
634         memset(&dto, 0, sizeof(dto));
635         dto.dwSize = sizeof(dto);
636         const UINT uFormat = DT_SINGLELINE|DT_CENTER|DT_VCENTER|DT_NOPREFIX;
637         RECT rctext ={0,0, rectDp.width(), rectDp.height()};
638 
639         dto.dwFlags = DTT_COMPOSITED|DTT_GLOWSIZE;
640         dto.iGlowSize = glowSize(wizard);
641 
642         DrawThemeTextEx(hTheme, dcMem, 0, 0, reinterpret_cast<LPCWSTR>(text.utf16()), -1, uFormat, &rctext, &dto );
643         BitBlt(hdc, rectDp.left(), rectDp.top(), rectDp.width(), rectDp.height(), dcMem, 0, 0, SRCCOPY);
644         SelectObject(dcMem, (HGDIOBJ) hOldBmp);
645         SelectObject(dcMem, (HGDIOBJ) hOldFont);
646         DeleteObject(bmp);
647         DeleteObject(hCaptionFont);
648         DeleteDC(dcMem);
649         //ReleaseDC(hwnd, hdc);
650     } else if (vistaState() == VistaBasic) {
651         painter->drawText(rect, text);
652     }
653     return value;
654 }
655 
drawBlackRect(const QRect & rect,HDC hdc)656 bool QVistaHelper::drawBlackRect(const QRect &rect, HDC hdc)
657 {
658     bool value = false;
659     if (vistaState() == VistaAero) {
660         // Set up a memory DC and bitmap that we'll draw into
661         const QRect rectDp = QRect(rect.topLeft() * QVistaHelper::m_devicePixelRatio,
662                                    rect.size() * QVistaHelper::m_devicePixelRatio);
663         HDC dcMem;
664         HBITMAP bmp;
665         BITMAPINFO dib;
666         ZeroMemory(&dib, sizeof(dib));
667         dcMem = CreateCompatibleDC(hdc);
668 
669         dib.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
670         dib.bmiHeader.biWidth = rectDp.width();
671         dib.bmiHeader.biHeight = -rectDp.height();
672         dib.bmiHeader.biPlanes = 1;
673         dib.bmiHeader.biBitCount = 32;
674         dib.bmiHeader.biCompression = BI_RGB;
675 
676         bmp = CreateDIBSection(hdc, &dib, DIB_RGB_COLORS, NULL, NULL, 0);
677         auto hOldBmp = reinterpret_cast<HBITMAP>(SelectObject(dcMem, (HGDIOBJ) bmp));
678 
679         BitBlt(hdc, rectDp.left(), rectDp.top(), rectDp.width(), rectDp.height(), dcMem, 0, 0, SRCCOPY);
680         SelectObject(dcMem, (HGDIOBJ) hOldBmp);
681 
682         DeleteObject(bmp);
683         DeleteDC(dcMem);
684     }
685     return value;
686 }
687 
688 #ifndef Q_CC_MSVC
getWindowBottomMargin()689 static inline int getWindowBottomMargin()
690 {
691     return GetSystemMetrics(SM_CYSIZEFRAME);
692 }
693 #else
694 // QTBUG-36192, GetSystemMetrics(SM_CYSIZEFRAME) returns bogus values
695 // for MSVC2012 which leads to the custom margin having no effect since
696 // that only works when removing the entire margin.
getWindowBottomMargin()697 static inline int getWindowBottomMargin()
698 {
699     RECT rect = {0, 0, 0, 0};
700     AdjustWindowRectEx(&rect, WS_POPUP | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_THICKFRAME | WS_DLGFRAME, FALSE, 0);
701     return qAbs(rect.bottom);
702 }
703 #endif // Q_CC_MSVC
704 
frameSizeDp()705 int QVistaHelper::frameSizeDp()
706 {
707     return getWindowBottomMargin();
708 }
709 
captionSizeDp()710 int QVistaHelper::captionSizeDp()
711 {
712     return GetSystemMetrics(SM_CYCAPTION);
713 }
714 
titleOffset()715 int QVistaHelper::titleOffset()
716 {
717     int iconOffset = wizard ->windowIcon().isNull() ? 0 : iconSize(wizard) + iconSpacing;
718     return leftMargin(wizard) + iconOffset;
719 }
720 
iconSize(const QPaintDevice * device)721 int QVistaHelper::iconSize(const QPaintDevice *device)
722 {
723     return QStyleHelper::dpiScaled(16, device); // Standard Aero
724 }
725 
glowSize(const QPaintDevice * device)726 int QVistaHelper::glowSize(const QPaintDevice *device)
727 {
728     return QStyleHelper::dpiScaled(10, device);
729 }
730 
topOffset(const QPaintDevice * device)731 int QVistaHelper::topOffset(const QPaintDevice *device)
732 {
733     if (vistaState() != VistaAero)
734         return titleBarSize() + 3;
735     static const int aeroOffset =
736         QOperatingSystemVersion::current() < QOperatingSystemVersion::Windows8 ?
737         QStyleHelper::dpiScaled(4, device) : QStyleHelper::dpiScaled(13, device);
738     return aeroOffset + titleBarSize();
739 }
740 
741 QT_END_NAMESPACE
742 
743 #endif // style_windowsvista
744