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