| /**************************************************************************** |
| ** |
| ** Copyright (C) 2016 The Qt Company Ltd. |
| ** Contact: https://www.qt.io/licensing/ |
| ** |
| ** This file is part of the QtWidgets module of the Qt Toolkit. |
| ** |
| ** $QT_BEGIN_LICENSE:LGPL$ |
| ** Commercial License Usage |
| ** Licensees holding valid commercial Qt licenses may use this file in |
| ** accordance with the commercial license agreement provided with the |
| ** Software or, alternatively, in accordance with the terms contained in |
| ** a written agreement between you and The Qt Company. For licensing terms |
| ** and conditions see https://www.qt.io/terms-conditions. For further |
| ** information use the contact form at https://www.qt.io/contact-us. |
| ** |
| ** GNU Lesser General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU Lesser |
| ** General Public License version 3 as published by the Free Software |
| ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
| ** packaging of this file. Please review the following information to |
| ** ensure the GNU Lesser General Public License version 3 requirements |
| ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
| ** |
| ** GNU General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU |
| ** General Public License version 2.0 or (at your option) the GNU General |
| ** Public license version 3 or any later version approved by the KDE Free |
| ** Qt Foundation. The licenses are as published by the Free Software |
| ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
| ** included in the packaging of this file. Please review the following |
| ** information to ensure the GNU General Public License requirements will |
| ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
| ** https://www.gnu.org/licenses/gpl-3.0.html. |
| ** |
| ** $QT_END_LICENSE$ |
| ** |
| ****************************************************************************/ |
| |
| #include <QtWidgets/private/qtwidgetsglobal_p.h> |
| |
| #if QT_CONFIG(style_windowsvista) |
| |
| #include "qwizard_win_p.h" |
| #include <private/qapplication_p.h> |
| #include <qpa/qplatformnativeinterface.h> |
| #include "qwizard.h" |
| #include "qpaintengine.h" |
| #include "qapplication.h" |
| #include <QtCore/QOperatingSystemVersion> |
| #include <QtCore/QVariant> |
| #include <QtCore/QDebug> |
| #include <QtGui/QMouseEvent> |
| #include <QtGui/QWindow> |
| #include <QtWidgets/QDesktopWidget> |
| |
| #include <uxtheme.h> |
| #include <vssym32.h> |
| #include <dwmapi.h> |
| |
| Q_DECLARE_METATYPE(QMargins) |
| |
| #ifndef WM_DWMCOMPOSITIONCHANGED |
| # define WM_DWMCOMPOSITIONCHANGED 0x031E |
| #endif |
| |
| QT_BEGIN_NAMESPACE |
| |
| int QVistaHelper::instanceCount = 0; |
| int QVistaHelper::m_devicePixelRatio = 1; |
| QVistaHelper::VistaState QVistaHelper::cachedVistaState = QVistaHelper::Dirty; |
| |
| /****************************************************************************** |
| ** QVistaBackButton |
| */ |
| |
| QVistaBackButton::QVistaBackButton(QWidget *widget) |
| : QAbstractButton(widget) |
| { |
| setFocusPolicy(Qt::NoFocus); |
| // Native dialogs use ALT-Left even in RTL mode, so do the same, even if it might be counter-intuitive. |
| #if QT_CONFIG(shortcut) |
| setShortcut(QKeySequence(Qt::ALT | Qt::Key_Left)); |
| #endif |
| } |
| |
| QSize QVistaBackButton::sizeHint() const |
| { |
| ensurePolished(); |
| int size = int(QStyleHelper::dpiScaled(32, this)); |
| int width = size, height = size; |
| return QSize(width, height); |
| } |
| |
| void QVistaBackButton::enterEvent(QEvent *event) |
| { |
| if (isEnabled()) |
| update(); |
| QAbstractButton::enterEvent(event); |
| } |
| |
| void QVistaBackButton::leaveEvent(QEvent *event) |
| { |
| if (isEnabled()) |
| update(); |
| QAbstractButton::leaveEvent(event); |
| } |
| |
| void QVistaBackButton::paintEvent(QPaintEvent *) |
| { |
| QPainter p(this); |
| QRect r = rect(); |
| const HANDLE theme = OpenThemeData(0, L"Navigation"); |
| //RECT rect; |
| QPoint origin; |
| const HDC hdc = QVistaHelper::backingStoreDC(parentWidget(), &origin); |
| RECT clipRect; |
| int xoffset = origin.x() + QWidget::mapToParent(r.topLeft()).x() - 1; |
| int yoffset = origin.y() + QWidget::mapToParent(r.topLeft()).y() - 1; |
| const int dpr = devicePixelRatio(); |
| const QRect rDp = QRect(r.topLeft() * dpr, r.size() * dpr); |
| const int xoffsetDp = xoffset * dpr; |
| const int yoffsetDp = yoffset * dpr; |
| |
| clipRect.top = rDp.top() + yoffsetDp; |
| clipRect.bottom = rDp.bottom() + yoffsetDp; |
| clipRect.left = rDp.left() + xoffsetDp; |
| clipRect.right = rDp.right() + xoffsetDp; |
| |
| int state = NAV_BB_NORMAL; |
| if (!isEnabled()) |
| state = NAV_BB_DISABLED; |
| else if (isDown()) |
| state = NAV_BB_PRESSED; |
| else if (underMouse()) |
| state = NAV_BB_HOT; |
| |
| DrawThemeBackground(theme, hdc, |
| layoutDirection() == Qt::LeftToRight ? NAV_BACKBUTTON : NAV_FORWARDBUTTON, |
| state, &clipRect, &clipRect); |
| } |
| |
| /****************************************************************************** |
| ** QVistaHelper |
| */ |
| |
| QVistaHelper::QVistaHelper(QWizard *wizard) |
| : QObject(wizard) |
| , pressed(false) |
| , wizard(wizard) |
| , backButton_(0) |
| { |
| QVistaHelper::m_devicePixelRatio = wizard->devicePixelRatio(); |
| if (instanceCount++ == 0) |
| cachedVistaState = Dirty; |
| backButton_ = new QVistaBackButton(wizard); |
| backButton_->hide(); |
| |
| iconSpacing = QStyleHelper::dpiScaled(7, wizard); |
| } |
| |
| QVistaHelper::~QVistaHelper() |
| { |
| --instanceCount; |
| } |
| |
| void QVistaHelper::updateCustomMargins(bool vistaMargins) |
| { |
| if (QWindow *window = wizard->windowHandle()) { |
| // Reduce top frame to zero since we paint it ourselves. Use |
| // device pixel to avoid rounding errors. |
| const QMargins customMarginsDp = vistaMargins |
| ? QMargins(0, -titleBarSizeDp(), 0, 0) |
| : QMargins(); |
| const QVariant customMarginsV = QVariant::fromValue(customMarginsDp); |
| // 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 bEnabled; |
| return SUCCEEDED(DwmIsCompositionEnabled(&bEnabled)) && bEnabled; |
| } |
| |
| bool QVistaHelper::isThemeActive() |
| { |
| return IsThemeActive(); |
| } |
| |
| QVistaHelper::VistaState QVistaHelper::vistaState() |
| { |
| if (instanceCount == 0 || cachedVistaState == Dirty) |
| cachedVistaState = |
| isCompositionEnabled() ? VistaAero : isThemeActive() ? VistaBasic : Classic; |
| return cachedVistaState; |
| } |
| |
| void QVistaHelper::disconnectBackButton() |
| { |
| if (backButton_) // Leave QStyleSheetStyle's connections on destroyed() intact. |
| backButton_->disconnect(SIGNAL(clicked())); |
| } |
| |
| QColor QVistaHelper::basicWindowFrameColor() |
| { |
| DWORD rgb; |
| const HANDLE hTheme = OpenThemeData(GetDesktopWindow(), L"WINDOW"); |
| GetThemeColor(hTheme, WP_CAPTION, CS_ACTIVE, |
| wizard->isActiveWindow() ? TMT_FILLCOLORHINT : TMT_BORDERCOLORHINT, &rgb); |
| BYTE r = GetRValue(rgb); |
| BYTE g = GetGValue(rgb); |
| BYTE b = GetBValue(rgb); |
| return QColor(r, g, b); |
| } |
| |
| bool QVistaHelper::setDWMTitleBar(TitleBarChangeType type) |
| { |
| bool value = false; |
| if (vistaState() == VistaAero) { |
| MARGINS mar = {0, 0, 0, 0}; |
| if (type == NormalTitleBar) |
| mar.cyTopHeight = 0; |
| else |
| mar.cyTopHeight = (titleBarSize() + topOffset(wizard)) * QVistaHelper::m_devicePixelRatio; |
| if (const HWND wizardHandle = wizardHWND()) |
| if (SUCCEEDED(DwmExtendFrameIntoClientArea(wizardHandle, &mar))) |
| value = true; |
| } |
| return value; |
| } |
| |
| Q_GUI_EXPORT HICON qt_pixmapToWinHICON(const QPixmap &); |
| |
| static LOGFONT getCaptionLogFont(HANDLE hTheme) |
| { |
| LOGFONT result = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, { 0 } }; |
| |
| if (!hTheme || FAILED(GetThemeSysFont(hTheme, TMT_CAPTIONFONT, &result))) { |
| NONCLIENTMETRICS ncm; |
| ncm.cbSize = sizeof(NONCLIENTMETRICS); |
| SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICS), &ncm, false); |
| result = ncm.lfMessageFont; |
| } |
| return result; |
| } |
| |
| static bool getCaptionQFont(int dpi, QFont *result) |
| { |
| const HANDLE hTheme = OpenThemeData(GetDesktopWindow(), L"WINDOW"); |
| if (!hTheme) |
| return false; |
| // Call into QWindowsNativeInterface to convert the LOGFONT into a QFont. |
| const LOGFONT logFont = getCaptionLogFont(hTheme); |
| QPlatformNativeInterface *ni = QGuiApplication::platformNativeInterface(); |
| return ni && QMetaObject::invokeMethod(ni, "logFontToQFont", Qt::DirectConnection, |
| Q_RETURN_ARG(QFont, *result), |
| Q_ARG(const void*, &logFont), |
| Q_ARG(int, dpi)); |
| } |
| |
| void QVistaHelper::drawTitleBar(QPainter *painter) |
| { |
| Q_ASSERT(backButton_); |
| QPoint origin; |
| const bool isWindow = wizard->isWindow(); |
| const HDC hdc = QVistaHelper::backingStoreDC(wizard, &origin); |
| |
| if (vistaState() == VistaAero && isWindow) |
| drawBlackRect(QRect(0, 0, wizard->width(), |
| titleBarSize() + topOffset(wizard)), hdc); |
| // The button is positioned in QWizardPrivate::handleAeroStyleChange(), |
| // all calculation is relative to it. |
| const int btnTop = backButton_->mapToParent(QPoint()).y(); |
| const int btnHeight = backButton_->size().height(); |
| const int verticalCenter = (btnTop + btnHeight / 2) - 1; |
| |
| const QString text = wizard->window()->windowTitle(); |
| QFont font; |
| if (!isWindow || !getCaptionQFont(wizard->logicalDpiY() * wizard->devicePixelRatio(), &font)) |
| font = QApplication::font("QMdiSubWindowTitleBar"); |
| const QFontMetrics fontMetrics(font); |
| const QRect brect = fontMetrics.boundingRect(text); |
| int textHeight = brect.height(); |
| int textWidth = brect.width(); |
| int glowOffset = 0; |
| |
| if (vistaState() == VistaAero) { |
| glowOffset = glowSize(wizard); |
| textHeight += 2 * glowOffset; |
| textWidth += 2 * glowOffset; |
| } |
| |
| const int titleLeft = (wizard->layoutDirection() == Qt::LeftToRight |
| ? titleOffset() - glowOffset |
| : wizard->width() - titleOffset() - textWidth + glowOffset); |
| |
| const QRect textRectangle(titleLeft, verticalCenter - textHeight / 2, textWidth, textHeight); |
| if (isWindow) { |
| drawTitleText(painter, text, textRectangle, hdc); |
| } else { |
| painter->save(); |
| painter->setFont(font); |
| painter->drawText(textRectangle, Qt::AlignVCenter | Qt::AlignHCenter, text); |
| painter->restore(); |
| } |
| |
| const QIcon windowIcon = wizard->windowIcon(); |
| if (!windowIcon.isNull()) { |
| const int size = QVistaHelper::iconSize(wizard); |
| const int iconLeft = (wizard->layoutDirection() == Qt::LeftToRight |
| ? leftMargin(wizard) |
| : wizard->width() - leftMargin(wizard) - size); |
| |
| const QPoint pos(origin.x() + iconLeft, origin.y() + verticalCenter - size / 2); |
| const QPoint posDp = pos * QVistaHelper::m_devicePixelRatio; |
| const HICON hIcon = qt_pixmapToWinHICON(windowIcon.pixmap(size * QVistaHelper::m_devicePixelRatio)); |
| DrawIconEx(hdc, posDp.x(), posDp.y(), hIcon, 0, 0, 0, NULL, DI_NORMAL | DI_COMPAT); |
| DestroyIcon(hIcon); |
| } |
| } |
| |
| void QVistaHelper::setTitleBarIconAndCaptionVisible(bool visible) |
| { |
| WTA_OPTIONS opt; |
| opt.dwFlags = WTNCA_NODRAWICON | WTNCA_NODRAWCAPTION; |
| if (visible) |
| opt.dwMask = 0; |
| else |
| opt.dwMask = WTNCA_NODRAWICON | WTNCA_NODRAWCAPTION; |
| if (const HWND handle = wizardHWND()) |
| SetWindowThemeAttribute(handle, WTA_NONCLIENT, &opt, sizeof(WTA_OPTIONS)); |
| } |
| |
| #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) |
| bool QVistaHelper::winEvent(MSG* msg, qintptr *result) |
| #else |
| bool QVistaHelper::winEvent(MSG* msg, long* result) |
| #endif |
| { |
| switch (msg->message) { |
| case WM_NCHITTEST: { |
| LRESULT lResult; |
| // Perform hit testing using DWM |
| if (DwmDefWindowProc(msg->hwnd, msg->message, msg->wParam, msg->lParam, &lResult)) { |
| // DWM returned a hit, no further processing necessary |
| *result = lResult; |
| } else { |
| // DWM didn't return a hit, process using DefWindowProc |
| lResult = DefWindowProc(msg->hwnd, msg->message, msg->wParam, msg->lParam); |
| // If DefWindowProc returns a window caption button, just return HTCLIENT (client area). |
| // This avoid unnecessary hits to Windows NT style caption buttons which aren't visible but are |
| // located just under the Aero style window close button. |
| if (lResult == HTCLOSE || lResult == HTMAXBUTTON || lResult == HTMINBUTTON || lResult == HTHELP) |
| *result = HTCLIENT; |
| else |
| *result = lResult; |
| } |
| break; |
| } |
| default: |
| LRESULT lResult; |
| // Pass to DWM to handle |
| if (DwmDefWindowProc(msg->hwnd, msg->message, msg->wParam, msg->lParam, &lResult)) |
| *result = lResult; |
| // If the message wasn't handled by DWM, continue processing it as normal |
| else |
| return false; |
| } |
| |
| return true; |
| } |
| |
| void QVistaHelper::setMouseCursor(QPoint pos) |
| { |
| #ifndef QT_NO_CURSOR |
| if (rtTop.contains(pos)) |
| wizard->setCursor(Qt::SizeVerCursor); |
| else |
| wizard->setCursor(Qt::ArrowCursor); |
| #endif |
| } |
| |
| void QVistaHelper::mouseEvent(QEvent *event) |
| { |
| switch (event->type()) { |
| case QEvent::MouseMove: |
| mouseMoveEvent(static_cast<QMouseEvent *>(event)); |
| break; |
| case QEvent::MouseButtonPress: |
| mousePressEvent(static_cast<QMouseEvent *>(event)); |
| break; |
| case QEvent::MouseButtonRelease: |
| mouseReleaseEvent(static_cast<QMouseEvent *>(event)); |
| break; |
| default: |
| break; |
| } |
| } |
| |
| #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) |
| bool QVistaHelper::handleWinEvent(MSG *message, qintptr *result) |
| #else |
| bool QVistaHelper::handleWinEvent(MSG *message, long *result) |
| #endif |
| { |
| if (message->message == WM_THEMECHANGED || message->message == WM_DWMCOMPOSITIONCHANGED) |
| cachedVistaState = Dirty; |
| |
| bool status = false; |
| if (wizard->wizardStyle() == QWizard::AeroStyle && vistaState() == VistaAero) { |
| status = winEvent(message, result); |
| if (message->message == WM_NCPAINT) |
| wizard->update(); |
| } |
| return status; |
| } |
| |
| void QVistaHelper::resizeEvent(QResizeEvent * event) |
| { |
| Q_UNUSED(event); |
| rtTop = QRect (0, 0, wizard->width(), frameSize()); |
| int height = captionSize() + topOffset(wizard); |
| if (vistaState() == VistaBasic) |
| height -= titleBarSize(); |
| rtTitle = QRect (0, frameSize(), wizard->width(), height); |
| } |
| |
| void QVistaHelper::paintEvent(QPaintEvent *event) |
| { |
| Q_UNUSED(event); |
| QPainter painter(wizard); |
| drawTitleBar(&painter); |
| } |
| |
| void QVistaHelper::mouseMoveEvent(QMouseEvent *event) |
| { |
| if (wizard->windowState() & Qt::WindowMaximized) { |
| event->ignore(); |
| return; |
| } |
| |
| QRect rect = wizard->geometry(); |
| if (pressed) { |
| switch (change) { |
| case resizeTop: |
| { |
| const int dy = event->pos().y() - pressedPos.y(); |
| if ((dy > 0 && rect.height() > wizard->minimumHeight()) |
| || (dy < 0 && rect.height() < wizard->maximumHeight())) |
| rect.setTop(rect.top() + dy); |
| } |
| break; |
| case movePosition: { |
| QPoint newPos = event->pos() - pressedPos; |
| rect.moveLeft(rect.left() + newPos.x()); |
| rect.moveTop(rect.top() + newPos.y()); |
| break; } |
| default: |
| break; |
| } |
| wizard->setGeometry(rect); |
| |
| } else if (vistaState() == VistaAero) { |
| setMouseCursor(event->pos()); |
| } |
| event->ignore(); |
| } |
| |
| void QVistaHelper::mousePressEvent(QMouseEvent *event) |
| { |
| change = noChange; |
| |
| if (event->button() != Qt::LeftButton || wizard->windowState() & Qt::WindowMaximized) { |
| event->ignore(); |
| return; |
| } |
| |
| if (rtTitle.contains(event->pos())) { |
| change = movePosition; |
| } else if (rtTop.contains(event->pos())) |
| change = (vistaState() == VistaAero) ? resizeTop : movePosition; |
| |
| if (change != noChange) { |
| if (vistaState() == VistaAero) |
| setMouseCursor(event->pos()); |
| pressed = true; |
| pressedPos = event->pos(); |
| } else { |
| event->ignore(); |
| } |
| } |
| |
| void QVistaHelper::mouseReleaseEvent(QMouseEvent *event) |
| { |
| change = noChange; |
| if (pressed) { |
| pressed = false; |
| wizard->releaseMouse(); |
| if (vistaState() == VistaAero) |
| setMouseCursor(event->pos()); |
| } |
| event->ignore(); |
| } |
| |
| bool QVistaHelper::eventFilter(QObject *obj, QEvent *event) |
| { |
| if (obj != wizard) |
| return QObject::eventFilter(obj, event); |
| |
| if (event->type() == QEvent::MouseMove) { |
| QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event); |
| #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) |
| qintptr result; |
| #else |
| long result; |
| #endif |
| MSG msg; |
| msg.message = WM_NCHITTEST; |
| msg.wParam = 0; |
| msg.lParam = MAKELPARAM(mouseEvent->globalX(), mouseEvent->globalY()); |
| msg.hwnd = wizardHWND(); |
| winEvent(&msg, &result); |
| msg.wParam = result; |
| msg.message = WM_NCMOUSEMOVE; |
| winEvent(&msg, &result); |
| } else if (event->type() == QEvent::MouseButtonPress) { |
| QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event); |
| |
| if (mouseEvent->button() == Qt::LeftButton) { |
| #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) |
| qintptr result; |
| #else |
| long result; |
| #endif |
| MSG msg; |
| msg.message = WM_NCHITTEST; |
| msg.wParam = 0; |
| msg.lParam = MAKELPARAM(mouseEvent->globalX(), mouseEvent->globalY()); |
| msg.hwnd = wizardHWND(); |
| winEvent(&msg, &result); |
| msg.wParam = result; |
| msg.message = WM_NCLBUTTONDOWN; |
| winEvent(&msg, &result); |
| } |
| } else if (event->type() == QEvent::MouseButtonRelease) { |
| QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event); |
| |
| if (mouseEvent->button() == Qt::LeftButton) { |
| #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) |
| qintptr result; |
| #else |
| long result; |
| #endif |
| MSG msg; |
| msg.message = WM_NCHITTEST; |
| msg.wParam = 0; |
| msg.lParam = MAKELPARAM(mouseEvent->globalX(), mouseEvent->globalY()); |
| msg.hwnd = wizardHWND(); |
| winEvent(&msg, &result); |
| msg.wParam = result; |
| msg.message = WM_NCLBUTTONUP; |
| winEvent(&msg, &result); |
| } |
| } |
| |
| return false; |
| } |
| |
| // Return a HDC for the wizard along with the transformation if the |
| // wizard is a child window. |
| HDC QVistaHelper::backingStoreDC(const QWidget *wizard, QPoint *offset) |
| { |
| HDC hdc = static_cast<HDC>(QGuiApplication::platformNativeInterface()->nativeResourceForBackingStore(QByteArrayLiteral("getDC"), wizard->backingStore())); |
| *offset = QPoint(0, 0); |
| if (!wizard->windowHandle()) |
| if (QWidget *nativeParent = wizard->nativeParentWidget()) |
| *offset = wizard->mapTo(nativeParent, *offset); |
| return hdc; |
| } |
| |
| HWND QVistaHelper::wizardHWND() const |
| { |
| // Obtain the HWND if the wizard is a top-level window. |
| // Do not use winId() as this enforces native children of the parent |
| // widget when called before show() as happens when calling setWizardStyle(). |
| if (QWindow *window = wizard->windowHandle()) |
| if (window->handle()) |
| if (void *vHwnd = QGuiApplication::platformNativeInterface()->nativeResourceForWindow(QByteArrayLiteral("handle"), window)) |
| return static_cast<HWND>(vHwnd); |
| qWarning().nospace() << "Failed to obtain HWND for wizard."; |
| return 0; |
| } |
| |
| bool QVistaHelper::drawTitleText(QPainter *painter, const QString &text, const QRect &rect, HDC hdc) |
| { |
| bool value = false; |
| if (vistaState() == VistaAero) { |
| const QRect rectDp = QRect(rect.topLeft() * QVistaHelper::m_devicePixelRatio, |
| rect.size() * QVistaHelper::m_devicePixelRatio); |
| const HANDLE hTheme = OpenThemeData(GetDesktopWindow(), L"WINDOW"); |
| if (!hTheme) return false; |
| // Set up a memory DC and bitmap that we'll draw into |
| HDC dcMem; |
| HBITMAP bmp; |
| BITMAPINFO dib; |
| ZeroMemory(&dib, sizeof(dib)); |
| dcMem = CreateCompatibleDC(hdc); |
| |
| dib.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); |
| dib.bmiHeader.biWidth = rectDp.width(); |
| dib.bmiHeader.biHeight = -rectDp.height(); |
| dib.bmiHeader.biPlanes = 1; |
| dib.bmiHeader.biBitCount = 32; |
| dib.bmiHeader.biCompression = BI_RGB; |
| |
| bmp = CreateDIBSection(hdc, &dib, DIB_RGB_COLORS, NULL, NULL, 0); |
| |
| // Set up the DC |
| const LOGFONT captionLogFont = getCaptionLogFont(hTheme); |
| const HFONT hCaptionFont = CreateFontIndirect(&captionLogFont); |
| auto hOldBmp = reinterpret_cast<HBITMAP>(SelectObject(dcMem, (HGDIOBJ) bmp)); |
| auto hOldFont = reinterpret_cast<HFONT>(SelectObject(dcMem, (HGDIOBJ) hCaptionFont)); |
| |
| // Draw the text! |
| DTTOPTS dto; |
| memset(&dto, 0, sizeof(dto)); |
| dto.dwSize = sizeof(dto); |
| const UINT uFormat = DT_SINGLELINE|DT_CENTER|DT_VCENTER|DT_NOPREFIX; |
| RECT rctext ={0,0, rectDp.width(), rectDp.height()}; |
| |
| dto.dwFlags = DTT_COMPOSITED|DTT_GLOWSIZE; |
| dto.iGlowSize = glowSize(wizard); |
| |
| DrawThemeTextEx(hTheme, dcMem, 0, 0, reinterpret_cast<LPCWSTR>(text.utf16()), -1, uFormat, &rctext, &dto ); |
| BitBlt(hdc, rectDp.left(), rectDp.top(), rectDp.width(), rectDp.height(), dcMem, 0, 0, SRCCOPY); |
| SelectObject(dcMem, (HGDIOBJ) hOldBmp); |
| SelectObject(dcMem, (HGDIOBJ) hOldFont); |
| DeleteObject(bmp); |
| DeleteObject(hCaptionFont); |
| DeleteDC(dcMem); |
| //ReleaseDC(hwnd, hdc); |
| } else if (vistaState() == VistaBasic) { |
| painter->drawText(rect, text); |
| } |
| return value; |
| } |
| |
| bool QVistaHelper::drawBlackRect(const QRect &rect, HDC hdc) |
| { |
| bool value = false; |
| if (vistaState() == VistaAero) { |
| // Set up a memory DC and bitmap that we'll draw into |
| const QRect rectDp = QRect(rect.topLeft() * QVistaHelper::m_devicePixelRatio, |
| rect.size() * QVistaHelper::m_devicePixelRatio); |
| HDC dcMem; |
| HBITMAP bmp; |
| BITMAPINFO dib; |
| ZeroMemory(&dib, sizeof(dib)); |
| dcMem = CreateCompatibleDC(hdc); |
| |
| dib.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); |
| dib.bmiHeader.biWidth = rectDp.width(); |
| dib.bmiHeader.biHeight = -rectDp.height(); |
| dib.bmiHeader.biPlanes = 1; |
| dib.bmiHeader.biBitCount = 32; |
| dib.bmiHeader.biCompression = BI_RGB; |
| |
| bmp = CreateDIBSection(hdc, &dib, DIB_RGB_COLORS, NULL, NULL, 0); |
| auto hOldBmp = reinterpret_cast<HBITMAP>(SelectObject(dcMem, (HGDIOBJ) bmp)); |
| |
| BitBlt(hdc, rectDp.left(), rectDp.top(), rectDp.width(), rectDp.height(), dcMem, 0, 0, SRCCOPY); |
| SelectObject(dcMem, (HGDIOBJ) hOldBmp); |
| |
| DeleteObject(bmp); |
| DeleteDC(dcMem); |
| } |
| return value; |
| } |
| |
| #ifndef Q_CC_MSVC |
| static inline int getWindowBottomMargin() |
| { |
| return GetSystemMetrics(SM_CYSIZEFRAME); |
| } |
| #else |
| // QTBUG-36192, GetSystemMetrics(SM_CYSIZEFRAME) returns bogus values |
| // for MSVC2012 which leads to the custom margin having no effect since |
| // that only works when removing the entire margin. |
| static inline int getWindowBottomMargin() |
| { |
| RECT rect = {0, 0, 0, 0}; |
| AdjustWindowRectEx(&rect, WS_POPUP | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_THICKFRAME | WS_DLGFRAME, FALSE, 0); |
| return qAbs(rect.bottom); |
| } |
| #endif // Q_CC_MSVC |
| |
| int QVistaHelper::frameSizeDp() |
| { |
| return getWindowBottomMargin(); |
| } |
| |
| int QVistaHelper::captionSizeDp() |
| { |
| return GetSystemMetrics(SM_CYCAPTION); |
| } |
| |
| int QVistaHelper::titleOffset() |
| { |
| int iconOffset = wizard ->windowIcon().isNull() ? 0 : iconSize(wizard) + iconSpacing; |
| return leftMargin(wizard) + iconOffset; |
| } |
| |
| int QVistaHelper::iconSize(const QPaintDevice *device) |
| { |
| return QStyleHelper::dpiScaled(16, device); // Standard Aero |
| } |
| |
| int QVistaHelper::glowSize(const QPaintDevice *device) |
| { |
| return QStyleHelper::dpiScaled(10, device); |
| } |
| |
| int QVistaHelper::topOffset(const QPaintDevice *device) |
| { |
| if (vistaState() != VistaAero) |
| return titleBarSize() + 3; |
| static const int aeroOffset = |
| QOperatingSystemVersion::current() < QOperatingSystemVersion::Windows8 ? |
| QStyleHelper::dpiScaled(4, device) : QStyleHelper::dpiScaled(13, device); |
| return aeroOffset + titleBarSize(); |
| } |
| |
| QT_END_NAMESPACE |
| |
| #endif // style_windowsvista |