From 46f98147f4e3a09b32042c0655d5de7285a795e3 Mon Sep 17 00:00:00 2001 From: Santhosh Kumar Date: Thu, 6 Oct 2022 15:57:53 +0200 Subject: Refactor windows vista style for Qt widgets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Removed windows xp style and Cleanup windows vista style for Qt widgets. Change-Id: Ia05e8bca9bad477cec535d2288d9773546e0e116 Reviewed-by: Jan Arve Sæther Reviewed-by: Volker Hilsheimer --- src/plugins/styles/windowsvista/CMakeLists.txt | 5 +- .../styles/windowsvista/qwindowsthemedata.cpp | 59 + .../styles/windowsvista/qwindowsthemedata_p.h | 183 + .../styles/windowsvista/qwindowsvistaanimation.cpp | 15 + .../styles/windowsvista/qwindowsvistaanimation_p.h | 49 + .../styles/windowsvista/qwindowsvistastyle.cpp | 4416 +++++++++++++++----- .../styles/windowsvista/qwindowsvistastyle_p.h | 4 +- .../styles/windowsvista/qwindowsvistastyle_p_p.h | 167 +- .../styles/windowsvista/qwindowsxpstyle.cpp | 3669 ---------------- .../styles/windowsvista/qwindowsxpstyle_p.h | 69 - .../styles/windowsvista/qwindowsxpstyle_p_p.h | 309 -- 11 files changed, 3881 insertions(+), 5064 deletions(-) create mode 100644 src/plugins/styles/windowsvista/qwindowsthemedata.cpp create mode 100644 src/plugins/styles/windowsvista/qwindowsthemedata_p.h create mode 100644 src/plugins/styles/windowsvista/qwindowsvistaanimation.cpp create mode 100644 src/plugins/styles/windowsvista/qwindowsvistaanimation_p.h delete mode 100644 src/plugins/styles/windowsvista/qwindowsxpstyle.cpp delete mode 100644 src/plugins/styles/windowsvista/qwindowsxpstyle_p.h delete mode 100644 src/plugins/styles/windowsvista/qwindowsxpstyle_p_p.h (limited to 'src/plugins/styles') diff --git a/src/plugins/styles/windowsvista/CMakeLists.txt b/src/plugins/styles/windowsvista/CMakeLists.txt index 8b43df1550..ab87ad4186 100644 --- a/src/plugins/styles/windowsvista/CMakeLists.txt +++ b/src/plugins/styles/windowsvista/CMakeLists.txt @@ -14,8 +14,9 @@ qt_internal_add_plugin(QWindowsVistaStylePlugin main.cpp qwindowsvistastyle.cpp qwindowsvistastyle_p.h qwindowsvistastyle_p_p.h - qwindowsxpstyle.cpp qwindowsxpstyle_p.h - qwindowsxpstyle_p_p.h + qwindowsvistaanimation.cpp qwindowsvistaanimation_p.h + qwindowsthemedata.cpp qwindowsthemedata_p.h + LIBRARIES gdi32 user32 diff --git a/src/plugins/styles/windowsvista/qwindowsthemedata.cpp b/src/plugins/styles/windowsvista/qwindowsthemedata.cpp new file mode 100644 index 0000000000..44569e054d --- /dev/null +++ b/src/plugins/styles/windowsvista/qwindowsthemedata.cpp @@ -0,0 +1,59 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "qwindowsthemedata_p.h" +#include "qwindowsvistastyle_p_p.h" + +/* \internal + Returns \c true if the QWindowsThemeData is valid for use. +*/ +bool QWindowsThemeData::isValid() +{ + return QWindowsVistaStylePrivate::useVista() && theme >= 0 && handle(); +} + +/* \internal + Returns the theme engine handle to the specific class. + If the handle hasn't been opened before, it opens the data, and + adds it to a static map, for caching. +*/ +HTHEME QWindowsThemeData::handle() +{ + if (!QWindowsVistaStylePrivate::useVista()) + return nullptr; + + if (!htheme) + htheme = QWindowsVistaStylePrivate::createTheme(theme, QWindowsVistaStylePrivate::winId(widget)); + return htheme; +} + +/* \internal + Converts a QRect to the native RECT structure. +*/ +RECT QWindowsThemeData::toRECT(const QRect &qr) +{ + RECT r; + r.left = qr.x(); + r.right = qr.x() + qr.width(); + r.top = qr.y(); + r.bottom = qr.y() + qr.height(); + return r; +} + +/* \internal + Returns the native region of a part, if the part is considered + transparent. The region is scaled to the parts size (rect). +*/ +HRGN QWindowsThemeData::mask(QWidget *widget) +{ + if (!IsThemeBackgroundPartiallyTransparent(handle(), partId, stateId)) + return nullptr; + + HRGN hrgn; + HDC dc = nullptr; + if (widget) + dc = QWindowsVistaStylePrivate::hdcForWidgetBackingStore(widget); + RECT nativeRect = toRECT(rect); + GetThemeBackgroundRegion(handle(), dc, partId, stateId, &nativeRect, &hrgn); + return hrgn; +} diff --git a/src/plugins/styles/windowsvista/qwindowsthemedata_p.h b/src/plugins/styles/windowsvista/qwindowsthemedata_p.h new file mode 100644 index 0000000000..5de2bcbdea --- /dev/null +++ b/src/plugins/styles/windowsvista/qwindowsthemedata_p.h @@ -0,0 +1,183 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef QWINDOWSTHEMEDATA_P_H +#define QWINDOWSTHEMEDATA_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include +#include +#include + + +// TMT_TEXTSHADOWCOLOR is wrongly defined in mingw +#if TMT_TEXTSHADOWCOLOR != 3818 +#undef TMT_TEXTSHADOWCOLOR +#define TMT_TEXTSHADOWCOLOR 3818 +#endif +#ifndef TST_NONE +# define TST_NONE 0 +#endif + +// These defines are missing from the tmschema, but still exist as +// states for their parts +#ifndef MINBS_INACTIVE +#define MINBS_INACTIVE 5 +#endif +#ifndef MAXBS_INACTIVE +#define MAXBS_INACTIVE 5 +#endif +#ifndef RBS_INACTIVE +#define RBS_INACTIVE 5 +#endif +#ifndef HBS_INACTIVE +#define HBS_INACTIVE 5 +#endif +#ifndef CBS_INACTIVE +#define CBS_INACTIVE 5 +#endif + +// Declarations ----------------------------------------------------------------------------------- +class QWindowsThemeData +{ +public: + explicit QWindowsThemeData(const QWidget *w = nullptr, QPainter *p = nullptr, int themeIn = -1, + int part = 0, int state = 0, const QRect &r = QRect()) + : widget(w), painter(p), theme(themeIn), partId(part), stateId(state), + mirrorHorizontally(false), mirrorVertically(false), noBorder(false), + noContent(false), invertPixels(false), rect(r) + {} + + HRGN mask(QWidget *widget); + HTHEME handle(); + bool isValid(); + QSizeF size(); + + static QSizeF themeSize(const QWidget *w = nullptr, QPainter *p = nullptr, + int themeIn = -1, int part = 0, int state = 0); + static RECT toRECT(const QRect &qr); + + QMarginsF margins(const QRect &rect, int propId = TMT_CONTENTMARGINS); + QMarginsF margins(int propId = TMT_CONTENTMARGINS); + + const QWidget *widget; + QPainter *painter; + + int theme; + HTHEME htheme = nullptr; + int partId; + int stateId; + + uint mirrorHorizontally : 1; + uint mirrorVertically : 1; + uint noBorder : 1; + uint noContent : 1; + uint invertPixels : 1; + uint rotate = 0; + QRect rect; +}; + +struct ThemeMapKey { + int theme = 0; + int partId = -1; + int stateId = -1; + bool noBorder = false; + bool noContent = false; + + ThemeMapKey() = default; + ThemeMapKey(const QWindowsThemeData &data) + : theme(data.theme), partId(data.partId), stateId(data.stateId), + noBorder(data.noBorder), noContent(data.noContent) {} + +}; + +inline size_t qHash(const ThemeMapKey &key) +{ return key.theme ^ key.partId ^ key.stateId; } + +inline bool operator==(const ThemeMapKey &k1, const ThemeMapKey &k2) +{ + return k1.theme == k2.theme + && k1.partId == k2.partId + && k1.stateId == k2.stateId; +} + +enum AlphaChannelType { + UnknownAlpha = -1, // Alpha of part & state not yet known + NoAlpha, // Totally opaque, no need to touch alpha (RGB) + MaskAlpha, // Alpha channel must be fixed (ARGB) + RealAlpha // Proper alpha values from Windows (ARGB_Premultiplied) +}; + +struct ThemeMapData { + AlphaChannelType alphaType = UnknownAlpha; // Which type of alpha on part & state + + bool dataValid : 1; // Only used to detect if hash value is ok + bool partIsTransparent : 1; + bool hasAlphaChannel : 1; // True = part & state has real Alpha + bool wasAlphaSwapped : 1; // True = alpha channel needs to be swapped + bool hadInvalidAlpha : 1; // True = alpha channel contained invalid alpha values + + ThemeMapData() : dataValid(false), partIsTransparent(false), + hasAlphaChannel(false), wasAlphaSwapped(false), hadInvalidAlpha(false) {} +}; + + +#ifndef QT_NO_DEBUG_STREAM +QDebug operator<<(QDebug d, const QWindowsThemeData &t); +QDebug operator<<(QDebug d, const ThemeMapKey &k); +QDebug operator<<(QDebug d, const ThemeMapData &td); +#endif + +inline QSizeF QWindowsThemeData::size() +{ + QSizeF result(0, 0); + if (isValid()) { + SIZE size; + if (SUCCEEDED(GetThemePartSize(handle(), nullptr, partId, stateId, nullptr, TS_TRUE, &size))) + result = QSize(size.cx, size.cy); + } + return result; +} + +inline QSizeF QWindowsThemeData::themeSize(const QWidget *w, QPainter *p, int themeIn, int part, int state) +{ + QWindowsThemeData theme(w, p, themeIn, part, state); + return theme.size(); +} + +inline QMarginsF QWindowsThemeData::margins(const QRect &qRect, int propId) +{ + QMarginsF result(0, 0, 0 ,0); + if (isValid()) { + MARGINS margins; + RECT rect = QWindowsThemeData::toRECT(qRect); + if (SUCCEEDED(GetThemeMargins(handle(), nullptr, partId, stateId, propId, &rect, &margins))) + result = QMargins(margins.cxLeftWidth, margins.cyTopHeight, margins.cxRightWidth, margins.cyBottomHeight); + } + return result; +} + +inline QMarginsF QWindowsThemeData::margins(int propId) +{ + QMarginsF result(0, 0, 0 ,0); + if (isValid()) { + MARGINS margins; + if (SUCCEEDED(GetThemeMargins(handle(), nullptr, partId, stateId, propId, nullptr, &margins))) + result = QMargins(margins.cxLeftWidth, margins.cyTopHeight, margins.cxRightWidth, margins.cyBottomHeight); + } + return result; +} + +#endif // QWINDOWSTHEMEDATA_P_H diff --git a/src/plugins/styles/windowsvista/qwindowsvistaanimation.cpp b/src/plugins/styles/windowsvista/qwindowsvistaanimation.cpp new file mode 100644 index 0000000000..ac9a5ad8c0 --- /dev/null +++ b/src/plugins/styles/windowsvista/qwindowsvistaanimation.cpp @@ -0,0 +1,15 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "qwindowsvistaanimation_p.h" +#include "qwindowsvistastyle_p_p.h" + +bool QWindowsVistaAnimation::isUpdateNeeded() const +{ + return QWindowsVistaStylePrivate::useVista(); +} + +void QWindowsVistaAnimation::paint(QPainter *painter, const QStyleOption *option) +{ + painter->drawImage(option->rect, currentImage()); +} diff --git a/src/plugins/styles/windowsvista/qwindowsvistaanimation_p.h b/src/plugins/styles/windowsvista/qwindowsvistaanimation_p.h new file mode 100644 index 0000000000..817fa5f4b5 --- /dev/null +++ b/src/plugins/styles/windowsvista/qwindowsvistaanimation_p.h @@ -0,0 +1,49 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef QWINDOWSVISTAANIMATION_P_H +#define QWINDOWSVISTAANIMATION_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include + +class QWindowsVistaAnimation : public QBlendStyleAnimation +{ + Q_OBJECT +public: + QWindowsVistaAnimation(Type type, QObject *target) : QBlendStyleAnimation(type, target) { } + + bool isUpdateNeeded() const override; + void paint(QPainter *painter, const QStyleOption *option); +}; + + +// Handles state transition animations +class QWindowsVistaTransition : public QWindowsVistaAnimation +{ + Q_OBJECT +public: + QWindowsVistaTransition(QObject *target) : QWindowsVistaAnimation(Transition, target) {} +}; + + +// Handles pulse animations (default buttons) +class QWindowsVistaPulse: public QWindowsVistaAnimation +{ + Q_OBJECT +public: + QWindowsVistaPulse(QObject *target) : QWindowsVistaAnimation(Pulse, target) {} +}; + +#endif // QWINDOWSVISTAANIMATION_P_H diff --git a/src/plugins/styles/windowsvista/qwindowsvistastyle.cpp b/src/plugins/styles/windowsvista/qwindowsvistastyle.cpp index 0380ef82d8..c80dc077ee 100644 --- a/src/plugins/styles/windowsvista/qwindowsvistastyle.cpp +++ b/src/plugins/styles/windowsvista/qwindowsvistastyle.cpp @@ -3,12 +3,18 @@ #include "qwindowsvistastyle_p.h" #include "qwindowsvistastyle_p_p.h" +#include "qwindowsvistaanimation_p.h" #include #include #include #include #include #include +#include + +#include "qdrawutil.h" // for now +#include + QT_BEGIN_NAMESPACE @@ -45,9 +51,997 @@ static const int windowsRightBorder = 15; // right border on windows Checks if we should use Vista style , or if we should fall back to Windows style. */ -bool QWindowsVistaStylePrivate::useVista() +// Theme names matching the QWindowsVistaStylePrivate::Theme enumeration. +static const wchar_t *themeNames[QWindowsVistaStylePrivate::NThemes] = +{ + L"BUTTON", L"COMBOBOX", L"EDIT", L"HEADER", L"LISTVIEW", + L"MENU", L"PROGRESS", L"REBAR", L"SCROLLBAR", L"SPIN", + L"TAB", L"TASKDIALOG", L"TOOLBAR", L"TOOLTIP", L"TRACKBAR", + L"WINDOW", L"STATUS", L"TREEVIEW" +}; + +// QWindowsVistaStylePrivate ------------------------------------------------------------------------- +// Static initializations +HWND QWindowsVistaStylePrivate::m_vistaTreeViewHelper = nullptr; +HTHEME QWindowsVistaStylePrivate::m_themes[NThemes]; +bool QWindowsVistaStylePrivate::useVistaTheme = false; +Q_CONSTINIT QBasicAtomicInt QWindowsVistaStylePrivate::ref = Q_BASIC_ATOMIC_INITIALIZER(-1); // -1 based refcounting + +static void qt_add_rect(HRGN &winRegion, QRect r) +{ + HRGN rgn = CreateRectRgn(r.left(), r.top(), r.x() + r.width(), r.y() + r.height()); + if (rgn) { + HRGN dest = CreateRectRgn(0,0,0,0); + int result = CombineRgn(dest, winRegion, rgn, RGN_OR); + if (result) { + DeleteObject(winRegion); + winRegion = dest; + } + DeleteObject(rgn); + } +} + +static HRGN qt_hrgn_from_qregion(const QRegion ®ion) +{ + HRGN hRegion = CreateRectRgn(0,0,0,0); + if (region.rectCount() == 1) { + qt_add_rect(hRegion, region.boundingRect()); + return hRegion; + } + for (const QRect &rect : region) + qt_add_rect(hRegion, rect); + return hRegion; +} + +static inline Qt::Orientation progressBarOrientation(const QStyleOption *option = nullptr) +{ + if (const auto *pb = qstyleoption_cast(option)) + return pb->state & QStyle::State_Horizontal ? Qt::Horizontal : Qt::Vertical; + return Qt::Horizontal; +} + +/* In order to obtain the correct VistaTreeViewTheme (arrows for PE_IndicatorBranch), + * we need to set the windows "explorer" theme explicitly on a native + * window and open the "TREEVIEW" theme handle passing its window handle + * in order to get Vista-style item view themes (particularly drawBackground() + * for selected items needs this). + * We invoke a service of the native Windows interface to create + * a non-visible window handle, open the theme on it and insert it into + * the cache so that it is found by QWindowsThemeData::handle() first. + */ +static inline HWND createTreeViewHelperWindow() +{ + using QWindowsApplication = QNativeInterface::Private::QWindowsApplication; + + HWND result = nullptr; + if (auto nativeWindowsApp = dynamic_cast(QGuiApplicationPrivate::platformIntegration())) + result = nativeWindowsApp->createMessageWindow(QStringLiteral("QTreeViewThemeHelperWindowClass"), + QStringLiteral("QTreeViewThemeHelperWindow")); + return result; +} + +enum TransformType { SimpleTransform, HighDpiScalingTransform, ComplexTransform }; + +static inline TransformType transformType(const QTransform &transform, qreal devicePixelRatio) { - return QWindowsVistaStylePrivate::useXP(); + if (transform.type() <= QTransform::TxTranslate) + return SimpleTransform; + if (transform.type() > QTransform::TxScale) + return ComplexTransform; + return qFuzzyCompare(transform.m11(), devicePixelRatio) + && qFuzzyCompare(transform.m22(), devicePixelRatio) + ? HighDpiScalingTransform : ComplexTransform; +} + +// QTBUG-60571: Exclude known fully opaque theme parts which produce values +// invalid in ARGB32_Premultiplied (for example, 0x00ffffff). +static inline bool isFullyOpaque(const QWindowsThemeData &themeData) +{ + return themeData.theme == QWindowsVistaStylePrivate::TaskDialogTheme && themeData.partId == TDLG_PRIMARYPANEL; +} + +static inline QRectF scaleRect(const QRectF &r, qreal factor) +{ + return r.isValid() && factor > 1 + ? QRectF(r.topLeft() * factor, r.size() * factor) : r; +} + +static QRegion scaleRegion(const QRegion ®ion, qreal factor) +{ + if (region.isEmpty() || qFuzzyCompare(factor, qreal(1))) + return region; + QRegion result; + for (const QRect &rect : region) + result += QRectF(QPointF(rect.topLeft()) * factor, QSizeF(rect.size() * factor)).toRect(); + return result; +} + + +/* \internal + Checks if the theme engine can/should be used, or if we should fall back + to Windows style. For Windows 10, this will still return false for the + High Contrast themes. +*/ +bool QWindowsVistaStylePrivate::useVista(bool update) +{ + if (update) { + useVistaTheme = IsThemeActive() && (IsAppThemed() || !QCoreApplication::instance()) + && !QWindowsStylePrivate::isDarkMode(); + } + return useVistaTheme; +} + +/* \internal + Handles refcounting, and queries the theme engine for usage. +*/ +void QWindowsVistaStylePrivate::init(bool force) +{ + if (ref.ref() && !force) + return; + if (!force) // -1 based atomic refcounting + ref.ref(); + + useVista(true); + std::fill(m_themes, m_themes + NThemes, nullptr); +} + +/* \internal + Cleans up all static data. +*/ +void QWindowsVistaStylePrivate::cleanup(bool force) +{ + if (bufferBitmap) { + if (bufferDC && nullBitmap) + SelectObject(bufferDC, nullBitmap); + DeleteObject(bufferBitmap); + bufferBitmap = nullptr; + } + + if (bufferDC) + DeleteDC(bufferDC); + bufferDC = nullptr; + + if (ref.deref() && !force) + return; + if (!force) // -1 based atomic refcounting + ref.deref(); + + useVistaTheme = false; + cleanupHandleMap(); +} + +bool QWindowsVistaStylePrivate::transitionsEnabled() const +{ + BOOL animEnabled = false; + if (SystemParametersInfo(SPI_GETCLIENTAREAANIMATION, 0, &animEnabled, 0)) + { + if (animEnabled) + return true; + } + return false; +} + +int QWindowsVistaStylePrivate::pixelMetricFromSystemDp(QStyle::PixelMetric pm, const QStyleOption *option, const QWidget *widget) +{ + switch (pm) { + case QStyle::PM_IndicatorWidth: + return QWindowsThemeData::themeSize(widget, nullptr, QWindowsVistaStylePrivate::ButtonTheme, BP_CHECKBOX, CBS_UNCHECKEDNORMAL).width(); + case QStyle::PM_IndicatorHeight: + return QWindowsThemeData::themeSize(widget, nullptr, QWindowsVistaStylePrivate::ButtonTheme, BP_CHECKBOX, CBS_UNCHECKEDNORMAL).height(); + case QStyle::PM_ExclusiveIndicatorWidth: + return QWindowsThemeData::themeSize(widget, nullptr, QWindowsVistaStylePrivate::ButtonTheme, BP_RADIOBUTTON, RBS_UNCHECKEDNORMAL).width(); + case QStyle::PM_ExclusiveIndicatorHeight: + return QWindowsThemeData::themeSize(widget, nullptr, QWindowsVistaStylePrivate::ButtonTheme, BP_RADIOBUTTON, RBS_UNCHECKEDNORMAL).height(); + case QStyle::PM_ProgressBarChunkWidth: + return progressBarOrientation(option) == Qt::Horizontal + ? QWindowsThemeData::themeSize(widget, nullptr, QWindowsVistaStylePrivate::ProgressTheme, PP_CHUNK).width() + : QWindowsThemeData::themeSize(widget, nullptr, QWindowsVistaStylePrivate::ProgressTheme, PP_CHUNKVERT).height(); + case QStyle::PM_SliderThickness: + return QWindowsThemeData::themeSize(widget, nullptr, QWindowsVistaStylePrivate::TrackBarTheme, TKP_THUMB).height(); + case QStyle::PM_TitleBarHeight: { + return widget && (widget->windowType() == Qt::Tool) + ? GetSystemMetrics(SM_CYSMCAPTION) + GetSystemMetrics(SM_CXSIZEFRAME) + : GetSystemMetrics(SM_CYCAPTION) + GetSystemMetrics(SM_CXSIZEFRAME); + } + case QStyle::PM_MdiSubWindowFrameWidth: + return QWindowsThemeData::themeSize(widget, nullptr, QWindowsVistaStylePrivate::WindowTheme, WP_FRAMELEFT, FS_ACTIVE).width(); + case QStyle::PM_DockWidgetFrameWidth: + return QWindowsThemeData::themeSize(widget, nullptr, QWindowsVistaStylePrivate::WindowTheme, WP_SMALLFRAMERIGHT, FS_ACTIVE).width(); + default: + break; + } + return QWindowsVistaStylePrivate::InvalidMetric; +} + +int QWindowsVistaStylePrivate::fixedPixelMetric(QStyle::PixelMetric pm) +{ + switch (pm) { + case QStyle::PM_DockWidgetTitleBarButtonMargin: + return 5; + case QStyle::PM_ScrollBarSliderMin: + return 18; + case QStyle::PM_MenuHMargin: + case QStyle::PM_MenuVMargin: + return 0; + case QStyle::PM_MenuPanelWidth: + return 3; + default: + break; + } + + return QWindowsVistaStylePrivate::InvalidMetric; +} + +bool QWindowsVistaStylePrivate::initVistaTreeViewTheming() +{ + if (m_vistaTreeViewHelper) + return true; + + m_vistaTreeViewHelper = createTreeViewHelperWindow(); + if (!m_vistaTreeViewHelper) { + qWarning("Unable to create the treeview helper window."); + return false; + } + if (FAILED(SetWindowTheme(m_vistaTreeViewHelper, L"explorer", nullptr))) { + qErrnoWarning("SetWindowTheme() failed."); + cleanupVistaTreeViewTheming(); + return false; + } + return true; +} + +void QWindowsVistaStylePrivate::cleanupVistaTreeViewTheming() +{ + if (m_vistaTreeViewHelper) { + DestroyWindow(m_vistaTreeViewHelper); + m_vistaTreeViewHelper = nullptr; + } +} + +/* \internal + Closes all open theme data handles to ensure that we don't leak + resources, and that we don't refer to old handles when for + example the user changes the theme style. +*/ +void QWindowsVistaStylePrivate::cleanupHandleMap() +{ + for (auto &theme : m_themes) { + if (theme) { + CloseThemeData(theme); + theme = nullptr; + } + } + QWindowsVistaStylePrivate::cleanupVistaTreeViewTheming(); +} + +HTHEME QWindowsVistaStylePrivate::createTheme(int theme, HWND hwnd) +{ + if (Q_UNLIKELY(theme < 0 || theme >= NThemes || !hwnd)) { + qWarning("Invalid parameters #%d, %p", theme, hwnd); + return nullptr; + } + if (!m_themes[theme]) { + const wchar_t *name = themeNames[theme]; + if (theme == VistaTreeViewTheme && QWindowsVistaStylePrivate::initVistaTreeViewTheming()) + hwnd = QWindowsVistaStylePrivate::m_vistaTreeViewHelper; + m_themes[theme] = OpenThemeData(hwnd, name); + if (Q_UNLIKELY(!m_themes[theme])) + qErrnoWarning("OpenThemeData() failed for theme %d (%s).", + theme, qPrintable(themeName(theme))); + } + return m_themes[theme]; +} + +QBackingStore *QWindowsVistaStylePrivate::backingStoreForWidget(const QWidget *widget) +{ + if (QBackingStore *backingStore = widget->backingStore()) + return backingStore; + if (const QWidget *topLevel = widget->nativeParentWidget()) + if (QBackingStore *topLevelBackingStore = topLevel->backingStore()) + return topLevelBackingStore; + return nullptr; +} + +HDC QWindowsVistaStylePrivate::hdcForWidgetBackingStore(const QWidget *widget) +{ + if (QBackingStore *backingStore = backingStoreForWidget(widget)) { + QPlatformNativeInterface *nativeInterface = QGuiApplication::platformNativeInterface(); + if (nativeInterface) + return static_cast(nativeInterface->nativeResourceForBackingStore(QByteArrayLiteral("getDC"), backingStore)); + } + return nullptr; +} + +QString QWindowsVistaStylePrivate::themeName(int theme) +{ + return theme >= 0 && theme < NThemes + ? QString::fromWCharArray(themeNames[theme]) : QString(); +} + +bool QWindowsVistaStylePrivate::isItemViewDelegateLineEdit(const QWidget *widget) +{ + if (!widget) + return false; + const QWidget *parent1 = widget->parentWidget(); + // Exclude dialogs or other toplevels parented on item views. + if (!parent1 || parent1->isWindow()) + return false; + const QWidget *parent2 = parent1->parentWidget(); + return parent2 && widget->inherits("QLineEdit") + && parent2->inherits("QAbstractItemView"); +} + +// Returns whether base color is set for this widget +bool QWindowsVistaStylePrivate::isLineEditBaseColorSet(const QStyleOption *option, const QWidget *widget) +{ + uint resolveMask = option->palette.resolveMask(); + if (widget) { + // Since spin box includes a line edit we need to resolve the palette mask also from + // the parent, as while the color is always correct on the palette supplied by panel, + // the mask can still be empty. If either mask specifies custom base color, use that. +#if QT_CONFIG(spinbox) + if (const QAbstractSpinBox *spinbox = qobject_cast(widget->parentWidget())) + resolveMask |= spinbox->palette().resolveMask(); +#endif // QT_CONFIG(spinbox) + } + return (resolveMask & (1 << QPalette::Base)) != 0; +} + +/*! \internal + This function will always return a valid window handle, and might + create a limbo widget to do so. + We often need a window handle to for example open theme data, so + this function ensures that we get one. +*/ +HWND QWindowsVistaStylePrivate::winId(const QWidget *widget) +{ + if (widget) { + if (const HWND hwnd = QApplicationPrivate::getHWNDForWidget(const_cast(widget))) + return hwnd; + } + + // Find top level with native window (there might be dialogs that do not have one). + const auto allWindows = QGuiApplication::allWindows(); + for (const QWindow *window : allWindows) { + if (window->isTopLevel() && window->type() != Qt::Desktop && window->handle() != nullptr) + return reinterpret_cast(window->winId()); + } + + return GetDesktopWindow(); +} + +/*! \internal + Returns a native buffer (DIB section) of at least the size of + ( \a x , \a y ). The buffer has a 32 bit depth, to not lose + the alpha values on proper alpha-pixmaps. +*/ +HBITMAP QWindowsVistaStylePrivate::buffer(int w, int h) +{ + // If we already have a HBITMAP which is of adequate size, just return that + if (bufferBitmap) { + if (bufferW >= w && bufferH >= h) + return bufferBitmap; + // Not big enough, discard the old one + if (bufferDC && nullBitmap) + SelectObject(bufferDC, nullBitmap); + DeleteObject(bufferBitmap); + bufferBitmap = nullptr; + } + + w = qMax(bufferW, w); + h = qMax(bufferH, h); + + if (!bufferDC) { + HDC displayDC = GetDC(nullptr); + bufferDC = CreateCompatibleDC(displayDC); + ReleaseDC(nullptr, displayDC); + } + + // Define the header + BITMAPINFO bmi; + memset(&bmi, 0, sizeof(bmi)); + bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + bmi.bmiHeader.biWidth = w; + bmi.bmiHeader.biHeight = -h; + bmi.bmiHeader.biPlanes = 1; + bmi.bmiHeader.biBitCount = 32; + bmi.bmiHeader.biCompression = BI_RGB; + + // Create the pixmap + bufferPixels = nullptr; + bufferBitmap = CreateDIBSection(bufferDC, &bmi, DIB_RGB_COLORS, reinterpret_cast(&bufferPixels), nullptr, 0); + GdiFlush(); + nullBitmap = static_cast(SelectObject(bufferDC, bufferBitmap)); + + if (Q_UNLIKELY(!bufferBitmap)) { + qErrnoWarning("QWindowsVistaStylePrivate::buffer(%dx%d), CreateDIBSection() failed.", w, h); + bufferW = 0; + bufferH = 0; + return nullptr; + } + if (Q_UNLIKELY(!bufferPixels)) { + qErrnoWarning("QWindowsVistaStylePrivate::buffer(%dx%d), CreateDIBSection() did not allocate pixel data.", w, h); + bufferW = 0; + bufferH = 0; + return nullptr; + } + bufferW = w; + bufferH = h; +#ifdef DEBUG_XP_STYLE + qDebug("Creating new dib section (%d, %d)", w, h); +#endif + return bufferBitmap; +} + +/*! \internal + Returns \c true if the part contains any transparency at all. This does + not indicate what kind of transparency we're dealing with. It can be + - Alpha transparency + - Masked transparency +*/ +bool QWindowsVistaStylePrivate::isTransparent(QWindowsThemeData &themeData) +{ + return IsThemeBackgroundPartiallyTransparent(themeData.handle(), themeData.partId, + themeData.stateId); +} + + +/*! \internal + Returns a QRegion of the region of the part +*/ +QRegion QWindowsVistaStylePrivate::region(QWindowsThemeData &themeData) +{ + HRGN hRgn = nullptr; + const qreal factor = QWindowsStylePrivate::nativeMetricScaleFactor(themeData.widget); + RECT rect = themeData.toRECT(QRect(themeData.rect.topLeft() / factor, themeData.rect.size() / factor)); + if (!SUCCEEDED(GetThemeBackgroundRegion(themeData.handle(), bufferHDC(), themeData.partId, + themeData.stateId, &rect, &hRgn))) { + return QRegion(); + } + + HRGN dest = CreateRectRgn(0, 0, 0, 0); + const bool success = CombineRgn(dest, hRgn, nullptr, RGN_COPY) != ERROR; + + QRegion region; + + if (success) { + const auto numBytes = GetRegionData(dest, 0, nullptr); + if (numBytes == 0) + return QRegion(); + + char *buf = new (std::nothrow) char[numBytes]; + if (!buf) + return QRegion(); + + RGNDATA *rd = reinterpret_cast(buf); + if (GetRegionData(dest, numBytes, rd) == 0) { + delete [] buf; + return QRegion(); + } + + RECT *r = reinterpret_cast(rd->Buffer); + for (uint i = 0; i < rd->rdh.nCount; ++i) { + QRect rect; + rect.setCoords(int(r->left * factor), int(r->top * factor), int((r->right - 1) * factor), int((r->bottom - 1) * factor)); + ++r; + region |= rect; + } + + delete [] buf; + } + + DeleteObject(hRgn); + DeleteObject(dest); + + return region; +} + +/*! \internal + Returns \c true if the native doublebuffer contains pixels with + varying alpha value. +*/ +bool QWindowsVistaStylePrivate::hasAlphaChannel(const QRect &rect) +{ + const int startX = rect.left(); + const int startY = rect.top(); + const int w = rect.width(); + const int h = rect.height(); + + int firstAlpha = -1; + for (int y = startY; y < h/2; ++y) { + auto buffer = reinterpret_cast(bufferPixels) + (y * bufferW); + for (int x = startX; x < w; ++x, ++buffer) { + int alpha = (*buffer) >> 24; + if (firstAlpha == -1) + firstAlpha = alpha; + else if (alpha != firstAlpha) + return true; + } + } + return false; +} + +/*! \internal + When the theme engine paints both a true alpha pixmap and a glyph + into our buffer, the glyph might not contain a proper alpha value. + The rule of thumb for premultiplied pixmaps is that the color + values of a pixel can never be higher than the alpha values, so + we use this to our advantage here, and fix all instances where + this occurs. +*/ +bool QWindowsVistaStylePrivate::fixAlphaChannel(const QRect &rect) +{ + const int startX = rect.left(); + const int startY = rect.top(); + const int w = rect.width(); + const int h = rect.height(); + bool hasFixedAlphaValue = false; + + for (int y = startY; y < h; ++y) { + auto buffer = reinterpret_cast(bufferPixels) + (y * bufferW); + for (int x = startX; x < w; ++x, ++buffer) { + uint pixel = *buffer; + int alpha = qAlpha(pixel); + if (qRed(pixel) > alpha || qGreen(pixel) > alpha || qBlue(pixel) > alpha) { + *buffer |= 0xff000000; + hasFixedAlphaValue = true; + } + } + } + return hasFixedAlphaValue; +} + +/*! \internal + Swaps the alpha values on certain pixels: + 0xFF?????? -> 0x00?????? + 0x00?????? -> 0xFF?????? + Used to determine the mask of a non-alpha transparent pixmap in + the native doublebuffer, and swap the alphas so we may paint + the image as a Premultiplied QImage with drawImage(), and obtain + the mask transparency. +*/ +bool QWindowsVistaStylePrivate::swapAlphaChannel(const QRect &rect, bool allPixels) +{ + const int startX = rect.left(); + const int startY = rect.top(); + const int w = rect.width(); + const int h = rect.height(); + bool valueChange = false; + + // Flip the alphas, so that 255-alpha pixels are 0, and 0-alpha are 255. + for (int y = startY; y < h; ++y) { + auto buffer = reinterpret_cast(bufferPixels) + (y * bufferW); + for (int x = startX; x < w; ++x, ++buffer) { + if (allPixels) { + *buffer |= 0xFF000000; + continue; + } + unsigned int alphaValue = (*buffer) & 0xFF000000; + if (alphaValue == 0xFF000000) { + *buffer = 0; + valueChange = true; + } else if (alphaValue == 0) { + *buffer |= 0xFF000000; + valueChange = true; + } + } + } + return valueChange; +} + +/*! \internal + Main theme drawing function. + Determines the correct lowlevel drawing method depending on several + factors. + Use drawBackgroundThruNativeBuffer() if: + - Painter does not have an HDC + - Theme part is flipped (mirrored horizontally) + else use drawBackgroundDirectly(). + \note drawBackgroundThruNativeBuffer() can return false for large + sizes due to buffer()/CreateDIBSection() failing. +*/ +bool QWindowsVistaStylePrivate::drawBackground(QWindowsThemeData &themeData, qreal correctionFactor) +{ + if (themeData.rect.isEmpty()) + return true; + + QPainter *painter = themeData.painter; + Q_ASSERT_X(painter != nullptr, "QWindowsVistaStylePrivate::drawBackground()", "Trying to draw a theme part without a painter"); + if (!painter || !painter->isActive()) + return false; + + painter->save(); + + // Access paintDevice via engine since the painter may + // return the clip device which can still be a widget device in case of grabWidget(). + + bool translucentToplevel = false; + const QPaintDevice *paintDevice = painter->device(); + const qreal aditionalDevicePixelRatio = themeData.widget ? themeData.widget->devicePixelRatio() : qreal(1); + if (paintDevice->devType() == QInternal::Widget) { + const QWidget *window = static_cast(paintDevice)->window(); + translucentToplevel = window->testAttribute(Qt::WA_TranslucentBackground); + } + + const TransformType tt = transformType(painter->deviceTransform(), aditionalDevicePixelRatio); + + bool canDrawDirectly = false; + if (themeData.widget && painter->opacity() == 1.0 && !themeData.rotate + && !isFullyOpaque(themeData) + && tt != ComplexTransform && !themeData.mirrorVertically && !themeData.invertPixels + && !translucentToplevel) { + // Draw on backing store DC only for real widgets or backing store images. + const QPaintDevice *enginePaintDevice = painter->paintEngine()->paintDevice(); + switch (enginePaintDevice->devType()) { + case QInternal::Widget: + canDrawDirectly = true; + break; + case QInternal::Image: + // Ensure the backing store has received as resize and is initialized. + if (QBackingStore *bs = backingStoreForWidget(themeData.widget)) { + if (bs->size().isValid() && bs->paintDevice() == enginePaintDevice) + canDrawDirectly = true; + } + break; + } + } + + const HDC dc = canDrawDirectly ? hdcForWidgetBackingStore(themeData.widget) : nullptr; + const bool result = dc && qFuzzyCompare(correctionFactor, qreal(1)) + ? drawBackgroundDirectly(dc, themeData, aditionalDevicePixelRatio) + : drawBackgroundThruNativeBuffer(themeData, aditionalDevicePixelRatio, correctionFactor); + painter->restore(); + return result; +} + +/*! \internal + This function draws the theme parts directly to the paintengines HDC. + Do not use this if you need to perform other transformations on the + resulting data. +*/ +bool QWindowsVistaStylePrivate::drawBackgroundDirectly(HDC dc, QWindowsThemeData &themeData, qreal additionalDevicePixelRatio) +{ + QPainter *painter = themeData.painter; + + const auto &deviceTransform = painter->deviceTransform(); + const QPointF redirectionDelta(deviceTransform.dx(), deviceTransform.dy()); + const QRect area = scaleRect(QRectF(themeData.rect), additionalDevicePixelRatio).translated(redirectionDelta).toRect(); + + QRegion sysRgn = painter->paintEngine()->systemClip(); + if (sysRgn.isEmpty()) + sysRgn = area; + else + sysRgn &= area; + if (painter->hasClipping()) + sysRgn &= scaleRegion(painter->clipRegion(), additionalDevicePixelRatio).translated(redirectionDelta.toPoint()); + HRGN hrgn = qt_hrgn_from_qregion(sysRgn); + SelectClipRgn(dc, hrgn); + +#ifdef DEBUG_XP_STYLE + printf("---[ DIRECT PAINTING ]------------------> Name(%-10s) Part(%d) State(%d)\n", + qPrintable(themeData.name), themeData.partId, themeData.stateId); + showProperties(themeData); +#endif + + RECT drawRECT = themeData.toRECT(area); + DTBGOPTS drawOptions; + memset(&drawOptions, 0, sizeof(drawOptions)); + drawOptions.dwSize = sizeof(drawOptions); + drawOptions.rcClip = themeData.toRECT(sysRgn.boundingRect()); + drawOptions.dwFlags = DTBG_CLIPRECT + | (themeData.noBorder ? DTBG_OMITBORDER : 0) + | (themeData.noContent ? DTBG_OMITCONTENT : 0) + | (themeData.mirrorHorizontally ? DTBG_MIRRORDC : 0); + + const HRESULT result = DrawThemeBackgroundEx(themeData.handle(), dc, themeData.partId, themeData.stateId, &(drawRECT), &drawOptions); + SelectClipRgn(dc, nullptr); + DeleteObject(hrgn); + return SUCCEEDED(result); +} + +/*! \internal + This function uses a secondary Native doublebuffer for painting parts. + It should only be used when the painteengine doesn't provide a proper + HDC for direct painting (e.g. when doing a grabWidget(), painting to + other pixmaps etc), or when special transformations are needed (e.g. + flips (horizontal mirroring only, vertical are handled by the theme + engine). + + \a correctionFactor is an additional factor used to scale up controls + that are too small on High DPI screens, as has been observed for + WP_MDICLOSEBUTTON, WP_MDIRESTOREBUTTON, WP_MDIMINBUTTON (QTBUG-75927). +*/ +bool QWindowsVistaStylePrivate::drawBackgroundThruNativeBuffer(QWindowsThemeData &themeData, + qreal additionalDevicePixelRatio, + qreal correctionFactor) +{ + QPainter *painter = themeData.painter; + QRectF rectF = scaleRect(QRectF(themeData.rect), additionalDevicePixelRatio); + + if ((themeData.rotate + 90) % 180 == 0) { // Catch 90,270,etc.. degree flips. + rectF = QRectF(0, 0, rectF.height(), rectF.width()); + } + rectF.moveTo(0, 0); + + const bool hasCorrectionFactor = !qFuzzyCompare(correctionFactor, qreal(1)); + QRect rect = rectF.toRect(); + const QRect drawRect = hasCorrectionFactor + ? QRectF(rectF.topLeft() / correctionFactor, rectF.size() / correctionFactor).toRect() + : rect; + int partId = themeData.partId; + int stateId = themeData.stateId; + int w = rect.width(); + int h = rect.height(); + + // Values initialized later, either from cached values, or from function calls + AlphaChannelType alphaType = UnknownAlpha; + bool stateHasData = true; // We assume so; + bool hasAlpha = false; + bool partIsTransparent; + bool potentialInvalidAlpha; + + QString pixmapCacheKey = QStringLiteral("$qt_xp_"); + pixmapCacheKey.append(themeName(themeData.theme)); + pixmapCacheKey.append(QLatin1Char('p')); + pixmapCacheKey.append(QString::number(partId)); + pixmapCacheKey.append(QLatin1Char('s')); + pixmapCacheKey.append(QString::number(stateId)); + pixmapCacheKey.append(QLatin1Char('s')); + pixmapCacheKey.append(themeData.noBorder ? QLatin1Char('0') : QLatin1Char('1')); + pixmapCacheKey.append(QLatin1Char('b')); + pixmapCacheKey.append(themeData.noContent ? QLatin1Char('0') : QLatin1Char('1')); + pixmapCacheKey.append(QString::number(w)); + pixmapCacheKey.append(QLatin1Char('w')); + pixmapCacheKey.append(QString::number(h)); + pixmapCacheKey.append(QLatin1Char('h')); + pixmapCacheKey.append(QString::number(additionalDevicePixelRatio)); + pixmapCacheKey.append(QLatin1Char('d')); + if (hasCorrectionFactor) { + pixmapCacheKey.append(QLatin1Char('c')); + pixmapCacheKey.append(QString::number(correctionFactor)); + } + + QPixmap cachedPixmap; + ThemeMapKey key(themeData); + ThemeMapData data = alphaCache.value(key); + + bool haveCachedPixmap = false; + bool isCached = data.dataValid; + if (isCached) { + partIsTransparent = data.partIsTransparent; + hasAlpha = data.hasAlphaChannel; + alphaType = data.alphaType; + potentialInvalidAlpha = data.hadInvalidAlpha; + + haveCachedPixmap = QPixmapCache::find(pixmapCacheKey, &cachedPixmap); + +#ifdef DEBUG_XP_STYLE + char buf[25]; + ::sprintf(buf, "+ Pixmap(%3d, %3d) ]", w, h); + printf("---[ CACHED %s--------> Name(%-10s) Part(%d) State(%d)\n", + haveCachedPixmap ? buf : "]-------------------", + qPrintable(themeData.name), themeData.partId, themeData.stateId); +#endif + } else { + // Not cached, so get values from Theme Engine + BOOL tmt_borderonly = false; + COLORREF tmt_transparentcolor = 0x0; + PROPERTYORIGIN proporigin = PO_NOTFOUND; + GetThemeBool(themeData.handle(), themeData.partId, themeData.stateId, TMT_BORDERONLY, &tmt_borderonly); + GetThemeColor(themeData.handle(), themeData.partId, themeData.stateId, TMT_TRANSPARENTCOLOR, &tmt_transparentcolor); + GetThemePropertyOrigin(themeData.handle(), themeData.partId, themeData.stateId, TMT_CAPTIONMARGINS, &proporigin); + + partIsTransparent = isTransparent(themeData); + + potentialInvalidAlpha = false; + GetThemePropertyOrigin(themeData.handle(), themeData.partId, themeData.stateId, TMT_GLYPHTYPE, &proporigin); + if (proporigin == PO_PART || proporigin == PO_STATE) { + int tmt_glyphtype = GT_NONE; + GetThemeEnumValue(themeData.handle(), themeData.partId, themeData.stateId, TMT_GLYPHTYPE, &tmt_glyphtype); + potentialInvalidAlpha = partIsTransparent && tmt_glyphtype == GT_IMAGEGLYPH; + } + +#ifdef DEBUG_XP_STYLE + printf("---[ NOT CACHED ]-----------------------> Name(%-10s) Part(%d) State(%d)\n", + qPrintable(themeData.name), themeData.partId, themeData.stateId); + printf("-->partIsTransparen = %d\n", partIsTransparent); + printf("-->potentialInvalidAlpha = %d\n", potentialInvalidAlpha); + showProperties(themeData); +#endif + } + bool wasAlphaSwapped = false; + bool wasAlphaFixed = false; + + // OLD PSDK Workaround ------------------------------------------------------------------------ + // See if we need extra clipping for the older PSDK, which does + // not have a DrawThemeBackgroundEx function for DTGB_OMITBORDER + // and DTGB_OMITCONTENT + bool addBorderContentClipping = false; + QRegion extraClip; + QRect area = drawRect; + if (themeData.noBorder || themeData.noContent) { + extraClip = area; + // We are running on a system where the uxtheme.dll does not have + // the DrawThemeBackgroundEx function, so we need to clip away + // borders or contents manually. + + int borderSize = 0; + PROPERTYORIGIN origin = PO_NOTFOUND; + GetThemePropertyOrigin(themeData.handle(), themeData.partId, themeData.stateId, TMT_BORDERSIZE, &origin); + GetThemeInt(themeData.handle(), themeData.partId, themeData.stateId, TMT_BORDERSIZE, &borderSize); + borderSize *= additionalDevicePixelRatio; + + // Clip away border region + if ((origin == PO_CLASS || origin == PO_PART || origin == PO_STATE) && borderSize > 0) { + if (themeData.noBorder) { + extraClip &= area; + area = area.adjusted(-borderSize, -borderSize, borderSize, borderSize); + } + + // Clip away content region + if (themeData.noContent) { + QRegion content = area.adjusted(borderSize, borderSize, -borderSize, -borderSize); + extraClip ^= content; + } + } + addBorderContentClipping = (themeData.noBorder | themeData.noContent); + } + + QImage img; + if (!haveCachedPixmap) { // If the pixmap is not cached, generate it! ------------------------- + if (!buffer(drawRect.width(), drawRect.height())) // Ensure a buffer of at least (w, h) in size + return false; + HDC dc = bufferHDC(); + + // Clear the buffer + if (alphaType != NoAlpha) { + // Consider have separate "memset" function for small chunks for more speedup + memset(bufferPixels, 0x00, bufferW * drawRect.height() * 4); + } + + // Difference between area and rect + int dx = area.x() - drawRect.x(); + int dy = area.y() - drawRect.y(); + + // Adjust so painting rect starts from Origo + rect.moveTo(0,0); + area.moveTo(dx,dy); + DTBGOPTS drawOptions; + drawOptions.dwSize = sizeof(drawOptions); + drawOptions.rcClip = themeData.toRECT(rect); + drawOptions.dwFlags = DTBG_CLIPRECT + | (themeData.noBorder ? DTBG_OMITBORDER : 0) + | (themeData.noContent ? DTBG_OMITCONTENT : 0); + + // Drawing the part into the backing store + RECT wRect(themeData.toRECT(area)); + DrawThemeBackgroundEx(themeData.handle(), dc, themeData.partId, themeData.stateId, &wRect, &drawOptions); + + // If not cached, analyze the buffer data to figure + // out alpha type, and if it contains data + if (!isCached) { + // SHORTCUT: If the part's state has no data, cache it for NOOP later + if (!stateHasData) { + memset(static_cast(&data), 0, sizeof(data)); + data.dataValid = true; + alphaCache.insert(key, data); + return true; + } + hasAlpha = hasAlphaChannel(rect); + if (!hasAlpha && partIsTransparent) + potentialInvalidAlpha = true; +#if defined(DEBUG_XP_STYLE) && 1 + dumpNativeDIB(drawRect.width(), drawRect.height()); +#endif + } + + // Fix alpha values, if needed + if (potentialInvalidAlpha) + wasAlphaFixed = fixAlphaChannel(drawRect); + + QImage::Format format; + if ((partIsTransparent && !wasAlphaSwapped) || (!partIsTransparent && hasAlpha)) { + format = QImage::Format_ARGB32_Premultiplied; + alphaType = RealAlpha; + } else if (wasAlphaSwapped) { + format = QImage::Format_ARGB32_Premultiplied; + alphaType = MaskAlpha; + } else { + format = QImage::Format_RGB32; + // The image data we got from the theme engine does not have any transparency, + // thus the alpha channel is set to 0. + // However, Format_RGB32 requires the alpha part to be set to 0xff, thus + // we must flip it from 0x00 to 0xff + swapAlphaChannel(rect, true); + alphaType = NoAlpha; + } +#if defined(DEBUG_XP_STYLE) && 1 + printf("Image format is: %s\n", alphaType == RealAlpha ? "Real Alpha" : alphaType == MaskAlpha ? "Masked Alpha" : "No Alpha"); +#endif + img = QImage(bufferPixels, bufferW, bufferH, format); + if (themeData.invertPixels) + img.invertPixels(); + + if (hasCorrectionFactor) + img = img.scaled(img.size() * correctionFactor, Qt::KeepAspectRatio, Qt::SmoothTransformation); + img.setDevicePixelRatio(additionalDevicePixelRatio); + } + + // Blitting backing store + bool useRegion = partIsTransparent && !hasAlpha && !wasAlphaSwapped; + + QRegion newRegion; + QRegion oldRegion; + if (useRegion) { + newRegion = region(themeData); + oldRegion = painter->clipRegion(); + painter->setClipRegion(newRegion); +#if defined(DEBUG_XP_STYLE) && 0 + printf("Using region:\n"); + for (const QRect &r : newRegion) + printf(" (%d, %d, %d, %d)\n", r.x(), r.y(), r.right(), r.bottom()); +#endif + } + + if (addBorderContentClipping) + painter->setClipRegion(scaleRegion(extraClip, 1.0 / additionalDevicePixelRatio), Qt::IntersectClip); + + if (!themeData.mirrorHorizontally && !themeData.mirrorVertically && !themeData.rotate) { + if (!haveCachedPixmap) + painter->drawImage(themeData.rect, img, rect); + else + painter->drawPixmap(themeData.rect, cachedPixmap); + } else { + // This is _slow_! + // Make a copy containing only the necessary data, and mirror + // on all wanted axes. Then draw the copy. + // If cached, the normal pixmap is cached, instead of caching + // all possible orientations for each part and state. + QImage imgCopy; + if (!haveCachedPixmap) + imgCopy = img.copy(rect); + else + imgCopy = cachedPixmap.toImage(); + + if (themeData.rotate) { + QTransform rotMatrix; + rotMatrix.rotate(themeData.rotate); + imgCopy = imgCopy.transformed(rotMatrix); + } + if (themeData.mirrorHorizontally || themeData.mirrorVertically) + imgCopy = imgCopy.mirrored(themeData.mirrorHorizontally, themeData.mirrorVertically); + painter->drawImage(themeData.rect, imgCopy); + } + + if (useRegion || addBorderContentClipping) { + if (oldRegion.isEmpty()) + painter->setClipping(false); + else + painter->setClipRegion(oldRegion); + } + + // Cache the pixmap to avoid expensive swapAlphaChannel() calls + if (!haveCachedPixmap && w && h) { + QPixmap pix = QPixmap::fromImage(img).copy(rect); + QPixmapCache::insert(pixmapCacheKey, pix); +#ifdef DEBUG_XP_STYLE + printf("+++Adding pixmap to cache, size(%d, %d), wasAlphaSwapped(%d), wasAlphaFixed(%d), name(%s)\n", + w, h, wasAlphaSwapped, wasAlphaFixed, qPrintable(pixmapCacheKey)); +#endif + } + + // Add to theme part cache + if (!isCached) { + memset(static_cast(&data), 0, sizeof(data)); + data.dataValid = true; + data.partIsTransparent = partIsTransparent; + data.alphaType = alphaType; + data.hasAlphaChannel = hasAlpha; + data.wasAlphaSwapped = wasAlphaSwapped; + data.hadInvalidAlpha = wasAlphaFixed; + alphaCache.insert(key, data); + } + return true; } /*! @@ -85,7 +1079,7 @@ bool canAnimate(const QStyleOption *option) { static inline QImage createAnimationBuffer(const QStyleOption *option, const QWidget *widget) { const qreal devicePixelRatio = widget - ? widget->devicePixelRatioF() : qApp->devicePixelRatio(); + ? widget->devicePixelRatioF() : qApp->devicePixelRatio(); QImage result(option->rect.size() * devicePixelRatio, QImage::Format_ARGB32_Premultiplied); result.setDevicePixelRatio(devicePixelRatio); result.fill(0); @@ -132,33 +1126,112 @@ void deleteClonedAnimationStyleOption(const QStyleOption *option) delete option; } -/*! - \class QWindowsVistaStyle - \brief The QWindowsVistaStyle class provides a look and feel suitable for applications on Microsoft Windows Vista. - \since 4.3 - \ingroup appearance - \inmodule QtWidgets - \internal +static void populateTitleBarButtonTheme(const QStyle *proxy, const QWidget *widget, + const QStyleOptionComplex *option, + QStyle::SubControl subControl, + bool isTitleBarActive, int part, + QWindowsThemeData *theme) +{ + theme->rect = proxy->subControlRect(QStyle::CC_TitleBar, option, subControl, widget); + theme->partId = part; + if (widget && !widget->isEnabled()) + theme->stateId = RBS_DISABLED; + else if (option->activeSubControls == subControl && option->state.testFlag(QStyle::State_Sunken)) + theme->stateId = RBS_PUSHED; + else if (option->activeSubControls == subControl && option->state.testFlag(QStyle::State_MouseOver)) + theme->stateId = RBS_HOT; + else if (!isTitleBarActive) + theme->stateId = RBS_INACTIVE; + else + theme->stateId = RBS_NORMAL; +} - \warning This style is only available on the Windows Vista platform - because it makes use of Windows Vista's style engine. +#if QT_CONFIG(mdiarea) +// Helper for drawing MDI buttons into the corner widget of QMenuBar in case a +// QMdiSubWindow is maximized. +static void populateMdiButtonTheme(const QStyle *proxy, const QWidget *widget, + const QStyleOptionComplex *option, + QStyle::SubControl subControl, int part, + QWindowsThemeData *theme) +{ + theme->partId = part; + theme->rect = proxy->subControlRect(QStyle::CC_MdiControls, option, subControl, widget); + if (!option->state.testFlag(QStyle::State_Enabled)) + theme->stateId = CBS_INACTIVE; + else if (option->state.testFlag(QStyle::State_Sunken) && option->activeSubControls.testFlag(subControl)) + theme->stateId = CBS_PUSHED; + else if (option->state.testFlag(QStyle::State_MouseOver) && option->activeSubControls.testFlag(subControl)) + theme->stateId = CBS_HOT; + else + theme->stateId = CBS_NORMAL; +} - \sa QMacStyle, QWindowsXPStyle, QFusionStyle -*/ +// Calculate an small (max 2), empirical correction factor for scaling up +// WP_MDICLOSEBUTTON, WP_MDIRESTOREBUTTON, WP_MDIMINBUTTON, which are too +// small on High DPI screens (QTBUG-75927). +static qreal mdiButtonCorrectionFactor(QWindowsThemeData &theme, const QPaintDevice *pd = nullptr) +{ + const auto dpr = pd ? pd->devicePixelRatio() : qApp->devicePixelRatio(); + const QSizeF nativeSize = QSizeF(theme.size()) / dpr; + const QSizeF requestedSize(theme.rect.size()); + const auto rawFactor = qMin(requestedSize.width() / nativeSize.width(), + requestedSize.height() / nativeSize.height()); + const auto factor = rawFactor >= qreal(2) ? qreal(2) : qreal(1); + return factor; +} +#endif // QT_CONFIG(mdiarea) -/*! - Constructs a QWindowsVistaStyle object. +/* + This function is used by subControlRect to check if a button + should be drawn for the given subControl given a set of window flags. */ -QWindowsVistaStyle::QWindowsVistaStyle() - : QWindowsXPStyle(*new QWindowsVistaStylePrivate) -{ +static bool buttonVisible(const QStyle::SubControl sc, const QStyleOptionTitleBar *tb){ + + bool isMinimized = tb->titleBarState & Qt::WindowMinimized; + bool isMaximized = tb->titleBarState & Qt::WindowMaximized; + const auto flags = tb->titleBarFlags; + bool retVal = false; + switch (sc) { + case QStyle::SC_TitleBarContextHelpButton: + if (flags & Qt::WindowContextHelpButtonHint) + retVal = true; + break; + case QStyle::SC_TitleBarMinButton: + if (!isMinimized && (flags & Qt::WindowMinimizeButtonHint)) + retVal = true; + break; + case QStyle::SC_TitleBarNormalButton: + if (isMinimized && (flags & Qt::WindowMinimizeButtonHint)) + retVal = true; + else if (isMaximized && (flags & Qt::WindowMaximizeButtonHint)) + retVal = true; + break; + case QStyle::SC_TitleBarMaxButton: + if (!isMaximized && (flags & Qt::WindowMaximizeButtonHint)) + retVal = true; + break; + case QStyle::SC_TitleBarShadeButton: + if (!isMinimized && flags & Qt::WindowShadeButtonHint) + retVal = true; + break; + case QStyle::SC_TitleBarUnshadeButton: + if (isMinimized && flags & Qt::WindowShadeButtonHint) + retVal = true; + break; + case QStyle::SC_TitleBarCloseButton: + if (flags & Qt::WindowSystemMenuHint) + retVal = true; + break; + case QStyle::SC_TitleBarSysMenu: + if (flags & Qt::WindowSystemMenuHint) + retVal = true; + break; + default : + retVal = true; + } + return retVal; } -/*! - Destructor. -*/ -QWindowsVistaStyle::~QWindowsVistaStyle() = default; - //convert Qt state flags to uxtheme button states static int buttonStateId(int flags, int partId) { @@ -191,16 +1264,6 @@ static int buttonStateId(int flags, int partId) return stateId; } -bool QWindowsVistaAnimation::isUpdateNeeded() const -{ - return QWindowsVistaStylePrivate::useVista(); -} - -void QWindowsVistaAnimation::paint(QPainter *painter, const QStyleOption *option) -{ - painter->drawImage(option->rect, currentImage()); -} - static inline bool supportsStateTransition(QStyle::PrimitiveElement element, const QStyleOption *option, const QWidget *widget) @@ -211,9 +1274,9 @@ static inline bool supportsStateTransition(QStyle::PrimitiveElement element, case QStyle::PE_IndicatorCheckBox: result = true; break; - // QTBUG-40634, do not animate when color is set in palette for PE_PanelLineEdit. + // QTBUG-40634, do not animate when color is set in palette for PE_PanelLineEdit. case QStyle::PE_FrameLineEdit: - result = !QWindowsXPStylePrivate::isLineEditBaseColorSet(option, widget); + result = !QWindowsVistaStylePrivate::isLineEditBaseColorSet(option, widget); break; default: break; @@ -221,6 +1284,33 @@ static inline bool supportsStateTransition(QStyle::PrimitiveElement element, return result; } +/*! + \class QWindowsVistaStyle + \brief The QWindowsVistaStyle class provides a look and feel suitable for applications on Microsoft Windows Vista. + \since 4.3 + \ingroup appearance + \inmodule QtWidgets + \internal + + \warning This style is only available on the Windows Vista platform + because it makes use of Windows Vista's style engine. + + \sa QMacStyle, QFusionStyle +*/ + +/*! + Constructs a QWindowsVistaStyle object. +*/ +QWindowsVistaStyle::QWindowsVistaStyle() : QWindowsStyle(*new QWindowsVistaStylePrivate) +{ +} + +/*! + Destructor. +*/ +QWindowsVistaStyle::~QWindowsVistaStyle() = default; + + /*! \internal @@ -251,228 +1341,306 @@ static inline bool supportsStateTransition(QStyle::PrimitiveElement element, starting image for the hover transition. */ - void QWindowsVistaStyle::drawPrimitive(PrimitiveElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget) const { - QWindowsVistaStylePrivate *d = const_cast(d_func()); - - int state = option->state; if (!QWindowsVistaStylePrivate::useVista()) { QWindowsStyle::drawPrimitive(element, option, painter, widget); return; } - if ((option->state & State_Enabled) && d->transitionsEnabled() && canAnimate(option)) { - { - QRect oldRect; - QRect newRect; - - if (supportsStateTransition(element, option, widget)) { - // Retrieve and update the dynamic properties tracking - // the previous state of the widget: - QObject *styleObject = option->styleObject; - styleObject->setProperty("_q_no_animation", true); + QWindowsVistaStylePrivate *d = const_cast(d_func()); - int oldState = styleObject->property("_q_stylestate").toInt(); - oldRect = styleObject->property("_q_stylerect").toRect(); - newRect = option->rect; - styleObject->setProperty("_q_stylestate", int(option->state)); - styleObject->setProperty("_q_stylerect", option->rect); + int state = option->state; + QRect rect = option->rect; - bool doTransition = oldState && - ((state & State_Sunken) != (oldState & State_Sunken) || - (state & State_On) != (oldState & State_On) || - (state & State_MouseOver) != (oldState & State_MouseOver)); + if ((state & State_Enabled) && d->transitionsEnabled() && canAnimate(option)) { + if (supportsStateTransition(element, option, widget)) { + // Retrieve and update the dynamic properties tracking + // the previous state of the widget: + QObject *styleObject = option->styleObject; + styleObject->setProperty("_q_no_animation", true); + int oldState = styleObject->property("_q_stylestate").toInt(); + QRect oldRect = styleObject->property("_q_stylerect").toRect(); + QRect newRect = rect; + styleObject->setProperty("_q_stylestate", int(option->state)); + styleObject->setProperty("_q_stylerect", option->rect); - if (oldRect != newRect || - (state & State_Enabled) != (oldState & State_Enabled) || - (state & State_Active) != (oldState & State_Active)) - d->stopAnimation(styleObject); + bool doTransition = oldState && + ((state & State_Sunken) != (oldState & State_Sunken) || + (state & State_On) != (oldState & State_On) || + (state & State_MouseOver) != (oldState & State_MouseOver)); - if (option->state & State_ReadOnly && element == PE_FrameLineEdit) // Do not animate read only line edits - doTransition = false; + if (oldRect != newRect || + (state & State_Enabled) != (oldState & State_Enabled) || + (state & State_Active) != (oldState & State_Active)) + d->stopAnimation(styleObject); - if (doTransition) { - QStyleOption *styleOption = clonedAnimationStyleOption(option); - styleOption->state = QStyle::State(oldState); + if (state & State_ReadOnly && element == PE_FrameLineEdit) // Do not animate read only line edits + doTransition = false; - QWindowsVistaAnimation *anim = qobject_cast(d->animation(styleObject)); - QWindowsVistaTransition *t = new QWindowsVistaTransition(styleObject); + if (doTransition) { + QStyleOption *styleOption = clonedAnimationStyleOption(option); + styleOption->state = QStyle::State(oldState); - // We create separate images for the initial and final transition states and store them in the - // Transition object. - QImage startImage = createAnimationBuffer(option, widget); - QPainter startPainter(&startImage); + QWindowsVistaAnimation *animate = qobject_cast(d->animation(styleObject)); + QWindowsVistaTransition *transition = new QWindowsVistaTransition(styleObject); - QImage endImage = createAnimationBuffer(option, widget); - QPainter endPainter(&endImage); + // We create separate images for the initial and final transition states and store them in the + // Transition object. + QImage startImage = createAnimationBuffer(option, widget); + QPainter startPainter(&startImage); - // If we have a running animation on the widget already, we will use that to paint the initial - // state of the new transition, this ensures a smooth transition from a current animation such as a - // pulsating default button into the intended target state. - if (!anim) - proxy()->drawPrimitive(element, styleOption, &startPainter, widget); - else - anim->paint(&startPainter, styleOption); + QImage endImage = createAnimationBuffer(option, widget); + QPainter endPainter(&endImage); - t->setStartImage(startImage); + // If we have a running animation on the widget already, we will use that to paint the initial + // state of the new transition, this ensures a smooth transition from a current animation such as a + // pulsating default button into the intended target state. + if (!animate) + proxy()->drawPrimitive(element, styleOption, &startPainter, widget); + else + animate->paint(&startPainter, styleOption); - // The end state of the transition is simply the result we would have painted - // if the style was not animated. - styleOption->styleObject = nullptr; - styleOption->state = option->state; - proxy()->drawPrimitive(element, styleOption, &endPainter, widget); + transition->setStartImage(startImage); + // The end state of the transition is simply the result we would have painted + // if the style was not animated. + styleOption->styleObject = nullptr; + styleOption->state = option->state; + proxy()->drawPrimitive(element, styleOption, &endPainter, widget); - t->setEndImage(endImage); + transition->setEndImage(endImage); - HTHEME theme; - int partId; - DWORD duration; - int fromState = 0; - int toState = 0; + HTHEME theme; + int partId; + DWORD duration; + int fromState = 0; + int toState = 0; - //translate state flags to UXTHEME states : - if (element == PE_FrameLineEdit) { - theme = OpenThemeData(nullptr, L"Edit"); - partId = EP_EDITBORDER_NOSCROLL; + //translate state flags to UXTHEME states : + if (element == PE_FrameLineEdit) { + theme = OpenThemeData(nullptr, L"Edit"); + partId = EP_EDITBORDER_NOSCROLL; - if (oldState & State_MouseOver) - fromState = ETS_HOT; - else if (oldState & State_HasFocus) - fromState = ETS_FOCUSED; - else - fromState = ETS_NORMAL; + if (oldState & State_MouseOver) + fromState = ETS_HOT; + else if (oldState & State_HasFocus) + fromState = ETS_FOCUSED; + else + fromState = ETS_NORMAL; - if (state & State_MouseOver) - toState = ETS_HOT; - else if (state & State_HasFocus) - toState = ETS_FOCUSED; - else - toState = ETS_NORMAL; + if (state & State_MouseOver) + toState = ETS_HOT; + else if (state & State_HasFocus) + toState = ETS_FOCUSED; + else + toState = ETS_NORMAL; - } else { - theme = OpenThemeData(nullptr, L"Button"); - if (element == PE_IndicatorRadioButton) - partId = BP_RADIOBUTTON; - else if (element == PE_IndicatorCheckBox) - partId = BP_CHECKBOX; - else - partId = BP_PUSHBUTTON; + } else { + theme = OpenThemeData(nullptr, L"Button"); + if (element == PE_IndicatorRadioButton) + partId = BP_RADIOBUTTON; + else if (element == PE_IndicatorCheckBox) + partId = BP_CHECKBOX; + else + partId = BP_PUSHBUTTON; - fromState = buttonStateId(oldState, partId); - toState = buttonStateId(option->state, partId); - } + fromState = buttonStateId(oldState, partId); + toState = buttonStateId(option->state, partId); + } - // Retrieve the transition time between the states from the system. - if (theme + // Retrieve the transition time between the states from the system. + if (theme && SUCCEEDED(GetThemeTransitionDuration(theme, partId, fromState, toState, TMT_TRANSITIONDURATIONS, &duration))) { - t->setDuration(int(duration)); - } - t->setStartTime(d->animationTime()); - - deleteClonedAnimationStyleOption(styleOption); - d->startAnimation(t); + transition->setDuration(int(duration)); } - styleObject->setProperty("_q_no_animation", false); - } + transition->setStartTime(d->animationTime()); - } // End of animation part + deleteClonedAnimationStyleOption(styleOption); + d->startAnimation(transition); + } + styleObject->setProperty("_q_no_animation", false); + } } - QRect rect = option->rect; + int themeNumber = -1; + int partId = 0; + int stateId = 0; + bool hMirrored = false; + bool vMirrored = false; + bool noBorder = false; + bool noContent = false; + int rotate = 0; switch (element) { + case PE_PanelButtonCommand: + if (const auto *btn = qstyleoption_cast(option)) { + QBrush fill; + if (!(state & State_Sunken) && (state & State_On)) + fill = QBrush(option->palette.light().color(), Qt::Dense4Pattern); + else + fill = option->palette.brush(QPalette::Button); + if (btn->features & QStyleOptionButton::DefaultButton && state & State_Sunken) { + painter->setPen(option->palette.dark().color()); + painter->setBrush(fill); + painter->drawRect(rect.adjusted(0, 0, -1, -1)); + } else if (state & (State_Raised | State_On | State_Sunken)) { + qDrawWinButton(painter, rect, option->palette, state & (State_Sunken | State_On), + &fill); + } else { + painter->fillRect(rect, fill); + } + } + break; + + case PE_PanelButtonTool: +#if QT_CONFIG(dockwidget) + if (widget && widget->inherits("QDockWidgetTitleButton")) { + if (const QWidget *dw = widget->parentWidget()) + if (dw->isWindow()) { + return; + } + } +#endif // QT_CONFIG(dockwidget) + themeNumber = QWindowsVistaStylePrivate::ToolBarTheme; + partId = TP_BUTTON; + if (!(option->state & State_Enabled)) + stateId = TS_DISABLED; + else if (option->state & State_Sunken) + stateId = TS_PRESSED; + else if (option->state & State_MouseOver) + stateId = option->state & State_On ? TS_HOTCHECKED : TS_HOT; + else if (option->state & State_On) + stateId = TS_CHECKED; + else if (!(option->state & State_AutoRaise)) + stateId = TS_HOT; + else + stateId = TS_NORMAL; + + break; + case PE_IndicatorHeaderArrow: - if (const QStyleOptionHeader *header = qstyleoption_cast(option)) { + if (const auto *header = qstyleoption_cast(option)) { int stateId = HSAS_SORTEDDOWN; if (header->sortIndicator & QStyleOptionHeader::SortDown) stateId = HSAS_SORTEDUP; //note that the uxtheme sort down indicator is the inverse of ours - XPThemeData theme(widget, painter, - QWindowsXPStylePrivate::HeaderTheme, - HP_HEADERSORTARROW, stateId, option->rect); + QWindowsThemeData theme(widget, painter, + QWindowsVistaStylePrivate::HeaderTheme, + HP_HEADERSORTARROW, stateId, option->rect); d->drawBackground(theme); + return; } break; - case PE_IndicatorBranch: - { - XPThemeData theme(widget, painter, QWindowsXPStylePrivate::VistaTreeViewTheme); - static int decoration_size = 0; - if (!decoration_size && theme.isValid()) { - XPThemeData themeSize = theme; - themeSize.partId = TVP_HOTGLYPH; - themeSize.stateId = GLPS_OPENED; - const QSizeF size = themeSize.size() * QWindowsStylePrivate::nativeMetricScaleFactor(widget); - decoration_size = qRound(qMax(size.width(), size.height())); - } - int mid_h = option->rect.x() + option->rect.width() / 2; - int mid_v = option->rect.y() + option->rect.height() / 2; - int bef_h = mid_h; - int bef_v = mid_v; - int aft_h = mid_h; - int aft_v = mid_v; - if (option->state & State_Children) { - int delta = decoration_size / 2; - theme.rect = QRect(bef_h - delta, bef_v - delta, decoration_size, decoration_size); - theme.partId = option->state & State_MouseOver ? TVP_HOTGLYPH : TVP_GLYPH; - theme.stateId = option->state & QStyle::State_Open ? GLPS_OPENED : GLPS_CLOSED; - if (option->direction == Qt::RightToLeft) - theme.mirrorHorizontally = true; - d->drawBackground(theme); - bef_h -= delta + 2; - bef_v -= delta + 2; - aft_h += delta - 2; - aft_v += delta - 2; - } -#if 0 - QBrush brush(option->palette.dark().color(), Qt::Dense4Pattern); - if (option->state & State_Item) { - if (option->direction == Qt::RightToLeft) - painter->fillRect(option->rect.left(), mid_v, bef_h - option->rect.left(), 1, brush); - else - painter->fillRect(aft_h, mid_v, option->rect.right() - aft_h + 1, 1, brush); - } - if (option->state & State_Sibling && option->rect.bottom() > aft_v) - painter->fillRect(mid_h, aft_v, 1, option->rect.bottom() - aft_v + 1, brush); - if (option->state & (State_Open | State_Children | State_Item | State_Sibling) && (bef_v > option->rect.y())) - painter->fillRect(mid_h, option->rect.y(), 1, bef_v - option->rect.y(), brush); -#endif + case PE_IndicatorCheckBox: + if (auto *animate = + qobject_cast(d->animation(styleObject(option)))) { + animate->paint(painter, option); + return; + } else { + themeNumber = QWindowsVistaStylePrivate::ButtonTheme; + partId = BP_CHECKBOX; + + if (!(option->state & State_Enabled)) + stateId = CBS_UNCHECKEDDISABLED; + else if (option->state & State_Sunken) + stateId = CBS_UNCHECKEDPRESSED; + else if (option->state & State_MouseOver) + stateId = CBS_UNCHECKEDHOT; + else + stateId = CBS_UNCHECKEDNORMAL; + + if (option->state & State_On) + stateId += CBS_CHECKEDNORMAL-1; + else if (option->state & State_NoChange) + stateId += CBS_MIXEDNORMAL-1; } break; + case PE_IndicatorItemViewItemCheck: { + QStyleOptionButton button; + button.QStyleOption::operator=(*option); + button.state &= ~State_MouseOver; + proxy()->drawPrimitive(PE_IndicatorCheckBox, &button, painter, widget); + return; + } + + case PE_IndicatorBranch: { + QWindowsThemeData theme(widget, painter, QWindowsVistaStylePrivate::VistaTreeViewTheme); + static int decoration_size = 0; + if (!decoration_size && theme.isValid()) { + QWindowsThemeData themeSize = theme; + themeSize.partId = TVP_HOTGLYPH; + themeSize.stateId = GLPS_OPENED; + const QSizeF size = themeSize.size() * QWindowsStylePrivate::nativeMetricScaleFactor(widget); + decoration_size = qRound(qMax(size.width(), size.height())); + } + int mid_h = option->rect.x() + option->rect.width() / 2; + int mid_v = option->rect.y() + option->rect.height() / 2; + if (option->state & State_Children) { + int delta = decoration_size / 2; + theme.rect = QRect(mid_h - delta, mid_v - delta, decoration_size, decoration_size); + theme.partId = option->state & State_MouseOver ? TVP_HOTGLYPH : TVP_GLYPH; + theme.stateId = option->state & QStyle::State_Open ? GLPS_OPENED : GLPS_CLOSED; + if (option->direction == Qt::RightToLeft) + theme.mirrorHorizontally = true; + d->drawBackground(theme); + } + return; + } + case PE_PanelButtonBevel: - case PE_IndicatorCheckBox: - case PE_IndicatorRadioButton: - { - if (QWindowsVistaAnimation *a = - qobject_cast(d->animation(styleObject(option)))){ - a->paint(painter, option); - } else { - QWindowsXPStyle::drawPrimitive(element, option, painter, widget); - } + if (QWindowsVistaAnimation *animate = + qobject_cast(d->animation(styleObject(option)))) { + animate->paint(painter, option); + return; } + + themeNumber = QWindowsVistaStylePrivate::ButtonTheme; + partId = BP_PUSHBUTTON; + if (!(option->state & State_Enabled)) + stateId = PBS_DISABLED; + else if ((option->state & State_Sunken) || (option->state & State_On)) + stateId = PBS_PRESSED; + else if (option->state & State_MouseOver) + stateId = PBS_HOT; + else + stateId = PBS_NORMAL; break; - case PE_FrameMenu: - { - int stateId = option->state & State_Active ? MB_ACTIVE : MB_INACTIVE; - XPThemeData theme(widget, painter, - QWindowsXPStylePrivate::MenuTheme, - MENU_POPUPBORDERS, stateId, option->rect); - d->drawBackground(theme); + case PE_IndicatorRadioButton: + if (QWindowsVistaAnimation *animate = + qobject_cast(d->animation(styleObject(option)))) { + animate->paint(painter, option); + return; + } else { + themeNumber = QWindowsVistaStylePrivate::ButtonTheme; + partId = BP_RADIOBUTTON; + + if (!(option->state & State_Enabled)) + stateId = RBS_UNCHECKEDDISABLED; + else if (option->state & State_Sunken) + stateId = RBS_UNCHECKEDPRESSED; + else if (option->state & State_MouseOver) + stateId = RBS_UNCHECKEDHOT; + else + stateId = RBS_UNCHECKEDNORMAL; + + if (option->state & State_On) + stateId += RBS_CHECKEDNORMAL-1; } break; - case PE_Frame: { + + case PE_Frame: #if QT_CONFIG(accessibility) if (QStyleHelper::isInstanceOf(option->styleObject, QAccessible::EditableText) - || QStyleHelper::isInstanceOf(option->styleObject, QAccessible::StaticText) || + || QStyleHelper::isInstanceOf(option->styleObject, QAccessible::StaticText) || #else if ( #endif - (widget && widget->inherits("QTextEdit"))) { + (widget && widget->inherits("QTextEdit"))) { painter->save(); int stateId = ETS_NORMAL; if (!(state & State_Enabled)) @@ -481,9 +1649,9 @@ void QWindowsVistaStyle::drawPrimitive(PrimitiveElement element, const QStyleOpt stateId = ETS_READONLY; else if (state & State_HasFocus) stateId = ETS_SELECTED; - XPThemeData theme(widget, painter, - QWindowsXPStylePrivate::EditTheme, - EP_EDITBORDER_HVSCROLL, stateId, option->rect); + QWindowsThemeData theme(widget, painter, + QWindowsVistaStylePrivate::EditTheme, + EP_EDITBORDER_HVSCROLL, stateId, option->rect); // Since EP_EDITBORDER_HVSCROLL does not us borderfill, theme.noContent cannot be used for clipping int borderSize = 1; GetThemeInt(theme.handle(), theme.partId, theme.stateId, TMT_BORDERSIZE, &borderSize); @@ -493,36 +1661,222 @@ void QWindowsVistaStyle::drawPrimitive(PrimitiveElement element, const QStyleOpt painter->setClipRegion(clipRegion); d->drawBackground(theme); painter->restore(); + return; } else { - QWindowsXPStyle::drawPrimitive(element, option, painter, widget); + if (option->state & State_Raised) + return; + + themeNumber = QWindowsVistaStylePrivate::ListViewTheme; + partId = LVP_LISTGROUP; + QWindowsThemeData theme(widget, nullptr, themeNumber, partId); + + if (!(option->state & State_Enabled)) + stateId = ETS_DISABLED; + else + stateId = ETS_NORMAL; + + int fillType; + + if (GetThemeEnumValue(theme.handle(), partId, stateId, TMT_BGTYPE, &fillType) == S_OK) { + if (fillType == BT_BORDERFILL) { + COLORREF bcRef; + GetThemeColor(theme.handle(), partId, stateId, TMT_BORDERCOLOR, &bcRef); + QColor bordercolor(qRgb(GetRValue(bcRef), GetGValue(bcRef), GetBValue(bcRef))); + QPen oldPen = painter->pen(); + + // Inner white border + painter->setPen(QPen(option->palette.base().color(), 0)); + const qreal dpi = QStyleHelper::dpi(option); + const auto topLevelAdjustment = QStyleHelper::dpiScaled(0.5, dpi); + const auto bottomRightAdjustment = QStyleHelper::dpiScaled(-1, dpi); + painter->drawRect(QRectF(option->rect).adjusted(topLevelAdjustment, topLevelAdjustment, + bottomRightAdjustment, bottomRightAdjustment)); + // Outer dark border + painter->setPen(QPen(bordercolor, 0)); + painter->drawRect(QRectF(option->rect).adjusted(0, 0, -topLevelAdjustment, -topLevelAdjustment)); + painter->setPen(oldPen); + } + + if (fillType == BT_BORDERFILL || fillType == BT_NONE) + return; + } } + break; + + case PE_FrameMenu: { + int stateId = option->state & State_Active ? MB_ACTIVE : MB_INACTIVE; + QWindowsThemeData theme(widget, painter, + QWindowsVistaStylePrivate::MenuTheme, + MENU_POPUPBORDERS, stateId, option->rect); + d->drawBackground(theme); + return; } - break; + + case PE_PanelMenuBar: + break; + +#if QT_CONFIG(dockwidget) + case PE_IndicatorDockWidgetResizeHandle: + return; + + case PE_FrameDockWidget: + if (const auto *frm = qstyleoption_cast(option)) { + themeNumber = QWindowsVistaStylePrivate::WindowTheme; + if (option->state & State_Active) + stateId = FS_ACTIVE; + else + stateId = FS_INACTIVE; + + int fwidth = proxy()->pixelMetric(PM_DockWidgetFrameWidth, frm, widget); + + QWindowsThemeData theme(widget, painter, themeNumber, 0, stateId); + + if (!theme.isValid()) + break; + + theme.rect = QRect(frm->rect.x(), frm->rect.y(), frm->rect.x()+fwidth, frm->rect.height()-fwidth); + theme.partId = WP_SMALLFRAMELEFT; + d->drawBackground(theme); + theme.rect = QRect(frm->rect.width()-fwidth, frm->rect.y(), fwidth, frm->rect.height()-fwidth); + theme.partId = WP_SMALLFRAMERIGHT; + d->drawBackground(theme); + theme.rect = QRect(frm->rect.x(), frm->rect.bottom()-fwidth+1, frm->rect.width(), fwidth); + theme.partId = WP_SMALLFRAMEBOTTOM; + d->drawBackground(theme); + return; + } + break; +#endif // QT_CONFIG(dockwidget) + + case PE_FrameTabWidget: + if (const auto *tab = qstyleoption_cast(option)) { + themeNumber = QWindowsVistaStylePrivate::TabTheme; + partId = TABP_PANE; + + if (widget) { + bool useGradient = true; + const int maxlength = 256; + wchar_t themeFileName[maxlength]; + wchar_t themeColor[maxlength]; + // Due to a a scaling issue with the XP Silver theme, tab gradients are not used with it + if (GetCurrentThemeName(themeFileName, maxlength, themeColor, maxlength, nullptr, 0) == S_OK) { + wchar_t *offset = nullptr; + if ((offset = wcsrchr(themeFileName, QChar(QLatin1Char('\\')).unicode())) != nullptr) { + offset++; + if (!lstrcmp(offset, L"Luna.msstyles") && !lstrcmp(offset, L"Metallic")) + useGradient = false; + } + } + // This should work, but currently there's an error in the ::drawBackgroundDirectly() + // code, when using the HDC directly.. + if (useGradient) { + QStyleOptionTabWidgetFrame frameOpt = *tab; + frameOpt.rect = widget->rect(); + QRect contentsRect = subElementRect(SE_TabWidgetTabContents, &frameOpt, widget); + QRegion reg = option->rect; + reg -= contentsRect; + painter->setClipRegion(reg); + QWindowsThemeData theme(widget, painter, themeNumber, partId, stateId, rect); + theme.mirrorHorizontally = hMirrored; + theme.mirrorVertically = vMirrored; + d->drawBackground(theme); + painter->setClipRect(contentsRect); + partId = TABP_BODY; + } + } + switch (tab->shape) { + case QTabBar::RoundedNorth: + case QTabBar::TriangularNorth: + break; + case QTabBar::RoundedSouth: + case QTabBar::TriangularSouth: + vMirrored = true; + break; + case QTabBar::RoundedEast: + case QTabBar::TriangularEast: + rotate = 90; + break; + case QTabBar::RoundedWest: + case QTabBar::TriangularWest: + rotate = 90; + hMirrored = true; + break; + default: + break; + } + } + break; + + case PE_FrameStatusBarItem: + themeNumber = QWindowsVistaStylePrivate::StatusTheme; + partId = SP_PANE; + break; + + case PE_FrameWindow: + if (const auto *frm = qstyleoption_cast(option)) { + themeNumber = QWindowsVistaStylePrivate::WindowTheme; + if (option->state & State_Active) + stateId = FS_ACTIVE; + else + stateId = FS_INACTIVE; + + int fwidth = int((frm->lineWidth + frm->midLineWidth) / QWindowsStylePrivate::nativeMetricScaleFactor(widget)); + + QWindowsThemeData theme(widget, painter, themeNumber, 0, stateId); + if (!theme.isValid()) + break; + + // May fail due to too-large buffers for large widgets, fall back to Windows style. + theme.rect = QRect(option->rect.x(), option->rect.y()+fwidth, option->rect.x()+fwidth, option->rect.height()-fwidth); + theme.partId = WP_FRAMELEFT; + if (!d->drawBackground(theme)) { + QWindowsStyle::drawPrimitive(element, option, painter, widget); + return; + } + theme.rect = QRect(option->rect.width()-fwidth, option->rect.y()+fwidth, fwidth, option->rect.height()-fwidth); + theme.partId = WP_FRAMERIGHT; + if (!d->drawBackground(theme)) { + QWindowsStyle::drawPrimitive(element, option, painter, widget); + return; + } + theme.rect = QRect(option->rect.x(), option->rect.height()-fwidth, option->rect.width(), fwidth); + theme.partId = WP_FRAMEBOTTOM; + if (!d->drawBackground(theme)) { + QWindowsStyle::drawPrimitive(element, option, painter, widget); + return; + } + theme.rect = QRect(option->rect.x(), option->rect.y(), option->rect.width(), option->rect.y()+fwidth); + theme.partId = WP_CAPTION; + if (!d->drawBackground(theme)) + QWindowsStyle::drawPrimitive(element, option, painter, widget); + return; + } + break; case PE_PanelLineEdit: - if (const QStyleOptionFrame *panel = qstyleoption_cast(option)) { - bool isEnabled = option->state & State_Enabled; - if (QWindowsXPStylePrivate::isLineEditBaseColorSet(option, widget)) { + if (const auto *panel = qstyleoption_cast(option)) { + bool isEnabled = state & State_Enabled; + if (QWindowsVistaStylePrivate::isLineEditBaseColorSet(option, widget)) { painter->fillRect(panel->rect, panel->palette.brush(QPalette::Base)); } else { int partId = EP_BACKGROUND; int stateId = EBS_NORMAL; if (!isEnabled) stateId = EBS_DISABLED; - else if (state & State_ReadOnly) + else if (option->state & State_ReadOnly) stateId = EBS_READONLY; - else if (state & State_MouseOver) + else if (option->state & State_MouseOver) stateId = EBS_HOT; - XPThemeData theme(nullptr, painter, QWindowsXPStylePrivate::EditTheme, - partId, stateId, rect); + QWindowsThemeData theme(nullptr, painter, QWindowsVistaStylePrivate::EditTheme, + partId, stateId, rect); if (!theme.isValid()) { QWindowsStyle::drawPrimitive(element, option, painter, widget); return; } int bgType; GetThemeEnumValue(theme.handle(), partId, stateId, TMT_BGTYPE, &bgType); - if ( bgType == BT_IMAGEFILE ) { + if (bgType == BT_IMAGEFILE) { d->drawBackground(theme); } else { QBrush fillColor = option->palette.brush(QPalette::Base); @@ -541,25 +1895,42 @@ void QWindowsVistaStyle::drawPrimitive(PrimitiveElement element, const QStyleOpt } if (panel->lineWidth > 0) proxy()->drawPrimitive(PE_FrameLineEdit, panel, painter, widget); - return; } + return; + + case PE_IndicatorButtonDropDown: + themeNumber = QWindowsVistaStylePrivate::ToolBarTheme; + partId = TP_SPLITBUTTONDROPDOWN; + if (!(option->state & State_Enabled)) + stateId = TS_DISABLED; + else if (option->state & State_Sunken) + stateId = TS_PRESSED; + else if (option->state & State_MouseOver) + stateId = option->state & State_On ? TS_HOTCHECKED : TS_HOT; + else if (option->state & State_On) + stateId = TS_CHECKED; + else if (!(option->state & State_AutoRaise)) + stateId = TS_HOT; + else + stateId = TS_NORMAL; + if (option->direction == Qt::RightToLeft) + hMirrored = true; break; case PE_FrameLineEdit: - if (QWindowsVistaAnimation *anim = qobject_cast(d->animation(styleObject(option)))) { - anim->paint(painter, option); + if (QWindowsVistaAnimation *animate = qobject_cast(d->animation(styleObject(option)))) { + animate->paint(painter, option); } else { - QPainter *p = painter; - if (QWindowsXPStylePrivate::isItemViewDelegateLineEdit(widget)) { + if (QWindowsVistaStylePrivate::isItemViewDelegateLineEdit(widget)) { // we try to check if this lineedit is a delegate on a QAbstractItemView-derived class. - QPen oldPen = p->pen(); + QPen oldPen = painter->pen(); // Inner white border - p->setPen(QPen(option->palette.base().color(), 1)); - p->drawRect(option->rect.adjusted(1, 1, -2, -2)); + painter->setPen(QPen(option->palette.base().color(), 1)); + painter->drawRect(option->rect.adjusted(1, 1, -2, -2)); // Outer dark border - p->setPen(QPen(option->palette.shadow().color(), 1)); - p->drawRect(option->rect.adjusted(0, 0, -1, -1)); - p->setPen(oldPen); + painter->setPen(QPen(option->palette.shadow().color(), 1)); + painter->drawRect(option->rect.adjusted(0, 0, -1, -1)); + painter->setPen(oldPen); return; } int stateId = ETS_NORMAL; @@ -571,9 +1942,9 @@ void QWindowsVistaStyle::drawPrimitive(PrimitiveElement element, const QStyleOpt stateId = ETS_HOT; else if (state & State_HasFocus) stateId = ETS_SELECTED; - XPThemeData theme(widget, painter, - QWindowsXPStylePrivate::EditTheme, - EP_EDITBORDER_NOSCROLL, stateId, option->rect); + QWindowsThemeData theme(widget, painter, + QWindowsVistaStylePrivate::EditTheme, + EP_EDITBORDER_NOSCROLL, stateId, option->rect); theme.noContent = true; painter->save(); QRegion clipRegion = option->rect; @@ -582,206 +1953,367 @@ void QWindowsVistaStyle::drawPrimitive(PrimitiveElement element, const QStyleOpt d->drawBackground(theme); painter->restore(); } - break; + return; + + case PE_FrameGroupBox: + themeNumber = QWindowsVistaStylePrivate::ButtonTheme; + partId = BP_GROUPBOX; + if (!(option->state & State_Enabled)) + stateId = GBS_DISABLED; + else + stateId = GBS_NORMAL; + if (const auto *frame = qstyleoption_cast(option)) { + if (frame->features & QStyleOptionFrame::Flat) { + // Windows XP does not have a theme part for a flat GroupBox, paint it with the windows style + QRect fr = frame->rect; + QPoint p1(fr.x(), fr.y() + 1); + QPoint p2(fr.x() + fr.width(), p1.y() + 1); + rect = QRect(p1, p2); + themeNumber = -1; + } + } + break; + + case PE_IndicatorToolBarHandle: { + QWindowsThemeData theme; + QRect rect; + if (option->state & State_Horizontal) { + theme = QWindowsThemeData(widget, painter, + QWindowsVistaStylePrivate::RebarTheme, + RP_GRIPPER, ETS_NORMAL, option->rect.adjusted(0, 1, -2, -2)); + rect = option->rect.adjusted(0, 1, 0, -2); + rect.setWidth(4); + } else { + theme = QWindowsThemeData(widget, painter, QWindowsVistaStylePrivate::RebarTheme, + RP_GRIPPERVERT, ETS_NORMAL, option->rect.adjusted(0, 1, -2, -2)); + rect = option->rect.adjusted(1, 0, -1, 0); + rect.setHeight(4); + } + theme.rect = rect; + d->drawBackground(theme); + return; + } + + case PE_IndicatorToolBarSeparator: { + QPen pen = painter->pen(); + int margin = 3; + painter->setPen(option->palette.window().color().darker(114)); + if (option->state & State_Horizontal) { + int x1 = option->rect.center().x(); + painter->drawLine(QPoint(x1, option->rect.top() + margin), QPoint(x1, option->rect.bottom() - margin)); + } else { + int y1 = option->rect.center().y(); + painter->drawLine(QPoint(option->rect.left() + margin, y1), QPoint(option->rect.right() - margin, y1)); + } + painter->setPen(pen); + return; + } + + case PE_PanelTipLabel: { + QWindowsThemeData theme(widget, painter, + QWindowsVistaStylePrivate::ToolTipTheme, + TTP_STANDARD, TTSS_NORMAL, option->rect); + d->drawBackground(theme); + return; + } + + case PE_FrameTabBarBase: + if (const auto *tbb = qstyleoption_cast(option)) { + painter->save(); + switch (tbb->shape) { + case QTabBar::RoundedNorth: + painter->setPen(QPen(tbb->palette.dark(), 0)); + painter->drawLine(tbb->rect.topLeft(), tbb->rect.topRight()); + break; + case QTabBar::RoundedWest: + painter->setPen(QPen(tbb->palette.dark(), 0)); + painter->drawLine(tbb->rect.left(), tbb->rect.top(), tbb->rect.left(), tbb->rect.bottom()); + break; + case QTabBar::RoundedSouth: + painter->setPen(QPen(tbb->palette.dark(), 0)); + painter->drawLine(tbb->rect.left(), tbb->rect.top(), + tbb->rect.right(), tbb->rect.top()); + break; + case QTabBar::RoundedEast: + painter->setPen(QPen(tbb->palette.dark(), 0)); + painter->drawLine(tbb->rect.topLeft(), tbb->rect.bottomLeft()); + break; + case QTabBar::TriangularNorth: + case QTabBar::TriangularEast: + case QTabBar::TriangularWest: + case QTabBar::TriangularSouth: + painter->restore(); + QWindowsStyle::drawPrimitive(element, option, painter, widget); + return; + } + painter->restore(); + } + return; - case PE_IndicatorToolBarHandle: - { - XPThemeData theme; - QRect rect; - if (option->state & State_Horizontal) { - theme = XPThemeData(widget, painter, - QWindowsXPStylePrivate::RebarTheme, - RP_GRIPPER, ETS_NORMAL, option->rect.adjusted(0, 1, -2, -2)); - rect = option->rect.adjusted(0, 1, 0, -2); - rect.setWidth(4); - } else { - theme = XPThemeData(widget, painter, QWindowsXPStylePrivate::RebarTheme, - RP_GRIPPERVERT, ETS_NORMAL, option->rect.adjusted(0, 1, -2, -2)); - rect = option->rect.adjusted(1, 0, -1, 0); - rect.setHeight(4); - } - theme.rect = rect; + case PE_Widget: { +#if QT_CONFIG(dialogbuttonbox) + const QDialogButtonBox *buttonBox = nullptr; + if (qobject_cast (widget)) + buttonBox = widget->findChild(QLatin1String("qt_msgbox_buttonbox")); +#if QT_CONFIG(inputdialog) + else if (qobject_cast (widget)) + buttonBox = widget->findChild(QLatin1String("qt_inputdlg_buttonbox")); +#endif // QT_CONFIG(inputdialog) + if (buttonBox) { + //draw white panel part + QWindowsThemeData theme(widget, painter, + QWindowsVistaStylePrivate::TaskDialogTheme, + TDLG_PRIMARYPANEL, 0, option->rect); + QRect toprect = option->rect; + toprect.setBottom(buttonBox->geometry().top()); + theme.rect = toprect; d->drawBackground(theme); - } - break; - case PE_IndicatorToolBarSeparator: - { - QPen pen = painter->pen(); - int margin = 3; - painter->setPen(option->palette.window().color().darker(114)); - if (option->state & State_Horizontal) { - int x1 = option->rect.center().x(); - painter->drawLine(QPoint(x1, option->rect.top() + margin), QPoint(x1, option->rect.bottom() - margin)); - } else { - int y1 = option->rect.center().y(); - painter->drawLine(QPoint(option->rect.left() + margin, y1), QPoint(option->rect.right() - margin, y1)); - } - painter->setPen(pen); + //draw bottom panel part + QRect buttonRect = option->rect; + buttonRect.setTop(buttonBox->geometry().top()); + theme.rect = buttonRect; + theme.partId = TDLG_SECONDARYPANEL; + d->drawBackground(theme); } - break; - - case PE_PanelTipLabel: { - XPThemeData theme(widget, painter, - QWindowsXPStylePrivate::ToolTipTheme, - TTP_STANDARD, TTSS_NORMAL, option->rect); - d->drawBackground(theme); - break; +#endif + return; } - case PE_PanelItemViewItem: - { - const QStyleOptionViewItem *vopt; - bool newStyle = true; - QAbstractItemView::SelectionBehavior selectionBehavior = QAbstractItemView::SelectRows; - QAbstractItemView::SelectionMode selectionMode = QAbstractItemView::NoSelection; - if (const QAbstractItemView *view = qobject_cast(widget)) { - newStyle = !qobject_cast(view); - selectionBehavior = view->selectionBehavior(); - selectionMode = view->selectionMode(); + case PE_PanelItemViewItem: { + const QStyleOptionViewItem *vopt; + bool newStyle = true; + QAbstractItemView::SelectionBehavior selectionBehavior = QAbstractItemView::SelectRows; + QAbstractItemView::SelectionMode selectionMode = QAbstractItemView::NoSelection; + if (const QAbstractItemView *view = qobject_cast(widget)) { + newStyle = !qobject_cast(view); + selectionBehavior = view->selectionBehavior(); + selectionMode = view->selectionMode(); #if QT_CONFIG(accessibility) - } else if (!widget) { - newStyle = !QStyleHelper::hasAncestor(option->styleObject, QAccessible::MenuItem) ; + } else if (!widget) { + newStyle = !QStyleHelper::hasAncestor(option->styleObject, QAccessible::MenuItem) ; #endif - } + } - if (newStyle && (vopt = qstyleoption_cast(option))) { - bool selected = vopt->state & QStyle::State_Selected; - const bool hover = selectionMode != QAbstractItemView::NoSelection && (vopt->state & QStyle::State_MouseOver); - bool active = vopt->state & QStyle::State_Active; + if (newStyle && (vopt = qstyleoption_cast(option))) { + bool selected = vopt->state & QStyle::State_Selected; + const bool hover = selectionMode != QAbstractItemView::NoSelection && (vopt->state & QStyle::State_MouseOver); + bool active = vopt->state & QStyle::State_Active; - if (vopt->features & QStyleOptionViewItem::Alternate) - painter->fillRect(vopt->rect, vopt->palette.alternateBase()); + if (vopt->features & QStyleOptionViewItem::Alternate) + painter->fillRect(vopt->rect, vopt->palette.alternateBase()); - QPalette::ColorGroup cg = vopt->state & QStyle::State_Enabled - ? QPalette::Normal : QPalette::Disabled; - if (cg == QPalette::Normal && !(vopt->state & QStyle::State_Active)) - cg = QPalette::Inactive; + QPalette::ColorGroup cg = vopt->state & QStyle::State_Enabled + ? QPalette::Normal : QPalette::Disabled; + if (cg == QPalette::Normal && !(vopt->state & QStyle::State_Active)) + cg = QPalette::Inactive; - QRect itemRect = subElementRect(QStyle::SE_ItemViewItemFocusRect, option, widget).adjusted(-1, 0, 1, 0); - itemRect.setTop(vopt->rect.top()); - itemRect.setBottom(vopt->rect.bottom()); + QRect itemRect = subElementRect(QStyle::SE_ItemViewItemFocusRect, option, widget).adjusted(-1, 0, 1, 0); + itemRect.setTop(vopt->rect.top()); + itemRect.setBottom(vopt->rect.bottom()); - QSize sectionSize = itemRect.size(); - if (vopt->showDecorationSelected) - sectionSize = vopt->rect.size(); + QSize sectionSize = itemRect.size(); + if (vopt->showDecorationSelected) + sectionSize = vopt->rect.size(); - if (selectionBehavior == QAbstractItemView::SelectRows) - sectionSize.setWidth(vopt->rect.width()); - QPixmap pixmap; + if (selectionBehavior == QAbstractItemView::SelectRows) + sectionSize.setWidth(vopt->rect.width()); + QPixmap pixmap; - if (vopt->backgroundBrush.style() != Qt::NoBrush) { - const QPointF oldBrushOrigin = painter->brushOrigin(); - painter->setBrushOrigin(vopt->rect.topLeft()); - painter->fillRect(vopt->rect, vopt->backgroundBrush); - painter->setBrushOrigin(oldBrushOrigin); - } + if (vopt->backgroundBrush.style() != Qt::NoBrush) { + const QPointF oldBrushOrigin = painter->brushOrigin(); + painter->setBrushOrigin(vopt->rect.topLeft()); + painter->fillRect(vopt->rect, vopt->backgroundBrush); + painter->setBrushOrigin(oldBrushOrigin); + } - if (hover || selected) { - if (sectionSize.width() > 0 && sectionSize.height() > 0) { - QString key = QString::fromLatin1("qvdelegate-%1-%2-%3-%4-%5").arg(sectionSize.width()) - .arg(sectionSize.height()).arg(selected).arg(active).arg(hover); - if (!QPixmapCache::find(key, &pixmap)) { - pixmap = QPixmap(sectionSize); - pixmap.fill(Qt::transparent); - - int state; - if (selected && hover) - state = LISS_HOTSELECTED; - else if (selected && !active) - state = LISS_SELECTEDNOTFOCUS; - else if (selected) - state = LISS_SELECTED; - else - state = LISS_HOT; - - QPainter pixmapPainter(&pixmap); - XPThemeData theme(widget, &pixmapPainter, - QWindowsXPStylePrivate::VistaTreeViewTheme, - LVP_LISTITEM, state, QRect(0, 0, sectionSize.width(), sectionSize.height())); - if (theme.isValid()) { - d->drawBackground(theme); - } else { - QWindowsXPStyle::drawPrimitive(PE_PanelItemViewItem, option, painter, widget); - break; - } - QPixmapCache::insert(key, pixmap); - } - } + if (hover || selected) { + if (sectionSize.width() > 0 && sectionSize.height() > 0) { + QString key = QString::fromLatin1("qvdelegate-%1-%2-%3-%4-%5").arg(sectionSize.width()) + .arg(sectionSize.height()).arg(selected).arg(active).arg(hover); + if (!QPixmapCache::find(key, &pixmap)) { + pixmap = QPixmap(sectionSize); + pixmap.fill(Qt::transparent); + + int state; + if (selected && hover) + state = LISS_HOTSELECTED; + else if (selected && !active) + state = LISS_SELECTEDNOTFOCUS; + else if (selected) + state = LISS_SELECTED; + else + state = LISS_HOT; - if (vopt->showDecorationSelected) { - const int frame = 2; //Assumes a 2 pixel pixmap border - QRect srcRect = QRect(0, 0, sectionSize.width(), sectionSize.height()); - QRect pixmapRect = vopt->rect; - bool reverse = vopt->direction == Qt::RightToLeft; - bool leftSection = vopt->viewItemPosition == QStyleOptionViewItem::Beginning; - bool rightSection = vopt->viewItemPosition == QStyleOptionViewItem::End; - if (vopt->viewItemPosition == QStyleOptionViewItem::OnlyOne - || vopt->viewItemPosition == QStyleOptionViewItem::Invalid) - painter->drawPixmap(pixmapRect.topLeft(), pixmap); - else if (reverse ? rightSection : leftSection){ - painter->drawPixmap(QRect(pixmapRect.topLeft(), - QSize(frame, pixmapRect.height())), pixmap, - QRect(QPoint(0, 0), QSize(frame, pixmapRect.height()))); - painter->drawPixmap(pixmapRect.adjusted(frame, 0, 0, 0), - pixmap, srcRect.adjusted(frame, 0, -frame, 0)); - } else if (reverse ? leftSection : rightSection) { - painter->drawPixmap(QRect(pixmapRect.topRight() - QPoint(frame - 1, 0), - QSize(frame, pixmapRect.height())), pixmap, - QRect(QPoint(pixmapRect.width() - frame, 0), - QSize(frame, pixmapRect.height()))); - painter->drawPixmap(pixmapRect.adjusted(0, 0, -frame, 0), - pixmap, srcRect.adjusted(frame, 0, -frame, 0)); - } else if (vopt->viewItemPosition == QStyleOptionViewItem::Middle) - painter->drawPixmap(pixmapRect, pixmap, - srcRect.adjusted(frame, 0, -frame, 0)); - } else { - if (vopt->text.isEmpty() && vopt->icon.isNull()) + QPainter pixmapPainter(&pixmap); + + QWindowsThemeData theme(widget, &pixmapPainter, + QWindowsVistaStylePrivate::VistaTreeViewTheme, + LVP_LISTITEM, state, QRect(0, 0, sectionSize.width(), sectionSize.height())); + + if (!theme.isValid()) break; - painter->drawPixmap(itemRect.topLeft(), pixmap); + + d->drawBackground(theme); + QPixmapCache::insert(key, pixmap); } } - } else { - QWindowsXPStyle::drawPrimitive(element, option, painter, widget); + + if (vopt->showDecorationSelected) { + const int frame = 2; //Assumes a 2 pixel pixmap border + QRect srcRect = QRect(0, 0, sectionSize.width(), sectionSize.height()); + QRect pixmapRect = vopt->rect; + bool reverse = vopt->direction == Qt::RightToLeft; + bool leftSection = vopt->viewItemPosition == QStyleOptionViewItem::Beginning; + bool rightSection = vopt->viewItemPosition == QStyleOptionViewItem::End; + if (vopt->viewItemPosition == QStyleOptionViewItem::OnlyOne + || vopt->viewItemPosition == QStyleOptionViewItem::Invalid) + painter->drawPixmap(pixmapRect.topLeft(), pixmap); + else if (reverse ? rightSection : leftSection){ + painter->drawPixmap(QRect(pixmapRect.topLeft(), + QSize(frame, pixmapRect.height())), pixmap, + QRect(QPoint(0, 0), QSize(frame, pixmapRect.height()))); + painter->drawPixmap(pixmapRect.adjusted(frame, 0, 0, 0), + pixmap, srcRect.adjusted(frame, 0, -frame, 0)); + } else if (reverse ? leftSection : rightSection) { + painter->drawPixmap(QRect(pixmapRect.topRight() - QPoint(frame - 1, 0), + QSize(frame, pixmapRect.height())), pixmap, + QRect(QPoint(pixmapRect.width() - frame, 0), + QSize(frame, pixmapRect.height()))); + painter->drawPixmap(pixmapRect.adjusted(0, 0, -frame, 0), + pixmap, srcRect.adjusted(frame, 0, -frame, 0)); + } else if (vopt->viewItemPosition == QStyleOptionViewItem::Middle) + painter->drawPixmap(pixmapRect, pixmap, + srcRect.adjusted(frame, 0, -frame, 0)); + } else { + if (vopt->text.isEmpty() && vopt->icon.isNull()) + break; + painter->drawPixmap(itemRect.topLeft(), pixmap); + } } - break; + return; } - case PE_Widget: - { -#if QT_CONFIG(dialogbuttonbox) - const QDialogButtonBox *buttonBox = nullptr; + break; + } - if (qobject_cast (widget)) - buttonBox = widget->findChild(QLatin1String("qt_msgbox_buttonbox")); -#if QT_CONFIG(inputdialog) - else if (qobject_cast (widget)) - buttonBox = widget->findChild(QLatin1String("qt_inputdlg_buttonbox")); -#endif // QT_CONFIG(inputdialog) + default: + break; + } - if (buttonBox) { - //draw white panel part - XPThemeData theme(widget, painter, - QWindowsXPStylePrivate::TaskDialogTheme, - TDLG_PRIMARYPANEL, 0, option->rect); - QRect toprect = option->rect; - toprect.setBottom(buttonBox->geometry().top()); - theme.rect = toprect; - d->drawBackground(theme); + QWindowsThemeData theme(widget, painter, themeNumber, partId, stateId, rect); - //draw bottom panel part - QRect buttonRect = option->rect; - buttonRect.setTop(buttonBox->geometry().top()); - theme.rect = buttonRect; - theme.partId = TDLG_SECONDARYPANEL; - d->drawBackground(theme); + if (!theme.isValid()) { + QWindowsStyle::drawPrimitive(element, option, painter, widget); + return; + } + + theme.mirrorHorizontally = hMirrored; + theme.mirrorVertically = vMirrored; + theme.noBorder = noBorder; + theme.noContent = noContent; + theme.rotate = rotate; + + d->drawBackground(theme); +} + +/*! \internal */ +int QWindowsVistaStyle::styleHint(StyleHint hint, const QStyleOption *option, const QWidget *widget, + QStyleHintReturn *returnData) const +{ + QWindowsVistaStylePrivate *d = const_cast(d_func()); + + int ret = 0; + switch (hint) { + case SH_EtchDisabledText: + ret = (qobject_cast(widget) != 0); + break; + + case SH_SpinControls_DisableOnBounds: + ret = 0; + break; + + case SH_TitleBar_AutoRaise: + case SH_TitleBar_NoBorder: + ret = 1; + break; + + case SH_GroupBox_TextLabelColor: + if (!widget || widget->isEnabled()) + ret = d->groupBoxTextColor; + else + ret = d->groupBoxTextColorDisabled; + break; + + case SH_WindowFrame_Mask: { + ret = 1; + auto *mask = qstyleoption_cast(returnData); + const auto *titlebar = qstyleoption_cast(option); + if (mask && titlebar) { + // Note certain themes will not return the whole window frame but only the titlebar part when + // queried This function needs to return the entire window mask, hence we will only fetch the mask for the + // titlebar itself and add the remaining part of the window rect at the bottom. + int tbHeight = proxy()->pixelMetric(PM_TitleBarHeight, option, widget); + QRect titleBarRect = option->rect; + titleBarRect.setHeight(tbHeight); + QWindowsThemeData themeData; + if (titlebar->titleBarState & Qt::WindowMinimized) { + themeData = QWindowsThemeData(widget, nullptr, + QWindowsVistaStylePrivate::WindowTheme, + WP_MINCAPTION, CS_ACTIVE, titleBarRect); + } else + themeData = QWindowsThemeData(widget, nullptr, + QWindowsVistaStylePrivate::WindowTheme, + WP_CAPTION, CS_ACTIVE, titleBarRect); + mask->region = d->region(themeData) + + QRect(0, tbHeight, option->rect.width(), option->rect.height() - tbHeight); + } + break; + } + +#if QT_CONFIG(rubberband) + case SH_RubberBand_Mask: + if (qstyleoption_cast(option)) + ret = 0; + break; +#endif // QT_CONFIG(rubberband) + + case SH_MessageBox_CenterButtons: + ret = false; + break; + + case SH_ToolTip_Mask: + if (option) { + if (QStyleHintReturnMask *mask = qstyleoption_cast(returnData)) { + ret = true; + QWindowsThemeData themeData(widget, nullptr, + QWindowsVistaStylePrivate::ToolTipTheme, + TTP_STANDARD, TTSS_NORMAL, option->rect); + mask->region = d->region(themeData); } -#endif } break; + + case SH_Table_GridLineColor: + if (option) + ret = int(option->palette.color(QPalette::Base).darker(118).rgba()); + else + ret = -1; + break; + + case SH_Header_ArrowAlignment: + ret = Qt::AlignTop | Qt::AlignHCenter; + break; + + case SH_ItemView_DrawDelegateFrame: + ret = 1; + break; + default: - QWindowsXPStyle::drawPrimitive(element, option, painter, widget); + ret = QWindowsStyle::styleHint(hint, option, widget, returnData); break; } + + return ret; } @@ -791,15 +2323,15 @@ void QWindowsVistaStyle::drawPrimitive(PrimitiveElement element, const QStyleOpt see drawPrimitive for comments on the animation support */ void QWindowsVistaStyle::drawControl(ControlElement element, const QStyleOption *option, - QPainter *painter, const QWidget *widget) const + QPainter *painter, const QWidget *widget) const { - QWindowsVistaStylePrivate *d = const_cast(d_func()); - if (!QWindowsVistaStylePrivate::useVista()) { QWindowsStyle::drawControl(element, option, painter, widget); return; } + QWindowsVistaStylePrivate *d = const_cast(d_func()); + bool selected = option->state & State_Selected; bool pressed = option->state & State_Sunken; bool disabled = !(option->state & State_Enabled); @@ -812,8 +2344,7 @@ void QWindowsVistaStyle::drawControl(ControlElement element, const QStyleOption int partId = 0; int stateId = 0; - if (d->transitionsEnabled() && canAnimate(option)) - { + if (d->transitionsEnabled() && canAnimate(option)) { if (element == CE_PushButtonBevel) { QRect oldRect; QRect newRect; @@ -894,11 +2425,15 @@ void QWindowsVistaStyle::drawControl(ControlElement element, const QStyleOption } } + + bool hMirrored = false; + bool vMirrored = false; + int rotate = 0; + switch (element) { case CE_PushButtonBevel: - if (const QStyleOptionButton *btn = qstyleoption_cast(option)) - { - themeNumber = QWindowsXPStylePrivate::ButtonTheme; + if (const QStyleOptionButton *btn = qstyleoption_cast(option)) { + themeNumber = QWindowsVistaStylePrivate::ButtonTheme; partId = BP_PUSHBUTTON; if (btn->features & QStyleOptionButton::CommandLinkButton) partId = BP_COMMANDLINK; @@ -932,13 +2467,14 @@ void QWindowsVistaStyle::drawControl(ControlElement element, const QStyleOption QPainter startPainter(&startImage); stateId = PBS_DEFAULTED; - XPThemeData theme(widget, &startPainter, themeNumber, partId, stateId, rect); + QWindowsThemeData theme(widget, &startPainter, themeNumber, partId, stateId, rect); d->drawBackground(theme); QPainter alternatePainter(&alternateImage); theme.stateId = PBS_DEFAULTED_ANIMATING; theme.painter = &alternatePainter; d->drawBackground(theme); + pulse->setStartImage(startImage); pulse->setEndImage(alternateImage); pulse->setStartTime(d->animationTime()); @@ -950,20 +2486,20 @@ void QWindowsVistaStyle::drawControl(ControlElement element, const QStyleOption if (anim) anim->paint(painter, option); else { - XPThemeData theme(widget, painter, themeNumber, partId, stateId, rect); + QWindowsThemeData theme(widget, painter, themeNumber, partId, stateId, rect); d->drawBackground(theme); } } else { - XPThemeData theme(widget, painter, themeNumber, partId, stateId, rect); + QWindowsThemeData theme(widget, painter, themeNumber, partId, stateId, rect); d->drawBackground(theme); } } if (btn->features & QStyleOptionButton::HasMenu) { int mbiw = 0, mbih = 0; - XPThemeData theme(widget, nullptr, QWindowsXPStylePrivate::ToolBarTheme, - TP_DROPDOWNBUTTON); + QWindowsThemeData theme(widget, nullptr, QWindowsVistaStylePrivate::ToolBarTheme, + TP_DROPDOWNBUTTON); if (theme.isValid()) { const QSizeF size = theme.size() * QStyleHelper::dpiScaled(1, option); if (!size.isEmpty()) { @@ -974,18 +2510,168 @@ void QWindowsVistaStyle::drawControl(ControlElement element, const QStyleOption QRect ir = subElementRect(SE_PushButtonContents, option, nullptr); QStyleOptionButton newBtn = *btn; newBtn.rect = QStyle::visualRect(option->direction, option->rect, - QRect(ir.right() - mbiw - 2, - option->rect.top() + (option->rect.height()/2) - (mbih/2), - mbiw + 1, mbih + 1)); + QRect(ir.right() - mbiw - 2, + option->rect.top() + (option->rect.height()/2) - (mbih/2), + mbiw + 1, mbih + 1)); proxy()->drawPrimitive(PE_IndicatorArrowDown, &newBtn, painter, widget); } - return; } + return; + + case CE_SizeGrip: { + themeNumber = QWindowsVistaStylePrivate::StatusTheme; + partId = SP_GRIPPER; + QWindowsThemeData theme(nullptr, painter, themeNumber, partId); + QSize size = (theme.size() * QWindowsStylePrivate::nativeMetricScaleFactor(widget)).toSize(); + size.rheight()--; + if (const auto *sg = qstyleoption_cast(option)) { + switch (sg->corner) { + case Qt::BottomRightCorner: + rect = QRect(QPoint(rect.right() - size.width(), rect.bottom() - size.height()), size); + break; + case Qt::BottomLeftCorner: + rect = QRect(QPoint(rect.left() + 1, rect.bottom() - size.height()), size); + hMirrored = true; + break; + case Qt::TopRightCorner: + rect = QRect(QPoint(rect.right() - size.width(), rect.top() + 1), size); + vMirrored = true; + break; + case Qt::TopLeftCorner: + rect = QRect(rect.topLeft() + QPoint(1, 1), size); + hMirrored = vMirrored = true; + } + } + break; + } + + case CE_Splitter: + painter->eraseRect(option->rect); + return; + + case CE_TabBarTab: + if (const auto *tab = qstyleoption_cast(option)) + stateId = tab->state & State_Enabled ? TIS_NORMAL : TIS_DISABLED; + break; + + case CE_TabBarTabShape: + if (const auto *tab = qstyleoption_cast(option)) { + themeNumber = QWindowsVistaStylePrivate::TabTheme; + const bool isDisabled = !(tab->state & State_Enabled); + const bool hasFocus = tab->state & State_HasFocus; + const bool isHot = tab->state & State_MouseOver; + const bool selected = tab->state & State_Selected; + bool lastTab = tab->position == QStyleOptionTab::End; + bool firstTab = tab->position == QStyleOptionTab::Beginning; + const bool onlyOne = tab->position == QStyleOptionTab::OnlyOneTab; + const bool leftAligned = proxy()->styleHint(SH_TabBar_Alignment, tab, widget) == Qt::AlignLeft; + const bool centerAligned = proxy()->styleHint(SH_TabBar_Alignment, tab, widget) == Qt::AlignCenter; + const int borderThickness = proxy()->pixelMetric(PM_DefaultFrameWidth, option, widget); + const int tabOverlap = proxy()->pixelMetric(PM_TabBarTabOverlap, option, widget); + + if (isDisabled) + stateId = TIS_DISABLED; + else if (selected) + stateId = TIS_SELECTED; + else if (hasFocus) + stateId = TIS_FOCUSED; + else if (isHot) + stateId = TIS_HOT; + else + stateId = TIS_NORMAL; + + // Selecting proper part depending on position + if (firstTab || onlyOne) { + if (leftAligned) + partId = TABP_TABITEMLEFTEDGE; + else if (centerAligned) + partId = TABP_TABITEM; + else // rightAligned + partId = TABP_TABITEMRIGHTEDGE; + } else { + partId = TABP_TABITEM; + } + + if (tab->direction == Qt::RightToLeft + && (tab->shape == QTabBar::RoundedNorth || tab->shape == QTabBar::RoundedSouth)) { + bool temp = firstTab; + firstTab = lastTab; + lastTab = temp; + } + + const bool begin = firstTab || onlyOne; + const bool end = lastTab || onlyOne; + + switch (tab->shape) { + case QTabBar::RoundedNorth: + if (selected) + rect.adjust(begin ? 0 : -tabOverlap, 0, end ? 0 : tabOverlap, borderThickness); + else + rect.adjust(begin? tabOverlap : 0, tabOverlap, end ? -tabOverlap : 0, 0); + break; + case QTabBar::RoundedSouth: + //vMirrored = true; + rotate = 180; // Not 100% correct, but works + if (selected) + rect.adjust(begin ? 0 : -tabOverlap , -borderThickness, end ? 0 : tabOverlap, 0); + else + rect.adjust(begin ? tabOverlap : 0, 0, end ? -tabOverlap : 0 , -tabOverlap); + break; + case QTabBar::RoundedEast: + rotate = 90; + if (selected) + rect.adjust(-borderThickness, begin ? 0 : -tabOverlap, 0, end ? 0 : tabOverlap); + else + rect.adjust(0, begin ? tabOverlap : 0, -tabOverlap, end ? -tabOverlap : 0); + break; + case QTabBar::RoundedWest: + hMirrored = true; + rotate = 90; + if (selected) + rect.adjust(0, begin ? 0 : -tabOverlap, borderThickness, end ? 0 : tabOverlap); + else + rect.adjust(tabOverlap, begin ? tabOverlap : 0, 0, end ? -tabOverlap : 0); + break; + default: + themeNumber = -1; // Do our own painting for triangular + break; + } + + if (!selected) { + switch (tab->shape) { + case QTabBar::RoundedNorth: + rect.adjust(0,0, 0,-1); + break; + case QTabBar::RoundedSouth: + rect.adjust(0,1, 0,0); + break; + case QTabBar::RoundedEast: + rect.adjust( 1,0, 0,0); + break; + case QTabBar::RoundedWest: + rect.adjust(0,0, -1,0); + break; + default: + break; + } + } + } + break; + + case CE_ProgressBarGroove: { + Qt::Orientation orient = Qt::Horizontal; + if (const auto *pb = qstyleoption_cast(option)) + if (!(pb->state & QStyle::State_Horizontal)) + orient = Qt::Vertical; + + partId = (orient == Qt::Horizontal) ? PP_BAR : PP_BARVERT; + themeNumber = QWindowsVistaStylePrivate::ProgressTheme; + stateId = 1; break; + } case CE_ProgressBarContents: - if (const QStyleOptionProgressBar *bar - = qstyleoption_cast(option)) { + if (const auto *bar = qstyleoption_cast(option)) { bool isIndeterminate = (bar->minimum == 0 && bar->maximum == 0); const bool vertical = !(bar->state & QStyle::State_Horizontal); const bool inverted = bar->invertedAppearance; @@ -997,20 +2683,20 @@ void QWindowsVistaStyle::drawControl(ControlElement element, const QStyleOption d->stopAnimation(styleObject(option)); } - XPThemeData theme(widget, painter, - QWindowsXPStylePrivate::ProgressTheme, - vertical ? PP_FILLVERT : PP_FILL); + QWindowsThemeData theme(widget, painter, + QWindowsVistaStylePrivate::ProgressTheme, + vertical ? PP_FILLVERT : PP_FILL); theme.rect = option->rect; bool reverse = (bar->direction == Qt::LeftToRight && inverted) || (bar->direction == Qt::RightToLeft && !inverted); QTime current = d->animationTime(); if (isIndeterminate) { - if (QProgressStyleAnimation *a = qobject_cast(d->animation(styleObject(option)))) { + if (auto *progressAnimation = qobject_cast(d->animation(styleObject(option)))) { int glowSize = 120; int animationWidth = glowSize * 2 + (vertical ? theme.rect.height() : theme.rect.width()); - int animOffset = a->startTime().msecsTo(current) / 4; + int animOffset = progressAnimation->startTime().msecsTo(current) / 4; if (animOffset > animationWidth) - a->setStartTime(d->animationTime()); + progressAnimation->setStartTime(d->animationTime()); painter->save(); painter->setClipRect(theme.rect); QRect animRect; @@ -1020,12 +2706,12 @@ void QWindowsVistaStyle::drawControl(ControlElement element, const QStyleOption inverted ? rect.top() - glowSize + animOffset : rect.bottom() + glowSize - animOffset, rect.width(), glowSize); - pixmapSize.setHeight(animRect.height()); + pixmapSize.setHeight(animRect.height()); } else { animRect = QRect(rect.left() - glowSize + animOffset, rect.top(), glowSize, rect.height()); animRect = QStyle::visualRect(reverse ? Qt::RightToLeft : Qt::LeftToRight, - option->rect, animRect); + option->rect, animRect); pixmapSize.setWidth(animRect.width()); } QString name = QString::fromLatin1("qiprogress-%1-%2").arg(pixmapSize.width()).arg(pixmapSize.height()); @@ -1052,8 +2738,7 @@ void QWindowsVistaStyle::drawControl(ControlElement element, const QStyleOption painter->drawPixmap(animRect, pixmap); painter->restore(); } - } - else { + } else { qint64 progress = qMax(bar->progress, bar->minimum); // workaround for bug in QProgressBar if (vertical) { @@ -1071,7 +2756,7 @@ void QWindowsVistaStyle::drawControl(ControlElement element, const QStyleOption int width = isIndeterminate ? maxWidth : qMax(int(vc6_workaround), minWidth); theme.rect.setWidth(width); theme.rect = QStyle::visualRect(reverse ? Qt::RightToLeft : Qt::LeftToRight, - option->rect, theme.rect); + option->rect, theme.rect); } d->drawBackground(theme); @@ -1085,7 +2770,7 @@ void QWindowsVistaStyle::drawControl(ControlElement element, const QStyleOption a->setStartTime(d->animationTime()); else d->stopAnimation(styleObject(option)); //we stop the glow motion only after it has - //moved out of view + //moved out of view } painter->save(); painter->setClipRect(theme.rect); @@ -1103,13 +2788,10 @@ void QWindowsVistaStyle::drawControl(ControlElement element, const QStyleOption } } } - break; + return; case CE_MenuBarItem: - { - - if (const QStyleOptionMenuItem *mbi = qstyleoption_cast(option)) - { + if (const auto *mbi = qstyleoption_cast(option)) { if (mbi->menuItemType == QStyleOptionMenuItem::DefaultItem) break; @@ -1122,43 +2804,52 @@ void QWindowsVistaStyle::drawControl(ControlElement element, const QStyleOption if (widget && mbi->palette.color(QPalette::Window) != Qt::transparent) { // Not needed for QtQuick Controls //The rect adjustment is a workaround for the menu not really filling its background. - XPThemeData theme(widget, painter, - QWindowsXPStylePrivate::MenuTheme, - MENU_BARBACKGROUND, 0, option->rect.adjusted(-1, 0, 2, 1)); + QWindowsThemeData theme(widget, painter, + QWindowsVistaStylePrivate::MenuTheme, + MENU_BARBACKGROUND, 0, option->rect.adjusted(-1, 0, 2, 1)); d->drawBackground(theme); - } - - int stateId = MBI_NORMAL; - if (disabled) - stateId = MBI_DISABLED; - else if (pressed) - stateId = MBI_PUSHED; - else if (selected) - stateId = MBI_HOT; - XPThemeData theme2(widget, painter, - QWindowsXPStylePrivate::MenuTheme, - MENU_BARITEM, stateId, option->rect); - d->drawBackground(theme2); + int stateId = MBI_NORMAL; + if (disabled) + stateId = MBI_DISABLED; + else if (pressed) + stateId = MBI_PUSHED; + else if (selected) + stateId = MBI_HOT; + + QWindowsThemeData theme2(widget, painter, + QWindowsVistaStylePrivate::MenuTheme, + MENU_BARITEM, stateId, option->rect); + d->drawBackground(theme2); + } if (!pix.isNull()) drawItemPixmap(painter, mbi->rect, alignment, pix); else drawItemText(painter, mbi->rect, alignment, mbi->palette, mbi->state & State_Enabled, mbi->text, textRole); } - } - break; + return; + #if QT_CONFIG(menu) + case CE_MenuEmptyArea: + if (const auto *menuitem = qstyleoption_cast(option)) { + QBrush fill = menuitem->palette.brush((menuitem->state & State_Selected) ? + QPalette::Highlight : QPalette::Button); + painter->fillRect(rect, fill); + break; + } + return; + case CE_MenuItem: - if (const QStyleOptionMenuItem *menuitem = qstyleoption_cast(option)) { + if (const auto *menuitem = qstyleoption_cast(option)) { // windows always has a check column, regardless whether we have an icon or not - const qreal factor = QWindowsXPStylePrivate::nativeMetricScaleFactor(widget); + const qreal factor = QWindowsVistaStylePrivate::nativeMetricScaleFactor(widget); int checkcol = qRound(qreal(25) * factor); const int gutterWidth = qRound(qreal(3) * factor); { - XPThemeData theme(widget, nullptr, QWindowsXPStylePrivate::MenuTheme, - MENU_POPUPCHECKBACKGROUND, MBI_HOT); - XPThemeData themeSize = theme; + QWindowsThemeData theme(widget, nullptr, QWindowsVistaStylePrivate::MenuTheme, + MENU_POPUPCHECKBACKGROUND, MBI_HOT); + QWindowsThemeData themeSize = theme; themeSize.partId = MENU_POPUPCHECK; themeSize.stateId = 0; const QSizeF size = themeSize.size() * QWindowsStylePrivate::nativeMetricScaleFactor(widget); @@ -1173,8 +2864,8 @@ void QWindowsVistaStyle::drawControl(ControlElement element, const QStyleOption QPoint p1 = QStyle::visualPos(option->direction, menuitem->rect, QPoint(checkcol, rect.top())); QPoint p2 = QStyle::visualPos(option->direction, menuitem->rect, QPoint(checkcol, rect.bottom())); QRect gutterRect(p1.x(), p1.y(), gutterWidth, p2.y() - p1.y() + 1); - XPThemeData theme2(widget, painter, QWindowsXPStylePrivate::MenuTheme, - MENU_POPUPGUTTER, stateId, gutterRect); + QWindowsThemeData theme2(widget, painter, QWindowsVistaStylePrivate::MenuTheme, + MENU_POPUPGUTTER, stateId, gutterRect); d->drawBackground(theme2); int x, y, w, h; @@ -1182,7 +2873,7 @@ void QWindowsVistaStyle::drawControl(ControlElement element, const QStyleOption int tab = menuitem->reservedShortcutWidth; bool dis = !(menuitem->state & State_Enabled); bool checked = menuitem->checkType != QStyleOptionMenuItem::NotCheckable - ? menuitem->checked : false; + ? menuitem->checked : false; bool act = menuitem->state & State_Selected; if (menuitem->menuItemType == QStyleOptionMenuItem::Separator) { @@ -1194,30 +2885,30 @@ void QWindowsVistaStyle::drawControl(ControlElement element, const QStyleOption QRect subRect(p1.x() + (gutterWidth - menuitem->rect.x()), p1.y(), p2.x() - p1.x(), separatorSize); subRect = QStyle::visualRect(option->direction, option->rect, subRect ); - XPThemeData theme2(widget, painter, - QWindowsXPStylePrivate::MenuTheme, - MENU_POPUPSEPARATOR, stateId, subRect); + QWindowsThemeData theme2(widget, painter, + QWindowsVistaStylePrivate::MenuTheme, + MENU_POPUPSEPARATOR, stateId, subRect); d->drawBackground(theme2); return; } QRect vCheckRect = visualRect(option->direction, menuitem->rect, QRect(menuitem->rect.x(), - menuitem->rect.y(), checkcol - (gutterWidth + menuitem->rect.x()), menuitem->rect.height())); + menuitem->rect.y(), checkcol - (gutterWidth + menuitem->rect.x()), menuitem->rect.height())); if (act) { stateId = dis ? MBI_DISABLED : MBI_HOT; - XPThemeData theme2(widget, painter, - QWindowsXPStylePrivate::MenuTheme, - MENU_POPUPITEM, stateId, option->rect); + QWindowsThemeData theme2(widget, painter, + QWindowsVistaStylePrivate::MenuTheme, + MENU_POPUPITEM, stateId, option->rect); d->drawBackground(theme2); } if (checked) { - XPThemeData theme(widget, painter, - QWindowsXPStylePrivate::MenuTheme, - MENU_POPUPCHECKBACKGROUND, - menuitem->icon.isNull() ? MBI_HOT : MBI_PUSHED, vCheckRect); - XPThemeData themeSize = theme; + QWindowsThemeData theme(widget, painter, + QWindowsVistaStylePrivate::MenuTheme, + MENU_POPUPCHECKBACKGROUND, + menuitem->icon.isNull() ? MBI_HOT : MBI_PUSHED, vCheckRect); + QWindowsThemeData themeSize = theme; themeSize.partId = MENU_POPUPCHECK; themeSize.stateId = 0; const QSizeF size = themeSize.size() * QWindowsStylePrivate::nativeMetricScaleFactor(widget); @@ -1279,7 +2970,7 @@ void QWindowsVistaStyle::drawControl(ControlElement element, const QStyleOption text_flags |= Qt::AlignLeft; if (t >= 0) { QRect vShortcutRect = visualRect(option->direction, menuitem->rect, - QRect(textRect.topRight(), QPoint(menuitem->rect.right(), textRect.bottom()))); + QRect(textRect.topRight(), QPoint(menuitem->rect.right(), textRect.bottom()))); painter->drawText(vShortcutRect, text_flags, s.mid(t + 1)); s = s.left(t); } @@ -1303,8 +2994,9 @@ void QWindowsVistaStyle::drawControl(ControlElement element, const QStyleOption proxy()->drawPrimitive(arrow, &newMI, painter, widget); } } - break; + return; #endif // QT_CONFIG(menu) + case CE_HeaderSection: if (const QStyleOptionHeader *header = qstyleoption_cast(option)) { partId = HP_HEADERITEM; @@ -1318,41 +3010,42 @@ void QWindowsVistaStyle::drawControl(ControlElement element, const QStyleOption if (header->sortIndicator != QStyleOptionHeader::None) stateId += 3; - XPThemeData theme(widget, painter, - QWindowsXPStylePrivate::HeaderTheme, - partId, stateId, option->rect); - d->drawBackground(theme); - } - break; - case CE_MenuBarEmptyArea: - { - stateId = MBI_NORMAL; - if (!(state & State_Enabled)) - stateId = MBI_DISABLED; - XPThemeData theme(widget, painter, - QWindowsXPStylePrivate::MenuTheme, - MENU_BARBACKGROUND, stateId, option->rect); + QWindowsThemeData theme(widget, painter, + QWindowsVistaStylePrivate::HeaderTheme, + partId, stateId, option->rect); d->drawBackground(theme); } - break; + return; + + case CE_MenuBarEmptyArea: { + stateId = MBI_NORMAL; + if (!(state & State_Enabled)) + stateId = MBI_DISABLED; + QWindowsThemeData theme(widget, painter, QWindowsVistaStylePrivate::MenuTheme, + MENU_BARBACKGROUND, stateId, option->rect); + d->drawBackground(theme); + return; + } + case CE_ToolBar: - if (const QStyleOptionToolBar *toolbar = qstyleoption_cast(option)) { + if (const auto *toolbar = qstyleoption_cast(option)) { QPalette pal = option->palette; pal.setColor(QPalette::Dark, option->palette.window().color().darker(130)); QStyleOptionToolBar copyOpt = *toolbar; copyOpt.palette = pal; QWindowsStyle::drawControl(element, ©Opt, painter, widget); } - break; + return; + #if QT_CONFIG(dockwidget) case CE_DockWidgetTitle: - if (const QStyleOptionDockWidget *dwOpt = qstyleoption_cast(option)) { - const QDockWidget *dockWidget = qobject_cast(widget); + if (const auto *dwOpt = qstyleoption_cast(option)) { QRect rect = option->rect; - if (dockWidget && dockWidget->isFloating()) { - QWindowsXPStyle::drawControl(element, option, painter, widget); - break; //otherwise fall through - } + const QDockWidget *dw = qobject_cast(widget); + bool isFloating = dw && dw->isFloating(); + int buttonMargin = 4; + int mw = proxy()->pixelMetric(QStyle::PM_DockWidgetTitleMargin, dwOpt, widget); + int fw = proxy()->pixelMetric(PM_DockWidgetFrameWidth, dwOpt, widget); const bool verticalTitleBar = dwOpt->verticalTitleBar; @@ -1364,16 +3057,6 @@ void QWindowsVistaStyle::drawControl(ControlElement element, const QStyleOption painter->translate(-rect.left() + 1, -rect.top()); } - painter->setBrush(option->palette.window().color().darker(110)); - painter->setPen(option->palette.window().color().darker(130)); - painter->drawRect(rect.adjusted(0, 1, -1, -3)); - - int buttonMargin = 4; - int mw = proxy()->pixelMetric(QStyle::PM_DockWidgetTitleMargin, dwOpt, widget); - int fw = proxy()->pixelMetric(PM_DockWidgetFrameWidth, dwOpt, widget); - const QDockWidget *dw = qobject_cast(widget); - bool isFloating = dw && dw->isFloating(); - QRect r = option->rect.adjusted(0, 2, -1, -3); QRect titleRect = r; @@ -1396,79 +3079,201 @@ void QWindowsVistaStyle::drawControl(ControlElement element, const QStyleOption if (!dwOpt->floatable && !dwOpt->closable) titleRect.adjust(0, 0, -mw, 0); } + if (!verticalTitleBar) titleRect = visualRect(dwOpt->direction, r, titleRect); - if (!dwOpt->title.isEmpty()) { - QString titleText = painter->fontMetrics().elidedText(dwOpt->title, Qt::ElideRight, - verticalTitleBar ? titleRect.height() : titleRect.width()); - const int indent = 4; - drawItemText(painter, rect.adjusted(indent + 1, 1, -indent - 1, -1), - Qt::AlignLeft | Qt::AlignVCenter | Qt::TextShowMnemonic, - dwOpt->palette, - dwOpt->state & State_Enabled, titleText, - QPalette::WindowText); + if (isFloating) { + const bool isActive = dwOpt->state & State_Active; + themeNumber = QWindowsVistaStylePrivate::WindowTheme; + if (isActive) + stateId = CS_ACTIVE; + else + stateId = CS_INACTIVE; + + int titleHeight = rect.height() - 2; + rect = rect.adjusted(-fw, -fw, fw, 0); + + QWindowsThemeData theme(widget, painter, themeNumber, 0, stateId); + if (!theme.isValid()) + break; + + // Draw small type title bar + theme.rect = rect; + theme.partId = WP_SMALLCAPTION; + d->drawBackground(theme); + + // Figure out maximal button space on title bar + + QIcon ico = widget->windowIcon(); + bool hasIcon = (ico.cacheKey() != QApplication::windowIcon().cacheKey()); + if (hasIcon) { + QPixmap pxIco = ico.pixmap(titleHeight); + if (!verticalTitleBar && dwOpt->direction == Qt::RightToLeft) + painter->drawPixmap(rect.width() - titleHeight - pxIco.width(), rect.bottom() - titleHeight - 2, pxIco); + else + painter->drawPixmap(fw, rect.bottom() - titleHeight - 2, pxIco); + } + if (!dwOpt->title.isEmpty()) { + QPen oldPen = painter->pen(); + QFont oldFont = painter->font(); + QFont titleFont = oldFont; + titleFont.setBold(true); + painter->setFont(titleFont); + QString titleText + = painter->fontMetrics().elidedText(dwOpt->title, Qt::ElideRight, titleRect.width()); + + int result = TST_NONE; + GetThemeEnumValue(theme.handle(), WP_SMALLCAPTION, isActive ? CS_ACTIVE : CS_INACTIVE, TMT_TEXTSHADOWTYPE, &result); + if (result != TST_NONE) { + COLORREF textShadowRef; + GetThemeColor(theme.handle(), WP_SMALLCAPTION, isActive ? CS_ACTIVE : CS_INACTIVE, TMT_TEXTSHADOWCOLOR, &textShadowRef); + QColor textShadow = qRgb(GetRValue(textShadowRef), GetGValue(textShadowRef), GetBValue(textShadowRef)); + painter->setPen(textShadow); + drawItemText(painter, titleRect.adjusted(1, 1, 1, 1), + Qt::AlignLeft | Qt::AlignBottom, dwOpt->palette, + dwOpt->state & State_Enabled, titleText); + } + + COLORREF captionText = GetSysColor(isActive ? COLOR_CAPTIONTEXT : COLOR_INACTIVECAPTIONTEXT); + QColor textColor = qRgb(GetRValue(captionText), GetGValue(captionText), GetBValue(captionText)); + painter->setPen(textColor); + drawItemText(painter, titleRect, + Qt::AlignLeft | Qt::AlignBottom, dwOpt->palette, + dwOpt->state & State_Enabled, titleText); + painter->setFont(oldFont); + painter->setPen(oldPen); + } + } else { + painter->setBrush(option->palette.window().color().darker(110)); + painter->setPen(option->palette.window().color().darker(130)); + painter->drawRect(rect.adjusted(0, 1, -1, -3)); + + if (!dwOpt->title.isEmpty()) { + QString titleText = painter->fontMetrics().elidedText(dwOpt->title, Qt::ElideRight, + verticalTitleBar ? titleRect.height() : titleRect.width()); + const int indent = 4; + drawItemText(painter, rect.adjusted(indent + 1, 1, -indent - 1, -1), + Qt::AlignLeft | Qt::AlignVCenter | Qt::TextShowMnemonic, + dwOpt->palette, + dwOpt->state & State_Enabled, titleText, + QPalette::WindowText); } } - break; + } + return; #endif // QT_CONFIG(dockwidget) + +#if QT_CONFIG(rubberband) + case CE_RubberBand: + if (qstyleoption_cast(option)) { + QColor highlight = option->palette.color(QPalette::Active, QPalette::Highlight); + painter->save(); + painter->setPen(highlight.darker(120)); + QColor dimHighlight(qMin(highlight.red()/2 + 110, 255), + qMin(highlight.green()/2 + 110, 255), + qMin(highlight.blue()/2 + 110, 255), + (widget && widget->isWindow())? 255 : 127); + painter->setBrush(dimHighlight); + painter->drawRect(option->rect.adjusted(0, 0, -1, -1)); + painter->restore(); + return; + } + break; +#endif // QT_CONFIG(rubberband) + + case CE_HeaderEmptyArea: + if (option->state & State_Horizontal) { + themeNumber = QWindowsVistaStylePrivate::HeaderTheme; + stateId = HIS_NORMAL; + } else { + QWindowsStyle::drawControl(CE_HeaderEmptyArea, option, painter, widget); + return; + } + break; + #if QT_CONFIG(itemviews) - case CE_ItemViewItem: - { - const QStyleOptionViewItem *vopt; - - const QAbstractItemView *view = qobject_cast(widget); - bool newStyle = true; - - if (qobject_cast(widget)) - newStyle = false; - - if (newStyle && view && (vopt = qstyleoption_cast(option))) { - /* - // We cannot currently get the correct selection color for "explorer style" views - COLORREF cref = 0; - XPThemeData theme(d->treeViewHelper(), 0, QLatin1String("LISTVIEW"), 0, 0); - unsigned int res = GetThemeColor(theme.handle(), LVP_LISTITEM, LISS_SELECTED, TMT_TEXTCOLOR, &cref); - QColor textColor(GetRValue(cref), GetGValue(cref), GetBValue(cref)); - */ - QPalette palette = vopt->palette; - palette.setColor(QPalette::All, QPalette::HighlightedText, palette.color(QPalette::Active, QPalette::Text)); - // Note that setting a saturated color here results in ugly XOR colors in the focus rect - palette.setColor(QPalette::All, QPalette::Highlight, palette.base().color().darker(108)); - QStyleOptionViewItem adjustedOption = *vopt; - adjustedOption.palette = palette; - // We hide the focusrect in singleselection as it is not required - if ((view->selectionMode() == QAbstractItemView::SingleSelection) + case CE_ItemViewItem: { + const QStyleOptionViewItem *vopt; + const QAbstractItemView *view = qobject_cast(widget); + bool newStyle = true; + + if (qobject_cast(widget)) + newStyle = false; + + QWindowsThemeData theme(widget, painter, themeNumber, partId, stateId, rect); + + if (newStyle && view && (vopt = qstyleoption_cast(option))) { + /* + // We cannot currently get the correct selection color for "explorer style" views + COLORREF cref = 0; + QWindowsThemeData theme(d->treeViewHelper(), 0, QLatin1String("LISTVIEW"), 0, 0); + unsigned int res = GetThemeColor(theme.handle(), LVP_LISTITEM, LISS_SELECTED, TMT_TEXTCOLOR, &cref); + QColor textColor(GetRValue(cref), GetGValue(cref), GetBValue(cref)); + */ + QPalette palette = vopt->palette; + palette.setColor(QPalette::All, QPalette::HighlightedText, palette.color(QPalette::Active, QPalette::Text)); + // Note that setting a saturated color here results in ugly XOR colors in the focus rect + palette.setColor(QPalette::All, QPalette::Highlight, palette.base().color().darker(108)); + QStyleOptionViewItem adjustedOption = *vopt; + adjustedOption.palette = palette; + // We hide the focusrect in singleselection as it is not required + if ((view->selectionMode() == QAbstractItemView::SingleSelection) && !(vopt->state & State_KeyboardFocusChange)) adjustedOption.state &= ~State_HasFocus; - QWindowsXPStyle::drawControl(element, &adjustedOption, painter, widget); - } else { - QWindowsXPStyle::drawControl(element, option, painter, widget); + if (!theme.isValid()) { + QWindowsStyle::drawControl(element, &adjustedOption, painter, widget); + return; + } + } else { + if (!theme.isValid()) { + QWindowsStyle::drawControl(element, option, painter, widget); + return; } - break; } + + theme.rotate = rotate; + theme.mirrorHorizontally = hMirrored; + theme.mirrorVertically = vMirrored; + d->drawBackground(theme); + return; + } #endif // QT_CONFIG(itemviews) + #if QT_CONFIG(combobox) case CE_ComboBoxLabel: QCommonStyle::drawControl(element, option, painter, widget); - break; + return; #endif // QT_CONFIG(combobox) + default: - QWindowsXPStyle::drawControl(element, option, painter, widget); break; } + + QWindowsThemeData theme(widget, painter, themeNumber, partId, stateId, rect); + + if (!theme.isValid()) { + QWindowsStyle::drawControl(element, option, painter, widget); + return; + } + + theme.rotate = rotate; + theme.mirrorHorizontally = hMirrored; + theme.mirrorVertically = vMirrored; + + d->drawBackground(theme); } /*! \internal - see drawPrimitive for comments on the animation support */ void QWindowsVistaStyle::drawComplexControl(ComplexControl control, const QStyleOptionComplex *option, - QPainter *painter, const QWidget *widget) const + QPainter *painter, const QWidget *widget) const { QWindowsVistaStylePrivate *d = const_cast(d_func()); + if (!QWindowsVistaStylePrivate::useVista()) { QWindowsStyle::drawComplexControl(control, option, painter, widget); return; @@ -1487,9 +3292,7 @@ void QWindowsVistaStyle::drawComplexControl(ComplexControl control, const QStyle if (d->transitionsEnabled() && canAnimate(option)) { - if (control == CC_ScrollBar || control == CC_SpinBox || control == CC_ComboBox) { - QObject *styleObject = option->styleObject; // Can be widget or qquickitem int oldState = styleObject->property("_q_stylestate").toInt(); @@ -1500,10 +3303,10 @@ void QWindowsVistaStyle::drawComplexControl(ComplexControl control, const QStyle styleObject->setProperty("_q_stylecontrols", int(option->activeSubControls)); styleObject->setProperty("_q_stylerect", option->rect); - bool doTransition = ((state & State_Sunken) != (oldState & State_Sunken) || - (state & State_On) != (oldState & State_On) || - (state & State_MouseOver) != (oldState & State_MouseOver) || - oldActiveControls != int(option->activeSubControls)); + bool doTransition = ((state & State_Sunken) != (oldState & State_Sunken) + || (state & State_On) != (oldState & State_On) + || (state & State_MouseOver) != (oldState & State_MouseOver) + || oldActiveControls != int(option->activeSubControls)); if (qstyleoption_cast(option)) { QRect oldSliderPos = styleObject->property("_q_stylesliderpos").toRect(); @@ -1575,9 +3378,399 @@ void QWindowsVistaStyle::drawComplexControl(ComplexControl control, const QStyle } switch (control) { + +#if QT_CONFIG(slider) + case CC_Slider: + if (const auto *slider = qstyleoption_cast(option)) { + QWindowsThemeData theme(widget, painter, QWindowsVistaStylePrivate::TrackBarTheme); + QRect slrect = slider->rect; + QRegion tickreg = slrect; + if (sub & SC_SliderGroove) { + theme.rect = proxy()->subControlRect(CC_Slider, option, SC_SliderGroove, widget); + if (slider->orientation == Qt::Horizontal) { + partId = TKP_TRACK; + stateId = TRS_NORMAL; + theme.rect = QRect(slrect.left(), theme.rect.center().y() - 2, slrect.width(), 4); + } else { + partId = TKP_TRACKVERT; + stateId = TRVS_NORMAL; + theme.rect = QRect(theme.rect.center().x() - 2, slrect.top(), 4, slrect.height()); + } + theme.partId = partId; + theme.stateId = stateId; + d->drawBackground(theme); + tickreg -= theme.rect; + } + if (sub & SC_SliderTickmarks) { + int tickOffset = proxy()->pixelMetric(PM_SliderTickmarkOffset, slider, widget); + int ticks = slider->tickPosition; + int thickness = proxy()->pixelMetric(PM_SliderControlThickness, slider, widget); + int len = proxy()->pixelMetric(PM_SliderLength, slider, widget); + int available = proxy()->pixelMetric(PM_SliderSpaceAvailable, slider, widget); + int interval = slider->tickInterval; + if (interval <= 0) { + interval = slider->singleStep; + if (QStyle::sliderPositionFromValue(slider->minimum, slider->maximum, interval, + available) + - QStyle::sliderPositionFromValue(slider->minimum, slider->maximum, + 0, available) < 3) + interval = slider->pageStep; + } + if (!interval) + interval = 1; + int fudge = len / 2; + int pos; + int bothOffset = (ticks & QSlider::TicksAbove && ticks & QSlider::TicksBelow) ? 1 : 0; + painter->setPen(d->sliderTickColor); + QVarLengthArray lines; + int v = slider->minimum; + while (v <= slider->maximum + 1) { + if (v == slider->maximum + 1 && interval == 1) + break; + const int v_ = qMin(v, slider->maximum); + int tickLength = (v_ == slider->minimum || v_ >= slider->maximum) ? 4 : 3; + pos = QStyle::sliderPositionFromValue(slider->minimum, slider->maximum, + v_, available) + fudge; + if (slider->orientation == Qt::Horizontal) { + if (ticks & QSlider::TicksAbove) { + lines.append(QLine(pos, tickOffset - 1 - bothOffset, + pos, tickOffset - 1 - bothOffset - tickLength)); + } + + if (ticks & QSlider::TicksBelow) { + lines.append(QLine(pos, tickOffset + thickness + bothOffset, + pos, tickOffset + thickness + bothOffset + tickLength)); + } + } else { + if (ticks & QSlider::TicksAbove) { + lines.append(QLine(tickOffset - 1 - bothOffset, pos, + tickOffset - 1 - bothOffset - tickLength, pos)); + } + + if (ticks & QSlider::TicksBelow) { + lines.append(QLine(tickOffset + thickness + bothOffset, pos, + tickOffset + thickness + bothOffset + tickLength, pos)); + } + } + // in the case where maximum is max int + int nextInterval = v + interval; + if (nextInterval < v) + break; + v = nextInterval; + } + if (!lines.isEmpty()) { + painter->save(); + painter->translate(slrect.topLeft()); + painter->drawLines(lines.constData(), lines.size()); + painter->restore(); + } + } + if (sub & SC_SliderHandle) { + theme.rect = proxy()->subControlRect(CC_Slider, option, SC_SliderHandle, widget); + if (slider->orientation == Qt::Horizontal) { + if (slider->tickPosition == QSlider::TicksAbove) + partId = TKP_THUMBTOP; + else if (slider->tickPosition == QSlider::TicksBelow) + partId = TKP_THUMBBOTTOM; + else + partId = TKP_THUMB; + + if (!(slider->state & State_Enabled)) + stateId = TUS_DISABLED; + else if (slider->activeSubControls & SC_SliderHandle && (slider->state & State_Sunken)) + stateId = TUS_PRESSED; + else if (slider->activeSubControls & SC_SliderHandle && (slider->state & State_MouseOver)) + stateId = TUS_HOT; + else if (flags & State_HasFocus) + stateId = TUS_FOCUSED; + else + stateId = TUS_NORMAL; + } else { + if (slider->tickPosition == QSlider::TicksLeft) + partId = TKP_THUMBLEFT; + else if (slider->tickPosition == QSlider::TicksRight) + partId = TKP_THUMBRIGHT; + else + partId = TKP_THUMBVERT; + + if (!(slider->state & State_Enabled)) + stateId = TUVS_DISABLED; + else if (slider->activeSubControls & SC_SliderHandle && (slider->state & State_Sunken)) + stateId = TUVS_PRESSED; + else if (slider->activeSubControls & SC_SliderHandle && (slider->state & State_MouseOver)) + stateId = TUVS_HOT; + else if (flags & State_HasFocus) + stateId = TUVS_FOCUSED; + else + stateId = TUVS_NORMAL; + } + theme.partId = partId; + theme.stateId = stateId; + d->drawBackground(theme); + } + if (slider->state & State_HasFocus) { + QStyleOptionFocusRect fropt; + fropt.QStyleOption::operator=(*slider); + fropt.rect = subElementRect(SE_SliderFocusRect, slider, widget); + proxy()->drawPrimitive(PE_FrameFocusRect, &fropt, painter, widget); + } + } + break; +#endif + +#if QT_CONFIG(toolbutton) + case CC_ToolButton: + if (const auto *toolbutton = qstyleoption_cast(option)) { + QRect button, menuarea; + button = proxy()->subControlRect(control, toolbutton, SC_ToolButton, widget); + menuarea = proxy()->subControlRect(control, toolbutton, SC_ToolButtonMenu, widget); + + State bflags = toolbutton->state & ~State_Sunken; + State mflags = bflags; + bool autoRaise = flags & State_AutoRaise; + if (autoRaise) { + if (!(bflags & State_MouseOver) || !(bflags & State_Enabled)) + bflags &= ~State_Raised; + } + + if (toolbutton->state & State_Sunken) { + if (toolbutton->activeSubControls & SC_ToolButton) { + bflags |= State_Sunken; + mflags |= State_MouseOver | State_Sunken; + } else if (toolbutton->activeSubControls & SC_ToolButtonMenu) { + mflags |= State_Sunken; + bflags |= State_MouseOver; + } + } + + QStyleOption tool = *toolbutton; + if (toolbutton->subControls & SC_ToolButton) { + if (flags & (State_Sunken | State_On | State_Raised) || !autoRaise) { + if (toolbutton->features & QStyleOptionToolButton::MenuButtonPopup && autoRaise) { + QWindowsThemeData theme(widget, painter, QWindowsVistaStylePrivate::ToolBarTheme); + theme.partId = TP_SPLITBUTTON; + theme.rect = button; + if (!(bflags & State_Enabled)) + stateId = TS_DISABLED; + else if (bflags & State_Sunken) + stateId = TS_PRESSED; + else if (bflags & State_MouseOver || !(flags & State_AutoRaise)) + stateId = flags & State_On ? TS_HOTCHECKED : TS_HOT; + else if (bflags & State_On) + stateId = TS_CHECKED; + else + stateId = TS_NORMAL; + if (option->direction == Qt::RightToLeft) + theme.mirrorHorizontally = true; + theme.stateId = stateId; + d->drawBackground(theme); + } else { + tool.rect = option->rect; + tool.state = bflags; + if (autoRaise) // for tool bars + proxy()->drawPrimitive(PE_PanelButtonTool, &tool, painter, widget); + else + proxy()->drawPrimitive(PE_PanelButtonBevel, &tool, painter, widget); + } + } + } + + if (toolbutton->state & State_HasFocus) { + QStyleOptionFocusRect fr; + fr.QStyleOption::operator=(*toolbutton); + fr.rect.adjust(3, 3, -3, -3); + if (toolbutton->features & QStyleOptionToolButton::MenuButtonPopup) + fr.rect.adjust(0, 0, -proxy()->pixelMetric(QStyle::PM_MenuButtonIndicator, + toolbutton, widget), 0); + proxy()->drawPrimitive(PE_FrameFocusRect, &fr, painter, widget); + } + QStyleOptionToolButton label = *toolbutton; + label.state = bflags; + int fw = 2; + if (!autoRaise) + label.state &= ~State_Sunken; + label.rect = button.adjusted(fw, fw, -fw, -fw); + proxy()->drawControl(CE_ToolButtonLabel, &label, painter, widget); + + if (toolbutton->subControls & SC_ToolButtonMenu) { + tool.rect = menuarea; + tool.state = mflags; + if (autoRaise) { + proxy()->drawPrimitive(PE_IndicatorButtonDropDown, &tool, painter, widget); + } else { + tool.state = mflags; + menuarea.adjust(-2, 0, 0, 0); + // Draw menu button + if ((bflags & State_Sunken) != (mflags & State_Sunken)){ + painter->save(); + painter->setClipRect(menuarea); + tool.rect = option->rect; + proxy()->drawPrimitive(PE_PanelButtonBevel, &tool, painter, nullptr); + painter->restore(); + } + // Draw arrow + painter->save(); + painter->setPen(option->palette.dark().color()); + painter->drawLine(menuarea.left(), menuarea.top() + 3, + menuarea.left(), menuarea.bottom() - 3); + painter->setPen(option->palette.light().color()); + painter->drawLine(menuarea.left() - 1, menuarea.top() + 3, + menuarea.left() - 1, menuarea.bottom() - 3); + + tool.rect = menuarea.adjusted(2, 3, -2, -1); + proxy()->drawPrimitive(PE_IndicatorArrowDown, &tool, painter, widget); + painter->restore(); + } + } else if (toolbutton->features & QStyleOptionToolButton::HasMenu) { + int mbi = proxy()->pixelMetric(PM_MenuButtonIndicator, toolbutton, widget); + QRect ir = toolbutton->rect; + QStyleOptionToolButton newBtn = *toolbutton; + newBtn.rect = QRect(ir.right() + 4 - mbi, ir.height() - mbi + 4, mbi - 5, mbi - 5); + proxy()->drawPrimitive(PE_IndicatorArrowDown, &newBtn, painter, widget); + } + } + break; +#endif // QT_CONFIG(toolbutton) + + case CC_TitleBar: + if (const auto *tb = qstyleoption_cast(option)) { + const qreal factor = QWindowsStylePrivate::nativeMetricScaleFactor(widget); + bool isActive = tb->titleBarState & QStyle::State_Active; + QWindowsThemeData theme(widget, painter, QWindowsVistaStylePrivate::WindowTheme); + if (sub & SC_TitleBarLabel) { + partId = (tb->titleBarState & Qt::WindowMinimized) ? WP_MINCAPTION : WP_CAPTION; + theme.rect = option->rect; + if (widget && !widget->isEnabled()) + stateId = CS_DISABLED; + else if (isActive) + stateId = CS_ACTIVE; + else + stateId = CS_INACTIVE; + + theme.partId = partId; + theme.stateId = stateId; + d->drawBackground(theme); + + QRect ir = proxy()->subControlRect(CC_TitleBar, tb, SC_TitleBarLabel, widget); + + int result = TST_NONE; + GetThemeEnumValue(theme.handle(), WP_CAPTION, isActive ? CS_ACTIVE : CS_INACTIVE, TMT_TEXTSHADOWTYPE, &result); + if (result != TST_NONE) { + COLORREF textShadowRef; + GetThemeColor(theme.handle(), WP_CAPTION, isActive ? CS_ACTIVE : CS_INACTIVE, TMT_TEXTSHADOWCOLOR, &textShadowRef); + QColor textShadow = qRgb(GetRValue(textShadowRef), GetGValue(textShadowRef), GetBValue(textShadowRef)); + painter->setPen(textShadow); + painter->drawText(int(ir.x() + 3 * factor), int(ir.y() + 2 * factor), + int(ir.width() - 1 * factor), ir.height(), + Qt::AlignLeft | Qt::AlignVCenter | Qt::TextSingleLine, tb->text); + } + COLORREF captionText = GetSysColor(isActive ? COLOR_CAPTIONTEXT : COLOR_INACTIVECAPTIONTEXT); + QColor textColor = qRgb(GetRValue(captionText), GetGValue(captionText), GetBValue(captionText)); + painter->setPen(textColor); + painter->drawText(int(ir.x() + 2 * factor), int(ir.y() + 1 * factor), + int(ir.width() - 2 * factor), ir.height(), + Qt::AlignLeft | Qt::AlignVCenter | Qt::TextSingleLine, tb->text); + } + if (sub & SC_TitleBarSysMenu && tb->titleBarFlags & Qt::WindowSystemMenuHint) { + theme.rect = proxy()->subControlRect(CC_TitleBar, option, SC_TitleBarSysMenu, widget); + partId = WP_SYSBUTTON; + if ((widget && !widget->isEnabled()) || !isActive) + stateId = SBS_DISABLED; + else if (option->activeSubControls == SC_TitleBarSysMenu && (option->state & State_Sunken)) + stateId = SBS_PUSHED; + else if (option->activeSubControls == SC_TitleBarSysMenu && (option->state & State_MouseOver)) + stateId = SBS_HOT; + else + stateId = SBS_NORMAL; + if (!tb->icon.isNull()) { + tb->icon.paint(painter, theme.rect); + } else { + theme.partId = partId; + theme.stateId = stateId; + if (theme.size().isEmpty()) { + int iconSize = proxy()->pixelMetric(PM_SmallIconSize, tb, widget); + QPixmap pm = proxy()->standardIcon(SP_TitleBarMenuButton, tb, widget).pixmap(iconSize, iconSize); + painter->save(); + drawItemPixmap(painter, theme.rect, Qt::AlignCenter, pm); + painter->restore(); + } else { + d->drawBackground(theme); + } + } + } + + if (sub & SC_TitleBarMinButton && tb->titleBarFlags & Qt::WindowMinimizeButtonHint + && !(tb->titleBarState & Qt::WindowMinimized)) { + populateTitleBarButtonTheme(proxy(), widget, option, SC_TitleBarMinButton, isActive, WP_MINBUTTON, &theme); + d->drawBackground(theme); + } + if (sub & SC_TitleBarMaxButton && tb->titleBarFlags & Qt::WindowMaximizeButtonHint + && !(tb->titleBarState & Qt::WindowMaximized)) { + populateTitleBarButtonTheme(proxy(), widget, option, SC_TitleBarMaxButton, isActive, WP_MAXBUTTON, &theme); + d->drawBackground(theme); + } + if (sub & SC_TitleBarContextHelpButton + && tb->titleBarFlags & Qt::WindowContextHelpButtonHint) { + populateTitleBarButtonTheme(proxy(), widget, option, SC_TitleBarContextHelpButton, isActive, WP_HELPBUTTON, &theme); + d->drawBackground(theme); + } + bool drawNormalButton = (sub & SC_TitleBarNormalButton) + && (((tb->titleBarFlags & Qt::WindowMinimizeButtonHint) + && (tb->titleBarState & Qt::WindowMinimized)) + || ((tb->titleBarFlags & Qt::WindowMaximizeButtonHint) + && (tb->titleBarState & Qt::WindowMaximized))); + if (drawNormalButton) { + populateTitleBarButtonTheme(proxy(), widget, option, SC_TitleBarNormalButton, isActive, WP_RESTOREBUTTON, &theme); + d->drawBackground(theme); + } + if (sub & SC_TitleBarShadeButton && tb->titleBarFlags & Qt::WindowShadeButtonHint + && !(tb->titleBarState & Qt::WindowMinimized)) { + populateTitleBarButtonTheme(proxy(), widget, option, SC_TitleBarShadeButton, isActive, WP_MINBUTTON, &theme); + d->drawBackground(theme); + } + if (sub & SC_TitleBarUnshadeButton && tb->titleBarFlags & Qt::WindowShadeButtonHint + && tb->titleBarState & Qt::WindowMinimized) { + populateTitleBarButtonTheme(proxy(), widget, option, SC_TitleBarUnshadeButton, isActive, WP_RESTOREBUTTON, &theme); + d->drawBackground(theme); + } + if (sub & SC_TitleBarCloseButton && tb->titleBarFlags & Qt::WindowSystemMenuHint) { + populateTitleBarButtonTheme(proxy(), widget, option, SC_TitleBarCloseButton, isActive, WP_CLOSEBUTTON, &theme); + d->drawBackground(theme); + } + } + break; + +#if QT_CONFIG(mdiarea) + case CC_MdiControls: { + QWindowsThemeData theme(widget, painter, QWindowsVistaStylePrivate::WindowTheme, WP_MDICLOSEBUTTON, CBS_NORMAL); + if (Q_UNLIKELY(!theme.isValid())) + return; + + if (option->subControls.testFlag(SC_MdiCloseButton)) { + populateMdiButtonTheme(proxy(), widget, option, SC_MdiCloseButton, WP_MDICLOSEBUTTON, &theme); + d->drawBackground(theme, mdiButtonCorrectionFactor(theme, widget)); + } + if (option->subControls.testFlag(SC_MdiNormalButton)) { + populateMdiButtonTheme(proxy(), widget, option, SC_MdiNormalButton, WP_MDIRESTOREBUTTON, &theme); + d->drawBackground(theme, mdiButtonCorrectionFactor(theme, widget)); + } + if (option->subControls.testFlag(QStyle::SC_MdiMinButton)) { + populateMdiButtonTheme(proxy(), widget, option, SC_MdiMinButton, WP_MDIMINBUTTON, &theme); + d->drawBackground(theme, mdiButtonCorrectionFactor(theme, widget)); + } + break; + } +#endif // QT_CONFIG(mdiarea) + +#if QT_CONFIG(dial) + case CC_Dial: + if (const auto *dial = qstyleoption_cast(option)) + QStyleHelper::drawDial(dial, painter); + break; +#endif // QT_CONFIG(dial) + case CC_ComboBox: - if (const QStyleOptionComboBox *cmb = qstyleoption_cast(option)) - { + if (const QStyleOptionComboBox *cmb = qstyleoption_cast(option)) { if (cmb->editable) { if (sub & SC_ComboBoxEditField) { partId = EP_EDITBORDER_NOSCROLL; @@ -1590,15 +3783,15 @@ void QWindowsVistaStyle::drawComplexControl(ComplexControl control, const QStyle else stateId = ETS_NORMAL; - XPThemeData theme(widget, painter, - QWindowsXPStylePrivate::EditTheme, - partId, stateId, r); + QWindowsThemeData theme(widget, painter, + QWindowsVistaStylePrivate::EditTheme, + partId, stateId, r); d->drawBackground(theme); } if (sub & SC_ComboBoxArrow) { QRect subRect = proxy()->subControlRect(CC_ComboBox, option, SC_ComboBoxArrow, widget); - XPThemeData theme(widget, painter, QWindowsXPStylePrivate::ComboboxTheme); + QWindowsThemeData theme(widget, painter, QWindowsVistaStylePrivate::ComboboxTheme); theme.rect = subRect; partId = option->direction == Qt::RightToLeft ? CP_DROPDOWNBUTTONLEFT : CP_DROPDOWNBUTTONRIGHT; @@ -1618,7 +3811,7 @@ void QWindowsVistaStyle::drawComplexControl(ComplexControl control, const QStyle } else { if (sub & SC_ComboBoxFrame) { - XPThemeData theme(widget, painter, QWindowsXPStylePrivate::ComboboxTheme); + QWindowsThemeData theme(widget, painter, QWindowsVistaStylePrivate::ComboboxTheme); theme.rect = option->rect; theme.partId = CP_READONLY; if (!(cmb->state & State_Enabled)) @@ -1632,7 +3825,7 @@ void QWindowsVistaStyle::drawComplexControl(ComplexControl control, const QStyle d->drawBackground(theme); } if (sub & SC_ComboBoxArrow) { - XPThemeData theme(widget, painter, QWindowsXPStylePrivate::ComboboxTheme); + QWindowsThemeData theme(widget, painter, QWindowsVistaStylePrivate::ComboboxTheme); theme.rect = proxy()->subControlRect(CC_ComboBox, option, SC_ComboBoxArrow, widget); theme.partId = option->direction == Qt::RightToLeft ? CP_DROPDOWNBUTTONLEFT : CP_DROPDOWNBUTTONRIGHT; if (!(cmb->state & State_Enabled)) @@ -1650,10 +3843,10 @@ void QWindowsVistaStyle::drawComplexControl(ComplexControl control, const QStyle } } break; + case CC_ScrollBar: - if (const QStyleOptionSlider *scrollbar = qstyleoption_cast(option)) - { - XPThemeData theme(widget, painter, QWindowsXPStylePrivate::ScrollBarTheme); + if (const QStyleOptionSlider *scrollbar = qstyleoption_cast(option)) { + QWindowsThemeData theme(widget, painter, QWindowsVistaStylePrivate::ScrollBarTheme); bool maxedOut = (scrollbar->maximum == scrollbar->minimum); if (maxedOut) flags &= ~State_Enabled; @@ -1755,11 +3948,11 @@ void QWindowsVistaStyle::drawComplexControl(ComplexControl control, const QStyle } } break; + #if QT_CONFIG(spinbox) case CC_SpinBox: - if (const QStyleOptionSpinBox *sb = qstyleoption_cast(option)) - { - XPThemeData theme(widget, painter, QWindowsXPStylePrivate::SpinTheme); + if (const QStyleOptionSpinBox *sb = qstyleoption_cast(option)) { + QWindowsThemeData theme(widget, painter, QWindowsVistaStylePrivate::SpinTheme); if (sb->frame && (sub & SC_SpinBoxFrame)) { partId = EP_EDITBORDER_NOSCROLL; if (!(flags & State_Enabled)) @@ -1771,9 +3964,9 @@ void QWindowsVistaStyle::drawComplexControl(ComplexControl control, const QStyle else stateId = ETS_NORMAL; - XPThemeData ftheme(widget, painter, - QWindowsXPStylePrivate::EditTheme, - partId, stateId, r); + QWindowsThemeData ftheme(widget, painter, + QWindowsVistaStylePrivate::EditTheme, + partId, stateId, r); // The spinbox in Windows QStyle is drawn with frameless QLineEdit inside it // That however breaks with QtQuickControls where this results in transparent // spinbox background, so if there's no "widget" passed (QtQuickControls case), @@ -1814,8 +4007,9 @@ void QWindowsVistaStyle::drawComplexControl(ComplexControl control, const QStyle } break; #endif // QT_CONFIG(spinbox) + default: - QWindowsXPStyle::drawComplexControl(control, option, painter, widget); + QWindowsStyle::drawComplexControl(control, option, painter, widget); break; } } @@ -1824,69 +4018,113 @@ void QWindowsVistaStyle::drawComplexControl(ComplexControl control, const QStyle \internal */ QSize QWindowsVistaStyle::sizeFromContents(ContentsType type, const QStyleOption *option, - const QSize &size, const QWidget *widget) const + const QSize &size, const QWidget *widget) const { if (!QWindowsVistaStylePrivate::useVista()) return QWindowsStyle::sizeFromContents(type, option, size, widget); - QSize sz(size); + QSize contentSize(size); + switch (type) { - case CT_MenuItem: - sz = QWindowsXPStyle::sizeFromContents(type, option, size, widget); - int minimumHeight; - { - XPThemeData theme(widget, nullptr, - QWindowsXPStylePrivate::MenuTheme, - MENU_POPUPCHECKBACKGROUND, MBI_HOT); - XPThemeData themeSize = theme; - themeSize.partId = MENU_POPUPCHECK; - themeSize.stateId = 0; - const QSizeF size = themeSize.size() * QWindowsStylePrivate::nativeMetricScaleFactor(widget); - const QMarginsF margins = themeSize.margins() * QWindowsStylePrivate::nativeMetricScaleFactor(widget); - minimumHeight = qMax(qRound(size.height() + margins.bottom() + margins.top()), sz.height()); - sz.rwidth() += qRound(size.width() + margins.left() + margins.right()); + case CT_LineEdit: + case CT_ComboBox: { + QWindowsThemeData buttontheme(widget, nullptr, QWindowsVistaStylePrivate::ButtonTheme, BP_PUSHBUTTON, PBS_NORMAL); + if (buttontheme.isValid()) { + const qreal factor = QWindowsStylePrivate::nativeMetricScaleFactor(widget); + const QMarginsF borderSize = buttontheme.margins() * factor; + if (!borderSize.isNull()) { + const qreal margin = qreal(2) * factor; + contentSize.rwidth() += qRound(borderSize.left() + borderSize.right() - margin); + contentSize.rheight() += int(borderSize.bottom() + borderSize.top() - margin + + qreal(1) / factor - 1); + } + const int textMargins = 2*(proxy()->pixelMetric(PM_FocusFrameHMargin, option) + 1); + contentSize += QSize(qMax(pixelMetric(QStyle::PM_ScrollBarExtent, option, widget) + + textMargins, 23), 0); //arrow button } + break; + } + + case CT_TabWidget: + contentSize += QSize(6, 6); + break; + + case CT_Menu: + contentSize += QSize(1, 0); + break; - if (const QStyleOptionMenuItem *menuitem = qstyleoption_cast(option)) { - if (menuitem->menuItemType != QStyleOptionMenuItem::Separator) - sz.setHeight(minimumHeight); - } - return sz; #if QT_CONFIG(menubar) case CT_MenuBarItem: - if (!sz.isEmpty()) - sz += QSize(windowsItemHMargin * 5 + 1, 5); - return sz; + if (!contentSize.isEmpty()) + contentSize += QSize(windowsItemHMargin * 5 + 1, 5); + break; #endif - case CT_ItemViewItem: - sz = QWindowsXPStyle::sizeFromContents(type, option, size, widget); - sz.rheight() += 2; - return sz; - case CT_SpinBox: - { - //Spinbox adds frame twice - sz = QWindowsStyle::sizeFromContents(type, option, size, widget); - int border = proxy()->pixelMetric(PM_SpinBoxFrameWidth, option, widget); - sz -= QSize(2*border, 2*border); + + case CT_MenuItem: { + contentSize = QWindowsStyle::sizeFromContents(type, option, size, widget); + QWindowsThemeData theme(widget, nullptr, + QWindowsVistaStylePrivate::MenuTheme, + MENU_POPUPCHECKBACKGROUND, MBI_HOT); + QWindowsThemeData themeSize = theme; + themeSize.partId = MENU_POPUPCHECK; + themeSize.stateId = 0; + const QSizeF size = themeSize.size() * QWindowsStylePrivate::nativeMetricScaleFactor(widget); + const QMarginsF margins = themeSize.margins() * QWindowsStylePrivate::nativeMetricScaleFactor(widget); + int minimumHeight = qMax(qRound(size.height() + margins.bottom() + margins.top()), contentSize.height()); + contentSize.rwidth() += qRound(size.width() + margins.left() + margins.right()); + if (const QStyleOptionMenuItem *menuitem = qstyleoption_cast(option)) { + if (menuitem->menuItemType != QStyleOptionMenuItem::Separator) + contentSize.setHeight(minimumHeight); + } + break; + } + + case CT_MdiControls: { + contentSize.setHeight(int(QStyleHelper::dpiScaled(19, option))); + int width = 54; + if (const auto *styleOpt = qstyleoption_cast(option)) { + width = 0; + if (styleOpt->subControls & SC_MdiMinButton) + width += 17 + 1; + if (styleOpt->subControls & SC_MdiNormalButton) + width += 17 + 1; + if (styleOpt->subControls & SC_MdiCloseButton) + width += 17 + 1; } - return sz; + contentSize.setWidth(int(QStyleHelper::dpiScaled(width, option))); + break; + } + + case CT_ItemViewItem: + contentSize = QWindowsStyle::sizeFromContents(type, option, size, widget); + contentSize.rheight() += 2; + break; + + case CT_SpinBox: { + //Spinbox adds frame twice + contentSize = QWindowsStyle::sizeFromContents(type, option, size, widget); + int border = proxy()->pixelMetric(PM_SpinBoxFrameWidth, option, widget); + contentSize -= QSize(2*border, 2*border); + break; + } + case CT_HeaderSection: - { - // When there is a sort indicator it adds to the width but it is shown - // above the text natively and not on the side - if (QStyleOptionHeader *hdr = qstyleoption_cast(const_cast(option))) { - QStyleOptionHeader::SortIndicator sortInd = hdr->sortIndicator; - hdr->sortIndicator = QStyleOptionHeader::None; - sz = QWindowsXPStyle::sizeFromContents(type, hdr, size, widget); - hdr->sortIndicator = sortInd; - return sz; - } - break; + // When there is a sort indicator it adds to the width but it is shown + // above the text natively and not on the side + if (QStyleOptionHeader *hdr = qstyleoption_cast(const_cast(option))) { + QStyleOptionHeader::SortIndicator sortInd = hdr->sortIndicator; + hdr->sortIndicator = QStyleOptionHeader::None; + contentSize = QWindowsStyle::sizeFromContents(type, hdr, size, widget); + hdr->sortIndicator = sortInd; } + break; + default: + contentSize = QWindowsStyle::sizeFromContents(type, option, size, widget); break; } - return QWindowsXPStyle::sizeFromContents(type, option, size, widget); + + return contentSize; } /*! @@ -1894,12 +4132,12 @@ QSize QWindowsVistaStyle::sizeFromContents(ContentsType type, const QStyleOption */ QRect QWindowsVistaStyle::subElementRect(SubElement element, const QStyleOption *option, const QWidget *widget) const { - if (!QWindowsVistaStylePrivate::useVista()) + if (!QWindowsVistaStylePrivate::useVista()) return QWindowsStyle::subElementRect(element, option, widget); - QRect rect = QWindowsXPStyle::subElementRect(element, option, widget); - switch (element) { + QRect rect(option->rect); + switch (element) { case SE_PushButtonContents: if (const QStyleOptionButton *btn = qstyleoption_cast(option)) { MARGINS borderSize; @@ -1927,184 +4165,136 @@ QRect QWindowsVistaStyle::subElementRect(SubElement element, const QStyleOption } break; - case SE_HeaderArrow: - { - QRect r = rect; - int h = option->rect.height(); - int w = option->rect.width(); - int x = option->rect.x(); - int y = option->rect.y(); - int margin = proxy()->pixelMetric(QStyle::PM_HeaderMargin, option, widget); - - XPThemeData theme(widget, nullptr, - QWindowsXPStylePrivate::HeaderTheme, - HP_HEADERSORTARROW, HSAS_SORTEDDOWN, option->rect); - - int arrowWidth = 13; - int arrowHeight = 5; - if (theme.isValid()) { - const QSizeF size = theme.size() * QWindowsStylePrivate::nativeMetricScaleFactor(widget); - if (!size.isEmpty()) { - arrowWidth = qRound(size.width()); - arrowHeight = qRound(size.height()); - } - } - if (option->state & State_Horizontal) { - r.setRect(x + w/2 - arrowWidth/2, y , arrowWidth, arrowHeight); - } else { - int vert_size = w / 2; - r.setRect(x + 5, y + h - margin * 2 - vert_size, - w - margin * 2 - 5, vert_size); - } - rect = visualRect(option->direction, option->rect, r); - } - break; - - case SE_HeaderLabel: - { - int margin = proxy()->pixelMetric(QStyle::PM_HeaderMargin, option, widget); - QRect r = option->rect; - r.setRect(option->rect.x() + margin, option->rect.y() + margin, - option->rect.width() - margin * 2, option->rect.height() - margin * 2); - if (const QStyleOptionHeader *header = qstyleoption_cast(option)) { - // Subtract width needed for arrow, if there is one - if (header->sortIndicator != QStyleOptionHeader::None) { - if (!(option->state & State_Horizontal)) //horizontal arrows are positioned on top - r.setHeight(r.height() - (option->rect.width() / 2) - (margin * 2)); - } + case SE_DockWidgetCloseButton: + case SE_DockWidgetFloatButton: + rect = QWindowsStyle::subElementRect(element, option, widget); + return rect.translated(0, 1); + + case SE_TabWidgetTabContents: + rect = QWindowsStyle::subElementRect(element, option, widget); + if (qstyleoption_cast(option)) { + rect = QWindowsStyle::subElementRect(element, option, widget); + if (const QTabWidget *tabWidget = qobject_cast(widget)) { + if (tabWidget->documentMode()) + break; + rect.adjust(0, 0, -2, -2); } - rect = visualRect(option->direction, option->rect, r); - } - break; - case SE_ProgressBarContents: - rect = QCommonStyle::subElementRect(SE_ProgressBarGroove, option, widget); - break; - case SE_ItemViewItemDecoration: - if (qstyleoption_cast(option)) - rect.adjust(-2, 0, 2, 0); - break; - case SE_ItemViewItemFocusRect: - if (const QStyleOptionViewItem *vopt = qstyleoption_cast(option)) { - QRect textRect = subElementRect(QStyle::SE_ItemViewItemText, option, widget); - QRect displayRect = subElementRect(QStyle::SE_ItemViewItemDecoration, option, widget); - if (!vopt->icon.isNull()) - rect = textRect.united(displayRect); - else - rect = textRect; - rect = rect.adjusted(1, 0, -1, 0); - } - break; - default: - break; - } - return rect; -} - - -/* - This function is used by subControlRect to check if a button - should be drawn for the given subControl given a set of window flags. -*/ -static bool buttonVisible(const QStyle::SubControl sc, const QStyleOptionTitleBar *tb){ - - bool isMinimized = tb->titleBarState & Qt::WindowMinimized; - bool isMaximized = tb->titleBarState & Qt::WindowMaximized; - const auto flags = tb->titleBarFlags; - bool retVal = false; - switch (sc) { - case QStyle::SC_TitleBarContextHelpButton: - if (flags & Qt::WindowContextHelpButtonHint) - retVal = true; - break; - case QStyle::SC_TitleBarMinButton: - if (!isMinimized && (flags & Qt::WindowMinimizeButtonHint)) - retVal = true; - break; - case QStyle::SC_TitleBarNormalButton: - if (isMinimized && (flags & Qt::WindowMinimizeButtonHint)) - retVal = true; - else if (isMaximized && (flags & Qt::WindowMaximizeButtonHint)) - retVal = true; - break; - case QStyle::SC_TitleBarMaxButton: - if (!isMaximized && (flags & Qt::WindowMaximizeButtonHint)) - retVal = true; - break; - case QStyle::SC_TitleBarShadeButton: - if (!isMinimized && flags & Qt::WindowShadeButtonHint) - retVal = true; - break; - case QStyle::SC_TitleBarUnshadeButton: - if (isMinimized && flags & Qt::WindowShadeButtonHint) - retVal = true; - break; - case QStyle::SC_TitleBarCloseButton: - if (flags & Qt::WindowSystemMenuHint) - retVal = true; + } break; - case QStyle::SC_TitleBarSysMenu: - if (flags & Qt::WindowSystemMenuHint) - retVal = true; + + case SE_TabWidgetTabBar: { + rect = QWindowsStyle::subElementRect(element, option, widget); + const auto *twfOption = qstyleoption_cast(option); + if (twfOption && twfOption->direction == Qt::RightToLeft + && (twfOption->shape == QTabBar::RoundedNorth + || twfOption->shape == QTabBar::RoundedSouth)) + { + QStyleOptionTab otherOption; + otherOption.shape = (twfOption->shape == QTabBar::RoundedNorth + ? QTabBar::RoundedEast : QTabBar::RoundedSouth); + int overlap = proxy()->pixelMetric(PM_TabBarBaseOverlap, &otherOption, widget); + int borderThickness = proxy()->pixelMetric(PM_DefaultFrameWidth, option, widget); + rect.adjust(-overlap + borderThickness, 0, -overlap + borderThickness, 0); + } break; - default : - retVal = true; } - return retVal; -} - -/*! \internal */ -int QWindowsVistaStyle::styleHint(StyleHint hint, const QStyleOption *option, const QWidget *widget, - QStyleHintReturn *returnData) const -{ - QWindowsVistaStylePrivate *d = const_cast(d_func()); - int ret = 0; - switch (hint) { - case SH_MessageBox_CenterButtons: - ret = false; + case SE_HeaderArrow: { + rect = QWindowsStyle::subElementRect(element, option, widget); + QRect r = rect; + int h = option->rect.height(); + int w = option->rect.width(); + int x = option->rect.x(); + int y = option->rect.y(); + int margin = proxy()->pixelMetric(QStyle::PM_HeaderMargin, option, widget); + + QWindowsThemeData theme(widget, nullptr, + QWindowsVistaStylePrivate::HeaderTheme, + HP_HEADERSORTARROW, HSAS_SORTEDDOWN, option->rect); + + int arrowWidth = 13; + int arrowHeight = 5; + if (theme.isValid()) { + const QSizeF size = theme.size() * QWindowsStylePrivate::nativeMetricScaleFactor(widget); + if (!size.isEmpty()) { + arrowWidth = qRound(size.width()); + arrowHeight = qRound(size.height()); + } + } + if (option->state & State_Horizontal) { + r.setRect(x + w/2 - arrowWidth/2, y , arrowWidth, arrowHeight); + } else { + int vert_size = w / 2; + r.setRect(x + 5, y + h - margin * 2 - vert_size, + w - margin * 2 - 5, vert_size); + } + rect = visualRect(option->direction, option->rect, r); break; - case SH_ToolTip_Mask: - if (option) { - if (QStyleHintReturnMask *mask = qstyleoption_cast(returnData)) { - ret = true; - XPThemeData themeData(widget, nullptr, - QWindowsXPStylePrivate::ToolTipTheme, - TTP_STANDARD, TTSS_NORMAL, option->rect); - mask->region = d->region(themeData); + } + + case SE_HeaderLabel: { + rect = QWindowsStyle::subElementRect(element, option, widget); + int margin = proxy()->pixelMetric(QStyle::PM_HeaderMargin, option, widget); + QRect r = option->rect; + r.setRect(option->rect.x() + margin, option->rect.y() + margin, + option->rect.width() - margin * 2, option->rect.height() - margin * 2); + if (const QStyleOptionHeader *header = qstyleoption_cast(option)) { + // Subtract width needed for arrow, if there is one + if (header->sortIndicator != QStyleOptionHeader::None) { + if (!(option->state & State_Horizontal)) //horizontal arrows are positioned on top + r.setHeight(r.height() - (option->rect.width() / 2) - (margin * 2)); } } + rect = visualRect(option->direction, option->rect, r); break; - case SH_Table_GridLineColor: - if (option) - ret = int(option->palette.color(QPalette::Base).darker(118).rgba()); - else - ret = -1; + } + + case SE_ProgressBarContents: + rect = QCommonStyle::subElementRect(SE_ProgressBarGroove, option, widget); break; - case SH_Header_ArrowAlignment: - ret = Qt::AlignTop | Qt::AlignHCenter; + + case SE_ItemViewItemDecoration: + rect = QWindowsStyle::subElementRect(element, option, widget); + if (qstyleoption_cast(option)) + rect.adjust(-2, 0, 2, 0); + break; + + case SE_ItemViewItemFocusRect: + rect = QWindowsStyle::subElementRect(element, option, widget); + if (const QStyleOptionViewItem *vopt = qstyleoption_cast(option)) { + QRect textRect = subElementRect(QStyle::SE_ItemViewItemText, option, widget); + QRect displayRect = subElementRect(QStyle::SE_ItemViewItemDecoration, option, widget); + if (!vopt->icon.isNull()) + rect = textRect.united(displayRect); + else + rect = textRect; + rect = rect.adjusted(1, 0, -1, 0); + } break; + default: - ret = QWindowsXPStyle::styleHint(hint, option, widget, returnData); + rect = QWindowsStyle::subElementRect(element, option, widget); break; } - return ret; -} + return rect; +} /*! \internal */ QRect QWindowsVistaStyle::subControlRect(ComplexControl control, const QStyleOptionComplex *option, - SubControl subControl, const QWidget *widget) const + SubControl subControl, const QWidget *widget) const { - if (!QWindowsVistaStylePrivate::useVista()) + if (!QWindowsVistaStylePrivate::useVista()) return QWindowsStyle::subControlRect(control, option, subControl, widget); - QRect rect = QWindowsXPStyle::subControlRect(control, option, subControl, widget); + QRect rect; + switch (control) { #if QT_CONFIG(combobox) case CC_ComboBox: - if (const QStyleOptionComboBox *cb = qstyleoption_cast(option)) { + if (const auto *cb = qstyleoption_cast(option)) { const int x = cb->rect.x(), y = cb->rect.y(), wi = cb->rect.width(), he = cb->rect.height(); const int margin = cb->frame ? 3 : 0; const int bmarg = cb->frame ? 2 : 0; @@ -2114,25 +4304,27 @@ QRect QWindowsVistaStyle::subControlRect(ComplexControl control, const QStyleOpt switch (subControl) { case SC_ComboBoxFrame: + case SC_ComboBoxListBoxPopup: rect = cb->rect; break; - case SC_ComboBoxArrow: + + case SC_ComboBoxArrow: { rect.setRect(xpos, y , arrowButtonWidth, he); + } break; - case SC_ComboBoxEditField: + + case SC_ComboBoxEditField: { rect.setRect(x + margin, y + margin, wi - 2 * margin - arrowWidth, he - 2 * margin); + } break; - case SC_ComboBoxListBoxPopup: - rect = cb->rect; - break; + default: break; } - rect = visualRect(cb->direction, cb->rect, rect); - return rect; } break; #endif // QT_CONFIG(combobox) + case CC_TitleBar: if (const QStyleOptionTitleBar *tb = qstyleoption_cast(option)) { if (!buttonVisible(subControl, tb)) @@ -2141,8 +4333,12 @@ QRect QWindowsVistaStyle::subControlRect(ComplexControl control, const QStyleOpt const bool isToolTitle = false; const int height = tb->rect.height(); const int width = tb->rect.width(); + + const int buttonMargin = int(QStyleHelper::dpiScaled(4, option)); + int buttonHeight = qRound(qreal(GetSystemMetrics(SM_CYSIZE)) * factor) + - buttonMargin; const int buttonWidth = - qRound(qreal(GetSystemMetrics(SM_CXSIZE)) * factor - QStyleHelper::dpiScaled(4, option)); + qRound(qreal(GetSystemMetrics(SM_CXSIZE)) * factor - QStyleHelper::dpiScaled(4, option)); const int frameWidth = proxy()->pixelMetric(PM_MdiSubWindowFrameWidth, option, widget); const bool sysmenuHint = (tb->titleBarFlags & Qt::WindowSystemMenuHint) != 0; @@ -2151,8 +4347,14 @@ QRect QWindowsVistaStyle::subControlRect(ComplexControl control, const QStyleOpt const bool contextHint = (tb->titleBarFlags & Qt::WindowContextHelpButtonHint) != 0; const bool shadeHint = (tb->titleBarFlags & Qt::WindowShadeButtonHint) != 0; + bool isMinimized = tb->titleBarState & Qt::WindowMinimized; + bool isMaximized = tb->titleBarState & Qt::WindowMaximized; + int offset = 0; + const int delta = buttonWidth + 2; + int controlTop = option->rect.bottom() - buttonHeight - 2; + switch (subControl) { - case SC_TitleBarLabel: + case SC_TitleBarLabel: { rect = QRect(frameWidth, 0, width - (buttonWidth + frameWidth + 10), height); if (isToolTitle) { if (sysmenuHint) { @@ -2175,62 +4377,130 @@ QRect QWindowsVistaStyle::subControlRect(ComplexControl control, const QStyleOpt rect.adjust(0, 0, int(-buttonWidth - 2 * factor), 0); } rect.translate(0, int(2 * factor)); - rect = visualRect(option->direction, option->rect, rect); + } break; - case SC_TitleBarSysMenu: - { - const int controlTop = int(6 * factor); - const int controlHeight = int(height - controlTop - 3 * factor); - int iconExtent = proxy()->pixelMetric(PM_SmallIconSize, option); - QSize iconSize = tb->icon.actualSize(QSize(iconExtent, iconExtent)); - if (tb->icon.isNull()) - iconSize = QSize(controlHeight, controlHeight); - int hPad = (controlHeight - iconSize.height())/2; - int vPad = (controlHeight - iconSize.width())/2; - rect = QRect(frameWidth + hPad, controlTop + vPad, iconSize.width(), iconSize.height()); - rect.translate(0, int(3 * factor)); - rect = visualRect(option->direction, option->rect, rect); - } + + case SC_TitleBarContextHelpButton: + if (tb->titleBarFlags & Qt::WindowContextHelpButtonHint) + offset += delta; + Q_FALLTHROUGH(); + case SC_TitleBarMinButton: + if (!isMinimized && (tb->titleBarFlags & Qt::WindowMinimizeButtonHint)) + offset += delta; + else if (subControl == SC_TitleBarMinButton) + break; + Q_FALLTHROUGH(); + case SC_TitleBarNormalButton: + if (isMinimized && (tb->titleBarFlags & Qt::WindowMinimizeButtonHint)) + offset += delta; + else if (isMaximized && (tb->titleBarFlags & Qt::WindowMaximizeButtonHint)) + offset += delta; + else if (subControl == SC_TitleBarNormalButton) + break; + Q_FALLTHROUGH(); + case SC_TitleBarMaxButton: + if (!isMaximized && (tb->titleBarFlags & Qt::WindowMaximizeButtonHint)) + offset += delta; + else if (subControl == SC_TitleBarMaxButton) + break; + Q_FALLTHROUGH(); + case SC_TitleBarShadeButton: + if (!isMinimized && (tb->titleBarFlags & Qt::WindowShadeButtonHint)) + offset += delta; + else if (subControl == SC_TitleBarShadeButton) + break; + Q_FALLTHROUGH(); + case SC_TitleBarUnshadeButton: + if (isMinimized && (tb->titleBarFlags & Qt::WindowShadeButtonHint)) + offset += delta; + else if (subControl == SC_TitleBarUnshadeButton) + break; + Q_FALLTHROUGH(); + case SC_TitleBarCloseButton: + if (tb->titleBarFlags & Qt::WindowSystemMenuHint) + offset += delta; + else if (subControl == SC_TitleBarCloseButton) + break; + + rect.setRect(width - offset - controlTop + 1, controlTop, + buttonWidth, buttonHeight); + break; + + case SC_TitleBarSysMenu: { + const int controlTop = int(6 * factor); + const int controlHeight = int(height - controlTop - 3 * factor); + int iconExtent = proxy()->pixelMetric(PM_SmallIconSize, option); + QSize iconSize = tb->icon.actualSize(QSize(iconExtent, iconExtent)); + if (tb->icon.isNull()) + iconSize = QSize(controlHeight, controlHeight); + int hPad = (controlHeight - iconSize.height())/2; + int vPad = (controlHeight - iconSize.width())/2; + rect = QRect(frameWidth + hPad, controlTop + vPad, iconSize.width(), iconSize.height()); + rect.translate(0, int(3 * factor)); + } break; + default: break; } } break; - default: + +#if QT_CONFIG(mdiarea) + case CC_MdiControls: { + int numSubControls = 0; + if (option->subControls & SC_MdiCloseButton) + ++numSubControls; + if (option->subControls & SC_MdiMinButton) + ++numSubControls; + if (option->subControls & SC_MdiNormalButton) + ++numSubControls; + if (numSubControls == 0) + break; + + int buttonWidth = option->rect.width() / numSubControls; + int offset = 0; + + switch (subControl) { + case SC_MdiCloseButton: + // Only one sub control, no offset needed. + if (numSubControls == 1) + break; + offset += buttonWidth; + Q_FALLTHROUGH(); + case SC_MdiNormalButton: + // No offset needed if + // 1) There's only one sub control + // 2) We have a close button and a normal button (offset already added in SC_MdiClose) + if (numSubControls == 1 || (numSubControls == 2 && !(option->subControls & SC_MdiMinButton))) + break; + if (option->subControls & SC_MdiNormalButton) + offset += buttonWidth; + break; + default: + break; + } + + rect = QRect(offset, 0, buttonWidth, option->rect.height()); break; } - return rect; +#endif // QT_CONFIG(mdiarea) + + default: + return visualRect(option->direction, option->rect, + QWindowsStyle::subControlRect(control, option, subControl, widget)); + } + + return visualRect(option->direction, option->rect, rect); } /*! \internal */ QStyle::SubControl QWindowsVistaStyle::hitTestComplexControl(ComplexControl control, const QStyleOptionComplex *option, - const QPoint &pos, const QWidget *widget) const -{ - if (!QWindowsVistaStylePrivate::useVista()) { - return QWindowsStyle::hitTestComplexControl(control, option, pos, widget); - } - return QWindowsXPStyle::hitTestComplexControl(control, option, pos, widget); -} - -int QWindowsVistaStylePrivate::fixedPixelMetric(QStyle::PixelMetric pm) + const QPoint &pos, const QWidget *widget) const { - switch (pm) { - case QStyle::PM_DockWidgetTitleBarButtonMargin: - return 5; - case QStyle::PM_ScrollBarSliderMin: - return 18; - case QStyle::PM_MenuHMargin: - case QStyle::PM_MenuVMargin: - return 0; - case QStyle::PM_MenuPanelWidth: - return 3; - default: - break; - } - return QWindowsVistaStylePrivate::InvalidMetric; + return QWindowsStyle::hitTestComplexControl(control, option, pos, widget); } /*! @@ -2245,7 +4515,82 @@ int QWindowsVistaStyle::pixelMetric(PixelMetric metric, const QStyleOption *opti if (ret != QWindowsStylePrivate::InvalidMetric) return int(QStyleHelper::dpiScaled(ret, option)); - return QWindowsXPStyle::pixelMetric(metric, option, widget); + int res = QWindowsVistaStylePrivate::pixelMetricFromSystemDp(metric, option, widget); + if (res != QWindowsStylePrivate::InvalidMetric) + return qRound(qreal(res) * QWindowsStylePrivate::nativeMetricScaleFactor(widget)); + + res = 0; + + switch (metric) { + case PM_MenuBarPanelWidth: + case PM_ButtonDefaultIndicator: + res = 0; + break; + + case PM_DefaultFrameWidth: + res = qobject_cast(widget) ? 2 : 1; + break; + case PM_MenuPanelWidth: + case PM_SpinBoxFrameWidth: + res = 1; + break; + + case PM_TabBarTabOverlap: + case PM_MenuHMargin: + case PM_MenuVMargin: + res = 2; + break; + + case PM_TabBarBaseOverlap: + if (const auto *tab = qstyleoption_cast(option)) { + switch (tab->shape) { + case QTabBar::RoundedNorth: + case QTabBar::TriangularNorth: + case QTabBar::RoundedWest: + case QTabBar::TriangularWest: + res = 1; + break; + case QTabBar::RoundedSouth: + case QTabBar::TriangularSouth: + res = 2; + break; + case QTabBar::RoundedEast: + case QTabBar::TriangularEast: + res = 3; + break; + } + } + break; + + case PM_SplitterWidth: + res = QStyleHelper::dpiScaled(5., option); + break; + + case PM_MdiSubWindowMinimizedWidth: + res = 160; + break; + +#if QT_CONFIG(toolbar) + case PM_ToolBarHandleExtent: + res = int(QStyleHelper::dpiScaled(8., option)); + break; + +#endif // QT_CONFIG(toolbar) + case PM_DockWidgetSeparatorExtent: + case PM_DockWidgetTitleMargin: + res = int(QStyleHelper::dpiScaled(4., option)); + break; + + case PM_ButtonShiftHorizontal: + case PM_ButtonShiftVertical: + res = qstyleoption_cast(option) ? 1 : 0; + break; + + default: + res = QWindowsStyle::pixelMetric(metric, option, widget); + } + + return res; } /*! @@ -2253,62 +4598,90 @@ int QWindowsVistaStyle::pixelMetric(PixelMetric metric, const QStyleOption *opti */ void QWindowsVistaStyle::polish(QWidget *widget) { - QWindowsXPStyle::polish(widget); + QWindowsStyle::polish(widget); + if (!QWindowsVistaStylePrivate::useVista()) + return; + + if (false +#if QT_CONFIG(abstractbutton) + || qobject_cast(widget) +#endif // QT_CONFIG(abstractbutton) + || qobject_cast(widget) + || qobject_cast(widget) +#if QT_CONFIG(combobox) + || qobject_cast(widget) +#endif // QT_CONFIG(combobox) + || qobject_cast(widget) + || qobject_cast(widget) + || qobject_cast(widget) +#if QT_CONFIG(spinbox) + || qobject_cast(widget) + || qobject_cast(widget) +#endif // QT_CONFIG(spinbox) + ) { + widget->setAttribute(Qt::WA_Hover); + } + +#if QT_CONFIG(rubberband) + if (qobject_cast(widget)) + widget->setWindowOpacity(0.6); +#endif + #if QT_CONFIG(lineedit) if (qobject_cast(widget)) widget->setAttribute(Qt::WA_Hover); else #endif // QT_CONFIG(lineedit) - if (qobject_cast(widget)) - widget->setAttribute(Qt::WA_Hover); + if (qobject_cast(widget)) + widget->setAttribute(Qt::WA_Hover); #if QT_CONFIG(commandlinkbutton) - else if (qobject_cast(widget)) { - widget->setProperty("_qt_usingVistaStyle", true); - QFont buttonFont = widget->font(); - buttonFont.setFamilies(QStringList{QLatin1String("Segoe UI")}); - widget->setFont(buttonFont); - QPalette pal = widget->palette(); - pal.setColor(QPalette::ButtonText, QColor(21, 28, 85)); - pal.setColor(QPalette::BrightText, QColor(7, 64, 229)); - widget->setPalette(pal); - } -#endif // QT_CONFIG(commandlinkbutton) - else if (widget->inherits("QTipLabel")){ - //note that since tooltips are not reused - //we do not have to care about unpolishing - widget->setContentsMargins(3, 0, 4, 0); - COLORREF bgRef; - HTHEME theme = OpenThemeData(widget ? QWindowsVistaStylePrivate::winId(widget) : nullptr, L"TOOLTIP"); - if (theme && SUCCEEDED(GetThemeColor(theme, TTP_STANDARD, TTSS_NORMAL, TMT_TEXTCOLOR, &bgRef))) { - QColor textColor = QColor::fromRgb(bgRef); - QPalette pal; - pal.setColor(QPalette::All, QPalette::ToolTipText, textColor); + else if (qobject_cast(widget)) { + widget->setProperty("_qt_usingVistaStyle", true); + QFont buttonFont = widget->font(); + buttonFont.setFamilies(QStringList{QLatin1String("Segoe UI")}); + widget->setFont(buttonFont); + QPalette pal = widget->palette(); + pal.setColor(QPalette::ButtonText, QColor(21, 28, 85)); + pal.setColor(QPalette::BrightText, QColor(7, 64, 229)); widget->setPalette(pal); } - } else if (qobject_cast (widget)) { - widget->setAttribute(Qt::WA_StyledBackground); +#endif // QT_CONFIG(commandlinkbutton) + else if (widget->inherits("QTipLabel")) { + //note that since tooltips are not reused + //we do not have to care about unpolishing + widget->setContentsMargins(3, 0, 4, 0); + COLORREF bgRef; + HTHEME theme = OpenThemeData(widget ? QWindowsVistaStylePrivate::winId(widget) : nullptr, L"TOOLTIP"); + if (theme && SUCCEEDED(GetThemeColor(theme, TTP_STANDARD, TTSS_NORMAL, TMT_TEXTCOLOR, &bgRef))) { + QColor textColor = QColor::fromRgb(bgRef); + QPalette pal; + pal.setColor(QPalette::All, QPalette::ToolTipText, textColor); + widget->setPalette(pal); + } + } else if (qobject_cast (widget)) { + widget->setAttribute(Qt::WA_StyledBackground); #if QT_CONFIG(dialogbuttonbox) - QDialogButtonBox *buttonBox = widget->findChild(QLatin1String("qt_msgbox_buttonbox")); - if (buttonBox) - buttonBox->setContentsMargins(0, 9, 0, 0); + QDialogButtonBox *buttonBox = widget->findChild(QLatin1String("qt_msgbox_buttonbox")); + if (buttonBox) + buttonBox->setContentsMargins(0, 9, 0, 0); #endif - } + } #if QT_CONFIG(inputdialog) - else if (qobject_cast (widget)) { - widget->setAttribute(Qt::WA_StyledBackground); + else if (qobject_cast (widget)) { + widget->setAttribute(Qt::WA_StyledBackground); #if QT_CONFIG(dialogbuttonbox) - QDialogButtonBox *buttonBox = widget->findChild(QLatin1String("qt_inputdlg_buttonbox")); - if (buttonBox) - buttonBox->setContentsMargins(0, 9, 0, 0); + QDialogButtonBox *buttonBox = widget->findChild(QLatin1String("qt_inputdlg_buttonbox")); + if (buttonBox) + buttonBox->setContentsMargins(0, 9, 0, 0); #endif - } + } #endif // QT_CONFIG(inputdialog) - else if (QTreeView *tree = qobject_cast (widget)) { - tree->viewport()->setAttribute(Qt::WA_Hover); - } - else if (QListView *list = qobject_cast (widget)) { - list->viewport()->setAttribute(Qt::WA_Hover); - } + else if (QTreeView *tree = qobject_cast (widget)) { + tree->viewport()->setAttribute(Qt::WA_Hover); + } + else if (QListView *list = qobject_cast (widget)) { + list->viewport()->setAttribute(Qt::WA_Hover); + } } /*! @@ -2316,48 +4689,88 @@ void QWindowsVistaStyle::polish(QWidget *widget) */ void QWindowsVistaStyle::unpolish(QWidget *widget) { - QWindowsXPStyle::unpolish(widget); + Q_D(QWindowsVistaStyle); - QWindowsVistaStylePrivate *d = d_func(); +#if QT_CONFIG(rubberband) + if (qobject_cast(widget)) + widget->setWindowOpacity(1.0); +#endif + + // Unpolish of widgets is the first thing that + // happens when a theme changes, or the theme + // engine is turned off. So we detect it here. + bool oldState = QWindowsVistaStylePrivate::useVista(); + bool newState = QWindowsVistaStylePrivate::useVista(true); + if ((oldState != newState) && newState) { + d->cleanup(true); + d->init(true); + } else { + // Cleanup handle map, if just changing style, + // or turning it on. In both cases the values + // already in the map might be old (other style). + d->cleanupHandleMap(); + } + if (false + #if QT_CONFIG(abstractbutton) + || qobject_cast(widget) + #endif + || qobject_cast(widget) + || qobject_cast(widget) + #if QT_CONFIG(combobox) + || qobject_cast(widget) + #endif // QT_CONFIG(combobox) + || qobject_cast(widget) + || qobject_cast(widget) + || qobject_cast(widget) + #if QT_CONFIG(spinbox) + || qobject_cast(widget) + || qobject_cast(widget) + #endif // QT_CONFIG(spinbox) + ) { + widget->setAttribute(Qt::WA_Hover, false); + } + + QWindowsStyle::unpolish(widget); d->stopAnimation(widget); #if QT_CONFIG(lineedit) if (qobject_cast(widget)) widget->setAttribute(Qt::WA_Hover, false); - else + else { #endif // QT_CONFIG(lineedit) - if (qobject_cast(widget)) - widget->setAttribute(Qt::WA_Hover, false); - else if (qobject_cast (widget)) { - widget->setAttribute(Qt::WA_StyledBackground, false); + if (qobject_cast(widget)) + widget->setAttribute(Qt::WA_Hover, false); + else if (qobject_cast (widget)) { + widget->setAttribute(Qt::WA_StyledBackground, false); #if QT_CONFIG(dialogbuttonbox) - QDialogButtonBox *buttonBox = widget->findChild(QLatin1String("qt_msgbox_buttonbox")); - if (buttonBox) - buttonBox->setContentsMargins(0, 0, 0, 0); + QDialogButtonBox *buttonBox = widget->findChild(QLatin1String("qt_msgbox_buttonbox")); + if (buttonBox) + buttonBox->setContentsMargins(0, 0, 0, 0); #endif - } + } #if QT_CONFIG(inputdialog) - else if (qobject_cast (widget)) { - widget->setAttribute(Qt::WA_StyledBackground, false); + else if (qobject_cast (widget)) { + widget->setAttribute(Qt::WA_StyledBackground, false); #if QT_CONFIG(dialogbuttonbox) - QDialogButtonBox *buttonBox = widget->findChild(QLatin1String("qt_inputdlg_buttonbox")); - if (buttonBox) - buttonBox->setContentsMargins(0, 0, 0, 0); + QDialogButtonBox *buttonBox = widget->findChild(QLatin1String("qt_inputdlg_buttonbox")); + if (buttonBox) + buttonBox->setContentsMargins(0, 0, 0, 0); #endif - } + } #endif // QT_CONFIG(inputdialog) - else if (QTreeView *tree = qobject_cast (widget)) { - tree->viewport()->setAttribute(Qt::WA_Hover, false); - } + else if (QTreeView *tree = qobject_cast (widget)) { + tree->viewport()->setAttribute(Qt::WA_Hover, false); + } #if QT_CONFIG(commandlinkbutton) - else if (qobject_cast(widget)) { - QFont font = QApplication::font("QCommandLinkButton"); - QFont widgetFont = widget->font(); - widgetFont.setFamilies(font.families()); //Only family set by polish - widget->setFont(widgetFont); - } + else if (qobject_cast(widget)) { + QFont font = QApplication::font("QCommandLinkButton"); + QFont widgetFont = widget->font(); + widgetFont.setFamilies(font.families()); //Only family set by polish + widget->setFont(widgetFont); + } #endif // QT_CONFIG(commandlinkbutton) + } } /*! @@ -2365,6 +4778,26 @@ void QWindowsVistaStyle::unpolish(QWidget *widget) */ void QWindowsVistaStyle::polish(QPalette &pal) { + Q_D(QWindowsVistaStyle); + + QPixmapCache::clear(); + d->alphaCache.clear(); + d->hasInitColors = false; + + if (!d->hasInitColors) { + // Get text color for group box labels + QWindowsThemeData theme(nullptr, nullptr, QWindowsVistaStylePrivate::ButtonTheme, 0, 0); + COLORREF cref; + GetThemeColor(theme.handle(), BP_GROUPBOX, GBS_NORMAL, TMT_TEXTCOLOR, &cref); + d->groupBoxTextColor = qRgb(GetRValue(cref), GetGValue(cref), GetBValue(cref)); + GetThemeColor(theme.handle(), BP_GROUPBOX, GBS_DISABLED, TMT_TEXTCOLOR, &cref); + d->groupBoxTextColorDisabled = qRgb(GetRValue(cref), GetGValue(cref), GetBValue(cref)); + // Where does this color come from? + //GetThemeColor(theme.handle(), TKP_TICS, TSS_NORMAL, TMT_COLOR, &cref); + d->sliderTickColor = qRgb(165, 162, 148); + d->hasInitColors = true; + } + QWindowsStyle::polish(pal); pal.setBrush(QPalette::AlternateBase, pal.base().color().darker(104)); } @@ -2373,23 +4806,31 @@ void QWindowsVistaStyle::polish(QPalette &pal) \internal */ QPixmap QWindowsVistaStyle::standardPixmap(StandardPixmap standardPixmap, const QStyleOption *option, - const QWidget *widget) const + const QWidget *widget) const { if (!QWindowsVistaStylePrivate::useVista()) { return QWindowsStyle::standardPixmap(standardPixmap, option, widget); } - return QWindowsXPStyle::standardPixmap(standardPixmap, option, widget); -} -bool QWindowsVistaStylePrivate::transitionsEnabled() const -{ - BOOL animEnabled = false; - if (SystemParametersInfo(SPI_GETCLIENTAREAANIMATION, 0, &animEnabled, 0)) - { - if (animEnabled) - return true; + switch (standardPixmap) { + case SP_TitleBarMaxButton: + case SP_TitleBarCloseButton: + if (qstyleoption_cast(option)) { + if (widget && widget->isWindow()) { + QWindowsThemeData theme(widget, nullptr, QWindowsVistaStylePrivate::WindowTheme, WP_SMALLCLOSEBUTTON, CBS_NORMAL); + if (theme.isValid()) { + const QSize size = (theme.size() * QWindowsStylePrivate::nativeMetricScaleFactor(widget)).toSize(); + return QIcon(QWindowsStyle::standardPixmap(standardPixmap, option, widget)).pixmap(size); + } + } + } + break; + + default: + break; } - return false; + + return QWindowsStyle::standardPixmap(standardPixmap, option, widget); } /*! @@ -2403,46 +4844,151 @@ QIcon QWindowsVistaStyle::standardIcon(StandardPixmap standardIcon, return QWindowsStyle::standardIcon(standardIcon, option, widget); } - QWindowsVistaStylePrivate *d = const_cast(d_func()); - switch(standardIcon) { - case SP_CommandLink: - { - XPThemeData theme(nullptr, nullptr, - QWindowsXPStylePrivate::ButtonTheme, - BP_COMMANDLINKGLYPH, CMDLGS_NORMAL); - if (theme.isValid()) { - const QSize size = theme.size().toSize(); - QIcon linkGlyph; - QPixmap pm(size); - pm.fill(Qt::transparent); - QPainter p(&pm); - theme.painter = &p; - theme.rect = QRect(QPoint(0, 0), size); - d->drawBackground(theme); - linkGlyph.addPixmap(pm, QIcon::Normal, QIcon::Off); // Normal - pm.fill(Qt::transparent); + auto *d = const_cast(d_func()); - theme.stateId = CMDLGS_PRESSED; - d->drawBackground(theme); - linkGlyph.addPixmap(pm, QIcon::Normal, QIcon::On); // Pressed - pm.fill(Qt::transparent); + switch (standardIcon) { + case SP_TitleBarMaxButton: + if (qstyleoption_cast(option)) { + if (d->dockFloat.isNull()) { + QWindowsThemeData themeSize(nullptr, nullptr, QWindowsVistaStylePrivate::WindowTheme, + WP_SMALLCLOSEBUTTON, CBS_NORMAL); + QWindowsThemeData theme(nullptr, nullptr, QWindowsVistaStylePrivate::WindowTheme, + WP_MAXBUTTON, MAXBS_NORMAL); + if (theme.isValid()) { + const QSize size = (themeSize.size() * QWindowsStylePrivate::nativeMetricScaleFactor(widget)).toSize(); + QPixmap pm(size); + pm.fill(Qt::transparent); + QPainter p(&pm); + theme.painter = &p; + theme.rect = QRect(QPoint(0, 0), size); + d->drawBackground(theme); + d->dockFloat.addPixmap(pm, QIcon::Normal, QIcon::Off); // Normal + pm.fill(Qt::transparent); + theme.stateId = MAXBS_PUSHED; + d->drawBackground(theme); + d->dockFloat.addPixmap(pm, QIcon::Normal, QIcon::On); // Pressed + pm.fill(Qt::transparent); + theme.stateId = MAXBS_HOT; + d->drawBackground(theme); + d->dockFloat.addPixmap(pm, QIcon::Active, QIcon::Off); // Hover + pm.fill(Qt::transparent); + theme.stateId = MAXBS_INACTIVE; + d->drawBackground(theme); + d->dockFloat.addPixmap(pm, QIcon::Disabled, QIcon::Off); // Disabled + } + } + if (widget && widget->isWindow()) + return d->dockFloat; + } + break; - theme.stateId = CMDLGS_HOT; - d->drawBackground(theme); - linkGlyph.addPixmap(pm, QIcon::Active, QIcon::Off); // Hover - pm.fill(Qt::transparent); + case SP_TitleBarCloseButton: + if (qstyleoption_cast(option)) { + if (d->dockClose.isNull()) { + QWindowsThemeData theme(nullptr, nullptr, QWindowsVistaStylePrivate::WindowTheme, + WP_SMALLCLOSEBUTTON, CBS_NORMAL); + if (theme.isValid()) { + const QSize size = (theme.size() * QWindowsStylePrivate::nativeMetricScaleFactor(widget)).toSize(); + QPixmap pm(size); + pm.fill(Qt::transparent); + QPainter p(&pm); + theme.painter = &p; + theme.partId = WP_CLOSEBUTTON; // #### + theme.rect = QRect(QPoint(0, 0), size); + d->drawBackground(theme); + d->dockClose.addPixmap(pm, QIcon::Normal, QIcon::Off); // Normal + pm.fill(Qt::transparent); + theme.stateId = CBS_PUSHED; + d->drawBackground(theme); + d->dockClose.addPixmap(pm, QIcon::Normal, QIcon::On); // Pressed + pm.fill(Qt::transparent); + theme.stateId = CBS_HOT; + d->drawBackground(theme); + d->dockClose.addPixmap(pm, QIcon::Active, QIcon::Off); // Hover + pm.fill(Qt::transparent); + theme.stateId = CBS_INACTIVE; + d->drawBackground(theme); + d->dockClose.addPixmap(pm, QIcon::Disabled, QIcon::Off); // Disabled + } + } + if (widget && widget->isWindow()) + return d->dockClose; + } + break; - theme.stateId = CMDLGS_DISABLED; - d->drawBackground(theme); - linkGlyph.addPixmap(pm, QIcon::Disabled, QIcon::Off); // Disabled - return linkGlyph; + case SP_TitleBarNormalButton: + if (qstyleoption_cast(option)) { + if (d->dockFloat.isNull()) { + QWindowsThemeData themeSize(nullptr, nullptr, QWindowsVistaStylePrivate::WindowTheme, + WP_SMALLCLOSEBUTTON, CBS_NORMAL); + QWindowsThemeData theme(nullptr, nullptr, QWindowsVistaStylePrivate::WindowTheme, + WP_RESTOREBUTTON, RBS_NORMAL); + if (theme.isValid()) { + const QSize size = (themeSize.size() * QWindowsStylePrivate::nativeMetricScaleFactor(widget)).toSize(); + QPixmap pm(size); + pm.fill(Qt::transparent); + QPainter p(&pm); + theme.painter = &p; + theme.rect = QRect(QPoint(0, 0), size); + d->drawBackground(theme); + d->dockFloat.addPixmap(pm, QIcon::Normal, QIcon::Off); // Normal + pm.fill(Qt::transparent); + theme.stateId = RBS_PUSHED; + d->drawBackground(theme); + d->dockFloat.addPixmap(pm, QIcon::Normal, QIcon::On); // Pressed + pm.fill(Qt::transparent); + theme.stateId = RBS_HOT; + d->drawBackground(theme); + d->dockFloat.addPixmap(pm, QIcon::Active, QIcon::Off); // Hover + pm.fill(Qt::transparent); + theme.stateId = RBS_INACTIVE; + d->drawBackground(theme); + d->dockFloat.addPixmap(pm, QIcon::Disabled, QIcon::Off); // Disabled + } } + if (widget && widget->isWindow()) + return d->dockFloat; + } + break; + + case SP_CommandLink: { + QWindowsThemeData theme(nullptr, nullptr, QWindowsVistaStylePrivate::ButtonTheme, + BP_COMMANDLINKGLYPH, CMDLGS_NORMAL); + if (theme.isValid()) { + const QSize size = theme.size().toSize(); + QIcon linkGlyph; + QPixmap pm(size); + pm.fill(Qt::transparent); + QPainter p(&pm); + theme.painter = &p; + theme.rect = QRect(QPoint(0, 0), size); + d->drawBackground(theme); + linkGlyph.addPixmap(pm, QIcon::Normal, QIcon::Off); // Normal + pm.fill(Qt::transparent); + + theme.stateId = CMDLGS_PRESSED; + d->drawBackground(theme); + linkGlyph.addPixmap(pm, QIcon::Normal, QIcon::On); // Pressed + pm.fill(Qt::transparent); + + theme.stateId = CMDLGS_HOT; + d->drawBackground(theme); + linkGlyph.addPixmap(pm, QIcon::Active, QIcon::Off); // Hover + pm.fill(Qt::transparent); + + theme.stateId = CMDLGS_DISABLED; + d->drawBackground(theme); + linkGlyph.addPixmap(pm, QIcon::Disabled, QIcon::Off); // Disabled + return linkGlyph; } break; + } + default: break; } - return QWindowsXPStyle::standardIcon(standardIcon, option, widget); + + return QWindowsStyle::standardIcon(standardIcon, option, widget); } QT_END_NAMESPACE diff --git a/src/plugins/styles/windowsvista/qwindowsvistastyle_p.h b/src/plugins/styles/windowsvista/qwindowsvistastyle_p.h index 9d31d5dfee..b600491248 100644 --- a/src/plugins/styles/windowsvista/qwindowsvistastyle_p.h +++ b/src/plugins/styles/windowsvista/qwindowsvistastyle_p.h @@ -16,12 +16,12 @@ // #include -#include "qwindowsxpstyle_p.h" +#include QT_BEGIN_NAMESPACE class QWindowsVistaStylePrivate; -class QWindowsVistaStyle : public QWindowsXPStyle +class QWindowsVistaStyle : public QWindowsStyle { Q_OBJECT public: diff --git a/src/plugins/styles/windowsvista/qwindowsvistastyle_p_p.h b/src/plugins/styles/windowsvista/qwindowsvistastyle_p_p.h index 03aa4fdf9a..7705d3200e 100644 --- a/src/plugins/styles/windowsvista/qwindowsvistastyle_p_p.h +++ b/src/plugins/styles/windowsvista/qwindowsvistastyle_p_p.h @@ -17,14 +17,16 @@ #include #include "qwindowsvistastyle_p.h" -#include "qwindowsxpstyle_p_p.h" -#include +#include "qwindowsthemedata_p.h" #include #include #include #include #include #include +#include +#include + #if QT_CONFIG(pushbutton) #include #endif @@ -73,93 +75,102 @@ #if QT_CONFIG(commandlinkbutton) #include #endif +#include +#include QT_BEGIN_NAMESPACE -#if !defined(SCHEMA_VERIFY_VSSYM32) -#define TMT_ANIMATIONDURATION 5006 -#define TMT_TRANSITIONDURATIONS 6000 -#define EP_EDITBORDER_NOSCROLL 6 -#define EP_EDITBORDER_HVSCROLL 9 -#define EP_BACKGROUND 3 -#define EBS_NORMAL 1 -#define EBS_HOT 2 -#define EBS_DISABLED 3 -#define EBS_READONLY 5 -#define PBS_DEFAULTED_ANIMATING 6 -#define MBI_NORMAL 1 -#define MBI_HOT 2 -#define MBI_PUSHED 3 -#define MBI_DISABLED 4 -#define MB_ACTIVE 1 -#define MB_INACTIVE 2 -#define PP_FILL 5 -#define PP_FILLVERT 6 -#define PP_MOVEOVERLAY 8 -#define PP_MOVEOVERLAYVERT 10 -#define MENU_BARBACKGROUND 7 -#define MENU_BARITEM 8 -#define MENU_POPUPCHECK 11 -#define MENU_POPUPCHECKBACKGROUND 12 -#define MENU_POPUPGUTTER 13 -#define MENU_POPUPITEM 14 -#define MENU_POPUPBORDERS 10 -#define MENU_POPUPSEPARATOR 15 -#define MC_CHECKMARKNORMAL 1 -#define MC_CHECKMARKDISABLED 2 -#define MC_BULLETNORMAL 3 -#define MC_BULLETDISABLED 4 -#define ABS_UPHOVER 17 -#define ABS_DOWNHOVER 18 -#define ABS_LEFTHOVER 19 -#define ABS_RIGHTHOVER 20 -#define CP_DROPDOWNBUTTONRIGHT 6 -#define CP_DROPDOWNBUTTONLEFT 7 -#define SCRBS_HOVER 5 -#define TVP_HOTGLYPH 4 -#define SPI_GETCLIENTAREAANIMATION 0x1042 -#define TDLG_PRIMARYPANEL 1 -#define TDLG_SECONDARYPANEL 8 -#endif - -class QWindowsVistaAnimation : public QBlendStyleAnimation +class QWindowsVistaStylePrivate : public QWindowsStylePrivate { - Q_OBJECT -public: - QWindowsVistaAnimation(Type type, QObject *target) : QBlendStyleAnimation(type, target) { } - - bool isUpdateNeeded() const override; - void paint(QPainter *painter, const QStyleOption *option); -}; - + Q_DECLARE_PUBLIC(QWindowsVistaStyle) -// Handles state transition animations -class QWindowsVistaTransition : public QWindowsVistaAnimation -{ - Q_OBJECT public: - QWindowsVistaTransition(QObject *target) : QWindowsVistaAnimation(Transition, target) {} -}; + enum Theme { + ButtonTheme, + ComboboxTheme, + EditTheme, + HeaderTheme, + ListViewTheme, + MenuTheme, + ProgressTheme, + RebarTheme, + ScrollBarTheme, + SpinTheme, + TabTheme, + TaskDialogTheme, + ToolBarTheme, + ToolTipTheme, + TrackBarTheme, + WindowTheme, + StatusTheme, + VistaTreeViewTheme, // arrow shape treeview indicators (Vista) obtained from "explorer" theme. + NThemes + }; + + QWindowsVistaStylePrivate() + { init(); } + + ~QWindowsVistaStylePrivate() + { cleanup(); } + + static HTHEME createTheme(int theme, HWND hwnd); + static QString themeName(int theme); + static inline bool hasTheme(int theme) { return theme >= 0 && theme < NThemes && m_themes[theme]; } + static bool isItemViewDelegateLineEdit(const QWidget *widget); + static int pixelMetricFromSystemDp(QStyle::PixelMetric pm, const QStyleOption *option = nullptr, const QWidget *widget = nullptr); + static int fixedPixelMetric(QStyle::PixelMetric pm); + static bool isLineEditBaseColorSet(const QStyleOption *option, const QWidget *widget); + static QRect scrollBarGripperBounds(QStyle::State flags, const QWidget *widget, QWindowsThemeData *theme); + static HWND winId(const QWidget *widget); + static bool useVista(bool update = false); + static QBackingStore *backingStoreForWidget(const QWidget *widget); + static HDC hdcForWidgetBackingStore(const QWidget *widget); + + void init(bool force = false); + void cleanup(bool force = false); + void cleanupHandleMap(); + + HBITMAP buffer(int w = 0, int h = 0); + HDC bufferHDC() + { return bufferDC; } + + bool isTransparent(QWindowsThemeData &QWindowsThemeData); + QRegion region(QWindowsThemeData &QWindowsThemeData); + + bool drawBackground(QWindowsThemeData &QWindowsThemeData, qreal correctionFactor = 1); + bool drawBackgroundThruNativeBuffer(QWindowsThemeData &QWindowsThemeData, qreal aditionalDevicePixelRatio, qreal correctionFactor); + bool drawBackgroundDirectly(HDC dc, QWindowsThemeData &QWindowsThemeData, qreal aditionalDevicePixelRatio); + + bool hasAlphaChannel(const QRect &rect); + bool fixAlphaChannel(const QRect &rect); + bool swapAlphaChannel(const QRect &rect, bool allPixels = false); + + QRgb groupBoxTextColor = 0; + QRgb groupBoxTextColorDisabled = 0; + QRgb sliderTickColor = 0; + bool hasInitColors = false; + QIcon dockFloat, dockClose; + QTime animationTime() const; + bool transitionsEnabled() const; -// Handles pulse animations (default buttons) -class QWindowsVistaPulse: public QWindowsVistaAnimation -{ - Q_OBJECT -public: - QWindowsVistaPulse(QObject *target) : QWindowsVistaAnimation(Pulse, target) {} -}; +private: + static bool initVistaTreeViewTheming(); + static void cleanupVistaTreeViewTheming(); + static QBasicAtomicInt ref; + static bool useVistaTheme; -class QWindowsVistaStylePrivate : public QWindowsXPStylePrivate -{ - Q_DECLARE_PUBLIC(QWindowsVistaStyle) + QHash alphaCache; + HDC bufferDC = nullptr; + HBITMAP bufferBitmap = nullptr; + HBITMAP nullBitmap = nullptr; + uchar *bufferPixels = nullptr; + int bufferW = 0; + int bufferH = 0; -public: - static int fixedPixelMetric(QStyle::PixelMetric pm); - static inline bool useVista(); - QTime animationTime() const; - bool transitionsEnabled() const; + static HWND m_vistaTreeViewHelper; + static HTHEME m_themes[NThemes]; }; QT_END_NAMESPACE diff --git a/src/plugins/styles/windowsvista/qwindowsxpstyle.cpp b/src/plugins/styles/windowsvista/qwindowsxpstyle.cpp deleted file mode 100644 index 8c4fe81d34..0000000000 --- a/src/plugins/styles/windowsvista/qwindowsxpstyle.cpp +++ /dev/null @@ -1,3669 +0,0 @@ -// Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only -#include "qwindowsxpstyle_p.h" -#include "qwindowsxpstyle_p_p.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#if QT_CONFIG(toolbutton) -#include -#endif -#if QT_CONFIG(tabbar) -#include -#endif -#if QT_CONFIG(combobox) -#include -#endif -#if QT_CONFIG(scrollbar) -#include -#endif -#include -#if QT_CONFIG(spinbox) -#include -#endif -#if QT_CONFIG(listview) -#include -#endif -#if QT_CONFIG(stackedwidget) -#include -#endif -#if QT_CONFIG(pushbutton) -#include -#endif -#if QT_CONFIG(toolbar) -#include -#endif -#include -#include -#include - -#include - -QT_BEGIN_NAMESPACE - -// General const values -static const int windowsItemFrame = 2; // menu item frame width -static const int windowsItemHMargin = 3; // menu item hor text margin -static const int windowsItemVMargin = 0; // menu item ver text margin -static const int windowsArrowHMargin = 6; // arrow horizontal margin -static const int windowsRightBorder = 12; // right border on windows - -// Theme names matching the QWindowsXPStylePrivate::Theme enumeration. -static const wchar_t *themeNames[QWindowsXPStylePrivate::NThemes] = -{ - L"BUTTON", L"COMBOBOX", L"EDIT", L"HEADER", L"LISTVIEW", - L"MENU", L"PROGRESS", L"REBAR", L"SCROLLBAR", L"SPIN", - L"TAB", L"TASKDIALOG", L"TOOLBAR", L"TOOLTIP", L"TRACKBAR", - L"TREEVIEW", L"WINDOW", L"STATUS", L"TREEVIEW" -}; - -static inline QBackingStore *backingStoreForWidget(const QWidget *widget) -{ - if (QBackingStore *backingStore = widget->backingStore()) - return backingStore; - if (const QWidget *topLevel = widget->nativeParentWidget()) - if (QBackingStore *topLevelBackingStore = topLevel->backingStore()) - return topLevelBackingStore; - return nullptr; -} - -static inline HDC hdcForWidgetBackingStore(const QWidget *widget) -{ - if (QBackingStore *backingStore = backingStoreForWidget(widget)) { - QPlatformNativeInterface *nativeInterface = QGuiApplication::platformNativeInterface(); - if (nativeInterface) - return static_cast(nativeInterface->nativeResourceForBackingStore(QByteArrayLiteral("getDC"), backingStore)); - } - return nullptr; -} - -// Theme data helper ------------------------------------------------------------------------------ -/* \internal - Returns \c true if the themedata is valid for use. -*/ -bool XPThemeData::isValid() -{ - return QWindowsXPStylePrivate::useXP() && theme >= 0 && handle(); -} - - -/* \internal - Returns the theme engine handle to the specific class. - If the handle hasn't been opened before, it opens the data, and - adds it to a static map, for caching. -*/ -HTHEME XPThemeData::handle() -{ - if (!QWindowsXPStylePrivate::useXP()) - return nullptr; - - if (!htheme) - htheme = QWindowsXPStylePrivate::createTheme(theme, QWindowsXPStylePrivate::winId(widget)); - return htheme; -} - -/* \internal - Converts a QRect to the native RECT structure. -*/ -RECT XPThemeData::toRECT(const QRect &qr) -{ - RECT r; - r.left = qr.x(); - r.right = qr.x() + qr.width(); - r.top = qr.y(); - r.bottom = qr.y() + qr.height(); - return r; -} - -/* \internal - Returns the native region of a part, if the part is considered - transparent. The region is scaled to the parts size (rect). -*/ -HRGN XPThemeData::mask(QWidget *widget) -{ - if (!IsThemeBackgroundPartiallyTransparent(handle(), partId, stateId)) - return nullptr; - - HRGN hrgn; - HDC dc = nullptr; - if (widget) - dc = hdcForWidgetBackingStore(widget); - RECT nativeRect = toRECT(rect); - GetThemeBackgroundRegion(handle(), dc, partId, stateId, &nativeRect, &hrgn); - return hrgn; -} - -// QWindowsXPStylePrivate ------------------------------------------------------------------------- -// Static initializations -HWND QWindowsXPStylePrivate::m_vistaTreeViewHelper = nullptr; -HTHEME QWindowsXPStylePrivate::m_themes[NThemes]; -bool QWindowsXPStylePrivate::use_xp = false; -Q_CONSTINIT QBasicAtomicInt QWindowsXPStylePrivate::ref = Q_BASIC_ATOMIC_INITIALIZER(-1); // -1 based refcounting - -static void qt_add_rect(HRGN &winRegion, QRect r) -{ - HRGN rgn = CreateRectRgn(r.left(), r.top(), r.x() + r.width(), r.y() + r.height()); - if (rgn) { - HRGN dest = CreateRectRgn(0,0,0,0); - int result = CombineRgn(dest, winRegion, rgn, RGN_OR); - if (result) { - DeleteObject(winRegion); - winRegion = dest; - } - DeleteObject(rgn); - } -} - -static HRGN qt_hrgn_from_qregion(const QRegion ®ion) -{ - HRGN hRegion = CreateRectRgn(0,0,0,0); - if (region.rectCount() == 1) { - qt_add_rect(hRegion, region.boundingRect()); - return hRegion; - } - for (const QRect &rect : region) - qt_add_rect(hRegion, rect); - return hRegion; -} - -/* \internal - Checks if the theme engine can/should be used, or if we should fall back - to Windows style. For Windows 10, this will still return false for the - High Contrast themes. -*/ -bool QWindowsXPStylePrivate::useXP(bool update) -{ - if (update) { - use_xp = IsThemeActive() && (IsAppThemed() || !QCoreApplication::instance()) - && !QWindowsStylePrivate::isDarkMode(); - } - return use_xp; -} - -/* \internal - Handles refcounting, and queries the theme engine for usage. -*/ -void QWindowsXPStylePrivate::init(bool force) -{ - if (ref.ref() && !force) - return; - if (!force) // -1 based atomic refcounting - ref.ref(); - - useXP(true); - std::fill(m_themes, m_themes + NThemes, nullptr); -} - -/* \internal - Cleans up all static data. -*/ -void QWindowsXPStylePrivate::cleanup(bool force) -{ - if (bufferBitmap) { - if (bufferDC && nullBitmap) - SelectObject(bufferDC, nullBitmap); - DeleteObject(bufferBitmap); - bufferBitmap = nullptr; - } - - if (bufferDC) - DeleteDC(bufferDC); - bufferDC = nullptr; - - if (ref.deref() && !force) - return; - if (!force) // -1 based atomic refcounting - ref.deref(); - - use_xp = false; - cleanupHandleMap(); -} - -/* In order to obtain the correct VistaTreeViewTheme (arrows for PE_IndicatorBranch), - * we need to set the windows "explorer" theme explicitly on a native - * window and open the "TREEVIEW" theme handle passing its window handle - * in order to get Vista-style item view themes (particularly drawBackground() - * for selected items needs this). - * We invoke a service of the native Windows interface to create - * a non-visible window handle, open the theme on it and insert it into - * the cache so that it is found by XPThemeData::handle() first. - */ - -static inline HWND createTreeViewHelperWindow() -{ - using QWindowsApplication = QNativeInterface::Private::QWindowsApplication; - - HWND result = nullptr; - if (auto nativeWindowsApp = dynamic_cast(QGuiApplicationPrivate::platformIntegration())) - result = nativeWindowsApp->createMessageWindow(QStringLiteral("QTreeViewThemeHelperWindowClass"), - QStringLiteral("QTreeViewThemeHelperWindow")); - return result; -} - -bool QWindowsXPStylePrivate::initVistaTreeViewTheming() -{ - if (m_vistaTreeViewHelper) - return true; - - m_vistaTreeViewHelper = createTreeViewHelperWindow(); - if (!m_vistaTreeViewHelper) { - qWarning("Unable to create the treeview helper window."); - return false; - } - if (FAILED(SetWindowTheme(m_vistaTreeViewHelper, L"explorer", nullptr))) { - qErrnoWarning("SetWindowTheme() failed."); - cleanupVistaTreeViewTheming(); - return false; - } - return true; -} - -void QWindowsXPStylePrivate::cleanupVistaTreeViewTheming() -{ - if (m_vistaTreeViewHelper) { - DestroyWindow(m_vistaTreeViewHelper); - m_vistaTreeViewHelper = nullptr; - } -} - -/* \internal - Closes all open theme data handles to ensure that we don't leak - resources, and that we don't refer to old handles when for - example the user changes the theme style. -*/ -void QWindowsXPStylePrivate::cleanupHandleMap() -{ - for (auto &theme : m_themes) { - if (theme) { - CloseThemeData(theme); - theme = nullptr; - } - } - QWindowsXPStylePrivate::cleanupVistaTreeViewTheming(); -} - -HTHEME QWindowsXPStylePrivate::createTheme(int theme, HWND hwnd) -{ - if (Q_UNLIKELY(theme < 0 || theme >= NThemes || !hwnd)) { - qWarning("Invalid parameters #%d, %p", theme, hwnd); - return nullptr; - } - if (!m_themes[theme]) { - const wchar_t *name = themeNames[theme]; - if (theme == VistaTreeViewTheme && QWindowsXPStylePrivate::initVistaTreeViewTheming()) - hwnd = QWindowsXPStylePrivate::m_vistaTreeViewHelper; - m_themes[theme] = OpenThemeData(hwnd, name); - if (Q_UNLIKELY(!m_themes[theme])) - qErrnoWarning("OpenThemeData() failed for theme %d (%s).", - theme, qPrintable(themeName(theme))); - } - return m_themes[theme]; -} - -QString QWindowsXPStylePrivate::themeName(int theme) -{ - return theme >= 0 && theme < NThemes - ? QString::fromWCharArray(themeNames[theme]) : QString(); -} - -bool QWindowsXPStylePrivate::isItemViewDelegateLineEdit(const QWidget *widget) -{ - if (!widget) - return false; - const QWidget *parent1 = widget->parentWidget(); - // Exclude dialogs or other toplevels parented on item views. - if (!parent1 || parent1->isWindow()) - return false; - const QWidget *parent2 = parent1->parentWidget(); - return parent2 && widget->inherits("QLineEdit") - && parent2->inherits("QAbstractItemView"); -} - -// Returns whether base color is set for this widget -bool QWindowsXPStylePrivate::isLineEditBaseColorSet(const QStyleOption *option, const QWidget *widget) -{ - uint resolveMask = option->palette.resolveMask(); - if (widget) { - // Since spin box includes a line edit we need to resolve the palette mask also from - // the parent, as while the color is always correct on the palette supplied by panel, - // the mask can still be empty. If either mask specifies custom base color, use that. -#if QT_CONFIG(spinbox) - if (const QAbstractSpinBox *spinbox = qobject_cast(widget->parentWidget())) - resolveMask |= spinbox->palette().resolveMask(); -#endif // QT_CONFIG(spinbox) - } - return (resolveMask & (1 << QPalette::Base)) != 0; -} - -static inline Qt::Orientation progressBarOrientation(const QStyleOption *option = nullptr) -{ - if (const auto *pb = qstyleoption_cast(option)) - return pb->state & QStyle::State_Horizontal ? Qt::Horizontal : Qt::Vertical; - return Qt::Horizontal; -} - -/*! \internal - This function will always return a valid window handle, and might - create a limbo widget to do so. - We often need a window handle to for example open theme data, so - this function ensures that we get one. -*/ -HWND QWindowsXPStylePrivate::winId(const QWidget *widget) -{ - if (widget) { - if (const HWND hwnd = QApplicationPrivate::getHWNDForWidget(const_cast(widget))) - return hwnd; - } - - // Find top level with native window (there might be dialogs that do not have one). - const auto allWindows = QGuiApplication::allWindows(); - for (const QWindow *window : allWindows) { - if (window->isTopLevel() && window->type() != Qt::Desktop && window->handle() != nullptr) - return reinterpret_cast(window->winId()); - } - - return GetDesktopWindow(); -} - -/*! \internal - Returns a native buffer (DIB section) of at least the size of - ( \a x , \a y ). The buffer has a 32 bit depth, to not lose - the alpha values on proper alpha-pixmaps. -*/ -HBITMAP QWindowsXPStylePrivate::buffer(int w, int h) -{ - // If we already have a HBITMAP which is of adequate size, just return that - if (bufferBitmap) { - if (bufferW >= w && bufferH >= h) - return bufferBitmap; - // Not big enough, discard the old one - if (bufferDC && nullBitmap) - SelectObject(bufferDC, nullBitmap); - DeleteObject(bufferBitmap); - bufferBitmap = nullptr; - } - - w = qMax(bufferW, w); - h = qMax(bufferH, h); - - if (!bufferDC) { - HDC displayDC = GetDC(nullptr); - bufferDC = CreateCompatibleDC(displayDC); - ReleaseDC(nullptr, displayDC); - } - - // Define the header - BITMAPINFO bmi; - memset(&bmi, 0, sizeof(bmi)); - bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); - bmi.bmiHeader.biWidth = w; - bmi.bmiHeader.biHeight = -h; - bmi.bmiHeader.biPlanes = 1; - bmi.bmiHeader.biBitCount = 32; - bmi.bmiHeader.biCompression = BI_RGB; - - // Create the pixmap - bufferPixels = nullptr; - bufferBitmap = CreateDIBSection(bufferDC, &bmi, DIB_RGB_COLORS, reinterpret_cast(&bufferPixels), nullptr, 0); - GdiFlush(); - nullBitmap = static_cast(SelectObject(bufferDC, bufferBitmap)); - - if (Q_UNLIKELY(!bufferBitmap)) { - qErrnoWarning("QWindowsXPStylePrivate::buffer(%dx%d), CreateDIBSection() failed.", w, h); - bufferW = 0; - bufferH = 0; - return nullptr; - } - if (Q_UNLIKELY(!bufferPixels)) { - qErrnoWarning("QWindowsXPStylePrivate::buffer(%dx%d), CreateDIBSection() did not allocate pixel data.", w, h); - bufferW = 0; - bufferH = 0; - return nullptr; - } - bufferW = w; - bufferH = h; -#ifdef DEBUG_XP_STYLE - qDebug("Creating new dib section (%d, %d)", w, h); -#endif - return bufferBitmap; -} - -/*! \internal - Returns \c true if the part contains any transparency at all. This does - not indicate what kind of transparency we're dealing with. It can be - - Alpha transparency - - Masked transparency -*/ -bool QWindowsXPStylePrivate::isTransparent(XPThemeData &themeData) -{ - return IsThemeBackgroundPartiallyTransparent(themeData.handle(), themeData.partId, - themeData.stateId); -} - - -/*! \internal - Returns a QRegion of the region of the part -*/ -QRegion QWindowsXPStylePrivate::region(XPThemeData &themeData) -{ - HRGN hRgn = nullptr; - const qreal factor = QWindowsStylePrivate::nativeMetricScaleFactor(themeData.widget); - RECT rect = themeData.toRECT(QRect(themeData.rect.topLeft() / factor, themeData.rect.size() / factor)); - if (!SUCCEEDED(GetThemeBackgroundRegion(themeData.handle(), bufferHDC(), themeData.partId, - themeData.stateId, &rect, &hRgn))) { - return QRegion(); - } - - HRGN dest = CreateRectRgn(0, 0, 0, 0); - const bool success = CombineRgn(dest, hRgn, nullptr, RGN_COPY) != ERROR; - - QRegion region; - - if (success) { - const auto numBytes = GetRegionData(dest, 0, nullptr); - if (numBytes == 0) - return QRegion(); - - char *buf = new (std::nothrow) char[numBytes]; - if (!buf) - return QRegion(); - - RGNDATA *rd = reinterpret_cast(buf); - if (GetRegionData(dest, numBytes, rd) == 0) { - delete [] buf; - return QRegion(); - } - - RECT *r = reinterpret_cast(rd->Buffer); - for (uint i = 0; i < rd->rdh.nCount; ++i) { - QRect rect; - rect.setCoords(int(r->left * factor), int(r->top * factor), int((r->right - 1) * factor), int((r->bottom - 1) * factor)); - ++r; - region |= rect; - } - - delete [] buf; - } - - DeleteObject(hRgn); - DeleteObject(dest); - - return region; -} - -/*! \internal - Returns \c true if the native doublebuffer contains pixels with - varying alpha value. -*/ -bool QWindowsXPStylePrivate::hasAlphaChannel(const QRect &rect) -{ - const int startX = rect.left(); - const int startY = rect.top(); - const int w = rect.width(); - const int h = rect.height(); - - int firstAlpha = -1; - for (int y = startY; y < h/2; ++y) { - auto buffer = reinterpret_cast(bufferPixels) + (y * bufferW); - for (int x = startX; x < w; ++x, ++buffer) { - int alpha = (*buffer) >> 24; - if (firstAlpha == -1) - firstAlpha = alpha; - else if (alpha != firstAlpha) - return true; - } - } - return false; -} - -/*! \internal - When the theme engine paints both a true alpha pixmap and a glyph - into our buffer, the glyph might not contain a proper alpha value. - The rule of thumb for premultiplied pixmaps is that the color - values of a pixel can never be higher than the alpha values, so - we use this to our advantage here, and fix all instances where - this occurs. -*/ -bool QWindowsXPStylePrivate::fixAlphaChannel(const QRect &rect) -{ - const int startX = rect.left(); - const int startY = rect.top(); - const int w = rect.width(); - const int h = rect.height(); - bool hasFixedAlphaValue = false; - - for (int y = startY; y < h; ++y) { - auto buffer = reinterpret_cast(bufferPixels) + (y * bufferW); - for (int x = startX; x < w; ++x, ++buffer) { - uint pixel = *buffer; - int alpha = qAlpha(pixel); - if (qRed(pixel) > alpha || qGreen(pixel) > alpha || qBlue(pixel) > alpha) { - *buffer |= 0xff000000; - hasFixedAlphaValue = true; - } - } - } - return hasFixedAlphaValue; -} - -/*! \internal - Swaps the alpha values on certain pixels: - 0xFF?????? -> 0x00?????? - 0x00?????? -> 0xFF?????? - Used to determine the mask of a non-alpha transparent pixmap in - the native doublebuffer, and swap the alphas so we may paint - the image as a Premultiplied QImage with drawImage(), and obtain - the mask transparency. -*/ -bool QWindowsXPStylePrivate::swapAlphaChannel(const QRect &rect, bool allPixels) -{ - const int startX = rect.left(); - const int startY = rect.top(); - const int w = rect.width(); - const int h = rect.height(); - bool valueChange = false; - - // Flip the alphas, so that 255-alpha pixels are 0, and 0-alpha are 255. - for (int y = startY; y < h; ++y) { - auto buffer = reinterpret_cast(bufferPixels) + (y * bufferW); - for (int x = startX; x < w; ++x, ++buffer) { - if (allPixels) { - *buffer |= 0xFF000000; - continue; - } - unsigned int alphaValue = (*buffer) & 0xFF000000; - if (alphaValue == 0xFF000000) { - *buffer = 0; - valueChange = true; - } else if (alphaValue == 0) { - *buffer |= 0xFF000000; - valueChange = true; - } - } - } - return valueChange; -} - -enum TransformType { SimpleTransform, HighDpiScalingTransform, ComplexTransform }; - -static inline TransformType transformType(const QTransform &transform, qreal devicePixelRatio) -{ - if (transform.type() <= QTransform::TxTranslate) - return SimpleTransform; - if (transform.type() > QTransform::TxScale) - return ComplexTransform; - return qFuzzyCompare(transform.m11(), devicePixelRatio) - && qFuzzyCompare(transform.m22(), devicePixelRatio) - ? HighDpiScalingTransform : ComplexTransform; -} - -// QTBUG-60571: Exclude known fully opaque theme parts which produce values -// invalid in ARGB32_Premultiplied (for example, 0x00ffffff). -static inline bool isFullyOpaque(const XPThemeData &themeData) -{ - return themeData.theme == QWindowsXPStylePrivate::TaskDialogTheme && themeData.partId == TDLG_PRIMARYPANEL; -} - -/*! \internal - Main theme drawing function. - Determines the correct lowlevel drawing method depending on several - factors. - Use drawBackgroundThruNativeBuffer() if: - - Painter does not have an HDC - - Theme part is flipped (mirrored horizontally) - else use drawBackgroundDirectly(). - \note drawBackgroundThruNativeBuffer() can return false for large - sizes due to buffer()/CreateDIBSection() failing. -*/ -bool QWindowsXPStylePrivate::drawBackground(XPThemeData &themeData, qreal correctionFactor) -{ - if (themeData.rect.isEmpty()) - return true; - - QPainter *painter = themeData.painter; - Q_ASSERT_X(painter != nullptr, "QWindowsXPStylePrivate::drawBackground()", "Trying to draw a theme part without a painter"); - if (!painter || !painter->isActive()) - return false; - - painter->save(); - - // Access paintDevice via engine since the painter may - // return the clip device which can still be a widget device in case of grabWidget(). - - bool translucentToplevel = false; - const QPaintDevice *paintDevice = painter->device(); - const qreal aditionalDevicePixelRatio = themeData.widget ? themeData.widget->devicePixelRatio() : qreal(1); - if (paintDevice->devType() == QInternal::Widget) { - const QWidget *window = static_cast(paintDevice)->window(); - translucentToplevel = window->testAttribute(Qt::WA_TranslucentBackground); - } - - const TransformType tt = transformType(painter->deviceTransform(), aditionalDevicePixelRatio); - - bool canDrawDirectly = false; - if (themeData.widget && painter->opacity() == 1.0 && !themeData.rotate - && !isFullyOpaque(themeData) - && tt != ComplexTransform && !themeData.mirrorVertically - && !translucentToplevel) { - // Draw on backing store DC only for real widgets or backing store images. - const QPaintDevice *enginePaintDevice = painter->paintEngine()->paintDevice(); - switch (enginePaintDevice->devType()) { - case QInternal::Widget: - canDrawDirectly = true; - break; - case QInternal::Image: - // Ensure the backing store has received as resize and is initialized. - if (QBackingStore *bs = backingStoreForWidget(themeData.widget)) { - if (bs->size().isValid() && bs->paintDevice() == enginePaintDevice) - canDrawDirectly = true; - } - } - } - - const HDC dc = canDrawDirectly ? hdcForWidgetBackingStore(themeData.widget) : nullptr; - const bool result = dc && qFuzzyCompare(correctionFactor, qreal(1)) - ? drawBackgroundDirectly(dc, themeData, aditionalDevicePixelRatio) - : drawBackgroundThruNativeBuffer(themeData, aditionalDevicePixelRatio, correctionFactor); - painter->restore(); - return result; -} - -static inline QRectF scaleRect(const QRectF &r, qreal factor) -{ - return r.isValid() && factor > 1 - ? QRectF(r.topLeft() * factor, r.size() * factor) : r; -} - -static QRegion scaleRegion(const QRegion ®ion, qreal factor) -{ - if (region.isEmpty() || qFuzzyCompare(factor, qreal(1))) - return region; - QRegion result; - for (const QRect &rect : region) - result += QRectF(QPointF(rect.topLeft()) * factor, QSizeF(rect.size() * factor)).toRect(); - return result; -} - -/*! \internal - This function draws the theme parts directly to the paintengines HDC. - Do not use this if you need to perform other transformations on the - resulting data. -*/ -bool QWindowsXPStylePrivate::drawBackgroundDirectly(HDC dc, XPThemeData &themeData, qreal additionalDevicePixelRatio) -{ - QPainter *painter = themeData.painter; - - const auto &deviceTransform = painter->deviceTransform(); - const QPointF redirectionDelta(deviceTransform.dx(), deviceTransform.dy()); - const QRect area = scaleRect(QRectF(themeData.rect), additionalDevicePixelRatio).translated(redirectionDelta).toRect(); - - QRegion sysRgn = painter->paintEngine()->systemClip(); - if (sysRgn.isEmpty()) - sysRgn = area; - else - sysRgn &= area; - if (painter->hasClipping()) - sysRgn &= scaleRegion(painter->clipRegion(), additionalDevicePixelRatio).translated(redirectionDelta.toPoint()); - HRGN hrgn = qt_hrgn_from_qregion(sysRgn); - SelectClipRgn(dc, hrgn); - -#ifdef DEBUG_XP_STYLE - printf("---[ DIRECT PAINTING ]------------------> Name(%-10s) Part(%d) State(%d)\n", - qPrintable(themeData.name), themeData.partId, themeData.stateId); - showProperties(themeData); -#endif - - RECT drawRECT = themeData.toRECT(area); - DTBGOPTS drawOptions; - memset(&drawOptions, 0, sizeof(drawOptions)); - drawOptions.dwSize = sizeof(drawOptions); - drawOptions.rcClip = themeData.toRECT(sysRgn.boundingRect()); - drawOptions.dwFlags = DTBG_CLIPRECT - | (themeData.noBorder ? DTBG_OMITBORDER : 0) - | (themeData.noContent ? DTBG_OMITCONTENT : 0) - | (themeData.mirrorHorizontally ? DTBG_MIRRORDC : 0); - - const HRESULT result = DrawThemeBackgroundEx(themeData.handle(), dc, themeData.partId, themeData.stateId, &(drawRECT), &drawOptions); - SelectClipRgn(dc, nullptr); - DeleteObject(hrgn); - return SUCCEEDED(result); -} - -/*! \internal - This function uses a secondary Native doublebuffer for painting parts. - It should only be used when the painteengine doesn't provide a proper - HDC for direct painting (e.g. when doing a grabWidget(), painting to - other pixmaps etc), or when special transformations are needed (e.g. - flips (horizontal mirroring only, vertical are handled by the theme - engine). - - \a correctionFactor is an additional factor used to scale up controls - that are too small on High DPI screens, as has been observed for - WP_MDICLOSEBUTTON, WP_MDIRESTOREBUTTON, WP_MDIMINBUTTON (QTBUG-75927). -*/ -bool QWindowsXPStylePrivate::drawBackgroundThruNativeBuffer(XPThemeData &themeData, - qreal additionalDevicePixelRatio, - qreal correctionFactor) -{ - QPainter *painter = themeData.painter; - QRectF rectF = scaleRect(QRectF(themeData.rect), additionalDevicePixelRatio); - - if ((themeData.rotate + 90) % 180 == 0) { // Catch 90,270,etc.. degree flips. - rectF = QRectF(0, 0, rectF.height(), rectF.width()); - } - rectF.moveTo(0, 0); - - const bool hasCorrectionFactor = !qFuzzyCompare(correctionFactor, qreal(1)); - QRect rect = rectF.toRect(); - const QRect drawRect = hasCorrectionFactor - ? QRectF(rectF.topLeft() / correctionFactor, rectF.size() / correctionFactor).toRect() - : rect; - int partId = themeData.partId; - int stateId = themeData.stateId; - int w = rect.width(); - int h = rect.height(); - - // Values initialized later, either from cached values, or from function calls - AlphaChannelType alphaType = UnknownAlpha; - bool stateHasData = true; // We assume so; - bool hasAlpha = false; - bool partIsTransparent; - bool potentialInvalidAlpha; - - QString pixmapCacheKey = QStringLiteral("$qt_xp_"); - pixmapCacheKey.append(themeName(themeData.theme)); - pixmapCacheKey.append(QLatin1Char('p')); - pixmapCacheKey.append(QString::number(partId)); - pixmapCacheKey.append(QLatin1Char('s')); - pixmapCacheKey.append(QString::number(stateId)); - pixmapCacheKey.append(QLatin1Char('s')); - pixmapCacheKey.append(themeData.noBorder ? QLatin1Char('0') : QLatin1Char('1')); - pixmapCacheKey.append(QLatin1Char('b')); - pixmapCacheKey.append(themeData.noContent ? QLatin1Char('0') : QLatin1Char('1')); - pixmapCacheKey.append(QString::number(w)); - pixmapCacheKey.append(QLatin1Char('w')); - pixmapCacheKey.append(QString::number(h)); - pixmapCacheKey.append(QLatin1Char('h')); - pixmapCacheKey.append(QString::number(additionalDevicePixelRatio)); - pixmapCacheKey.append(QLatin1Char('d')); - if (hasCorrectionFactor) { - pixmapCacheKey.append(QLatin1Char('c')); - pixmapCacheKey.append(QString::number(correctionFactor)); - } - - QPixmap cachedPixmap; - ThemeMapKey key(themeData); - ThemeMapData data = alphaCache.value(key); - - bool haveCachedPixmap = false; - bool isCached = data.dataValid; - if (isCached) { - partIsTransparent = data.partIsTransparent; - hasAlpha = data.hasAlphaChannel; - alphaType = data.alphaType; - potentialInvalidAlpha = data.hadInvalidAlpha; - - haveCachedPixmap = QPixmapCache::find(pixmapCacheKey, &cachedPixmap); - -#ifdef DEBUG_XP_STYLE - char buf[25]; - ::sprintf(buf, "+ Pixmap(%3d, %3d) ]", w, h); - printf("---[ CACHED %s--------> Name(%-10s) Part(%d) State(%d)\n", - haveCachedPixmap ? buf : "]-------------------", - qPrintable(themeData.name), themeData.partId, themeData.stateId); -#endif - } else { - // Not cached, so get values from Theme Engine - BOOL tmt_borderonly = false; - COLORREF tmt_transparentcolor = 0x0; - PROPERTYORIGIN proporigin = PO_NOTFOUND; - GetThemeBool(themeData.handle(), themeData.partId, themeData.stateId, TMT_BORDERONLY, &tmt_borderonly); - GetThemeColor(themeData.handle(), themeData.partId, themeData.stateId, TMT_TRANSPARENTCOLOR, &tmt_transparentcolor); - GetThemePropertyOrigin(themeData.handle(), themeData.partId, themeData.stateId, TMT_CAPTIONMARGINS, &proporigin); - - partIsTransparent = isTransparent(themeData); - - potentialInvalidAlpha = false; - GetThemePropertyOrigin(themeData.handle(), themeData.partId, themeData.stateId, TMT_GLYPHTYPE, &proporigin); - if (proporigin == PO_PART || proporigin == PO_STATE) { - int tmt_glyphtype = GT_NONE; - GetThemeEnumValue(themeData.handle(), themeData.partId, themeData.stateId, TMT_GLYPHTYPE, &tmt_glyphtype); - potentialInvalidAlpha = partIsTransparent && tmt_glyphtype == GT_IMAGEGLYPH; - } - -#ifdef DEBUG_XP_STYLE - printf("---[ NOT CACHED ]-----------------------> Name(%-10s) Part(%d) State(%d)\n", - qPrintable(themeData.name), themeData.partId, themeData.stateId); - printf("-->partIsTransparen = %d\n", partIsTransparent); - printf("-->potentialInvalidAlpha = %d\n", potentialInvalidAlpha); - showProperties(themeData); -#endif - } - bool wasAlphaSwapped = false; - bool wasAlphaFixed = false; - - // OLD PSDK Workaround ------------------------------------------------------------------------ - // See if we need extra clipping for the older PSDK, which does - // not have a DrawThemeBackgroundEx function for DTGB_OMITBORDER - // and DTGB_OMITCONTENT - bool addBorderContentClipping = false; - QRegion extraClip; - QRect area = drawRect; - if (themeData.noBorder || themeData.noContent) { - extraClip = area; - // We are running on a system where the uxtheme.dll does not have - // the DrawThemeBackgroundEx function, so we need to clip away - // borders or contents manually. - - int borderSize = 0; - PROPERTYORIGIN origin = PO_NOTFOUND; - GetThemePropertyOrigin(themeData.handle(), themeData.partId, themeData.stateId, TMT_BORDERSIZE, &origin); - GetThemeInt(themeData.handle(), themeData.partId, themeData.stateId, TMT_BORDERSIZE, &borderSize); - borderSize *= additionalDevicePixelRatio; - - // Clip away border region - if ((origin == PO_CLASS || origin == PO_PART || origin == PO_STATE) && borderSize > 0) { - if (themeData.noBorder) { - extraClip &= area; - area = area.adjusted(-borderSize, -borderSize, borderSize, borderSize); - } - - // Clip away content region - if (themeData.noContent) { - QRegion content = area.adjusted(borderSize, borderSize, -borderSize, -borderSize); - extraClip ^= content; - } - } - addBorderContentClipping = (themeData.noBorder | themeData.noContent); - } - - QImage img; - if (!haveCachedPixmap) { // If the pixmap is not cached, generate it! ------------------------- - if (!buffer(drawRect.width(), drawRect.height())) // Ensure a buffer of at least (w, h) in size - return false; - HDC dc = bufferHDC(); - - // Clear the buffer - if (alphaType != NoAlpha) { - // Consider have separate "memset" function for small chunks for more speedup - memset(bufferPixels, 0x00, bufferW * drawRect.height() * 4); - } - - // Difference between area and rect - int dx = area.x() - drawRect.x(); - int dy = area.y() - drawRect.y(); - - // Adjust so painting rect starts from Origo - rect.moveTo(0,0); - area.moveTo(dx,dy); - DTBGOPTS drawOptions; - drawOptions.dwSize = sizeof(drawOptions); - drawOptions.rcClip = themeData.toRECT(rect); - drawOptions.dwFlags = DTBG_CLIPRECT - | (themeData.noBorder ? DTBG_OMITBORDER : 0) - | (themeData.noContent ? DTBG_OMITCONTENT : 0); - - // Drawing the part into the backing store - RECT wRect(themeData.toRECT(area)); - DrawThemeBackgroundEx(themeData.handle(), dc, themeData.partId, themeData.stateId, &wRect, &drawOptions); - - // If not cached, analyze the buffer data to figure - // out alpha type, and if it contains data - if (!isCached) { - // SHORTCUT: If the part's state has no data, cache it for NOOP later - if (!stateHasData) { - memset(static_cast(&data), 0, sizeof(data)); - data.dataValid = true; - alphaCache.insert(key, data); - return true; - } - hasAlpha = hasAlphaChannel(rect); - if (!hasAlpha && partIsTransparent) - potentialInvalidAlpha = true; -#if defined(DEBUG_XP_STYLE) && 1 - dumpNativeDIB(drawRect.width(), drawRect.height()); -#endif - } - - // Fix alpha values, if needed - if (potentialInvalidAlpha) - wasAlphaFixed = fixAlphaChannel(drawRect); - - QImage::Format format; - if ((partIsTransparent && !wasAlphaSwapped) || (!partIsTransparent && hasAlpha)) { - format = QImage::Format_ARGB32_Premultiplied; - alphaType = RealAlpha; - } else if (wasAlphaSwapped) { - format = QImage::Format_ARGB32_Premultiplied; - alphaType = MaskAlpha; - } else { - format = QImage::Format_RGB32; - // The image data we got from the theme engine does not have any transparency, - // thus the alpha channel is set to 0. - // However, Format_RGB32 requires the alpha part to be set to 0xff, thus - // we must flip it from 0x00 to 0xff - swapAlphaChannel(rect, true); - alphaType = NoAlpha; - } -#if defined(DEBUG_XP_STYLE) && 1 - printf("Image format is: %s\n", alphaType == RealAlpha ? "Real Alpha" : alphaType == MaskAlpha ? "Masked Alpha" : "No Alpha"); -#endif - img = QImage(bufferPixels, bufferW, bufferH, format); - if (hasCorrectionFactor) - img = img.scaled(img.size() * correctionFactor, Qt::KeepAspectRatio, Qt::SmoothTransformation); - img.setDevicePixelRatio(additionalDevicePixelRatio); - } - - // Blitting backing store - bool useRegion = partIsTransparent && !hasAlpha && !wasAlphaSwapped; - - QRegion newRegion; - QRegion oldRegion; - if (useRegion) { - newRegion = region(themeData); - oldRegion = painter->clipRegion(); - painter->setClipRegion(newRegion); -#if defined(DEBUG_XP_STYLE) && 0 - printf("Using region:\n"); - for (const QRect &r : newRegion) - printf(" (%d, %d, %d, %d)\n", r.x(), r.y(), r.right(), r.bottom()); -#endif - } - - if (addBorderContentClipping) - painter->setClipRegion(scaleRegion(extraClip, 1.0 / additionalDevicePixelRatio), Qt::IntersectClip); - - if (!themeData.mirrorHorizontally && !themeData.mirrorVertically && !themeData.rotate) { - if (!haveCachedPixmap) - painter->drawImage(themeData.rect, img, rect); - else - painter->drawPixmap(themeData.rect, cachedPixmap); - } else { - // This is _slow_! - // Make a copy containing only the necessary data, and mirror - // on all wanted axes. Then draw the copy. - // If cached, the normal pixmap is cached, instead of caching - // all possible orientations for each part and state. - QImage imgCopy; - if (!haveCachedPixmap) - imgCopy = img.copy(rect); - else - imgCopy = cachedPixmap.toImage(); - - if (themeData.rotate) { - QTransform rotMatrix; - rotMatrix.rotate(themeData.rotate); - imgCopy = imgCopy.transformed(rotMatrix); - } - if (themeData.mirrorHorizontally || themeData.mirrorVertically) - imgCopy = imgCopy.mirrored(themeData.mirrorHorizontally, themeData.mirrorVertically); - painter->drawImage(themeData.rect, imgCopy); - } - - if (useRegion || addBorderContentClipping) { - if (oldRegion.isEmpty()) - painter->setClipping(false); - else - painter->setClipRegion(oldRegion); - } - - // Cache the pixmap to avoid expensive swapAlphaChannel() calls - if (!haveCachedPixmap && w && h) { - QPixmap pix = QPixmap::fromImage(img).copy(rect); - QPixmapCache::insert(pixmapCacheKey, pix); -#ifdef DEBUG_XP_STYLE - printf("+++Adding pixmap to cache, size(%d, %d), wasAlphaSwapped(%d), wasAlphaFixed(%d), name(%s)\n", - w, h, wasAlphaSwapped, wasAlphaFixed, qPrintable(pixmapCacheKey)); -#endif - } - - // Add to theme part cache - if (!isCached) { - memset(static_cast(&data), 0, sizeof(data)); - data.dataValid = true; - data.partIsTransparent = partIsTransparent; - data.alphaType = alphaType; - data.hasAlphaChannel = hasAlpha; - data.wasAlphaSwapped = wasAlphaSwapped; - data.hadInvalidAlpha = wasAlphaFixed; - alphaCache.insert(key, data); - } - return true; -} - - -// ------------------------------------------------------------------------------------------------ - -/*! - \class QWindowsXPStyle - \brief The QWindowsXPStyle class provides a Microsoft Windows XP-like look and feel. - - \ingroup appearance - \inmodule QtWidgets - \internal - - \warning This style is only available on the Windows XP platform - because it makes use of Windows XP's style engine. - - Most of the functions are documented in the base classes - QWindowsStyle, QCommonStyle, and QStyle, but the - QWindowsXPStyle overloads of drawComplexControl(), drawControl(), - drawControlMask(), drawPrimitive(), proxy()->subControlRect(), and - sizeFromContents(), are documented here. - - \image qwindowsxpstyle.png - \sa QMacStyle, QWindowsStyle, QFusionStyle -*/ - -/*! - \internal - - Constructs a QWindowsXPStyle object. -*/ -QWindowsXPStyle::QWindowsXPStyle(QWindowsXPStylePrivate &dd) : QWindowsStyle(dd) -{ -} - -/*! - Destroys the style. -*/ -QWindowsXPStyle::~QWindowsXPStyle() = default; - -/*! \reimp */ -void QWindowsXPStyle::polish(QWidget *widget) -{ - QWindowsStyle::polish(widget); - if (!QWindowsXPStylePrivate::useXP()) - return; - - if (false -#if QT_CONFIG(abstractbutton) - || qobject_cast(widget) -#endif - || qobject_cast(widget) - || qobject_cast(widget) -#if QT_CONFIG(combobox) - || qobject_cast(widget) -#endif // QT_CONFIG(combobox) - || qobject_cast(widget) - || qobject_cast(widget) - || qobject_cast(widget) -#if QT_CONFIG(spinbox) - || qobject_cast(widget) - || qobject_cast(widget) -#endif // QT_CONFIG(spinbox) - ) { - widget->setAttribute(Qt::WA_Hover); - } - -#if QT_CONFIG(rubberband) - if (qobject_cast(widget)) - widget->setWindowOpacity(0.6); -#endif - - Q_D(QWindowsXPStyle); - if (!d->hasInitColors) { - // Get text color for group box labels - COLORREF cref; - XPThemeData theme(widget, nullptr, QWindowsXPStylePrivate::ButtonTheme, 0, 0); - GetThemeColor(theme.handle(), BP_GROUPBOX, GBS_NORMAL, TMT_TEXTCOLOR, &cref); - d->groupBoxTextColor = qRgb(GetRValue(cref), GetGValue(cref), GetBValue(cref)); - GetThemeColor(theme.handle(), BP_GROUPBOX, GBS_DISABLED, TMT_TEXTCOLOR, &cref); - d->groupBoxTextColorDisabled = qRgb(GetRValue(cref), GetGValue(cref), GetBValue(cref)); - // Where does this color come from? - //GetThemeColor(theme.handle(), TKP_TICS, TSS_NORMAL, TMT_COLOR, &cref); - d->sliderTickColor = qRgb(165, 162, 148); - d->hasInitColors = true; - } -} - -/*! \reimp */ -void QWindowsXPStyle::polish(QPalette &pal) -{ - QWindowsStyle::polish(pal); - pal.setBrush(QPalette::AlternateBase, pal.base().color().darker(110)); -} - -/*! \reimp */ -void QWindowsXPStyle::unpolish(QWidget *widget) -{ -#if QT_CONFIG(rubberband) - if (qobject_cast(widget)) - widget->setWindowOpacity(1.0); -#endif - Q_D(QWindowsXPStyle); - // Unpolish of widgets is the first thing that - // happens when a theme changes, or the theme - // engine is turned off. So we detect it here. - bool oldState = QWindowsXPStylePrivate::useXP(); - bool newState = QWindowsXPStylePrivate::useXP(true); - if ((oldState != newState) && newState) { - d->cleanup(true); - d->init(true); - } else { - // Cleanup handle map, if just changing style, - // or turning it on. In both cases the values - // already in the map might be old (other style). - d->cleanupHandleMap(); - } - if (false -#if QT_CONFIG(abstractbutton) - || qobject_cast(widget) -#endif - || qobject_cast(widget) - || qobject_cast(widget) -#if QT_CONFIG(combobox) - || qobject_cast(widget) -#endif // QT_CONFIG(combobox) - || qobject_cast(widget) - || qobject_cast(widget) - || qobject_cast(widget) -#if QT_CONFIG(spinbox) - || qobject_cast(widget) - || qobject_cast(widget) -#endif // QT_CONFIG(spinbox) - ) { - widget->setAttribute(Qt::WA_Hover, false); - } - QWindowsStyle::unpolish(widget); -} - -/*! \reimp */ -QRect QWindowsXPStyle::subElementRect(SubElement sr, const QStyleOption *option, const QWidget *widget) const -{ - if (!QWindowsXPStylePrivate::useXP()) - return QWindowsStyle::subElementRect(sr, option, widget); - - QRect rect(option->rect); - switch(sr) { - case SE_DockWidgetCloseButton: - case SE_DockWidgetFloatButton: - rect = QWindowsStyle::subElementRect(sr, option, widget); - return rect.translated(0, 1); - break; - case SE_TabWidgetTabContents: - if (qstyleoption_cast(option)) { - rect = QWindowsStyle::subElementRect(sr, option, widget); - if (sr == SE_TabWidgetTabContents) { - if (const QTabWidget *tabWidget = qobject_cast(widget)) { - if (tabWidget->documentMode()) - break; - } - - rect.adjust(0, 0, -2, -2); - } - } - break; - case SE_TabWidgetTabBar: { - rect = QWindowsStyle::subElementRect(sr, option, widget); - const auto *twfOption = qstyleoption_cast(option); - if (twfOption && twfOption->direction == Qt::RightToLeft - && (twfOption->shape == QTabBar::RoundedNorth - || twfOption->shape == QTabBar::RoundedSouth)) - { - QStyleOptionTab otherOption; - otherOption.shape = (twfOption->shape == QTabBar::RoundedNorth - ? QTabBar::RoundedEast : QTabBar::RoundedSouth); - int overlap = proxy()->pixelMetric(PM_TabBarBaseOverlap, &otherOption, widget); - int borderThickness = proxy()->pixelMetric(PM_DefaultFrameWidth, option, widget); - rect.adjust(-overlap + borderThickness, 0, -overlap + borderThickness, 0); - } - break; - } - case SE_PushButtonContents: - if (const auto *btn = qstyleoption_cast(option)) { - MARGINS borderSize; - if (widget) { - XPThemeData buttontheme(widget, nullptr, QWindowsXPStylePrivate::ButtonTheme); - HTHEME theme = buttontheme.handle(); - if (theme) { - int stateId; - if (!(option->state & State_Enabled)) - stateId = PBS_DISABLED; - else if (option->state & State_Sunken) - stateId = PBS_PRESSED; - else if (option->state & State_MouseOver) - stateId = PBS_HOT; - else if (btn->features & QStyleOptionButton::DefaultButton) - stateId = PBS_DEFAULTED; - else - stateId = PBS_NORMAL; - - int border = proxy()->pixelMetric(PM_DefaultFrameWidth, btn, widget); - rect = option->rect.adjusted(border, border, -border, -border); - - if (SUCCEEDED(GetThemeMargins(theme, nullptr, BP_PUSHBUTTON, stateId, TMT_CONTENTMARGINS, nullptr, &borderSize))) { - rect.adjust(borderSize.cxLeftWidth, borderSize.cyTopHeight, - -borderSize.cxRightWidth, -borderSize.cyBottomHeight); - rect = visualRect(option->direction, option->rect, rect); - } - } - } - } - break; - case SE_ProgressBarContents: - rect = QCommonStyle::subElementRect(SE_ProgressBarGroove, option, widget); - if (option->state & QStyle::State_Horizontal) - rect.adjust(4, 3, -4, -3); - else - rect.adjust(3, 2, -3, -2); - break; - default: - rect = QWindowsStyle::subElementRect(sr, option, widget); - } - return rect; -} - -/*! - \reimp -*/ -void QWindowsXPStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *option, QPainter *p, - const QWidget *widget) const -{ - if (!QWindowsXPStylePrivate::useXP()) { - QWindowsStyle::drawPrimitive(pe, option, p, widget); - return; - } - - auto *d = const_cast(d_func()); - int themeNumber = -1; - int partId = 0; - int stateId = 0; - QRect rect = option->rect; - State flags = option->state; - bool hMirrored = false; - bool vMirrored = false; - bool noBorder = false; - bool noContent = false; - int rotate = 0; - - switch (pe) { - case PE_FrameTabBarBase: - if (const auto *tbb = qstyleoption_cast(option)) { - p->save(); - switch (tbb->shape) { - case QTabBar::RoundedNorth: - p->setPen(QPen(tbb->palette.dark(), 0)); - p->drawLine(tbb->rect.topLeft(), tbb->rect.topRight()); - break; - case QTabBar::RoundedWest: - p->setPen(QPen(tbb->palette.dark(), 0)); - p->drawLine(tbb->rect.left(), tbb->rect.top(), tbb->rect.left(), tbb->rect.bottom()); - break; - case QTabBar::RoundedSouth: - p->setPen(QPen(tbb->palette.dark(), 0)); - p->drawLine(tbb->rect.left(), tbb->rect.top(), - tbb->rect.right(), tbb->rect.top()); - break; - case QTabBar::RoundedEast: - p->setPen(QPen(tbb->palette.dark(), 0)); - p->drawLine(tbb->rect.topLeft(), tbb->rect.bottomLeft()); - break; - case QTabBar::TriangularNorth: - case QTabBar::TriangularEast: - case QTabBar::TriangularWest: - case QTabBar::TriangularSouth: - p->restore(); - QWindowsStyle::drawPrimitive(pe, option, p, widget); - return; - } - p->restore(); - } - return; - case PE_PanelButtonBevel: - themeNumber = QWindowsXPStylePrivate::ButtonTheme; - partId = BP_PUSHBUTTON; - if (!(flags & State_Enabled)) - stateId = PBS_DISABLED; - else if ((flags & State_Sunken) || (flags & State_On)) - stateId = PBS_PRESSED; - else if (flags & State_MouseOver) - stateId = PBS_HOT; - //else if (flags & State_ButtonDefault) - // stateId = PBS_DEFAULTED; - else - stateId = PBS_NORMAL; - break; - - case PE_PanelButtonTool: - if (widget && widget->inherits("QDockWidgetTitleButton")) { - if (const QWidget *dw = widget->parentWidget()) { - if (dw->isWindow()) - return; - } - } - themeNumber = QWindowsXPStylePrivate::ToolBarTheme; - partId = TP_BUTTON; - if (!(flags & State_Enabled)) - stateId = TS_DISABLED; - else if (flags & State_Sunken) - stateId = TS_PRESSED; - else if (flags & State_MouseOver) - stateId = flags & State_On ? TS_HOTCHECKED : TS_HOT; - else if (flags & State_On) - stateId = TS_CHECKED; - else if (!(flags & State_AutoRaise)) - stateId = TS_HOT; - else - stateId = TS_NORMAL; - break; - - case PE_IndicatorButtonDropDown: - themeNumber = QWindowsXPStylePrivate::ToolBarTheme; - partId = TP_SPLITBUTTONDROPDOWN; - if (!(flags & State_Enabled)) - stateId = TS_DISABLED; - else if (flags & State_Sunken) - stateId = TS_PRESSED; - else if (flags & State_MouseOver) - stateId = flags & State_On ? TS_HOTCHECKED : TS_HOT; - else if (flags & State_On) - stateId = TS_CHECKED; - else if (!(flags & State_AutoRaise)) - stateId = TS_HOT; - else - stateId = TS_NORMAL; - if (option->direction == Qt::RightToLeft) - hMirrored = true; - break; - - case PE_IndicatorCheckBox: - themeNumber = QWindowsXPStylePrivate::ButtonTheme; - partId = BP_CHECKBOX; - if (!(flags & State_Enabled)) - stateId = CBS_UNCHECKEDDISABLED; - else if (flags & State_Sunken) - stateId = CBS_UNCHECKEDPRESSED; - else if (flags & State_MouseOver) - stateId = CBS_UNCHECKEDHOT; - else - stateId = CBS_UNCHECKEDNORMAL; - - if (flags & State_On) - stateId += CBS_CHECKEDNORMAL-1; - else if (flags & State_NoChange) - stateId += CBS_MIXEDNORMAL-1; - - break; - - case PE_IndicatorRadioButton: - themeNumber = QWindowsXPStylePrivate::ButtonTheme; - partId = BP_RADIOBUTTON; - if (!(flags & State_Enabled)) - stateId = RBS_UNCHECKEDDISABLED; - else if (flags & State_Sunken) - stateId = RBS_UNCHECKEDPRESSED; - else if (flags & State_MouseOver) - stateId = RBS_UNCHECKEDHOT; - else - stateId = RBS_UNCHECKEDNORMAL; - - if (flags & State_On) - stateId += RBS_CHECKEDNORMAL-1; - break; - - case PE_IndicatorDockWidgetResizeHandle: - return; - - case PE_Frame: { - if (flags & State_Raised) - return; - themeNumber = QWindowsXPStylePrivate::ListViewTheme; - partId = LVP_LISTGROUP; - XPThemeData theme(widget, nullptr, themeNumber, partId); - - if (!(flags & State_Enabled)) - stateId = ETS_DISABLED; - else - stateId = ETS_NORMAL; - int fillType; - if (GetThemeEnumValue(theme.handle(), partId, stateId, TMT_BGTYPE, &fillType) == S_OK) { - if (fillType == BT_BORDERFILL) { - COLORREF bcRef; - GetThemeColor(theme.handle(), partId, stateId, TMT_BORDERCOLOR, &bcRef); - QColor bordercolor(qRgb(GetRValue(bcRef), GetGValue(bcRef), GetBValue(bcRef))); - QPen oldPen = p->pen(); - // int borderSize = 1; - // GetThemeInt(theme.handle(), partId, stateId, TMT_BORDERCOLOR, &borderSize); - - // Inner white border - p->setPen(QPen(option->palette.base().color(), 0)); - const qreal dpi = QStyleHelper::dpi(option); - const auto topLevelAdjustment = QStyleHelper::dpiScaled(0.5, dpi); - const auto bottomRightAdjustment = QStyleHelper::dpiScaled(-1, dpi); - p->drawRect(QRectF(option->rect).adjusted(topLevelAdjustment, topLevelAdjustment, - bottomRightAdjustment, bottomRightAdjustment)); - // Outer dark border - p->setPen(QPen(bordercolor, 0)); - p->drawRect(QRectF(option->rect).adjusted(0, 0, -topLevelAdjustment, -topLevelAdjustment)); - p->setPen(oldPen); - return; - } - if (fillType == BT_NONE) - return; - } - break; - } - - case PE_FrameTabWidget: - if (const auto *tab = qstyleoption_cast(option)) { - themeNumber = QWindowsXPStylePrivate::TabTheme; - partId = TABP_PANE; - - if (widget) { - bool useGradient = true; - const int maxlength = 256; - wchar_t themeFileName[maxlength]; - wchar_t themeColor[maxlength]; - // Due to a a scaling issue with the XP Silver theme, tab gradients are not used with it - if (GetCurrentThemeName(themeFileName, maxlength, themeColor, maxlength, nullptr, 0) == S_OK) { - wchar_t *offset = nullptr; - if ((offset = wcsrchr(themeFileName, QChar(QLatin1Char('\\')).unicode())) != nullptr) { - offset++; - if (!lstrcmp(offset, L"Luna.msstyles") && !lstrcmp(offset, L"Metallic")) - useGradient = false; - } - } - // This should work, but currently there's an error in the ::drawBackgroundDirectly() - // code, when using the HDC directly.. - if (useGradient) { - QStyleOptionTabWidgetFrame frameOpt = *tab; - frameOpt.rect = widget->rect(); - QRect contentsRect = subElementRect(SE_TabWidgetTabContents, &frameOpt, widget); - QRegion reg = option->rect; - reg -= contentsRect; - p->setClipRegion(reg); - XPThemeData theme(widget, p, themeNumber, partId, stateId, rect); - theme.mirrorHorizontally = hMirrored; - theme.mirrorVertically = vMirrored; - d->drawBackground(theme); - p->setClipRect(contentsRect); - partId = TABP_BODY; - } - } - switch (tab->shape) { - case QTabBar::RoundedNorth: - case QTabBar::TriangularNorth: - break; - case QTabBar::RoundedSouth: - case QTabBar::TriangularSouth: - vMirrored = true; - break; - case QTabBar::RoundedEast: - case QTabBar::TriangularEast: - rotate = 90; - break; - case QTabBar::RoundedWest: - case QTabBar::TriangularWest: - rotate = 90; - hMirrored = true; - break; - default: - break; - } - } - break; - - case PE_FrameMenu: - p->save(); - p->setPen(option->palette.dark().color()); - p->drawRect(rect.adjusted(0, 0, -1, -1)); - p->restore(); - return; - - case PE_PanelMenuBar: - break; - - case PE_FrameDockWidget: - if (const auto *frm = qstyleoption_cast(option)) { - themeNumber = QWindowsXPStylePrivate::WindowTheme; - if (flags & State_Active) - stateId = FS_ACTIVE; - else - stateId = FS_INACTIVE; - - int fwidth = proxy()->pixelMetric(PM_DockWidgetFrameWidth, frm, widget); - - XPThemeData theme(widget, p, themeNumber, 0, stateId); - if (!theme.isValid()) - break; - theme.rect = QRect(frm->rect.x(), frm->rect.y(), frm->rect.x()+fwidth, frm->rect.height()-fwidth); - theme.partId = WP_SMALLFRAMELEFT; - d->drawBackground(theme); - theme.rect = QRect(frm->rect.width()-fwidth, frm->rect.y(), fwidth, frm->rect.height()-fwidth); - theme.partId = WP_SMALLFRAMERIGHT; - d->drawBackground(theme); - theme.rect = QRect(frm->rect.x(), frm->rect.bottom()-fwidth+1, frm->rect.width(), fwidth); - theme.partId = WP_SMALLFRAMEBOTTOM; - d->drawBackground(theme); - return; - } - break; - - case PE_IndicatorHeaderArrow: { -#if 0 // XP theme engine doesn't know about this :( - name = QWindowsXPStylePrivate::HeaderTheme; - partId = HP_HEADERSORTARROW; - if (flags & State_Down) - stateId = HSAS_SORTEDDOWN; - else - stateId = HSAS_SORTEDUP; -#else - if (const auto *header = qstyleoption_cast(option)) { - p->save(); - p->setPen(option->palette.dark().color()); - p->translate(0, option->rect.height()/2 - 4); - if (header->sortIndicator & QStyleOptionHeader::SortUp) { // invert logic to follow Windows style guide - p->drawLine(option->rect.x(), option->rect.y(), option->rect.x()+8, option->rect.y()); - p->drawLine(option->rect.x()+1, option->rect.y()+1, option->rect.x()+7, option->rect.y()+1); - p->drawLine(option->rect.x()+2, option->rect.y()+2, option->rect.x()+6, option->rect.y()+2); - p->drawLine(option->rect.x()+3, option->rect.y()+3, option->rect.x()+5, option->rect.y()+3); - p->drawPoint(option->rect.x()+4, option->rect.y()+4); - } else if (header->sortIndicator & QStyleOptionHeader::SortDown) { - p->drawLine(option->rect.x(), option->rect.y()+4, option->rect.x()+8, option->rect.y()+4); - p->drawLine(option->rect.x()+1, option->rect.y()+3, option->rect.x()+7, option->rect.y()+3); - p->drawLine(option->rect.x()+2, option->rect.y()+2, option->rect.x()+6, option->rect.y()+2); - p->drawLine(option->rect.x()+3, option->rect.y()+1, option->rect.x()+5, option->rect.y()+1); - p->drawPoint(option->rect.x()+4, option->rect.y()); - } - p->restore(); - return; - } -#endif - break; - } - - case PE_FrameStatusBarItem: - themeNumber = QWindowsXPStylePrivate::StatusTheme; - partId = SP_PANE; - break; - - case PE_FrameGroupBox: - themeNumber = QWindowsXPStylePrivate::ButtonTheme; - partId = BP_GROUPBOX; - if (!(flags & State_Enabled)) - stateId = GBS_DISABLED; - else - stateId = GBS_NORMAL; - if (const auto *frame = qstyleoption_cast(option)) { - if (frame->features & QStyleOptionFrame::Flat) { - // Windows XP does not have a theme part for a flat GroupBox, paint it with the windows style - QRect fr = frame->rect; - QPoint p1(fr.x(), fr.y() + 1); - QPoint p2(fr.x() + fr.width(), p1.y() + 1); - rect = QRect(p1, p2); - themeNumber = -1; - } - } - break; - - case PE_FrameWindow: - if (const auto *frm = qstyleoption_cast(option)) { - themeNumber = QWindowsXPStylePrivate::WindowTheme; - if (flags & State_Active) - stateId = FS_ACTIVE; - else - stateId = FS_INACTIVE; - - int fwidth = int((frm->lineWidth + frm->midLineWidth) / QWindowsStylePrivate::nativeMetricScaleFactor(widget)); - - XPThemeData theme(widget, p, themeNumber, 0, stateId); - if (!theme.isValid()) - break; - - // May fail due to too-large buffers for large widgets, fall back to Windows style. - theme.rect = QRect(option->rect.x(), option->rect.y()+fwidth, option->rect.x()+fwidth, option->rect.height()-fwidth); - theme.partId = WP_FRAMELEFT; - if (!d->drawBackground(theme)) { - QWindowsStyle::drawPrimitive(pe, option, p, widget); - return; - } - theme.rect = QRect(option->rect.width()-fwidth, option->rect.y()+fwidth, fwidth, option->rect.height()-fwidth); - theme.partId = WP_FRAMERIGHT; - if (!d->drawBackground(theme)) { - QWindowsStyle::drawPrimitive(pe, option, p, widget); - return; - } - theme.rect = QRect(option->rect.x(), option->rect.height()-fwidth, option->rect.width(), fwidth); - theme.partId = WP_FRAMEBOTTOM; - if (!d->drawBackground(theme)) { - QWindowsStyle::drawPrimitive(pe, option, p, widget); - return; - } - theme.rect = QRect(option->rect.x(), option->rect.y(), option->rect.width(), option->rect.y()+fwidth); - theme.partId = WP_CAPTION; - if (!d->drawBackground(theme)) - QWindowsStyle::drawPrimitive(pe, option, p, widget); - return; - } - break; - - case PE_IndicatorToolBarSeparator: - if (option->rect.height() < 3) { - // XP style requires a few pixels for the separator - // to be visible. - QWindowsStyle::drawPrimitive(pe, option, p, widget); - return; - } - themeNumber = QWindowsXPStylePrivate::ToolBarTheme; - partId = TP_SEPARATOR; - - if (option->state & State_Horizontal) - partId = TP_SEPARATOR; - else - partId = TP_SEPARATORVERT; - - break; - - case PE_IndicatorToolBarHandle: - themeNumber = QWindowsXPStylePrivate::RebarTheme; - partId = RP_GRIPPER; - if (option->state & State_Horizontal) { - partId = RP_GRIPPER; - rect.adjust(0, 0, -2, 0); - } - else { - partId = RP_GRIPPERVERT; - rect.adjust(0, 0, 0, -2); - } - break; - - case PE_IndicatorItemViewItemCheck: { - QStyleOptionButton button; - button.QStyleOption::operator=(*option); - button.state &= ~State_MouseOver; - proxy()->drawPrimitive(PE_IndicatorCheckBox, &button, p, widget); - return; - } - - default: - break; - } - - XPThemeData theme(widget, p, themeNumber, partId, stateId, rect); - if (!theme.isValid()) { - QWindowsStyle::drawPrimitive(pe, option, p, widget); - return; - } - theme.mirrorHorizontally = hMirrored; - theme.mirrorVertically = vMirrored; - theme.noBorder = noBorder; - theme.noContent = noContent; - theme.rotate = rotate; - d->drawBackground(theme); -} - -/*! - \reimp -*/ -void QWindowsXPStyle::drawControl(ControlElement element, const QStyleOption *option, QPainter *p, - const QWidget *widget) const -{ - if (!QWindowsXPStylePrivate::useXP()) { - QWindowsStyle::drawControl(element, option, p, widget); - return; - } - - auto *d = const_cast(d_func()); - QRect rect(option->rect); - - int rotate = 0; - bool hMirrored = false; - bool vMirrored = false; - - int themeNumber = -1; - int partId = 0; - int stateId = 0; - switch (element) { - case CE_SizeGrip: { - themeNumber = QWindowsXPStylePrivate::StatusTheme; - partId = SP_GRIPPER; - XPThemeData theme(nullptr, p, themeNumber, partId); - QSize size = (theme.size() * QWindowsStylePrivate::nativeMetricScaleFactor(widget)).toSize(); - size.rheight()--; - if (const auto *sg = qstyleoption_cast(option)) { - switch (sg->corner) { - case Qt::BottomRightCorner: - rect = QRect(QPoint(rect.right() - size.width(), rect.bottom() - size.height()), size); - break; - case Qt::BottomLeftCorner: - rect = QRect(QPoint(rect.left() + 1, rect.bottom() - size.height()), size); - hMirrored = true; - break; - case Qt::TopRightCorner: - rect = QRect(QPoint(rect.right() - size.width(), rect.top() + 1), size); - vMirrored = true; - break; - case Qt::TopLeftCorner: - rect = QRect(rect.topLeft() + QPoint(1, 1), size); - hMirrored = vMirrored = true; - } - } - break; - } - - case CE_Splitter: - p->eraseRect(option->rect); - return; - - case CE_TabBarTab: - if (const auto *tab = qstyleoption_cast(option)) - stateId = tab->state & State_Enabled ? TIS_NORMAL : TIS_DISABLED; - break; - - case CE_TabBarTabShape: - if (const auto *tab = qstyleoption_cast(option)) { - themeNumber = QWindowsXPStylePrivate::TabTheme; - const bool isDisabled = !(tab->state & State_Enabled); - const bool hasFocus = tab->state & State_HasFocus; - const bool isHot = tab->state & State_MouseOver; - const bool selected = tab->state & State_Selected; - bool lastTab = tab->position == QStyleOptionTab::End; - bool firstTab = tab->position == QStyleOptionTab::Beginning; - const bool onlyOne = tab->position == QStyleOptionTab::OnlyOneTab; - const bool leftAligned = proxy()->styleHint(SH_TabBar_Alignment, tab, widget) == Qt::AlignLeft; - const bool centerAligned = proxy()->styleHint(SH_TabBar_Alignment, tab, widget) == Qt::AlignCenter; - const int borderThickness = proxy()->pixelMetric(PM_DefaultFrameWidth, option, widget); - const int tabOverlap = proxy()->pixelMetric(PM_TabBarTabOverlap, option, widget); - - if (isDisabled) - stateId = TIS_DISABLED; - else if (selected) - stateId = TIS_SELECTED; - else if (hasFocus) - stateId = TIS_FOCUSED; - else if (isHot) - stateId = TIS_HOT; - else - stateId = TIS_NORMAL; - - // Selecting proper part depending on position - if (firstTab || onlyOne) { - if (leftAligned) - partId = TABP_TABITEMLEFTEDGE; - else if (centerAligned) - partId = TABP_TABITEM; - else // rightAligned - partId = TABP_TABITEMRIGHTEDGE; - } else { - partId = TABP_TABITEM; - } - - if (tab->direction == Qt::RightToLeft - && (tab->shape == QTabBar::RoundedNorth || tab->shape == QTabBar::RoundedSouth)) { - bool temp = firstTab; - firstTab = lastTab; - lastTab = temp; - } - const bool begin = firstTab || onlyOne; - const bool end = lastTab || onlyOne; - switch (tab->shape) { - case QTabBar::RoundedNorth: - if (selected) - rect.adjust(begin ? 0 : -tabOverlap, 0, end ? 0 : tabOverlap, borderThickness); - else - rect.adjust(begin? tabOverlap : 0, tabOverlap, end ? -tabOverlap : 0, 0); - break; - case QTabBar::RoundedSouth: - //vMirrored = true; - rotate = 180; // Not 100% correct, but works - if (selected) - rect.adjust(begin ? 0 : -tabOverlap , -borderThickness, end ? 0 : tabOverlap, 0); - else - rect.adjust(begin ? tabOverlap : 0, 0, end ? -tabOverlap : 0 , -tabOverlap); - break; - case QTabBar::RoundedEast: - rotate = 90; - if (selected) - rect.adjust(-borderThickness, begin ? 0 : -tabOverlap, 0, end ? 0 : tabOverlap); - else - rect.adjust(0, begin ? tabOverlap : 0, -tabOverlap, end ? -tabOverlap : 0); - break; - case QTabBar::RoundedWest: - hMirrored = true; - rotate = 90; - if (selected) - rect.adjust(0, begin ? 0 : -tabOverlap, borderThickness, end ? 0 : tabOverlap); - else - rect.adjust(tabOverlap, begin ? tabOverlap : 0, 0, end ? -tabOverlap : 0); - break; - default: - themeNumber = -1; // Do our own painting for triangular - break; - } - - if (!selected) { - switch (tab->shape) { - case QTabBar::RoundedNorth: - rect.adjust(0,0, 0,-1); - break; - case QTabBar::RoundedSouth: - rect.adjust(0,1, 0,0); - break; - case QTabBar::RoundedEast: - rect.adjust( 1,0, 0,0); - break; - case QTabBar::RoundedWest: - rect.adjust(0,0, -1,0); - break; - default: - break; - } - } - } - break; - - case CE_ProgressBarGroove: { - Qt::Orientation orient = progressBarOrientation(option); - partId = (orient == Qt::Horizontal) ? PP_BAR : PP_BARVERT; - themeNumber = QWindowsXPStylePrivate::ProgressTheme; - stateId = 1; - break; - } - case CE_MenuEmptyArea: - case CE_MenuItem: - if (const auto *menuitem = qstyleoption_cast(option)) { - const int tab = menuitem->reservedShortcutWidth; - const bool dis = !(menuitem->state & State_Enabled); - const bool act = menuitem->state & State_Selected; - const bool checkable = menuitem->menuHasCheckableItems; - const bool checked = checkable ? menuitem->checked : false; - - // windows always has a check column, regardless whether we have an icon or not - const int checkcol = qMax(menuitem->maxIconWidth, 12); - - int x, y, w, h; - rect.getRect(&x, &y, &w, &h); - - QBrush fill = menuitem->palette.brush(act ? QPalette::Highlight : QPalette::Button); - p->fillRect(rect, fill); - - if (element == CE_MenuEmptyArea) - break; - - // draw separator ------------------------------------------------- - if (menuitem->menuItemType == QStyleOptionMenuItem::Separator) { - int yoff = y-1 + h / 2; - p->setPen(menuitem->palette.dark().color()); - p->drawLine(x, yoff, x+w, yoff); - ++yoff; - p->setPen(menuitem->palette.light().color()); - p->drawLine(x, yoff, x+w, yoff); - return; - } - - int xpos = x; - - // draw icon ------------------------------------------------------ - if (!menuitem->icon.isNull()) { - QIcon::Mode mode = dis ? QIcon::Disabled : QIcon::Normal; - if (act && !dis) - mode = QIcon::Active; - QPixmap pixmap = checked ? - menuitem->icon.pixmap(proxy()->pixelMetric(PM_SmallIconSize, option, widget), mode, QIcon::On) : - menuitem->icon.pixmap(proxy()->pixelMetric(PM_SmallIconSize, option, widget), mode); - QRect iconRect(QPoint(0, 0), pixmap.deviceIndependentSize().toSize()); - iconRect.moveCenter(QRect(xpos, y, checkcol, h).center()); - QRect vIconRect = visualRect(option->direction, option->rect, iconRect); - p->setPen(menuitem->palette.text().color()); - p->setBrush(Qt::NoBrush); - if (checked) - p->drawRect(vIconRect.adjusted(-1, -1, 0, 0)); - p->drawPixmap(vIconRect.topLeft(), pixmap); - - // draw checkmark ------------------------------------------------- - } else if (checked) { - QStyleOptionMenuItem newMi = *menuitem; - newMi.state = State_None; - if (!dis) - newMi.state |= State_Enabled; - if (act) - newMi.state |= State_On; - - QRect checkMarkRect = QRect(menuitem->rect.x() + windowsItemFrame, - menuitem->rect.y() + windowsItemFrame, - checkcol - 2 * windowsItemFrame, - menuitem->rect.height() - 2*windowsItemFrame); - newMi.rect = visualRect(option->direction, option->rect, checkMarkRect); - proxy()->drawPrimitive(PE_IndicatorMenuCheckMark, &newMi, p, widget); - } - - QColor textColor = dis ? menuitem->palette.text().color() : - act ? menuitem->palette.highlightedText().color() : menuitem->palette.buttonText().color(); - p->setPen(textColor); - - // draw text ------------------------------------------------------ - int xm = windowsItemFrame + checkcol + windowsItemHMargin; - xpos = menuitem->rect.x() + xm; - QRect textRect(xpos, y + windowsItemVMargin, w - xm - windowsRightBorder - tab + 1, h - 2 * windowsItemVMargin); - QRect vTextRect = visualRect(option->direction, option->rect, textRect); - QString s = menuitem->text; - if (!s.isEmpty()) { - p->save(); - int t = s.indexOf(QLatin1Char('\t')); - int text_flags = Qt::AlignVCenter|Qt::TextShowMnemonic | Qt::TextDontClip | Qt::TextSingleLine | Qt::AlignLeft; - if (!proxy()->styleHint(SH_UnderlineShortcut, menuitem, widget)) - text_flags |= Qt::TextHideMnemonic; - // draw tab text ---------------- - if (t >= 0) { - QRect vShortcutRect = visualRect(option->direction, option->rect, QRect(textRect.topRight(), menuitem->rect.bottomRight())); - if (dis && !act && proxy()->styleHint(SH_EtchDisabledText, option, widget)) { - p->setPen(menuitem->palette.light().color()); - p->drawText(vShortcutRect.adjusted(1,1,1,1), text_flags, s.mid(t + 1)); - p->setPen(textColor); - } - p->drawText(vShortcutRect, text_flags, s.mid(t + 1)); - s = s.left(t); - } - QFont font = menuitem->font; - if (menuitem->menuItemType == QStyleOptionMenuItem::DefaultItem) - font.setBold(true); - p->setFont(font); - if (dis && !act && proxy()->styleHint(SH_EtchDisabledText, option, widget)) { - p->setPen(menuitem->palette.light().color()); - p->drawText(vTextRect.adjusted(1,1,1,1), text_flags, s.left(t)); - p->setPen(textColor); - } - p->drawText(vTextRect, text_flags, s); - p->restore(); - } - - // draw sub menu arrow -------------------------------------------- - if (menuitem->menuItemType == QStyleOptionMenuItem::SubMenu) { - int dim = (h - 2) / 2; - PrimitiveElement arrow; - arrow = (option->direction == Qt::RightToLeft) ? PE_IndicatorArrowLeft : PE_IndicatorArrowRight; - xpos = x + w - windowsArrowHMargin - windowsItemFrame - dim; - QRect vSubMenuRect = visualRect(option->direction, option->rect, QRect(xpos, y + h / 2 - dim / 2, dim, dim)); - QStyleOptionMenuItem newMI = *menuitem; - newMI.rect = vSubMenuRect; - newMI.state = dis ? State_None : State_Enabled; - if (act) - newMI.palette.setColor(QPalette::ButtonText, newMI.palette.highlightedText().color()); - proxy()->drawPrimitive(arrow, &newMI, p, widget); - } - } - return; - - case CE_MenuBarItem: - if (const auto *mbi = qstyleoption_cast(option)) { - if (mbi->menuItemType == QStyleOptionMenuItem::DefaultItem) - break; - - bool act = mbi->state & State_Selected; - bool dis = !(mbi->state & State_Enabled); - - QBrush fill = mbi->palette.brush(act ? QPalette::Highlight : QPalette::Button); - QPalette::ColorRole textRole = dis ? QPalette::Text: - act ? QPalette::HighlightedText : QPalette::ButtonText; - QPixmap pix = mbi->icon.pixmap(proxy()->pixelMetric(PM_SmallIconSize, option, widget), QIcon::Normal); - - uint alignment = Qt::AlignCenter | Qt::TextShowMnemonic | Qt::TextDontClip | Qt::TextSingleLine; - if (!proxy()->styleHint(SH_UnderlineShortcut, mbi, widget)) - alignment |= Qt::TextHideMnemonic; - - p->fillRect(rect, fill); - if (!pix.isNull()) - drawItemPixmap(p, mbi->rect, alignment, pix); - else - drawItemText(p, mbi->rect, alignment, mbi->palette, mbi->state & State_Enabled, mbi->text, textRole); - } - return; -#if QT_CONFIG(dockwidget) - case CE_DockWidgetTitle: - if (const auto *dwOpt = qstyleoption_cast(option)) { - const int buttonMargin = 4; - const int mw = proxy()->pixelMetric(QStyle::PM_DockWidgetTitleMargin, dwOpt, widget); - const int fw = proxy()->pixelMetric(PM_DockWidgetFrameWidth, dwOpt, widget); - const bool isFloating = widget && widget->isWindow(); - const bool isActive = dwOpt->state & State_Active; - - const bool verticalTitleBar = dwOpt->verticalTitleBar; - - if (verticalTitleBar) { - rect = rect.transposed(); - - p->translate(rect.left() - 1, rect.top() + rect.width()); - p->rotate(-90); - p->translate(-rect.left() + 1, -rect.top()); - } - QRect r = rect.adjusted(0, 2, -1, -3); - QRect titleRect = r; - - if (dwOpt->closable) { - QSize sz = proxy()->standardIcon(QStyle::SP_TitleBarCloseButton, dwOpt, widget).actualSize(QSize(10, 10)); - titleRect.adjust(0, 0, -sz.width() - mw - buttonMargin, 0); - } - - if (dwOpt->floatable) { - QSize sz = proxy()->standardIcon(QStyle::SP_TitleBarMaxButton, dwOpt, widget).actualSize(QSize(10, 10)); - titleRect.adjust(0, 0, -sz.width() - mw - buttonMargin, 0); - } - - if (isFloating) { - titleRect.adjust(0, -fw, 0, 0); - if (widget != nullptr && widget->windowIcon().cacheKey() != QApplication::windowIcon().cacheKey()) - titleRect.adjust(titleRect.height() + mw, 0, 0, 0); - } else { - titleRect.adjust(mw, 0, 0, 0); - if (!dwOpt->floatable && !dwOpt->closable) - titleRect.adjust(0, 0, -mw, 0); - } - - if (!verticalTitleBar) - titleRect = visualRect(dwOpt->direction, r, titleRect); - - if (!isFloating) { - QPen oldPen = p->pen(); - QString titleText = p->fontMetrics().elidedText(dwOpt->title, Qt::ElideRight, titleRect.width()); - p->setPen(dwOpt->palette.color(QPalette::Dark)); - p->drawRect(r); - - if (!titleText.isEmpty()) { - drawItemText(p, titleRect, - Qt::AlignLeft | Qt::AlignVCenter | Qt::TextShowMnemonic, dwOpt->palette, - dwOpt->state & State_Enabled, titleText, - QPalette::WindowText); - } - - p->setPen(oldPen); - } else { - themeNumber = QWindowsXPStylePrivate::WindowTheme; - if (isActive) - stateId = CS_ACTIVE; - else - stateId = CS_INACTIVE; - - int titleHeight = rect.height() - 2; - rect = rect.adjusted(-fw, -fw, fw, 0); - - XPThemeData theme(widget, p, themeNumber, 0, stateId); - if (!theme.isValid()) - break; - - // Draw small type title bar - theme.rect = rect; - theme.partId = WP_SMALLCAPTION; - d->drawBackground(theme); - - // Figure out maximal button space on title bar - - QIcon ico = widget->windowIcon(); - bool hasIcon = (ico.cacheKey() != QApplication::windowIcon().cacheKey()); - if (hasIcon) { - QPixmap pxIco = ico.pixmap(titleHeight); - if (!verticalTitleBar && dwOpt->direction == Qt::RightToLeft) - p->drawPixmap(rect.width() - titleHeight - pxIco.width(), rect.bottom() - titleHeight - 2, pxIco); - else - p->drawPixmap(fw, rect.bottom() - titleHeight - 2, pxIco); - } - if (!dwOpt->title.isEmpty()) { - QPen oldPen = p->pen(); - QFont oldFont = p->font(); - QFont titleFont = oldFont; - titleFont.setBold(true); - p->setFont(titleFont); - QString titleText - = p->fontMetrics().elidedText(dwOpt->title, Qt::ElideRight, titleRect.width()); - - int result = TST_NONE; - GetThemeEnumValue(theme.handle(), WP_SMALLCAPTION, isActive ? CS_ACTIVE : CS_INACTIVE, TMT_TEXTSHADOWTYPE, &result); - if (result != TST_NONE) { - COLORREF textShadowRef; - GetThemeColor(theme.handle(), WP_SMALLCAPTION, isActive ? CS_ACTIVE : CS_INACTIVE, TMT_TEXTSHADOWCOLOR, &textShadowRef); - QColor textShadow = qRgb(GetRValue(textShadowRef), GetGValue(textShadowRef), GetBValue(textShadowRef)); - p->setPen(textShadow); - drawItemText(p, titleRect.adjusted(1, 1, 1, 1), - Qt::AlignLeft | Qt::AlignBottom, dwOpt->palette, - dwOpt->state & State_Enabled, titleText); - } - - COLORREF captionText = GetSysColor(isActive ? COLOR_CAPTIONTEXT : COLOR_INACTIVECAPTIONTEXT); - QColor textColor = qRgb(GetRValue(captionText), GetGValue(captionText), GetBValue(captionText)); - p->setPen(textColor); - drawItemText(p, titleRect, - Qt::AlignLeft | Qt::AlignBottom, dwOpt->palette, - dwOpt->state & State_Enabled, titleText); - p->setFont(oldFont); - p->setPen(oldPen); - } - - } - - return; - } - break; -#endif // QT_CONFIG(dockwidget) -#if QT_CONFIG(rubberband) - case CE_RubberBand: - if (qstyleoption_cast(option)) { - QColor highlight = option->palette.color(QPalette::Active, QPalette::Highlight); - p->save(); - p->setPen(highlight.darker(120)); - QColor dimHighlight(qMin(highlight.red()/2 + 110, 255), - qMin(highlight.green()/2 + 110, 255), - qMin(highlight.blue()/2 + 110, 255), - (widget && widget->isWindow())? 255 : 127); - p->setBrush(dimHighlight); - p->drawRect(option->rect.adjusted(0, 0, -1, -1)); - p->restore(); - return; - } - break; -#endif // QT_CONFIG(rubberband) - case CE_HeaderEmptyArea: - if (option->state & State_Horizontal) { - themeNumber = QWindowsXPStylePrivate::HeaderTheme; - stateId = HIS_NORMAL; - } else { - QWindowsStyle::drawControl(CE_HeaderEmptyArea, option, p, widget); - return; - } - break; - default: - break; - } - - XPThemeData theme(widget, p, themeNumber, partId, stateId, rect); - if (!theme.isValid()) { - QWindowsStyle::drawControl(element, option, p, widget); - return; - } - - theme.rotate = rotate; - theme.mirrorHorizontally = hMirrored; - theme.mirrorVertically = vMirrored; - d->drawBackground(theme); -} - -QRect QWindowsXPStylePrivate::scrollBarGripperBounds(QStyle::State flags, const QWidget *widget, XPThemeData *theme) -{ - const bool horizontal = flags & QStyle::State_Horizontal; - const qreal factor = QWindowsStylePrivate::nativeMetricScaleFactor(widget); - const QMargins contentsMargin = - (theme->margins(theme->rect, TMT_SIZINGMARGINS) * factor).toMargins(); - theme->partId = horizontal ? SBP_GRIPPERHORZ : SBP_GRIPPERVERT; - const QSize size = (theme->size() * factor).toSize(); - - const int hSpace = theme->rect.width() - size.width(); - const int vSpace = theme->rect.height() - size.height(); - const bool sufficientSpace = (horizontal && hSpace > (contentsMargin.left() + contentsMargin.right())) - || vSpace > contentsMargin.top() + contentsMargin.bottom(); - return sufficientSpace ? QRect(theme->rect.topLeft() + QPoint(hSpace, vSpace) / 2, size) : QRect(); -} - -#if QT_CONFIG(mdiarea) -// Helper for drawing MDI buttons into the corner widget of QMenuBar in case a -// QMdiSubWindow is maximized. -static void populateMdiButtonTheme(const QStyle *proxy, const QWidget *widget, - const QStyleOptionComplex *option, - QStyle::SubControl subControl, int part, - XPThemeData *theme) -{ - theme->partId = part; - theme->rect = proxy->subControlRect(QStyle::CC_MdiControls, option, subControl, widget); - if (!option->state.testFlag(QStyle::State_Enabled)) - theme->stateId = CBS_INACTIVE; - else if (option->state.testFlag(QStyle::State_Sunken) && option->activeSubControls.testFlag(subControl)) - theme->stateId = CBS_PUSHED; - else if (option->state.testFlag(QStyle::State_MouseOver) && option->activeSubControls.testFlag(subControl)) - theme->stateId = CBS_HOT; - else - theme->stateId = CBS_NORMAL; -} - -// Calculate an small (max 2), empirical correction factor for scaling up -// WP_MDICLOSEBUTTON, WP_MDIRESTOREBUTTON, WP_MDIMINBUTTON, which are too -// small on High DPI screens (QTBUG-75927). -static qreal mdiButtonCorrectionFactor(XPThemeData &theme, const QPaintDevice *pd = nullptr) -{ - const auto dpr = pd ? pd->devicePixelRatio() : qApp->devicePixelRatio(); - const QSizeF nativeSize = QSizeF(theme.size()) / dpr; - const QSizeF requestedSize(theme.rect.size()); - const auto rawFactor = qMin(requestedSize.width() / nativeSize.width(), - requestedSize.height() / nativeSize.height()); - const auto factor = rawFactor >= qreal(2) ? qreal(2) : qreal(1); - return factor; -} -#endif // QT_CONFIG(mdiarea) - -static void populateTitleBarButtonTheme(const QStyle *proxy, const QWidget *widget, - const QStyleOptionComplex *option, - QStyle::SubControl subControl, - bool isTitleBarActive, int part, - XPThemeData *theme) -{ - theme->rect = proxy->subControlRect(QStyle::CC_TitleBar, option, subControl, widget); - theme->partId = part; - if (widget && !widget->isEnabled()) - theme->stateId = RBS_DISABLED; - else if (option->activeSubControls == subControl && option->state.testFlag(QStyle::State_Sunken)) - theme->stateId = RBS_PUSHED; - else if (option->activeSubControls == subControl && option->state.testFlag(QStyle::State_MouseOver)) - theme->stateId = RBS_HOT; - else if (!isTitleBarActive) - theme->stateId = RBS_INACTIVE; - else - theme->stateId = RBS_NORMAL; -} - -/*! - \reimp -*/ -void QWindowsXPStyle::drawComplexControl(ComplexControl cc, const QStyleOptionComplex *option, - QPainter *p, const QWidget *widget) const -{ - if (!QWindowsXPStylePrivate::useXP()) { - QWindowsStyle::drawComplexControl(cc, option, p, widget); - return; - } - - auto *d = const_cast(d_func()); - - State flags = option->state; - SubControls sub = option->subControls; - - int partId = 0; - int stateId = 0; - if (widget && widget->testAttribute(Qt::WA_UnderMouse) && widget->isActiveWindow()) - flags |= State_MouseOver; - - switch (cc) { -#if QT_CONFIG(slider) - case CC_Slider: - if (const auto *slider = qstyleoption_cast(option)) { - XPThemeData theme(widget, p, QWindowsXPStylePrivate::TrackBarTheme); - QRect slrect = slider->rect; - QRegion tickreg = slrect; - if (sub & SC_SliderGroove) { - theme.rect = proxy()->subControlRect(CC_Slider, option, SC_SliderGroove, widget); - if (slider->orientation == Qt::Horizontal) { - partId = TKP_TRACK; - stateId = TRS_NORMAL; - theme.rect = QRect(slrect.left(), theme.rect.center().y() - 2, slrect.width(), 4); - } else { - partId = TKP_TRACKVERT; - stateId = TRVS_NORMAL; - theme.rect = QRect(theme.rect.center().x() - 2, slrect.top(), 4, slrect.height()); - } - theme.partId = partId; - theme.stateId = stateId; - d->drawBackground(theme); - tickreg -= theme.rect; - } - if (sub & SC_SliderTickmarks) { - int tickOffset = proxy()->pixelMetric(PM_SliderTickmarkOffset, slider, widget); - int ticks = slider->tickPosition; - int thickness = proxy()->pixelMetric(PM_SliderControlThickness, slider, widget); - int len = proxy()->pixelMetric(PM_SliderLength, slider, widget); - int available = proxy()->pixelMetric(PM_SliderSpaceAvailable, slider, widget); - int interval = slider->tickInterval; - if (interval <= 0) { - interval = slider->singleStep; - if (QStyle::sliderPositionFromValue(slider->minimum, slider->maximum, interval, - available) - - QStyle::sliderPositionFromValue(slider->minimum, slider->maximum, - 0, available) < 3) - interval = slider->pageStep; - } - if (!interval) - interval = 1; - int fudge = len / 2; - int pos; - int bothOffset = (ticks & QSlider::TicksAbove && ticks & QSlider::TicksBelow) ? 1 : 0; - p->setPen(d->sliderTickColor); - QVarLengthArray lines; - int v = slider->minimum; - while (v <= slider->maximum + 1) { - if (v == slider->maximum + 1 && interval == 1) - break; - const int v_ = qMin(v, slider->maximum); - int tickLength = (v_ == slider->minimum || v_ >= slider->maximum) ? 4 : 3; - pos = QStyle::sliderPositionFromValue(slider->minimum, slider->maximum, - v_, available) + fudge; - if (slider->orientation == Qt::Horizontal) { - if (ticks & QSlider::TicksAbove) { - lines.append(QLine(pos, tickOffset - 1 - bothOffset, - pos, tickOffset - 1 - bothOffset - tickLength)); - } - - if (ticks & QSlider::TicksBelow) { - lines.append(QLine(pos, tickOffset + thickness + bothOffset, - pos, tickOffset + thickness + bothOffset + tickLength)); - } - } else { - if (ticks & QSlider::TicksAbove) { - lines.append(QLine(tickOffset - 1 - bothOffset, pos, - tickOffset - 1 - bothOffset - tickLength, pos)); - } - - if (ticks & QSlider::TicksBelow) { - lines.append(QLine(tickOffset + thickness + bothOffset, pos, - tickOffset + thickness + bothOffset + tickLength, pos)); - } - } - // in the case where maximum is max int - int nextInterval = v + interval; - if (nextInterval < v) - break; - v = nextInterval; - } - if (!lines.isEmpty()) { - p->save(); - p->translate(slrect.topLeft()); - p->drawLines(lines.constData(), lines.size()); - p->restore(); - } - } - if (sub & SC_SliderHandle) { - theme.rect = proxy()->subControlRect(CC_Slider, option, SC_SliderHandle, widget); - if (slider->orientation == Qt::Horizontal) { - if (slider->tickPosition == QSlider::TicksAbove) - partId = TKP_THUMBTOP; - else if (slider->tickPosition == QSlider::TicksBelow) - partId = TKP_THUMBBOTTOM; - else - partId = TKP_THUMB; - - if (!(slider->state & State_Enabled)) - stateId = TUS_DISABLED; - else if (slider->activeSubControls & SC_SliderHandle && (slider->state & State_Sunken)) - stateId = TUS_PRESSED; - else if (slider->activeSubControls & SC_SliderHandle && (slider->state & State_MouseOver)) - stateId = TUS_HOT; - else if (flags & State_HasFocus) - stateId = TUS_FOCUSED; - else - stateId = TUS_NORMAL; - } else { - if (slider->tickPosition == QSlider::TicksLeft) - partId = TKP_THUMBLEFT; - else if (slider->tickPosition == QSlider::TicksRight) - partId = TKP_THUMBRIGHT; - else - partId = TKP_THUMBVERT; - - if (!(slider->state & State_Enabled)) - stateId = TUVS_DISABLED; - else if (slider->activeSubControls & SC_SliderHandle && (slider->state & State_Sunken)) - stateId = TUVS_PRESSED; - else if (slider->activeSubControls & SC_SliderHandle && (slider->state & State_MouseOver)) - stateId = TUVS_HOT; - else if (flags & State_HasFocus) - stateId = TUVS_FOCUSED; - else - stateId = TUVS_NORMAL; - } - theme.partId = partId; - theme.stateId = stateId; - d->drawBackground(theme); - } - if (slider->state & State_HasFocus) { - QStyleOptionFocusRect fropt; - fropt.QStyleOption::operator=(*slider); - fropt.rect = subElementRect(SE_SliderFocusRect, slider, widget); - proxy()->drawPrimitive(PE_FrameFocusRect, &fropt, p, widget); - } - } - break; -#endif -#if QT_CONFIG(toolbutton) - case CC_ToolButton: - if (const auto *toolbutton = qstyleoption_cast(option)) { - QRect button, menuarea; - button = proxy()->subControlRect(cc, toolbutton, SC_ToolButton, widget); - menuarea = proxy()->subControlRect(cc, toolbutton, SC_ToolButtonMenu, widget); - - State bflags = toolbutton->state & ~State_Sunken; - State mflags = bflags; - bool autoRaise = flags & State_AutoRaise; - if (autoRaise) { - if (!(bflags & State_MouseOver) || !(bflags & State_Enabled)) - bflags &= ~State_Raised; - } - - if (toolbutton->state & State_Sunken) { - if (toolbutton->activeSubControls & SC_ToolButton) { - bflags |= State_Sunken; - mflags |= State_MouseOver | State_Sunken; - } else if (toolbutton->activeSubControls & SC_ToolButtonMenu) { - mflags |= State_Sunken; - bflags |= State_MouseOver; - } - } - - QStyleOption tool = *toolbutton; - if (toolbutton->subControls & SC_ToolButton) { - if (flags & (State_Sunken | State_On | State_Raised) || !autoRaise) { - if (toolbutton->features & QStyleOptionToolButton::MenuButtonPopup && autoRaise) { - XPThemeData theme(widget, p, QWindowsXPStylePrivate::ToolBarTheme); - theme.partId = TP_SPLITBUTTON; - theme.rect = button; - if (!(bflags & State_Enabled)) - stateId = TS_DISABLED; - else if (bflags & State_Sunken) - stateId = TS_PRESSED; - else if (bflags & State_MouseOver || !(flags & State_AutoRaise)) - stateId = flags & State_On ? TS_HOTCHECKED : TS_HOT; - else if (bflags & State_On) - stateId = TS_CHECKED; - else - stateId = TS_NORMAL; - if (option->direction == Qt::RightToLeft) - theme.mirrorHorizontally = true; - theme.stateId = stateId; - d->drawBackground(theme); - } else { - tool.rect = option->rect; - tool.state = bflags; - if (autoRaise) // for tool bars - proxy()->drawPrimitive(PE_PanelButtonTool, &tool, p, widget); - else - proxy()->drawPrimitive(PE_PanelButtonBevel, &tool, p, widget); - } - } - } - - if (toolbutton->state & State_HasFocus) { - QStyleOptionFocusRect fr; - fr.QStyleOption::operator=(*toolbutton); - fr.rect.adjust(3, 3, -3, -3); - if (toolbutton->features & QStyleOptionToolButton::MenuButtonPopup) - fr.rect.adjust(0, 0, -proxy()->pixelMetric(QStyle::PM_MenuButtonIndicator, - toolbutton, widget), 0); - proxy()->drawPrimitive(PE_FrameFocusRect, &fr, p, widget); - } - QStyleOptionToolButton label = *toolbutton; - label.state = bflags; - int fw = 2; - if (!autoRaise) - label.state &= ~State_Sunken; - label.rect = button.adjusted(fw, fw, -fw, -fw); - proxy()->drawControl(CE_ToolButtonLabel, &label, p, widget); - - if (toolbutton->subControls & SC_ToolButtonMenu) { - tool.rect = menuarea; - tool.state = mflags; - if (autoRaise) { - proxy()->drawPrimitive(PE_IndicatorButtonDropDown, &tool, p, widget); - } else { - tool.state = mflags; - menuarea.adjust(-2, 0, 0, 0); - // Draw menu button - if ((bflags & State_Sunken) != (mflags & State_Sunken)){ - p->save(); - p->setClipRect(menuarea); - tool.rect = option->rect; - proxy()->drawPrimitive(PE_PanelButtonBevel, &tool, p, nullptr); - p->restore(); - } - // Draw arrow - p->save(); - p->setPen(option->palette.dark().color()); - p->drawLine(menuarea.left(), menuarea.top() + 3, - menuarea.left(), menuarea.bottom() - 3); - p->setPen(option->palette.light().color()); - p->drawLine(menuarea.left() - 1, menuarea.top() + 3, - menuarea.left() - 1, menuarea.bottom() - 3); - - tool.rect = menuarea.adjusted(2, 3, -2, -1); - proxy()->drawPrimitive(PE_IndicatorArrowDown, &tool, p, widget); - p->restore(); - } - } else if (toolbutton->features & QStyleOptionToolButton::HasMenu) { - int mbi = proxy()->pixelMetric(PM_MenuButtonIndicator, toolbutton, widget); - QRect ir = toolbutton->rect; - QStyleOptionToolButton newBtn = *toolbutton; - newBtn.rect = QRect(ir.right() + 4 - mbi, ir.height() - mbi + 4, mbi - 5, mbi - 5); - proxy()->drawPrimitive(PE_IndicatorArrowDown, &newBtn, p, widget); - } - } - break; -#endif // QT_CONFIG(toolbutton) - - case CC_TitleBar: - if (const auto *tb = qstyleoption_cast(option)) { - const qreal factor = QWindowsStylePrivate::nativeMetricScaleFactor(widget); - bool isActive = tb->titleBarState & QStyle::State_Active; - XPThemeData theme(widget, p, QWindowsXPStylePrivate::WindowTheme); - if (sub & SC_TitleBarLabel) { - partId = (tb->titleBarState & Qt::WindowMinimized) ? WP_MINCAPTION : WP_CAPTION; - theme.rect = option->rect; - if (widget && !widget->isEnabled()) - stateId = CS_DISABLED; - else if (isActive) - stateId = CS_ACTIVE; - else - stateId = CS_INACTIVE; - - theme.partId = partId; - theme.stateId = stateId; - d->drawBackground(theme); - - QRect ir = proxy()->subControlRect(CC_TitleBar, tb, SC_TitleBarLabel, widget); - - int result = TST_NONE; - GetThemeEnumValue(theme.handle(), WP_CAPTION, isActive ? CS_ACTIVE : CS_INACTIVE, TMT_TEXTSHADOWTYPE, &result); - if (result != TST_NONE) { - COLORREF textShadowRef; - GetThemeColor(theme.handle(), WP_CAPTION, isActive ? CS_ACTIVE : CS_INACTIVE, TMT_TEXTSHADOWCOLOR, &textShadowRef); - QColor textShadow = qRgb(GetRValue(textShadowRef), GetGValue(textShadowRef), GetBValue(textShadowRef)); - p->setPen(textShadow); - p->drawText(int(ir.x() + 3 * factor), int(ir.y() + 2 * factor), - int(ir.width() - 1 * factor), ir.height(), - Qt::AlignLeft | Qt::AlignVCenter | Qt::TextSingleLine, tb->text); - } - COLORREF captionText = GetSysColor(isActive ? COLOR_CAPTIONTEXT : COLOR_INACTIVECAPTIONTEXT); - QColor textColor = qRgb(GetRValue(captionText), GetGValue(captionText), GetBValue(captionText)); - p->setPen(textColor); - p->drawText(int(ir.x() + 2 * factor), int(ir.y() + 1 * factor), - int(ir.width() - 2 * factor), ir.height(), - Qt::AlignLeft | Qt::AlignVCenter | Qt::TextSingleLine, tb->text); - } - if (sub & SC_TitleBarSysMenu && tb->titleBarFlags & Qt::WindowSystemMenuHint) { - theme.rect = proxy()->subControlRect(CC_TitleBar, option, SC_TitleBarSysMenu, widget); - partId = WP_SYSBUTTON; - if ((widget && !widget->isEnabled()) || !isActive) - stateId = SBS_DISABLED; - else if (option->activeSubControls == SC_TitleBarSysMenu && (option->state & State_Sunken)) - stateId = SBS_PUSHED; - else if (option->activeSubControls == SC_TitleBarSysMenu && (option->state & State_MouseOver)) - stateId = SBS_HOT; - else - stateId = SBS_NORMAL; - if (!tb->icon.isNull()) { - tb->icon.paint(p, theme.rect); - } else { - theme.partId = partId; - theme.stateId = stateId; - if (theme.size().isEmpty()) { - int iconSize = proxy()->pixelMetric(PM_SmallIconSize, tb, widget); - QPixmap pm = proxy()->standardIcon(SP_TitleBarMenuButton, tb, widget).pixmap(iconSize, iconSize); - p->save(); - drawItemPixmap(p, theme.rect, Qt::AlignCenter, pm); - p->restore(); - } else { - d->drawBackground(theme); - } - } - } - - if (sub & SC_TitleBarMinButton && tb->titleBarFlags & Qt::WindowMinimizeButtonHint - && !(tb->titleBarState & Qt::WindowMinimized)) { - populateTitleBarButtonTheme(proxy(), widget, option, SC_TitleBarMinButton, isActive, WP_MINBUTTON, &theme); - d->drawBackground(theme); - } - if (sub & SC_TitleBarMaxButton && tb->titleBarFlags & Qt::WindowMaximizeButtonHint - && !(tb->titleBarState & Qt::WindowMaximized)) { - populateTitleBarButtonTheme(proxy(), widget, option, SC_TitleBarMaxButton, isActive, WP_MAXBUTTON, &theme); - d->drawBackground(theme); - } - if (sub & SC_TitleBarContextHelpButton - && tb->titleBarFlags & Qt::WindowContextHelpButtonHint) { - populateTitleBarButtonTheme(proxy(), widget, option, SC_TitleBarContextHelpButton, isActive, WP_HELPBUTTON, &theme); - d->drawBackground(theme); - } - bool drawNormalButton = (sub & SC_TitleBarNormalButton) - && (((tb->titleBarFlags & Qt::WindowMinimizeButtonHint) - && (tb->titleBarState & Qt::WindowMinimized)) - || ((tb->titleBarFlags & Qt::WindowMaximizeButtonHint) - && (tb->titleBarState & Qt::WindowMaximized))); - if (drawNormalButton) { - populateTitleBarButtonTheme(proxy(), widget, option, SC_TitleBarNormalButton, isActive, WP_RESTOREBUTTON, &theme); - d->drawBackground(theme); - } - if (sub & SC_TitleBarShadeButton && tb->titleBarFlags & Qt::WindowShadeButtonHint - && !(tb->titleBarState & Qt::WindowMinimized)) { - populateTitleBarButtonTheme(proxy(), widget, option, SC_TitleBarShadeButton, isActive, WP_MINBUTTON, &theme); - d->drawBackground(theme); - } - if (sub & SC_TitleBarUnshadeButton && tb->titleBarFlags & Qt::WindowShadeButtonHint - && tb->titleBarState & Qt::WindowMinimized) { - populateTitleBarButtonTheme(proxy(), widget, option, SC_TitleBarUnshadeButton, isActive, WP_RESTOREBUTTON, &theme); - d->drawBackground(theme); - } - if (sub & SC_TitleBarCloseButton && tb->titleBarFlags & Qt::WindowSystemMenuHint) { - populateTitleBarButtonTheme(proxy(), widget, option, SC_TitleBarCloseButton, isActive, WP_CLOSEBUTTON, &theme); - d->drawBackground(theme); - } - } - break; - -#if QT_CONFIG(mdiarea) - case CC_MdiControls: { - XPThemeData theme(widget, p, QWindowsXPStylePrivate::WindowTheme, WP_MDICLOSEBUTTON, CBS_NORMAL); - if (Q_UNLIKELY(!theme.isValid())) - return; - - if (option->subControls.testFlag(SC_MdiCloseButton)) { - populateMdiButtonTheme(proxy(), widget, option, SC_MdiCloseButton, WP_MDICLOSEBUTTON, &theme); - d->drawBackground(theme, mdiButtonCorrectionFactor(theme, widget)); - } - if (option->subControls.testFlag(SC_MdiNormalButton)) { - populateMdiButtonTheme(proxy(), widget, option, SC_MdiNormalButton, WP_MDIRESTOREBUTTON, &theme); - d->drawBackground(theme, mdiButtonCorrectionFactor(theme, widget)); - } - if (option->subControls.testFlag(QStyle::SC_MdiMinButton)) { - populateMdiButtonTheme(proxy(), widget, option, SC_MdiMinButton, WP_MDIMINBUTTON, &theme); - d->drawBackground(theme, mdiButtonCorrectionFactor(theme, widget)); - } - break; - } -#endif // QT_CONFIG(mdiarea) -#if QT_CONFIG(dial) - case CC_Dial: - if (const auto *dial = qstyleoption_cast(option)) - QStyleHelper::drawDial(dial, p); - break; -#endif // QT_CONFIG(dial) - default: - QWindowsStyle::drawComplexControl(cc, option, p, widget); - break; - } -} - -int QWindowsXPStylePrivate::pixelMetricFromSystemDp(QStyle::PixelMetric pm, const QStyleOption *option, const QWidget *widget) -{ - switch (pm) { - case QStyle::PM_IndicatorWidth: - return XPThemeData::themeSize(widget, nullptr, QWindowsXPStylePrivate::ButtonTheme, BP_CHECKBOX, CBS_UNCHECKEDNORMAL).width(); - case QStyle::PM_IndicatorHeight: - return XPThemeData::themeSize(widget, nullptr, QWindowsXPStylePrivate::ButtonTheme, BP_CHECKBOX, CBS_UNCHECKEDNORMAL).height(); - case QStyle::PM_ExclusiveIndicatorWidth: - return XPThemeData::themeSize(widget, nullptr, QWindowsXPStylePrivate::ButtonTheme, BP_RADIOBUTTON, RBS_UNCHECKEDNORMAL).width(); - case QStyle::PM_ExclusiveIndicatorHeight: - return XPThemeData::themeSize(widget, nullptr, QWindowsXPStylePrivate::ButtonTheme, BP_RADIOBUTTON, RBS_UNCHECKEDNORMAL).height(); - case QStyle::PM_ProgressBarChunkWidth: - return progressBarOrientation(option) == Qt::Horizontal - ? XPThemeData::themeSize(widget, nullptr, QWindowsXPStylePrivate::ProgressTheme, PP_CHUNK).width() - : XPThemeData::themeSize(widget, nullptr, QWindowsXPStylePrivate::ProgressTheme, PP_CHUNKVERT).height(); - case QStyle::PM_SliderThickness: - return XPThemeData::themeSize(widget, nullptr, QWindowsXPStylePrivate::TrackBarTheme, TKP_THUMB).height(); - case QStyle::PM_TitleBarHeight: - return widget && (widget->windowType() == Qt::Tool) - ? GetSystemMetrics(SM_CYSMCAPTION) + GetSystemMetrics(SM_CXSIZEFRAME) - : GetSystemMetrics(SM_CYCAPTION) + GetSystemMetrics(SM_CXSIZEFRAME); - case QStyle::PM_MdiSubWindowFrameWidth: - return XPThemeData::themeSize(widget, nullptr, QWindowsXPStylePrivate::WindowTheme, WP_FRAMELEFT, FS_ACTIVE).width(); - case QStyle::PM_DockWidgetFrameWidth: - return XPThemeData::themeSize(widget, nullptr, QWindowsXPStylePrivate::WindowTheme, WP_SMALLFRAMERIGHT, FS_ACTIVE).width(); - default: - break; - } - return QWindowsXPStylePrivate::InvalidMetric; -} - -/*! \reimp */ -int QWindowsXPStyle::pixelMetric(PixelMetric pm, const QStyleOption *option, const QWidget *widget) const -{ - if (!QWindowsXPStylePrivate::useXP()) - return QWindowsStyle::pixelMetric(pm, option, widget); - - int res = QWindowsXPStylePrivate::pixelMetricFromSystemDp(pm, option, widget); - if (res != QWindowsStylePrivate::InvalidMetric) - return qRound(qreal(res) * QWindowsStylePrivate::nativeMetricScaleFactor(widget)); - - res = 0; - switch (pm) { - case PM_MenuBarPanelWidth: - case PM_ButtonDefaultIndicator: - res = 0; - break; - - case PM_DefaultFrameWidth: - res = qobject_cast(widget) ? 2 : 1; - break; - case PM_MenuPanelWidth: - case PM_SpinBoxFrameWidth: - res = 1; - break; - - case PM_TabBarTabOverlap: - case PM_MenuHMargin: - case PM_MenuVMargin: - res = 2; - break; - - case PM_TabBarBaseOverlap: - if (const auto *tab = qstyleoption_cast(option)) { - switch (tab->shape) { - case QTabBar::RoundedNorth: - case QTabBar::TriangularNorth: - case QTabBar::RoundedWest: - case QTabBar::TriangularWest: - res = 1; - break; - case QTabBar::RoundedSouth: - case QTabBar::TriangularSouth: - res = 2; - break; - case QTabBar::RoundedEast: - case QTabBar::TriangularEast: - res = 3; - break; - } - } - break; - - case PM_SplitterWidth: - res = QStyleHelper::dpiScaled(5., option); - break; - - case PM_MdiSubWindowMinimizedWidth: - res = 160; - break; - -#if QT_CONFIG(toolbar) - case PM_ToolBarHandleExtent: - res = int(QStyleHelper::dpiScaled(8., option)); - break; - -#endif // QT_CONFIG(toolbar) - case PM_DockWidgetSeparatorExtent: - case PM_DockWidgetTitleMargin: - res = int(QStyleHelper::dpiScaled(4., option)); - break; - - case PM_ButtonShiftHorizontal: - case PM_ButtonShiftVertical: - res = qstyleoption_cast(option) ? 1 : 0; - break; - - default: - res = QWindowsStyle::pixelMetric(pm, option, widget); - } - - return res; -} - -/* - This function is used by subControlRect to check if a button - should be drawn for the given subControl given a set of window flags. -*/ -static bool buttonVisible(const QStyle::SubControl sc, const QStyleOptionTitleBar *tb) -{ - bool isMinimized = tb->titleBarState & Qt::WindowMinimized; - bool isMaximized = tb->titleBarState & Qt::WindowMaximized; - const uint flags = tb->titleBarFlags; - bool retVal = false; - switch (sc) { - case QStyle::SC_TitleBarContextHelpButton: - if (flags & Qt::WindowContextHelpButtonHint) - retVal = true; - break; - case QStyle::SC_TitleBarMinButton: - if (!isMinimized && (flags & Qt::WindowMinimizeButtonHint)) - retVal = true; - break; - case QStyle::SC_TitleBarNormalButton: - if (isMinimized && (flags & Qt::WindowMinimizeButtonHint)) - retVal = true; - else if (isMaximized && (flags & Qt::WindowMaximizeButtonHint)) - retVal = true; - break; - case QStyle::SC_TitleBarMaxButton: - if (!isMaximized && (flags & Qt::WindowMaximizeButtonHint)) - retVal = true; - break; - case QStyle::SC_TitleBarShadeButton: - if (!isMinimized && flags & Qt::WindowShadeButtonHint) - retVal = true; - break; - case QStyle::SC_TitleBarUnshadeButton: - if (isMinimized && flags & Qt::WindowShadeButtonHint) - retVal = true; - break; - case QStyle::SC_TitleBarCloseButton: - if (flags & Qt::WindowSystemMenuHint) - retVal = true; - break; - case QStyle::SC_TitleBarSysMenu: - if (flags & Qt::WindowSystemMenuHint) - retVal = true; - break; - default : - retVal = true; - } - return retVal; -} - -/*! - \reimp -*/ -QRect QWindowsXPStyle::subControlRect(ComplexControl cc, const QStyleOptionComplex *option, - SubControl subControl, const QWidget *widget) const -{ - if (!QWindowsXPStylePrivate::useXP()) - return QWindowsStyle::subControlRect(cc, option, subControl, widget); - - QRect rect; - - switch (cc) { - case CC_TitleBar: - if (const auto *tb = qstyleoption_cast(option)) { - if (!buttonVisible(subControl, tb)) - return rect; - const bool isToolTitle = false; - const int height = tb->rect.height(); - const int width = tb->rect.width(); - const int buttonMargin = int(QStyleHelper::dpiScaled(4, option)); - const qreal factor = QWindowsStylePrivate::nativeMetricScaleFactor(widget); - int buttonHeight = qRound(qreal(GetSystemMetrics(SM_CYSIZE)) * factor) - - buttonMargin; - int buttonWidth = qRound(qreal(GetSystemMetrics(SM_CXSIZE)) * factor) - - buttonMargin; - const int delta = buttonWidth + 2; - int controlTop = option->rect.bottom() - buttonHeight - 2; - const int frameWidth = proxy()->pixelMetric(PM_MdiSubWindowFrameWidth, option, widget); - const bool sysmenuHint = (tb->titleBarFlags & Qt::WindowSystemMenuHint) != 0; - const bool minimizeHint = (tb->titleBarFlags & Qt::WindowMinimizeButtonHint) != 0; - const bool maximizeHint = (tb->titleBarFlags & Qt::WindowMaximizeButtonHint) != 0; - const bool contextHint = (tb->titleBarFlags & Qt::WindowContextHelpButtonHint) != 0; - const bool shadeHint = (tb->titleBarFlags & Qt::WindowShadeButtonHint) != 0; - bool isMinimized = tb->titleBarState & Qt::WindowMinimized; - bool isMaximized = tb->titleBarState & Qt::WindowMaximized; - int offset = 0; - - switch (subControl) { - case SC_TitleBarLabel: - rect = QRect(frameWidth, 0, width - (buttonWidth + frameWidth + 10), height); - if (isToolTitle) { - if (sysmenuHint) { - rect.adjust(0, 0, -buttonWidth - 3, 0); - } - if (minimizeHint || maximizeHint) - rect.adjust(0, 0, -buttonWidth - 2, 0); - } else { - if (sysmenuHint) { - const int leftOffset = height - 8; - rect.adjust(leftOffset, 0, 0, 0); - } - if (minimizeHint) - rect.adjust(0, 0, -buttonWidth - 2, 0); - if (maximizeHint) - rect.adjust(0, 0, -buttonWidth - 2, 0); - if (contextHint) - rect.adjust(0, 0, -buttonWidth - 2, 0); - if (shadeHint) - rect.adjust(0, 0, -buttonWidth - 2, 0); - } - break; - - case SC_TitleBarContextHelpButton: - if (tb->titleBarFlags & Qt::WindowContextHelpButtonHint) - offset += delta; - Q_FALLTHROUGH(); - case SC_TitleBarMinButton: - if (!isMinimized && (tb->titleBarFlags & Qt::WindowMinimizeButtonHint)) - offset += delta; - else if (subControl == SC_TitleBarMinButton) - break; - Q_FALLTHROUGH(); - case SC_TitleBarNormalButton: - if (isMinimized && (tb->titleBarFlags & Qt::WindowMinimizeButtonHint)) - offset += delta; - else if (isMaximized && (tb->titleBarFlags & Qt::WindowMaximizeButtonHint)) - offset += delta; - else if (subControl == SC_TitleBarNormalButton) - break; - Q_FALLTHROUGH(); - case SC_TitleBarMaxButton: - if (!isMaximized && (tb->titleBarFlags & Qt::WindowMaximizeButtonHint)) - offset += delta; - else if (subControl == SC_TitleBarMaxButton) - break; - Q_FALLTHROUGH(); - case SC_TitleBarShadeButton: - if (!isMinimized && (tb->titleBarFlags & Qt::WindowShadeButtonHint)) - offset += delta; - else if (subControl == SC_TitleBarShadeButton) - break; - Q_FALLTHROUGH(); - case SC_TitleBarUnshadeButton: - if (isMinimized && (tb->titleBarFlags & Qt::WindowShadeButtonHint)) - offset += delta; - else if (subControl == SC_TitleBarUnshadeButton) - break; - Q_FALLTHROUGH(); - case SC_TitleBarCloseButton: - if (tb->titleBarFlags & Qt::WindowSystemMenuHint) - offset += delta; - else if (subControl == SC_TitleBarCloseButton) - break; - - rect.setRect(width - offset - controlTop + 1, controlTop, - buttonWidth, buttonHeight); - break; - - case SC_TitleBarSysMenu: { - const int controlTop = 6; - const int controlHeight = height - controlTop - 3; - const int iconExtent = proxy()->pixelMetric(PM_SmallIconSize, option); - QSize iconSize = tb->icon.actualSize(QSize(iconExtent, iconExtent)); - if (tb->icon.isNull()) - iconSize = QSize(controlHeight, controlHeight); - int hPad = (controlHeight - iconSize.height())/2; - int vPad = (controlHeight - iconSize.width())/2; - rect = QRect(frameWidth + hPad, controlTop + vPad, iconSize.width(), iconSize.height()); - break; - } - default: - break; - } - } - break; - - case CC_ComboBox: - if (const auto *cmb = qstyleoption_cast(option)) { - const int x = cmb->rect.x(), y = cmb->rect.y(), wi = cmb->rect.width(), he = cmb->rect.height(); - const int xpos = x + wi - qRound(QStyleHelper::dpiScaled(1 + 16, option)); - - switch (subControl) { - case SC_ComboBoxFrame: - rect = cmb->rect; - break; - - case SC_ComboBoxArrow: { - const qreal dpi = QStyleHelper::dpi(option); - rect = QRect(xpos, y + qRound(QStyleHelper::dpiScaled(1, dpi)), - qRound(QStyleHelper::dpiScaled(16, dpi)), - he - qRound(QStyleHelper::dpiScaled(2, dpi))); - break; - } - - case SC_ComboBoxEditField: { - const qreal dpi = QStyleHelper::dpi(option); - const int frame = qRound(QStyleHelper::dpiScaled(2, dpi)); - rect = QRect(x + frame, y + frame, - wi - qRound(QStyleHelper::dpiScaled(3 + 16, dpi)), - he - qRound(QStyleHelper::dpiScaled(4, dpi))); - break; - } - - case SC_ComboBoxListBoxPopup: - rect = cmb->rect; - break; - - default: - break; - } - } - break; -#if QT_CONFIG(mdiarea) - case CC_MdiControls: { - int numSubControls = 0; - if (option->subControls & SC_MdiCloseButton) - ++numSubControls; - if (option->subControls & SC_MdiMinButton) - ++numSubControls; - if (option->subControls & SC_MdiNormalButton) - ++numSubControls; - if (numSubControls == 0) - break; - - int buttonWidth = option->rect.width() / numSubControls; - int offset = 0; - switch (subControl) { - case SC_MdiCloseButton: - // Only one sub control, no offset needed. - if (numSubControls == 1) - break; - offset += buttonWidth; - Q_FALLTHROUGH(); - case SC_MdiNormalButton: - // No offset needed if - // 1) There's only one sub control - // 2) We have a close button and a normal button (offset already added in SC_MdiClose) - if (numSubControls == 1 || (numSubControls == 2 && !(option->subControls & SC_MdiMinButton))) - break; - if (option->subControls & SC_MdiNormalButton) - offset += buttonWidth; - break; - default: - break; - } - rect = QRect(offset, 0, buttonWidth, option->rect.height()); - break; - } -#endif // QT_CONFIG(mdiarea) - - default: - rect = visualRect(option->direction, option->rect, - QWindowsStyle::subControlRect(cc, option, subControl, widget)); - break; - } - return visualRect(option->direction, option->rect, rect); -} - -/*! - \reimp -*/ -QSize QWindowsXPStyle::sizeFromContents(ContentsType ct, const QStyleOption *option, - const QSize &contentsSize, const QWidget *widget) const -{ - if (!QWindowsXPStylePrivate::useXP()) - return QWindowsStyle::sizeFromContents(ct, option, contentsSize, widget); - - QSize sz(contentsSize); - switch (ct) { - case CT_LineEdit: - case CT_ComboBox: { - XPThemeData buttontheme(widget, nullptr, QWindowsXPStylePrivate::ButtonTheme, BP_PUSHBUTTON, PBS_NORMAL); - if (buttontheme.isValid()) { - const qreal factor = QWindowsXPStylePrivate::nativeMetricScaleFactor(widget); - const QMarginsF borderSize = buttontheme.margins() * factor; - if (!borderSize.isNull()) { - const qreal margin = qreal(2) * factor; - sz.rwidth() += qRound(borderSize.left() + borderSize.right() - margin); - sz.rheight() += int(borderSize.bottom() + borderSize.top() - margin - + qreal(1) / factor - 1); - } - const int textMargins = 2*(proxy()->pixelMetric(PM_FocusFrameHMargin, option) + 1); - sz += QSize(qMax(pixelMetric(QStyle::PM_ScrollBarExtent, option, widget) - + textMargins, 23), 0); //arrow button - } - break; - } - case CT_TabWidget: - sz += QSize(6, 6); - break; - case CT_Menu: - sz += QSize(1, 0); - break; -#if QT_CONFIG(menubar) - case CT_MenuBarItem: - if (!sz.isEmpty()) - sz += QSize(windowsItemHMargin * 5 + 1, 6); - break; -#endif - case CT_MenuItem: - if (const auto *menuitem = qstyleoption_cast(option)) { - if (menuitem->menuItemType != QStyleOptionMenuItem::Separator) { - sz = QWindowsStyle::sizeFromContents(ct, option, sz, widget); - sz.setHeight(sz.height() - 2); - return sz; - } - } - sz = QWindowsStyle::sizeFromContents(ct, option, sz, widget); - break; - - case CT_MdiControls: { - sz.setHeight(int(QStyleHelper::dpiScaled(19, option))); - int width = 54; - if (const auto *styleOpt = qstyleoption_cast(option)) { - width = 0; - if (styleOpt->subControls & SC_MdiMinButton) - width += 17 + 1; - if (styleOpt->subControls & SC_MdiNormalButton) - width += 17 + 1; - if (styleOpt->subControls & SC_MdiCloseButton) - width += 17 + 1; - } - sz.setWidth(int(QStyleHelper::dpiScaled(width, option))); - break; - } - - default: - sz = QWindowsStyle::sizeFromContents(ct, option, sz, widget); - break; - } - - return sz; -} - - -/*! \reimp */ -int QWindowsXPStyle::styleHint(StyleHint hint, const QStyleOption *option, const QWidget *widget, - QStyleHintReturn *returnData) const -{ - if (!QWindowsXPStylePrivate::useXP()) - return QWindowsStyle::styleHint(hint, option, widget, returnData); - - auto *d = const_cast(d_func()); - int res = 0; - switch (hint) { - case SH_EtchDisabledText: - res = (qobject_cast(widget) != 0); - break; - - case SH_SpinControls_DisableOnBounds: - res = 0; - break; - - case SH_TitleBar_AutoRaise: - case SH_TitleBar_NoBorder: - res = 1; - break; - - case SH_GroupBox_TextLabelColor: - if (!widget || widget->isEnabled()) - res = d->groupBoxTextColor; - else - res = d->groupBoxTextColorDisabled; - break; - - case SH_Table_GridLineColor: - res = 0xC0C0C0; - break; - - case SH_WindowFrame_Mask: { - res = 1; - auto *mask = qstyleoption_cast(returnData); - const auto *titlebar = qstyleoption_cast(option); - if (mask && titlebar) { - // Note certain themes will not return the whole window frame but only the titlebar part when - // queried This function needs to return the entire window mask, hence we will only fetch the mask for the - // titlebar itself and add the remaining part of the window rect at the bottom. - int tbHeight = proxy()->pixelMetric(PM_TitleBarHeight, option, widget); - QRect titleBarRect = option->rect; - titleBarRect.setHeight(tbHeight); - XPThemeData themeData; - if (titlebar->titleBarState & Qt::WindowMinimized) { - themeData = XPThemeData(widget, nullptr, - QWindowsXPStylePrivate::WindowTheme, - WP_MINCAPTION, CS_ACTIVE, titleBarRect); - } else - themeData = XPThemeData(widget, nullptr, - QWindowsXPStylePrivate::WindowTheme, - WP_CAPTION, CS_ACTIVE, titleBarRect); - mask->region = d->region(themeData) + - QRect(0, tbHeight, option->rect.width(), option->rect.height() - tbHeight); - } - break; - } -#if QT_CONFIG(rubberband) - case SH_RubberBand_Mask: - if (qstyleoption_cast(option)) - res = 0; - break; -#endif // QT_CONFIG(rubberband) - - case SH_ItemView_DrawDelegateFrame: - res = 1; - break; - - default: - res = QWindowsStyle::styleHint(hint, option, widget, returnData); - } - - return res; -} - -/*! \reimp */ -QPalette QWindowsXPStyle::standardPalette() const -{ - return QWindowsXPStylePrivate::useXP() ? QPalette() : QWindowsStyle::standardPalette(); -} - -/*! - \reimp -*/ -QPixmap QWindowsXPStyle::standardPixmap(StandardPixmap standardPixmap, const QStyleOption *option, - const QWidget *widget) const -{ - if (!QWindowsXPStylePrivate::useXP()) - return QWindowsStyle::standardPixmap(standardPixmap, option, widget); - - switch(standardPixmap) { - case SP_TitleBarMaxButton: - case SP_TitleBarCloseButton: - if (qstyleoption_cast(option)) { - if (widget && widget->isWindow()) { - XPThemeData theme(widget, nullptr, QWindowsXPStylePrivate::WindowTheme, WP_SMALLCLOSEBUTTON, CBS_NORMAL); - if (theme.isValid()) { - const QSize size = (theme.size() * QWindowsStylePrivate::nativeMetricScaleFactor(widget)).toSize(); - return QIcon(QWindowsStyle::standardPixmap(standardPixmap, option, widget)).pixmap(size); - } - } - } - break; - default: - break; - } - return QWindowsStyle::standardPixmap(standardPixmap, option, widget); -} - -/*! - \reimp -*/ -QIcon QWindowsXPStyle::standardIcon(StandardPixmap standardIcon, - const QStyleOption *option, - const QWidget *widget) const -{ - if (!QWindowsXPStylePrivate::useXP()) - return QWindowsStyle::standardIcon(standardIcon, option, widget); - - auto *d = const_cast(d_func()); - switch(standardIcon) { - case SP_TitleBarMaxButton: - if (qstyleoption_cast(option)) { - if (d->dockFloat.isNull()) { - XPThemeData themeSize(nullptr, nullptr, QWindowsXPStylePrivate::WindowTheme, - WP_SMALLCLOSEBUTTON, CBS_NORMAL); - XPThemeData theme(nullptr, nullptr, QWindowsXPStylePrivate::WindowTheme, - WP_MAXBUTTON, MAXBS_NORMAL); - if (theme.isValid()) { - const QSize size = (themeSize.size() * QWindowsStylePrivate::nativeMetricScaleFactor(widget)).toSize(); - QPixmap pm(size); - pm.fill(Qt::transparent); - QPainter p(&pm); - theme.painter = &p; - theme.rect = QRect(QPoint(0, 0), size); - d->drawBackground(theme); - d->dockFloat.addPixmap(pm, QIcon::Normal, QIcon::Off); // Normal - pm.fill(Qt::transparent); - theme.stateId = MAXBS_PUSHED; - d->drawBackground(theme); - d->dockFloat.addPixmap(pm, QIcon::Normal, QIcon::On); // Pressed - pm.fill(Qt::transparent); - theme.stateId = MAXBS_HOT; - d->drawBackground(theme); - d->dockFloat.addPixmap(pm, QIcon::Active, QIcon::Off); // Hover - pm.fill(Qt::transparent); - theme.stateId = MAXBS_INACTIVE; - d->drawBackground(theme); - d->dockFloat.addPixmap(pm, QIcon::Disabled, QIcon::Off); // Disabled - } - } - if (widget && widget->isWindow()) - return d->dockFloat; - } - break; - case SP_TitleBarCloseButton: - if (qstyleoption_cast(option)) { - if (d->dockClose.isNull()) { - XPThemeData theme(nullptr, nullptr, QWindowsXPStylePrivate::WindowTheme, - WP_SMALLCLOSEBUTTON, CBS_NORMAL); - if (theme.isValid()) { - const QSize size = (theme.size() * QWindowsStylePrivate::nativeMetricScaleFactor(widget)).toSize(); - QPixmap pm(size); - pm.fill(Qt::transparent); - QPainter p(&pm); - theme.painter = &p; - theme.partId = WP_CLOSEBUTTON; // #### - theme.rect = QRect(QPoint(0, 0), size); - d->drawBackground(theme); - d->dockClose.addPixmap(pm, QIcon::Normal, QIcon::Off); // Normal - pm.fill(Qt::transparent); - theme.stateId = CBS_PUSHED; - d->drawBackground(theme); - d->dockClose.addPixmap(pm, QIcon::Normal, QIcon::On); // Pressed - pm.fill(Qt::transparent); - theme.stateId = CBS_HOT; - d->drawBackground(theme); - d->dockClose.addPixmap(pm, QIcon::Active, QIcon::Off); // Hover - pm.fill(Qt::transparent); - theme.stateId = CBS_INACTIVE; - d->drawBackground(theme); - d->dockClose.addPixmap(pm, QIcon::Disabled, QIcon::Off); // Disabled - } - } - if (widget && widget->isWindow()) - return d->dockClose; - } - break; - case SP_TitleBarNormalButton: - if (qstyleoption_cast(option)) { - if (d->dockFloat.isNull()) { - XPThemeData themeSize(nullptr, nullptr, QWindowsXPStylePrivate::WindowTheme, - WP_SMALLCLOSEBUTTON, CBS_NORMAL); - XPThemeData theme(nullptr, nullptr, QWindowsXPStylePrivate::WindowTheme, - WP_RESTOREBUTTON, RBS_NORMAL); - if (theme.isValid()) { - const QSize size = (themeSize.size() * QWindowsStylePrivate::nativeMetricScaleFactor(widget)).toSize(); - QPixmap pm(size); - pm.fill(Qt::transparent); - QPainter p(&pm); - theme.painter = &p; - theme.rect = QRect(QPoint(0, 0), size); - d->drawBackground(theme); - d->dockFloat.addPixmap(pm, QIcon::Normal, QIcon::Off); // Normal - pm.fill(Qt::transparent); - theme.stateId = RBS_PUSHED; - d->drawBackground(theme); - d->dockFloat.addPixmap(pm, QIcon::Normal, QIcon::On); // Pressed - pm.fill(Qt::transparent); - theme.stateId = RBS_HOT; - d->drawBackground(theme); - d->dockFloat.addPixmap(pm, QIcon::Active, QIcon::Off); // Hover - pm.fill(Qt::transparent); - theme.stateId = RBS_INACTIVE; - d->drawBackground(theme); - d->dockFloat.addPixmap(pm, QIcon::Disabled, QIcon::Off); // Disabled - } - } - if (widget && widget->isWindow()) - return d->dockFloat; - } - break; - default: - break; - } - - return QWindowsStyle::standardIcon(standardIcon, option, widget); -} - -#ifndef QT_NO_DEBUG_STREAM -QDebug operator<<(QDebug d, const XPThemeData &t) -{ - QDebugStateSaver saver(d); - d.nospace(); - d << "XPThemeData(" << t.widget << ", theme=#" << t.theme << ", " << t.htheme - << ", partId=" << t.partId << ", stateId=" << t.stateId << ", rect=" << t.rect - << ", mirrorHorizontally=" << t.mirrorHorizontally << ", mirrorVertically=" - << t.mirrorVertically << ", noBorder=" << t.noBorder << ", noContent=" << t.noContent - << ", rotate=" << t.rotate << ')'; - return d; -} - -QDebug operator<<(QDebug d, const ThemeMapKey &k) -{ - QDebugStateSaver saver(d); - d.nospace(); - d << "ThemeMapKey(theme=#" << k.theme - << ", partId=" << k.partId << ", stateId=" << k.stateId - << ", noBorder=" << k.noBorder << ", noContent=" << k.noContent << ')'; - return d; -} - -QDebug operator<<(QDebug d, const ThemeMapData &td) -{ - QDebugStateSaver saver(d); - d.nospace(); - d << "ThemeMapData(alphaType=" << td.alphaType - << ", dataValid=" << td.dataValid << ", partIsTransparent=" << td.partIsTransparent - << ", hasAlphaChannel=" << td.hasAlphaChannel << ", wasAlphaSwapped=" << td.wasAlphaSwapped - << ", hadInvalidAlpha=" << td.hadInvalidAlpha << ')'; - return d; -} -#endif // QT_NO_DEBUG_STREAM - -// Debugging code ---------------------------------------------------------------------[ START ]--- -// The code for this point on is not compiled by default, but only used as assisting -// debugging code when you uncomment the DEBUG_XP_STYLE define at the top of the file. - -#ifdef DEBUG_XP_STYLE -// The schema file expects these to be defined by the user. -#define TMT_ENUMDEF 8 -#define TMT_ENUMVAL TEXT('A') -#define TMT_ENUM TEXT('B') -#define SCHEMA_STRINGS // For 2nd pass on schema file -QT_BEGIN_INCLUDE_NAMESPACE -#include -QT_END_INCLUDE_NAMESPACE - -// A property's value, type and name combo -struct PropPair { - int propValue; - int propType; - LPCWSTR propName; -}; - -// Operator for sorting of PropPairs -bool operator<(PropPair a, PropPair b) { - return wcscmp(a.propName, b.propName) < 0; -} - -// Our list of all possible properties -static QList all_props; - - -/*! \internal - Dumps a portion of the full native DIB section double buffer. - The DIB section double buffer is only used when doing special - transformations to the theme part, or when the real double - buffer in the paintengine does not have an HDC we may use - directly. - Since we cannot rely on the pixel data we get from Microsoft - when drawing into the DIB section, we use this function to - see the actual data we got, and can determine the appropriate - action. -*/ -void QWindowsXPStylePrivate::dumpNativeDIB(int w, int h) -{ - if (w && h) { - static int pCount = 0; - DWORD *bufPix = (DWORD*)bufferPixels; - - char *bufferDump = new char[bufferH * bufferW * 16]; - char *bufferPos = bufferDump; - - memset(bufferDump, 0, sizeof(bufferDump)); - bufferPos += sprintf(bufferPos, "const int pixelBufferW%d = %d;\n", pCount, w); - bufferPos += sprintf(bufferPos, "const int pixelBufferH%d = %d;\n", pCount, h); - bufferPos += sprintf(bufferPos, "const unsigned DWORD pixelBuffer%d[] = {", pCount); - for (int iy = 0; iy < h; ++iy) { - bufferPos += sprintf(bufferPos, "\n "); - bufPix = (DWORD*)(bufferPixels + (iy * bufferW * 4)); - for (int ix = 0; ix < w; ++ix) { - bufferPos += sprintf(bufferPos, "0x%08x, ", *bufPix); - ++bufPix; - } - } - bufferPos += sprintf(bufferPos, "\n};\n\n"); - printf(bufferDump); - - delete[] bufferDump; - ++pCount; - } -} - -/*! \internal - Shows the value of a given property for a part. -*/ -static void showProperty(XPThemeData &themeData, const PropPair &prop) -{ - PROPERTYORIGIN origin = PO_NOTFOUND; - GetThemePropertyOrigin(themeData.handle(), themeData.partId, themeData.stateId, prop.propValue, &origin); - const char *originStr; - switch(origin) { - case PO_STATE: - originStr = "State "; - break; - case PO_PART: - originStr = "Part "; - break; - case PO_CLASS: - originStr = "Class "; - break; - case PO_GLOBAL: - originStr = "Globl "; - break; - case PO_NOTFOUND: - default: - originStr = "Unkwn "; - break; - } - - switch(prop.propType) { - case TMT_STRING: - { - wchar_t buffer[512]; - GetThemeString(themeData.handle(), themeData.partId, themeData.stateId, prop.propValue, buffer, 512); - printf(" (%sString) %-20S: %S\n", originStr, prop.propName, buffer); - } - break; - case TMT_ENUM: - { - int result = -1; - GetThemeEnumValue(themeData.handle(), themeData.partId, themeData.stateId, prop.propValue, &result); - printf(" (%sEnum) %-20S: %d\n", originStr, prop.propName, result); - } - break; - case TMT_INT: - { - int result = -1; - GetThemeInt(themeData.handle(), themeData.partId, themeData.stateId, prop.propValue, &result); - printf(" (%sint) %-20S: %d\n", originStr, prop.propName, result); - } - break; - case TMT_BOOL: - { - BOOL result = false; - GetThemeBool(themeData.handle(), themeData.partId, themeData.stateId, prop.propValue, &result); - printf(" (%sbool) %-20S: %d\n", originStr, prop.propName, result); - } - break; - case TMT_COLOR: - { - COLORREF result = 0; - GetThemeColor(themeData.handle(), themeData.partId, themeData.stateId, prop.propValue, &result); - printf(" (%scolor) %-20S: 0x%08X\n", originStr, prop.propName, result); - } - break; - case TMT_MARGINS: - { - MARGINS result; - memset(&result, 0, sizeof(result)); - GetThemeMargins(themeData.handle(), 0, themeData.partId, themeData.stateId, prop.propValue, 0, &result); - printf(" (%smargins) %-20S: (%d, %d, %d, %d)\n", originStr, - prop.propName, result.cxLeftWidth, result.cyTopHeight, result.cxRightWidth, result.cyBottomHeight); - } - break; - case TMT_FILENAME: - { - wchar_t buffer[512]; - GetThemeFilename(themeData.handle(), themeData.partId, themeData.stateId, prop.propValue, buffer, 512); - printf(" (%sfilename)%-20S: %S\n", originStr, prop.propName, buffer); - } - break; - case TMT_SIZE: - { - SIZE result1; - SIZE result2; - SIZE result3; - memset(&result1, 0, sizeof(result1)); - memset(&result2, 0, sizeof(result2)); - memset(&result3, 0, sizeof(result3)); - GetThemePartSize(themeData.handle(), 0, themeData.partId, themeData.stateId, 0, TS_MIN, &result1); - GetThemePartSize(themeData.handle(), 0, themeData.partId, themeData.stateId, 0, TS_TRUE, &result2); - GetThemePartSize(themeData.handle(), 0, themeData.partId, themeData.stateId, 0, TS_DRAW, &result3); - printf(" (%ssize) %-20S: Min (%d, %d), True(%d, %d), Draw(%d, %d)\n", originStr, prop.propName, - result1.cx, result1.cy, result2.cx, result2.cy, result3.cx, result3.cy); - } - break; - case TMT_POSITION: - { - POINT result; - memset(&result, 0, sizeof(result)); - GetThemePosition(themeData.handle(), themeData.partId, themeData.stateId, prop.propValue, &result); - printf(" (%sPosition)%-20S: (%d, %d)\n", originStr, prop.propName, result.x, result.y); - } - break; - case TMT_RECT: - { - RECT result; - memset(&result, 0, sizeof(result)); - GetThemeRect(themeData.handle(), themeData.partId, themeData.stateId, prop.propValue, &result); - printf(" (%sRect) %-20S: (%d, %d, %d, %d)\n", originStr, prop.propName, result.left, result.top, result.right, result.bottom); - } - break; - case TMT_FONT: - { - LOGFONT result; - memset(&result, 0, sizeof(result)); - GetThemeFont(themeData.handle(), 0, themeData.partId, themeData.stateId, prop.propValue, &result); - printf(" (%sFont) %-20S: %S height(%d) width(%d) weight(%d)\n", originStr, prop.propName, - result.lfFaceName, result.lfHeight, result.lfWidth, result.lfWeight); - } - break; - case TMT_INTLIST: - { - INTLIST result; - memset(&result, 0, sizeof(result)); - GetThemeIntList(themeData.handle(), themeData.partId, themeData.stateId, prop.propValue, &result); - printf(" (%sInt list)%-20S: { ", originStr, prop.propName); - for (int i = 0; i < result.iValueCount; ++i) - printf("%d ", result.iValues[i]); - printf("}\n"); - } - break; - default: - printf(" %s%S : Unknown property type (%d)!\n", originStr, prop.propName, prop.propType); - } -} - -/*! \internal - Dump all valid properties for a part. - If it's the first time this function is called, then the name, - enum value and documentation of all properties are shown, as - well as all global properties. -*/ -void QWindowsXPStylePrivate::showProperties(XPThemeData &themeData) -{ - if (!all_props.count()) { - const TMSCHEMAINFO *infoTable = GetSchemaInfo(); - for (int i = 0; i < infoTable->iPropCount; ++i) { - int propType = infoTable->pPropTable[i].bPrimVal; - int propValue = infoTable->pPropTable[i].sEnumVal; - LPCWSTR propName = infoTable->pPropTable[i].pszName; - - switch(propType) { - case TMT_ENUMDEF: - case TMT_ENUMVAL: - continue; - default: - if (propType != propValue) { - PropPair prop; - prop.propValue = propValue; - prop.propName = propName; - prop.propType = propType; - all_props.append(prop); - } - } - } - std::sort(all_props.begin(), all_props.end()); - - {// List all properties - printf("part properties count = %d:\n", all_props.count()); - printf(" Enum Property Name Description\n"); - printf("-----------------------------------------------------------\n"); - wchar_t themeName[256]; - pGetCurrentThemeName(themeName, 256, 0, 0, 0, 0); - for (int j = 0; j < all_props.count(); ++j) { - PropPair prop = all_props.at(j); - wchar_t buf[500]; - GetThemeDocumentationProperty(themeName, prop.propName, buf, 500); - printf("%3d: (%4d) %-20S %S\n", j, prop.propValue, prop.propName, buf); - } - } - - {// Show Global values - printf("Global Properties:\n"); - for (int j = 0; j < all_props.count(); ++j) { - PropPair prop = all_props.at(j); - PROPERTYORIGIN origin = PO_NOTFOUND; - GetThemePropertyOrigin(themeData.handle(), themeData.partId, themeData.stateId, prop.propValue, &origin); - if (origin == PO_GLOBAL) { - showProperty(themeData, prop); - } - } - } - } - - for (int j = 0; j < all_props.count(); ++j) { - PropPair prop = all_props.at(j); - PROPERTYORIGIN origin = PO_NOTFOUND; - GetThemePropertyOrigin(themeData.handle(), themeData.partId, themeData.stateId, prop.propValue, &origin); - if (origin != PO_NOTFOUND) { - showProperty(themeData, prop); - } - } -} -#endif -// Debugging code -----------------------------------------------------------------------[ END ]--- - - -QT_END_NAMESPACE diff --git a/src/plugins/styles/windowsvista/qwindowsxpstyle_p.h b/src/plugins/styles/windowsvista/qwindowsxpstyle_p.h deleted file mode 100644 index 6fa7105ea6..0000000000 --- a/src/plugins/styles/windowsvista/qwindowsxpstyle_p.h +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only - -#ifndef QWINDOWSXPSTYLE_P_H -#define QWINDOWSXPSTYLE_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include -#include - -QT_BEGIN_NAMESPACE - -class QWindowsXPStylePrivate; -class QWindowsXPStyle : public QWindowsStyle -{ - Q_OBJECT -public: - ~QWindowsXPStyle() override; - - void polish(QWidget*) override; - void polish(QPalette&) override; - void unpolish(QWidget*) override; - - void drawPrimitive(PrimitiveElement pe, const QStyleOption *option, QPainter *p, - const QWidget *widget = nullptr) const override; - void drawControl(ControlElement element, const QStyleOption *option, QPainter *p, - const QWidget *wwidget = nullptr) const override; - QRect subElementRect(SubElement r, const QStyleOption *option, - const QWidget *widget = nullptr) const override; - QRect subControlRect(ComplexControl cc, const QStyleOptionComplex *option, SubControl sc, - const QWidget *widget = nullptr) const override; - void drawComplexControl(ComplexControl cc, const QStyleOptionComplex *option, QPainter *p, - const QWidget *widget = nullptr) const override; - QSize sizeFromContents(ContentsType ct, const QStyleOption *option, const QSize &contentsSize, - const QWidget *widget = nullptr) const override; - int pixelMetric(PixelMetric pm, const QStyleOption *option = nullptr, - const QWidget *widget = nullptr) const override; - int styleHint(StyleHint hint, const QStyleOption *option = nullptr, - const QWidget *widget = nullptr, - QStyleHintReturn *returnData = nullptr) const override; - - QPalette standardPalette() const override; - QPixmap standardPixmap(StandardPixmap standardIcon, const QStyleOption *option, - const QWidget *widget = nullptr) const override; - QIcon standardIcon(StandardPixmap standardIcon, const QStyleOption *option = nullptr, - const QWidget *widget = nullptr) const override; - -protected: - QWindowsXPStyle(QWindowsXPStylePrivate &dd); - -private: - Q_DISABLE_COPY_MOVE(QWindowsXPStyle) - Q_DECLARE_PRIVATE(QWindowsXPStyle) - friend class QStyleFactory; -}; - -QT_END_NAMESPACE - -#endif // QWINDOWSXPSTYLE_P_H diff --git a/src/plugins/styles/windowsvista/qwindowsxpstyle_p_p.h b/src/plugins/styles/windowsvista/qwindowsxpstyle_p_p.h deleted file mode 100644 index fbbd493f54..0000000000 --- a/src/plugins/styles/windowsvista/qwindowsxpstyle_p_p.h +++ /dev/null @@ -1,309 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only - -#ifndef QWINDOWSXPSTYLE_P_P_H -#define QWINDOWSXPSTYLE_P_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists for the convenience -// of qapplication_*.cpp, qwidget*.cpp and qfiledialog.cpp. This header -// file may change from version to version without notice, or even be removed. -// -// We mean it. -// - -#include -#include "qwindowsxpstyle_p.h" -#include -#include -#include - -#include -#include - -#include - -QT_BEGIN_NAMESPACE - -class QDebug; - -// TMT_TEXTSHADOWCOLOR is wrongly defined in mingw -#if TMT_TEXTSHADOWCOLOR != 3818 -#undef TMT_TEXTSHADOWCOLOR -#define TMT_TEXTSHADOWCOLOR 3818 -#endif -#ifndef TST_NONE -# define TST_NONE 0 -#endif - -// These defines are missing from the tmschema, but still exist as -// states for their parts -#ifndef MINBS_INACTIVE -#define MINBS_INACTIVE 5 -#endif -#ifndef MAXBS_INACTIVE -#define MAXBS_INACTIVE 5 -#endif -#ifndef RBS_INACTIVE -#define RBS_INACTIVE 5 -#endif -#ifndef HBS_INACTIVE -#define HBS_INACTIVE 5 -#endif -#ifndef CBS_INACTIVE -#define CBS_INACTIVE 5 -#endif - -// Uncomment define below to build debug assisting code, and output -// #define DEBUG_XP_STYLE - -// Declarations ----------------------------------------------------------------------------------- -class XPThemeData -{ -public: - explicit XPThemeData(const QWidget *w = nullptr, QPainter *p = nullptr, int themeIn = -1, - int part = 0, int state = 0, const QRect &r = QRect()) - : widget(w), painter(p), theme(themeIn), partId(part), stateId(state), - mirrorHorizontally(false), mirrorVertically(false), noBorder(false), - noContent(false), rect(r) - {} - - HRGN mask(QWidget *widget); - HTHEME handle(); - - static RECT toRECT(const QRect &qr); - bool isValid(); - - QSizeF size(); - QMarginsF margins(const QRect &rect, int propId = TMT_CONTENTMARGINS); - QMarginsF margins(int propId = TMT_CONTENTMARGINS); - - static QSizeF themeSize(const QWidget *w = nullptr, QPainter *p = nullptr, int themeIn = -1, int part = 0, int state = 0); - static QMarginsF themeMargins(const QRect &rect, const QWidget *w = nullptr, QPainter *p = nullptr, int themeIn = -1, - int part = 0, int state = 0, int propId = TMT_CONTENTMARGINS); - static QMarginsF themeMargins(const QWidget *w = nullptr, QPainter *p = nullptr, int themeIn = -1, - int part = 0, int state = 0, int propId = TMT_CONTENTMARGINS); - - const QWidget *widget; - QPainter *painter; - - int theme; - HTHEME htheme = nullptr; - int partId; - int stateId; - - uint mirrorHorizontally : 1; - uint mirrorVertically : 1; - uint noBorder : 1; - uint noContent : 1; - uint rotate = 0; - QRect rect; -}; - -struct ThemeMapKey { - int theme = 0; - int partId = -1; - int stateId = -1; - bool noBorder = false; - bool noContent = false; - - ThemeMapKey() = default; - ThemeMapKey(const XPThemeData &data) - : theme(data.theme), partId(data.partId), stateId(data.stateId), - noBorder(data.noBorder), noContent(data.noContent) {} - -}; - -inline size_t qHash(const ThemeMapKey &key) -{ return key.theme ^ key.partId ^ key.stateId; } - -inline bool operator==(const ThemeMapKey &k1, const ThemeMapKey &k2) -{ - return k1.theme == k2.theme - && k1.partId == k2.partId - && k1.stateId == k2.stateId; -} - -enum AlphaChannelType { - UnknownAlpha = -1, // Alpha of part & state not yet known - NoAlpha, // Totally opaque, no need to touch alpha (RGB) - MaskAlpha, // Alpha channel must be fixed (ARGB) - RealAlpha // Proper alpha values from Windows (ARGB_Premultiplied) -}; - -struct ThemeMapData { - AlphaChannelType alphaType = UnknownAlpha; // Which type of alpha on part & state - - bool dataValid : 1; // Only used to detect if hash value is ok - bool partIsTransparent : 1; - bool hasAlphaChannel : 1; // True = part & state has real Alpha - bool wasAlphaSwapped : 1; // True = alpha channel needs to be swapped - bool hadInvalidAlpha : 1; // True = alpha channel contained invalid alpha values - - ThemeMapData() : dataValid(false), partIsTransparent(false), - hasAlphaChannel(false), wasAlphaSwapped(false), hadInvalidAlpha(false) {} -}; - -#ifndef QT_NO_DEBUG_STREAM -QDebug operator<<(QDebug d, const XPThemeData &t); -QDebug operator<<(QDebug d, const ThemeMapKey &k); -QDebug operator<<(QDebug d, const ThemeMapData &td); -#endif - -class QWindowsXPStylePrivate : public QWindowsStylePrivate -{ - Q_DECLARE_PUBLIC(QWindowsXPStyle) -public: - enum Theme { - ButtonTheme, - ComboboxTheme, - EditTheme, - HeaderTheme, - ListViewTheme, - MenuTheme, - ProgressTheme, - RebarTheme, - ScrollBarTheme, - SpinTheme, - TabTheme, - TaskDialogTheme, - ToolBarTheme, - ToolTipTheme, - TrackBarTheme, - XpTreeViewTheme, // '+'/'-' shape treeview indicators (XP) - WindowTheme, - StatusTheme, - VistaTreeViewTheme, // arrow shape treeview indicators (Vista) obtained from "explorer" theme. - NThemes - }; - - QWindowsXPStylePrivate() - { init(); } - - ~QWindowsXPStylePrivate() - { cleanup(); } - - static int pixelMetricFromSystemDp(QStyle::PixelMetric pm, const QStyleOption *option = nullptr, const QWidget *widget = nullptr); - static int fixedPixelMetric(QStyle::PixelMetric pm, const QStyleOption *option = nullptr, const QWidget *widget = nullptr); - - static HWND winId(const QWidget *widget); - - void init(bool force = false); - void cleanup(bool force = false); - void cleanupHandleMap(); - - HBITMAP buffer(int w = 0, int h = 0); - HDC bufferHDC() - { return bufferDC;} - - static bool useXP(bool update = false); - static QRect scrollBarGripperBounds(QStyle::State flags, const QWidget *widget, XPThemeData *theme); - - bool isTransparent(XPThemeData &themeData); - QRegion region(XPThemeData &themeData); - - bool drawBackground(XPThemeData &themeData, qreal correctionFactor = 1); - bool drawBackgroundThruNativeBuffer(XPThemeData &themeData, qreal aditionalDevicePixelRatio, qreal correctionFactor); - bool drawBackgroundDirectly(HDC dc, XPThemeData &themeData, qreal aditionalDevicePixelRatio); - - bool hasAlphaChannel(const QRect &rect); - bool fixAlphaChannel(const QRect &rect); - bool swapAlphaChannel(const QRect &rect, bool allPixels = false); - - QRgb groupBoxTextColor = 0; - QRgb groupBoxTextColorDisabled = 0; - QRgb sliderTickColor = 0; - bool hasInitColors = false; - - static HTHEME createTheme(int theme, HWND hwnd); - static QString themeName(int theme); - static inline bool hasTheme(int theme) { return theme >= 0 && theme < NThemes && m_themes[theme]; } - static bool isItemViewDelegateLineEdit(const QWidget *widget); - static bool isLineEditBaseColorSet(const QStyleOption *option, const QWidget *widget); - - QIcon dockFloat, dockClose; - -private: -#ifdef DEBUG_XP_STYLE - void dumpNativeDIB(int w, int h); - void showProperties(XPThemeData &themeData); -#endif - - static bool initVistaTreeViewTheming(); - static void cleanupVistaTreeViewTheming(); - - static QBasicAtomicInt ref; - static bool use_xp; - - QHash alphaCache; - HDC bufferDC = nullptr; - HBITMAP bufferBitmap = nullptr; - HBITMAP nullBitmap = nullptr; - uchar *bufferPixels = nullptr; - int bufferW = 0; - int bufferH = 0; - - static HWND m_vistaTreeViewHelper; - static HTHEME m_themes[NThemes]; -}; - -inline QSizeF XPThemeData::size() -{ - QSizeF result(0, 0); - if (isValid()) { - SIZE size; - if (SUCCEEDED(GetThemePartSize(handle(), nullptr, partId, stateId, nullptr, TS_TRUE, &size))) - result = QSize(size.cx, size.cy); - } - return result; -} - -inline QMarginsF XPThemeData::margins(const QRect &qRect, int propId) -{ - QMarginsF result(0, 0, 0 ,0); - if (isValid()) { - MARGINS margins; - RECT rect = XPThemeData::toRECT(qRect); - if (SUCCEEDED(GetThemeMargins(handle(), nullptr, partId, stateId, propId, &rect, &margins))) - result = QMargins(margins.cxLeftWidth, margins.cyTopHeight, margins.cxRightWidth, margins.cyBottomHeight); - } - return result; -} - -inline QMarginsF XPThemeData::margins(int propId) -{ - QMarginsF result(0, 0, 0 ,0); - if (isValid()) { - MARGINS margins; - if (SUCCEEDED(GetThemeMargins(handle(), nullptr, partId, stateId, propId, nullptr, &margins))) - result = QMargins(margins.cxLeftWidth, margins.cyTopHeight, margins.cxRightWidth, margins.cyBottomHeight); - } - return result; -} - -inline QSizeF XPThemeData::themeSize(const QWidget *w, QPainter *p, int themeIn, int part, int state) -{ - XPThemeData theme(w, p, themeIn, part, state); - return theme.size(); -} - -inline QMarginsF XPThemeData::themeMargins(const QRect &rect, const QWidget *w, QPainter *p, int themeIn, - int part, int state, int propId) -{ - XPThemeData theme(w, p, themeIn, part, state); - return theme.margins(rect, propId); -} - -inline QMarginsF XPThemeData::themeMargins(const QWidget *w, QPainter *p, int themeIn, - int part, int state, int propId) -{ - XPThemeData theme(w, p, themeIn, part, state); - return theme.margins(propId); -} - -QT_END_NAMESPACE - -#endif //QWINDOWSXPSTYLE_P_P_H -- cgit v1.2.3