5#include <QtWidgets/private/qtwidgetsglobal_p.h>
7#if QT_CONFIG(style_windowsvista)
9#include "qwizard_win_p.h"
10#include <private/qapplication_p.h>
11#include <private/qwindowsfontdatabasebase_p.h>
12#include <qpa/qplatformwindow.h>
13#include <qpa/qplatformwindow_p.h>
15#include "qpaintengine.h"
16#include "qapplication.h"
17#include <QtCore/QOperatingSystemVersion>
18#include <QtCore/QVariant>
19#include <QtCore/QDebug>
20#include <QtGui/QMouseEvent>
21#include <QtGui/QWindow>
22#include <QtGui/private/qhighdpiscaling_p.h>
29Q_DECLARE_METATYPE(QMargins)
31#ifndef WM_DWMCOMPOSITIONCHANGED
32# define WM_DWMCOMPOSITIONCHANGED 0x031E
37qreal QVistaHelper::m_devicePixelRatio = 1.0;
40
41
43QVistaBackButton::QVistaBackButton(QWidget *widget)
44 : QAbstractButton(widget)
46 setFocusPolicy(Qt::NoFocus);
48#if QT_CONFIG(shortcut)
49 setShortcut(QKeySequence(Qt::ALT | Qt::Key_Left));
53QSize QVistaBackButton::sizeHint()
const
56 int size =
int(QStyleHelper::dpiScaled(32,
this));
57 int width = size, height = size;
58 return QSize(width, height);
61void QVistaBackButton::enterEvent(QEnterEvent *event)
65 QAbstractButton::enterEvent(event);
68void QVistaBackButton::leaveEvent(QEvent *event)
72 QAbstractButton::leaveEvent(event);
75void QVistaBackButton::paintEvent(QPaintEvent *)
79 const HANDLE theme = OpenThemeData(0, L"Navigation");
82 const HDC hdc = QVistaHelper::backingStoreDC(parentWidget(), &origin);
84 int xoffset = origin.x() + QWidget::mapToParent(r.topLeft()).x() - 1;
85 int yoffset = origin.y() + QWidget::mapToParent(r.topLeft()).y() - 1;
86 const qreal dpr = devicePixelRatio();
87 const QRect rDp = QRect(r.topLeft() * dpr, r.size() * dpr);
88 const int xoffsetDp = xoffset * dpr;
89 const int yoffsetDp = yoffset * dpr;
91 clipRect.top = rDp.top() + yoffsetDp;
92 clipRect.bottom = rDp.bottom() + yoffsetDp;
93 clipRect.left = rDp.left() + xoffsetDp;
94 clipRect.right = rDp.right() + xoffsetDp;
96 int state = NAV_BB_NORMAL;
98 state = NAV_BB_DISABLED;
100 state = NAV_BB_PRESSED;
101 else if (underMouse())
104 DrawThemeBackground(theme, hdc,
105 layoutDirection() == Qt::LeftToRight ? NAV_BACKBUTTON : NAV_FORWARDBUTTON,
106 state, &clipRect, &clipRect);
110
111
113QVistaHelper::QVistaHelper(QWizard *wizard)
119 QVistaHelper::m_devicePixelRatio = wizard->devicePixelRatio();
121 backButton_ =
new QVistaBackButton(wizard);
124 iconSpacing = QStyleHelper::dpiScaled(7, wizard);
127QVistaHelper::~QVistaHelper() =
default;
129void QVistaHelper::updateCustomMargins(
bool vistaMargins)
131 using namespace QNativeInterface::Private;
133 if (QWindow *window = wizard->windowHandle()) {
136 const QMargins customMarginsDp = vistaMargins
137 ? QMargins(0, -titleBarSizeDp(), 0, 0)
139 const QVariant customMarginsV = QVariant::fromValue(customMarginsDp);
141 window->setProperty(
"_q_windowsCustomMargins", customMarginsV);
143 if (
auto platformWindow =
dynamic_cast<QWindowsWindow *>(window->handle()))
144 platformWindow->setCustomMargins(customMarginsDp);
148void QVistaHelper::disconnectBackButton()
151 backButton_->disconnect(SIGNAL(clicked()));
154QColor QVistaHelper::basicWindowFrameColor()
157 const HANDLE hTheme = OpenThemeData(GetDesktopWindow(), L"WINDOW");
158 GetThemeColor(hTheme, WP_CAPTION, CS_ACTIVE,
159 wizard->isActiveWindow() ? TMT_FILLCOLORHINT : TMT_BORDERCOLORHINT, &rgb);
160 BYTE r = GetRValue(rgb);
161 BYTE g = GetGValue(rgb);
162 BYTE b = GetBValue(rgb);
163 return QColor(r, g, b);
166bool QVistaHelper::setDWMTitleBar(TitleBarChangeType type)
168 MARGINS mar = {0, 0, 0, 0};
169 if (type == NormalTitleBar)
172 mar.cyTopHeight = (titleBarSize() + topOffset(wizard)) * QVistaHelper::m_devicePixelRatio;
173 if (
const HWND wizardHandle = wizardHWND()) {
174 if (SUCCEEDED(DwmExtendFrameIntoClientArea(wizardHandle, &mar)))
180Q_GUI_EXPORT HICON qt_pixmapToWinHICON(
const QPixmap &);
182static LOGFONT getCaptionLogFont(HANDLE hTheme)
184 LOGFONT result = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, { 0 } };
186 if (!hTheme || FAILED(GetThemeSysFont(hTheme, TMT_CAPTIONFONT, &result))) {
187 NONCLIENTMETRICS ncm;
188 ncm.cbSize =
sizeof(NONCLIENTMETRICS);
189 SystemParametersInfo(SPI_GETNONCLIENTMETRICS,
sizeof(NONCLIENTMETRICS), &ncm,
false);
190 result = ncm.lfMessageFont;
195static bool getCaptionQFont(
int dpi, QFont *result)
197 const HANDLE hTheme = OpenThemeData(GetDesktopWindow(), L"WINDOW");
201 const LOGFONT logFont = getCaptionLogFont(hTheme);
202 *result = QWindowsFontDatabaseBase::LOGFONT_to_QFont(logFont, dpi);
206void QVistaHelper::drawTitleBar(QPainter *painter)
208 Q_ASSERT(backButton_);
210 const bool isWindow = wizard->isWindow();
211 const HDC hdc = QVistaHelper::backingStoreDC(wizard, &origin);
214 drawBlackRect(QRect(0, 0, wizard->width(),
215 titleBarSize() + topOffset(wizard)), hdc);
218 const int btnTop = backButton_->mapToParent(QPoint()).y();
219 const int btnHeight = backButton_->size().height();
220 const int verticalCenter = (btnTop + btnHeight / 2) - 1;
222 const QString text = wizard->window()->windowTitle();
224 if (!isWindow || !getCaptionQFont(wizard->logicalDpiY() * wizard->devicePixelRatio(), &font))
225 font = QApplication::font(
"QMdiSubWindowTitleBar");
226 const QFontMetrics fontMetrics(font);
227 const QRect brect = fontMetrics.boundingRect(text);
228 const int glowOffset = glowSize(wizard);
229 int textHeight = brect.height() + 2 * glowOffset;
230 int textWidth = brect.width() + 2 * glowOffset;
232 const int titleLeft = (wizard->layoutDirection() == Qt::LeftToRight
233 ? titleOffset() - glowOffset
234 : wizard->width() - titleOffset() - textWidth + glowOffset);
236 const QRect textRectangle(titleLeft, verticalCenter - textHeight / 2, textWidth, textHeight);
238 drawTitleText(painter, text, textRectangle, hdc);
241 painter->setFont(font);
242 painter->drawText(textRectangle, Qt::AlignVCenter | Qt::AlignHCenter, text);
246 const QIcon windowIcon = wizard->windowIcon();
247 if (!windowIcon.isNull()) {
248 const int size = QVistaHelper::iconSize(wizard);
249 const int iconLeft = (wizard->layoutDirection() == Qt::LeftToRight
251 : wizard->width() - leftMargin(wizard) - size);
253 const QPoint pos(origin.x() + iconLeft, origin.y() + verticalCenter - size / 2);
254 const QPoint posDp = pos * QVistaHelper::m_devicePixelRatio;
255 const HICON hIcon = qt_pixmapToWinHICON(windowIcon.pixmap(QSize(size, size), QVistaHelper::m_devicePixelRatio));
256 DrawIconEx(hdc, posDp.x(), posDp.y(), hIcon, 0, 0, 0, NULL, DI_NORMAL | DI_COMPAT);
261void QVistaHelper::setTitleBarIconAndCaptionVisible(
bool visible)
264 opt.dwFlags = WTNCA_NODRAWICON | WTNCA_NODRAWCAPTION;
268 opt.dwMask = WTNCA_NODRAWICON | WTNCA_NODRAWCAPTION;
269 if (
const HWND handle = wizardHWND())
270 SetWindowThemeAttribute(handle, WTA_NONCLIENT, &opt,
sizeof(WTA_OPTIONS));
273bool QVistaHelper::winEvent(MSG* msg, qintptr *result)
275 switch (msg->message) {
279 if (DwmDefWindowProc(msg->hwnd, msg->message, msg->wParam, msg->lParam, &lResult)) {
284 lResult = DefWindowProc(msg->hwnd, msg->message, msg->wParam, msg->lParam);
288 if (lResult == HTCLOSE || lResult == HTMAXBUTTON || lResult == HTMINBUTTON || lResult == HTHELP)
298 if (DwmDefWindowProc(msg->hwnd, msg->message, msg->wParam, msg->lParam, &lResult))
308void QVistaHelper::setMouseCursor(QPoint pos)
311 if (rtTop.contains(pos))
312 wizard->setCursor(Qt::SizeVerCursor);
314 wizard->setCursor(Qt::ArrowCursor);
318void QVistaHelper::mouseEvent(QEvent *event)
320 switch (event->type()) {
321 case QEvent::MouseMove:
322 mouseMoveEvent(
static_cast<QMouseEvent *>(event));
324 case QEvent::MouseButtonPress:
325 mousePressEvent(
static_cast<QMouseEvent *>(event));
327 case QEvent::MouseButtonRelease:
328 mouseReleaseEvent(
static_cast<QMouseEvent *>(event));
335bool QVistaHelper::handleWinEvent(MSG *message, qintptr *result)
338 if (wizard->wizardStyle() == QWizard::AeroStyle) {
339 status = winEvent(message, result);
340 if (message->message == WM_NCPAINT)
346void QVistaHelper::resizeEvent(QResizeEvent * event)
349 rtTop = QRect (0, 0, wizard->width(), frameSize());
350 int height = captionSize() + topOffset(wizard);
351 rtTitle = QRect (0, frameSize(), wizard->width(), height);
354void QVistaHelper::paintEvent(QPaintEvent *event)
357 QPainter painter(wizard);
358 drawTitleBar(&painter);
361void QVistaHelper::mouseMoveEvent(QMouseEvent *event)
363 if (wizard->windowState() & Qt::WindowMaximized) {
368 QRect rect = wizard->geometry();
373 const int dy = event->pos().y() - pressedPos.y();
374 if ((dy > 0 && rect.height() > wizard->minimumHeight())
375 || (dy < 0 && rect.height() < wizard->maximumHeight()))
376 rect.setTop(rect.top() + dy);
380 QPoint newPos = event->pos() - pressedPos;
381 rect.moveLeft(rect.left() + newPos.x());
382 rect.moveTop(rect.top() + newPos.y());
387 wizard->setGeometry(rect);
390 setMouseCursor(event->pos());
395void QVistaHelper::mousePressEvent(QMouseEvent *event)
399 if (event->button() != Qt::LeftButton || wizard->windowState() & Qt::WindowMaximized) {
404 if (rtTitle.contains(event->pos())) {
405 change = movePosition;
406 }
else if (rtTop.contains(event->pos()))
409 if (change != noChange) {
410 setMouseCursor(event->pos());
412 pressedPos = event->pos();
418void QVistaHelper::mouseReleaseEvent(QMouseEvent *event)
423 wizard->releaseMouse();
424 setMouseCursor(event->pos());
429static inline LPARAM pointToLParam(
const QPointF &p,
const QWidget *w)
431 const auto point = QHighDpi::toNativePixels(p, w->screen()).toPoint();
432 return MAKELPARAM(point.x(), point.y());
435bool QVistaHelper::eventFilter(QObject *obj, QEvent *event)
438 return QObject::eventFilter(obj, event);
440 if (event->type() == QEvent::MouseMove) {
441 QMouseEvent *mouseEvent =
static_cast<QMouseEvent*>(event);
444 msg.message = WM_NCHITTEST;
446 msg.lParam = pointToLParam(mouseEvent->globalPosition(), wizard);
447 msg.hwnd = wizardHWND();
448 winEvent(&msg, &result);
450 msg.message = WM_NCMOUSEMOVE;
451 winEvent(&msg, &result);
452 }
else if (event->type() == QEvent::MouseButtonPress) {
453 QMouseEvent *mouseEvent =
static_cast<QMouseEvent*>(event);
455 if (mouseEvent->button() == Qt::LeftButton) {
458 msg.message = WM_NCHITTEST;
460 msg.lParam = pointToLParam(mouseEvent->globalPosition(), wizard);
461 msg.hwnd = wizardHWND();
462 winEvent(&msg, &result);
464 msg.message = WM_NCLBUTTONDOWN;
465 winEvent(&msg, &result);
467 }
else if (event->type() == QEvent::MouseButtonRelease) {
468 QMouseEvent *mouseEvent =
static_cast<QMouseEvent*>(event);
470 if (mouseEvent->button() == Qt::LeftButton) {
473 msg.message = WM_NCHITTEST;
475 msg.lParam = pointToLParam(mouseEvent->globalPosition(), wizard);
476 msg.hwnd = wizardHWND();
477 winEvent(&msg, &result);
479 msg.message = WM_NCLBUTTONUP;
480 winEvent(&msg, &result);
489HDC QVistaHelper::backingStoreDC(
const QWidget *wizard, QPoint *offset)
491 HDC hdc =
static_cast<HDC>(QGuiApplication::platformNativeInterface()->nativeResourceForBackingStore(QByteArrayLiteral(
"getDC"), wizard->backingStore()));
492 *offset = QPoint(0, 0);
493 if (!wizard->windowHandle())
494 if (QWidget *nativeParent = wizard->nativeParentWidget())
495 *offset = wizard->mapTo(nativeParent, *offset);
499HWND QVistaHelper::wizardHWND()
const
504 if (QWindow *window = wizard->windowHandle())
505 if (window->handle())
506 if (
void *vHwnd = QGuiApplication::platformNativeInterface()->nativeResourceForWindow(QByteArrayLiteral(
"handle"), window))
507 return static_cast<HWND>(vHwnd);
508 qWarning().nospace() <<
"Failed to obtain HWND for wizard.";
512void QVistaHelper::drawTitleText(QPainter *painter,
const QString &text,
const QRect &rect, HDC hdc)
516 const QRect rectDp = QRect(rect.topLeft() * QVistaHelper::m_devicePixelRatio,
517 rect.size() * QVistaHelper::m_devicePixelRatio);
518 const HANDLE hTheme = OpenThemeData(GetDesktopWindow(), L"WINDOW");
525 ZeroMemory(&dib,
sizeof(dib));
526 dcMem = CreateCompatibleDC(hdc);
528 dib.bmiHeader.biSize =
sizeof(BITMAPINFOHEADER);
529 dib.bmiHeader.biWidth = rectDp.width();
530 dib.bmiHeader.biHeight = -rectDp.height();
531 dib.bmiHeader.biPlanes = 1;
532 dib.bmiHeader.biBitCount = 32;
533 dib.bmiHeader.biCompression = BI_RGB;
535 bmp = CreateDIBSection(hdc, &dib, DIB_RGB_COLORS, NULL, NULL, 0);
538 const LOGFONT captionLogFont = getCaptionLogFont(hTheme);
539 const HFONT hCaptionFont = CreateFontIndirect(&captionLogFont);
540 auto hOldBmp =
reinterpret_cast<HBITMAP>(SelectObject(dcMem, (HGDIOBJ) bmp));
541 auto hOldFont =
reinterpret_cast<HFONT>(SelectObject(dcMem, (HGDIOBJ) hCaptionFont));
545 memset(&dto, 0,
sizeof(dto));
546 dto.dwSize =
sizeof(dto);
547 const UINT uFormat = DT_SINGLELINE|DT_CENTER|DT_VCENTER|DT_NOPREFIX;
548 RECT rctext ={0,0, rectDp.width(), rectDp.height()};
550 dto.dwFlags = DTT_COMPOSITED|DTT_GLOWSIZE;
551 dto.iGlowSize = glowSize(wizard);
553 DrawThemeTextEx(hTheme, dcMem, 0, 0,
reinterpret_cast<LPCWSTR>(text.utf16()), -1, uFormat, &rctext, &dto );
554 BitBlt(hdc, rectDp.left(), rectDp.top(), rectDp.width(), rectDp.height(), dcMem, 0, 0, SRCCOPY);
555 SelectObject(dcMem, (HGDIOBJ) hOldBmp);
556 SelectObject(dcMem, (HGDIOBJ) hOldFont);
558 DeleteObject(hCaptionFont);
563void QVistaHelper::drawBlackRect(
const QRect &rect, HDC hdc)
566 const QRect rectDp = QRect(rect.topLeft() * QVistaHelper::m_devicePixelRatio,
567 rect.size() * QVistaHelper::m_devicePixelRatio);
571 ZeroMemory(&dib,
sizeof(dib));
572 dcMem = CreateCompatibleDC(hdc);
574 dib.bmiHeader.biSize =
sizeof(BITMAPINFOHEADER);
575 dib.bmiHeader.biWidth = rectDp.width();
576 dib.bmiHeader.biHeight = -rectDp.height();
577 dib.bmiHeader.biPlanes = 1;
578 dib.bmiHeader.biBitCount = 32;
579 dib.bmiHeader.biCompression = BI_RGB;
581 bmp = CreateDIBSection(hdc, &dib, DIB_RGB_COLORS, NULL, NULL, 0);
582 auto hOldBmp =
reinterpret_cast<HBITMAP>(SelectObject(dcMem, (HGDIOBJ) bmp));
584 BitBlt(hdc, rectDp.left(), rectDp.top(), rectDp.width(), rectDp.height(), dcMem, 0, 0, SRCCOPY);
585 SelectObject(dcMem, (HGDIOBJ) hOldBmp);
591int QVistaHelper::frameSizeDp()
593 return GetSystemMetrics(SM_CXSIZEFRAME) + GetSystemMetrics(SM_CXPADDEDBORDER);
596int QVistaHelper::captionSizeDp()
598 return GetSystemMetrics(SM_CYCAPTION);
601int QVistaHelper::titleOffset()
603 int iconOffset = wizard ->windowIcon().isNull() ? 0 : iconSize(wizard) + iconSpacing;
604 return leftMargin(wizard) + iconOffset;
607int QVistaHelper::iconSize(
const QPaintDevice *device)
609 return QStyleHelper::dpiScaled(16, device);
612int QVistaHelper::glowSize(
const QPaintDevice *device)
614 return QStyleHelper::dpiScaled(10, device);
617int QVistaHelper::topOffset(
const QPaintDevice *device)
619 static const int aeroOffset = QStyleHelper::dpiScaled(13, device);
620 return aeroOffset + titleBarSize();