diff options
Diffstat (limited to 'src/plugins/styles')
34 files changed, 8234 insertions, 8129 deletions
diff --git a/src/plugins/styles/.prev_CMakeLists.txt b/src/plugins/styles/.prev_CMakeLists.txt deleted file mode 100644 index 73329915f6..0000000000 --- a/src/plugins/styles/.prev_CMakeLists.txt +++ /dev/null @@ -1,11 +0,0 @@ -# Generated from styles.pro. - -if(QT_FEATURE_style_android) - add_subdirectory(android) -endif() -if(QT_FEATURE_style_mac) - add_subdirectory(mac) -endif() -if(QT_FEATURE_style_windowsvista) - add_subdirectory(windowsvista) -endif() diff --git a/src/plugins/styles/CMakeLists.txt b/src/plugins/styles/CMakeLists.txt index d24e3daff8..b809042c14 100644 --- a/src/plugins/styles/CMakeLists.txt +++ b/src/plugins/styles/CMakeLists.txt @@ -1,11 +1,12 @@ -# Generated from styles.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause if(QT_FEATURE_style_android) - # add_subdirectory(android) # special case TODO + add_subdirectory(android) endif() if(QT_FEATURE_style_mac) add_subdirectory(mac) endif() if(QT_FEATURE_style_windowsvista) - add_subdirectory(windowsvista) + add_subdirectory(modernwindows) endif() diff --git a/src/plugins/styles/android/CMakeLists.txt b/src/plugins/styles/android/CMakeLists.txt new file mode 100644 index 0000000000..b8bda6fd5f --- /dev/null +++ b/src/plugins/styles/android/CMakeLists.txt @@ -0,0 +1,18 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +##################################################################### +## QAndroidStylePlugin Plugin: +##################################################################### + +qt_internal_add_plugin(QAndroidStylePlugin + OUTPUT_NAME qandroidstyle + PLUGIN_TYPE styles + SOURCES + main.cpp + qandroidstyle.cpp qandroidstyle_p.h + LIBRARIES + Qt::Core + Qt::Gui + Qt::WidgetsPrivate +) diff --git a/src/plugins/styles/android/android.pro b/src/plugins/styles/android/android.pro deleted file mode 100644 index 4ca35d8046..0000000000 --- a/src/plugins/styles/android/android.pro +++ /dev/null @@ -1,16 +0,0 @@ -TARGET = qandroidstyle - -QT += widgets-private - -SOURCES += \ - main.cpp \ - qandroidstyle.cpp - -HEADERS += \ - qandroidstyle_p.h - -DISTFILES += androidstyle.json - -PLUGIN_TYPE = styles -PLUGIN_CLASS_NAME = QAndroidStylePlugin -load(qt_plugin) diff --git a/src/plugins/styles/android/main.cpp b/src/plugins/styles/android/main.cpp index 2121538b0a..3be434e362 100644 --- a/src/plugins/styles/android/main.cpp +++ b/src/plugins/styles/android/main.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 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 <QtWidgets/qstyleplugin.h> #include "qandroidstyle_p.h" diff --git a/src/plugins/styles/android/qandroidstyle.cpp b/src/plugins/styles/android/qandroidstyle.cpp index 8036655b04..64940ed4d8 100644 --- a/src/plugins/styles/android/qandroidstyle.cpp +++ b/src/plugins/styles/android/qandroidstyle.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2013 BogDan Vatra <bogdan@kde.org> -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWidgets module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2013 BogDan Vatra <bogdan@kde.org> +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qandroidstyle_p.h" @@ -607,7 +571,7 @@ QSize QAndroidStyle::sizeFromContents(ContentsType ct, if (qApp->styleSheet().isEmpty()) txt = hdr->fontMetrics.size(0, hdr->text); else - txt = qApp->fontMetrics().size(0, hdr->text); + txt = QFontMetrics(QApplication::font()).size(0, hdr->text); sz.setHeight(margin + qMax(iconSize, txt.height()) + margin); sz.setWidth((nullIcon ? 0 : margin) + iconSize @@ -677,6 +641,9 @@ int QAndroidStyle::styleHint(QStyle::StyleHint hint, const QStyleOption *option, case SH_RequestSoftwareInputPanel: return RSIP_OnMouseClick; + case SH_SpinBox_SelectOnStep: + return 0; + default: return QFusionStyle::styleHint(hint, option, widget, returnData); } @@ -1167,7 +1134,7 @@ QAndroidStyle::AndroidStateDrawable::AndroidStateDrawable(const QVariantMap &dra QAndroidStyle::AndroidStateDrawable::~AndroidStateDrawable() { - for (const StateType &type : qAsConst(m_states)) + for (const StateType &type : std::as_const(m_states)) delete type.second; } @@ -1193,7 +1160,7 @@ QSize QAndroidStyle::AndroidStateDrawable::sizeImage(const QStyleOption *opt) co const QAndroidStyle::AndroidDrawable * QAndroidStyle::AndroidStateDrawable::bestAndroidStateMatch(const QStyleOption *opt) const { - const AndroidDrawable *bestMatch = 0; + const AndroidDrawable *bestMatch = nullptr; if (!opt) { if (m_states.size()) return m_states[0].second; @@ -1242,7 +1209,7 @@ const QAndroidStyle::AndroidDrawable * QAndroidStyle::AndroidStateDrawable::best int QAndroidStyle::AndroidStateDrawable::extractState(const QVariantMap &value) { - QStyle::State state = QStyle::State_Enabled | QStyle::State_Active;; + QStyle::State state = QStyle::State_Enabled | QStyle::State_Active; for (auto it = value.cbegin(), end = value.cend(); it != end; ++it) { const QString &key = it.key(); bool val = it.value().toString() == QLatin1String("true"); @@ -1292,7 +1259,7 @@ int QAndroidStyle::AndroidStateDrawable::extractState(const QVariantMap &value) void QAndroidStyle::AndroidStateDrawable::setPaddingLeftToSizeWidth() { - for (const StateType &type : qAsConst(m_states)) + for (const StateType &type : std::as_const(m_states)) const_cast<AndroidDrawable *>(type.second)->setPaddingLeftToSizeWidth(); } @@ -1318,7 +1285,7 @@ QAndroidStyle::AndroidLayerDrawable::AndroidLayerDrawable(const QVariantMap &dra QAndroidStyle::AndroidLayerDrawable::~AndroidLayerDrawable() { - for (const LayerType &layer : qAsConst(m_layers)) + for (const LayerType &layer : std::as_const(m_layers)) delete layer.second; } @@ -1352,9 +1319,10 @@ void QAndroidStyle::AndroidLayerDrawable::draw(QPainter *painter, const QStyleOp QAndroidStyle::AndroidDrawable *QAndroidStyle::AndroidLayerDrawable::layer(int id) const { - for (const LayerType &layer : m_layers) + for (const LayerType &layer : m_layers) { if (layer.first == id) return layer.second; + } return 0; } @@ -1407,7 +1375,7 @@ void QAndroidStyle::AndroidControl::drawControl(const QStyleOption *opt, QPainte qDrawShadePanel(p, frame->rect, frame->palette, frame->state & State_Sunken, frame->lineWidth); } else { - qDrawPlainRect(p, frame->rect, frame->palette.foreground().color(), frame->lineWidth); + qDrawPlainRect(p, frame->rect, frame->palette.windowText().color(), frame->lineWidth); } } else { if (const QStyleOptionFocusRect *fropt = qstyleoption_cast<const QStyleOptionFocusRect *>(opt)) { @@ -1421,13 +1389,13 @@ void QAndroidStyle::AndroidControl::drawControl(const QStyleOption *opt, QPainte else p->setPen(Qt::white); } else { - p->setPen(opt->palette.foreground().color()); + p->setPen(opt->palette.windowText().color()); } QRect focusRect = opt->rect.adjusted(1, 1, -1, -1); p->drawRect(focusRect.adjusted(0, 0, -1, -1)); //draw pen inclusive p->setPen(oldPen); } else { - p->fillRect(opt->rect, opt->palette.brush(QPalette::Background)); + p->fillRect(opt->rect, opt->palette.window()); } } } diff --git a/src/plugins/styles/android/qandroidstyle_p.h b/src/plugins/styles/android/qandroidstyle_p.h index 5730bdfe2c..38abc24b02 100644 --- a/src/plugins/styles/android/qandroidstyle_p.h +++ b/src/plugins/styles/android/qandroidstyle_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2013 BogDan Vatra <bogdan@kde.org> -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWidgets module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2013 BogDan Vatra <bogdan@kde.org> +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QANDROIDSTYLE_P_H #define QANDROIDSTYLE_P_H @@ -257,10 +221,10 @@ public: virtual void drawControl(const QStyleOption *opt, QPainter *p, const QWidget *w); virtual QRect subElementRect(SubElement subElement, const QStyleOption *option, - const QWidget *widget = 0) const; + const QWidget *widget = nullptr) const; virtual QRect subControlRect(const QStyleOptionComplex *option, SubControl sc, - const QWidget *widget = 0) const; + const QWidget *widget = nullptr) const; virtual QSize sizeFromContents(const QStyleOption *opt, const QSize &contentsSize, const QWidget *w) const; @@ -294,7 +258,7 @@ public: virtual void drawControl(const QStyleOption *option, QPainter *p, const QWidget *w); virtual QRect subElementRect(SubElement subElement, const QStyleOption *option, - const QWidget *widget = 0) const; + const QWidget *widget = nullptr) const; QSize sizeFromContents(const QStyleOption *opt, const QSize &contentsSize, @@ -315,7 +279,7 @@ public: QSize sizeFromContents(const QStyleOption *opt, const QSize &contentsSize, const QWidget *w) const; QRect subControlRect(const QStyleOptionComplex *option, SubControl sc, - const QWidget *widget = 0) const; + const QWidget *widget = nullptr) const; private: AndroidDrawable *m_seekBarThumb; }; @@ -327,7 +291,7 @@ public: virtual ~AndroidSpinnerControl(){} virtual QRect subControlRect(const QStyleOptionComplex *option, SubControl sc, - const QWidget *widget = 0) const; + const QWidget *widget = nullptr) const; }; typedef QList<AndroidItemStateInfo *> AndroidItemStateInfoList; @@ -337,34 +301,34 @@ public: ~QAndroidStyle(); virtual void drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, QPainter *p, - const QWidget *w = 0) const; + const QWidget *w = nullptr) const; virtual void drawControl(QStyle::ControlElement element, const QStyleOption *opt, QPainter *p, - const QWidget *w = 0) const; + const QWidget *w = nullptr) const; virtual QRect subElementRect(SubElement subElement, const QStyleOption *option, - const QWidget *widget = 0) const; + const QWidget *widget = nullptr) const; virtual void drawComplexControl(ComplexControl cc, const QStyleOptionComplex *opt, QPainter *p, - const QWidget *widget = 0) const; + const QWidget *widget = nullptr) const; virtual SubControl hitTestComplexControl(ComplexControl cc, const QStyleOptionComplex *opt, - const QPoint &pt, const QWidget *widget = 0) const; + const QPoint &pt, const QWidget *widget = nullptr) const; virtual QRect subControlRect(ComplexControl cc, const QStyleOptionComplex *opt, - SubControl sc, const QWidget *widget = 0) const; + SubControl sc, const QWidget *widget = nullptr) const; - virtual int pixelMetric(PixelMetric metric, const QStyleOption *option = 0, - const QWidget *widget = 0) const; + virtual int pixelMetric(PixelMetric metric, const QStyleOption *option = nullptr, + const QWidget *widget = nullptr) const; virtual QSize sizeFromContents(ContentsType ct, const QStyleOption *opt, - const QSize &contentsSize, const QWidget *w = 0) const; + const QSize &contentsSize, const QWidget *w = nullptr) const; - virtual QPixmap standardPixmap(StandardPixmap standardPixmap, const QStyleOption *opt = 0, - const QWidget *widget = 0) const; + virtual QPixmap standardPixmap(StandardPixmap standardPixmap, const QStyleOption *opt = nullptr, + const QWidget *widget = nullptr) const; virtual QPixmap generatedIconPixmap(QIcon::Mode iconMode, const QPixmap &pixmap, const QStyleOption *opt) const; - int styleHint(StyleHint hint, const QStyleOption *option = 0, const QWidget *widget = 0, - QStyleHintReturn *returnData = 0) const; + int styleHint(StyleHint hint, const QStyleOption *option = nullptr, const QWidget *widget = nullptr, + QStyleHintReturn *returnData = nullptr) const; virtual QPalette standardPalette() const; void polish(QWidget *widget); diff --git a/src/plugins/styles/mac/CMakeLists.txt b/src/plugins/styles/mac/CMakeLists.txt index 4c4e488d91..9dc8d01cc7 100644 --- a/src/plugins/styles/mac/CMakeLists.txt +++ b/src/plugins/styles/mac/CMakeLists.txt @@ -1,4 +1,5 @@ -# Generated from mac.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## QMacStylePlugin Plugin: @@ -6,18 +7,19 @@ qt_internal_add_plugin(QMacStylePlugin OUTPUT_NAME qmacstyle - TYPE styles + PLUGIN_TYPE styles SOURCES main.mm qmacstyle_mac.mm qmacstyle_mac_p.h qmacstyle_mac_p_p.h LIBRARIES ${FWAppKit} - PUBLIC_LIBRARIES Qt::Core Qt::Gui Qt::WidgetsPrivate ) -#### Keys ignored in scope 1:.:.:mac.pro:<TRUE>: -# DISTFILES = "macstyle.json" +set_target_properties(QMacStylePlugin + PROPERTIES + DISABLE_PRECOMPILE_HEADERS ON +) diff --git a/src/plugins/styles/mac/mac.pro b/src/plugins/styles/mac/mac.pro deleted file mode 100644 index 9044354c07..0000000000 --- a/src/plugins/styles/mac/mac.pro +++ /dev/null @@ -1,19 +0,0 @@ -TARGET = qmacstyle - -QT += widgets-private - -SOURCES += \ - main.mm \ - qmacstyle_mac.mm - -HEADERS += \ - qmacstyle_mac_p.h \ - qmacstyle_mac_p_p.h - -LIBS_PRIVATE += -framework AppKit - -DISTFILES += macstyle.json - -PLUGIN_TYPE = styles -PLUGIN_CLASS_NAME = QMacStylePlugin -load(qt_plugin) diff --git a/src/plugins/styles/mac/main.mm b/src/plugins/styles/mac/main.mm index 2f11b5cd43..5f4fbd9eb8 100644 --- a/src/plugins/styles/mac/main.mm +++ b/src/plugins/styles/mac/main.mm @@ -1,45 +1,11 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 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 <QtWidgets/qstyleplugin.h> #include "qmacstyle_mac_p.h" +#include <QtCore/private/qcore_mac_p.h> + QT_BEGIN_NAMESPACE class QMacStylePlugin : public QStylePlugin diff --git a/src/plugins/styles/mac/qmacstyle_mac.mm b/src/plugins/styles/mac/qmacstyle_mac.mm index 4f51f63665..226da495f4 100644 --- a/src/plugins/styles/mac/qmacstyle_mac.mm +++ b/src/plugins/styles/mac/qmacstyle_mac.mm @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWidgets module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// 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 /* Note: The qdoc comments for QMacStyle are contained in @@ -57,6 +21,7 @@ #include <QtCore/private/qcore_mac_p.h> #include <QtGui/qpainterpath.h> +#include <QtGui/qstylehints.h> #include <QtGui/private/qcoregraphics_p.h> #include <QtGui/qpa/qplatformfontdatabase.h> #include <QtGui/qpa/qplatformtheme.h> @@ -190,45 +155,15 @@ const int QMacStylePrivate::PushButtonLeftOffset = 6; const int QMacStylePrivate::PushButtonRightOffset = 12; const int QMacStylePrivate::PushButtonContentPadding = 6; -QVector<QPointer<QObject> > QMacStylePrivate::scrollBars; - -// Title bar gradient colors for Lion were determined by inspecting PSDs exported -// using CoreUI's CoreThemeDocument; there is no public API to retrieve them +const int pushButtonBevelRectOffsets[3] = { + QMacStylePrivate::PushButtonLeftOffset, 5, 5 +}; -static QLinearGradient titlebarGradientActive() -{ - static QLinearGradient darkGradient = [](){ - QLinearGradient gradient; - // FIXME: colors are chosen somewhat arbitrarily and could be fine-tuned, - // or ideally determined by calling a native API. - gradient.setColorAt(0, QColor(47, 47, 47)); - return gradient; - }(); - static QLinearGradient lightGradient = [](){ - QLinearGradient gradient; - gradient.setColorAt(0, QColor(235, 235, 235)); - gradient.setColorAt(0.5, QColor(210, 210, 210)); - gradient.setColorAt(0.75, QColor(195, 195, 195)); - gradient.setColorAt(1, QColor(180, 180, 180)); - return gradient; - }(); - return qt_mac_applicationIsInDarkMode() ? darkGradient : lightGradient; -} +QVector<QPointer<QObject> > QMacStylePrivate::scrollBars; -static QLinearGradient titlebarGradientInactive() +static inline bool isDarkMode() { - static QLinearGradient darkGradient = [](){ - QLinearGradient gradient; - gradient.setColorAt(1, QColor(42, 42, 42)); - return gradient; - }(); - static QLinearGradient lightGradient = [](){ - QLinearGradient gradient; - gradient.setColorAt(0, QColor(250, 250, 250)); - gradient.setColorAt(1, QColor(225, 225, 225)); - return gradient; - }(); - return qt_mac_applicationIsInDarkMode() ? darkGradient : lightGradient; + return QGuiApplication::styleHints()->colorScheme() == Qt::ColorScheme::Dark; } #if QT_CONFIG(tabwidget) @@ -250,7 +185,7 @@ static void clipTabBarFrame(const QStyleOption *option, const QMacStyle *style, Q_ASSERT(style); Q_ASSERT(ctx); - if (qt_mac_applicationIsInDarkMode()) { + if (isDarkMode()) { QTabWidget *tabWidget = qobject_cast<QTabWidget *>(option->styleObject); Q_ASSERT(tabWidget); @@ -282,7 +217,7 @@ static const QColor titlebarSeparatorLineInactive(131, 131, 131); static const QColor darkModeSeparatorLine(88, 88, 88); // Gradient colors used for the dock widget title bar and -// non-unifed tool bar bacground. +// non-unifed tool bar background. static const QColor lightMainWindowGradientBegin(240, 240, 240); static const QColor lightMainWindowGradientEnd(200, 200, 200); static const QColor darkMainWindowGradientBegin(47, 47, 47); @@ -298,7 +233,6 @@ static const qreal titleBarButtonSpacing = 8; // active: window is active // selected: tab is selected // hovered: tab is hovered -bool isDarkMode() { return qt_mac_applicationIsInDarkMode(); } #if QT_CONFIG(tabbar) static const QColor lightTabBarTabBackgroundActive(190, 190, 190); @@ -348,7 +282,7 @@ static const int closeButtonSize = 14; static const qreal closeButtonCornerRadius = 2.0; #endif // QT_CONFIG(tabbar) -#ifndef QT_NO_ACCESSIBILITY // This ifdef to avoid "unused function" warning. +#if QT_CONFIG(accessibility) // This ifdef to avoid "unused function" warning. QBrush brushForToolButton(bool isOnKeyWindow) { // When a toolbutton in a toolbar is in the 'ON' state, we draw a @@ -359,7 +293,7 @@ QBrush brushForToolButton(bool isOnKeyWindow) return isOnKeyWindow ? QColor(0, 0, 0, 28) : QColor(0, 0, 0, 21); } -#endif // QT_NO_ACCESSIBILITY +#endif // QT_CONFIG(accessibility) static const int headerSectionArrowHeight = 6; @@ -384,16 +318,15 @@ static const QMarginsF pushButtonShadowMargins[3] = { { 1.5, 0.5, 1.5, 2.5 } }; -// These are frame heights as reported by Xcode 9's Interface Builder. -// Alignemnet rectangle's heights match for push and popup buttons -// with respective values 21, 18 and 15. +// These are frame heights as reported by Xcode 9's Interface Builder +// and determined experimentally. static const qreal comboBoxDefaultHeight[3] = { 26, 22, 19 }; static const qreal pushButtonDefaultHeight[3] = { - 32, 28, 16 + 32, 28, 24 }; static const qreal popupButtonDefaultHeight[3] = { @@ -417,16 +350,14 @@ class AppearanceSync { public: AppearanceSync() { -#if QT_MACOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_14) if (QOperatingSystemVersion::current() >= QOperatingSystemVersion::MacOSMojave - && !qt_mac_applicationIsInDarkMode()) { + && !isDarkMode()) { auto requiredAppearanceName = NSApplication.sharedApplication.effectiveAppearance.name; if (![NSAppearance.currentAppearance.name isEqualToString:requiredAppearanceName]) { previous = NSAppearance.currentAppearance; NSAppearance.currentAppearance = [NSAppearance appearanceNamed:requiredAppearanceName]; } } -#endif // QT_MACOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_14) } ~AppearanceSync() @@ -464,6 +395,7 @@ static bool setupSlider(NSSlider *slider, const QStyleOptionSlider *sl) return false; slider.frame = sl->rect.toCGRect(); + slider.minValue = sl->minimum; slider.maxValue = sl->maximum; slider.intValue = sl->sliderPosition; @@ -489,38 +421,11 @@ static bool setupSlider(NSSlider *slider, const QStyleOptionSlider *sl) slider.numberOfTickMarks = 0; } - return true; -} + // Ensure the values set above are reflected when asking + // the cell for its metrics and to draw itself. + [slider layoutSubtreeIfNeeded]; -static void fixStaleGeometry(NSSlider *slider) -{ - // If it's later fixed in AppKit, this function is not needed. - // On macOS Mojave we suddenly have NSSliderCell with a cached - // (and stale) geometry, thus its -drawKnob, -drawBarInside:flipped:, - // -drawTickMarks fail to render the slider properly. Setting the number - // of tickmarks triggers an update in geometry. - - Q_ASSERT(slider); - - if (QOperatingSystemVersion::current() < QOperatingSystemVersion::MacOSMojave) - return; - - NSSliderCell *cell = slider.cell; - const NSRect barRect = [cell barRectFlipped:slider.isFlipped]; - const NSSize sliderSize = slider.frame.size; - CGFloat difference = 0.; - if (slider.vertical) - difference = std::abs(sliderSize.height - barRect.size.height); - else - difference = std::abs(sliderSize.width - barRect.size.width); - - if (difference > 6.) { - // Stale ... - const auto nOfTicks = slider.numberOfTickMarks; - // Non-zero, different from nOfTicks to force update - slider.numberOfTickMarks = nOfTicks + 10; - slider.numberOfTickMarks = nOfTicks; - } + return true; } static bool isInMacUnifiedToolbarArea(QWindow *window, int windowY) @@ -703,7 +608,7 @@ static inline bool isTreeView(const QWidget *widget) static QString qt_mac_removeMnemonics(const QString &original) { - QString returnText(original.size(), 0); + QString returnText(original.size(), QChar(0)); int finalDest = 0; int currPos = 0; int l = original.length(); @@ -867,6 +772,8 @@ static QSize qt_aqua_get_known_size(QStyle::ContentsType ct, const QStyleOption return ret; } + const bool isBigSurOrAbove = QOperatingSystemVersion::current() >= QOperatingSystemVersion::MacOSBigSur; + if (ct == QStyle::CT_CustomBase && widg) { #if QT_CONFIG(pushbutton) if (qobject_cast<const QPushButton *>(widg)) @@ -1003,7 +910,7 @@ static QSize qt_aqua_get_known_size(QStyle::ContentsType ct, const QStyleOption #if QT_CONFIG(toolbutton) const QStyleOptionToolButton *bt = qstyleoption_cast<const QStyleOptionToolButton *>(opt); // If this conversion fails then the widget was not what it claimed to be. - if(bt) { + if (bt) { if (!bt->icon.isNull()) { QSize iconSize = bt->iconSize; QSize pmSize = bt->icon.actualSize(QSize(32, 32), QIcon::Normal); @@ -1041,12 +948,14 @@ static QSize qt_aqua_get_known_size(QStyle::ContentsType ct, const QStyleOption int w = -1; const QStyleOptionSlider *sld = qstyleoption_cast<const QStyleOptionSlider *>(opt); // If this conversion fails then the widget was not what it claimed to be. - if(sld) { + if (sld) { if (sz == QStyleHelper::SizeLarge) { if (sld->orientation == Qt::Horizontal) { w = qt_mac_aqua_get_metric(HSliderHeight); if (sld->tickPosition != QSlider::NoTicks) w += qt_mac_aqua_get_metric(HSliderTickHeight); + else if (isBigSurOrAbove) + w += 3; } else { w = qt_mac_aqua_get_metric(VSliderWidth); if (sld->tickPosition != QSlider::NoTicks) @@ -1110,7 +1019,7 @@ static QSize qt_aqua_get_known_size(QStyle::ContentsType ct, const QStyleOption #if QT_CONFIG(combobox) case QStyle::CT_LineEdit: if (!widg || !qobject_cast<QComboBox *>(widg->parentWidget())) { - //should I take into account the font dimentions of the lineedit? -Sam + //should I take into account the font dimensions of the lineedit? -Sam if (sz == QStyleHelper::SizeLarge) ret = QSize(-1, 21); else @@ -1172,6 +1081,8 @@ static QStyleHelper::WidgetSizePolicy qt_aqua_guess_size(const QWidget *widg, QS void QMacStylePrivate::drawFocusRing(QPainter *p, const QRectF &targetRect, int hMargin, int vMargin, const CocoaControl &cw) const { + const bool isBigSurOrAbove = QOperatingSystemVersion::current() >= QOperatingSystemVersion::MacOSBigSur; + QPainterPath focusRingPath; focusRingPath.setFillRule(Qt::OddEvenFill); @@ -1183,6 +1094,8 @@ void QMacStylePrivate::drawFocusRing(QPainter *p, const QRectF &targetRect, int case SegmentedControl_Middle: case TextField: { auto innerRect = targetRect; + if (cw.type == Button_SquareButton) + innerRect = cw.adjustedControlFrame(targetRect.adjusted(hMargin, vMargin, -hMargin, -vMargin)); if (cw.type == TextField) innerRect = innerRect.adjusted(hMargin, vMargin, -hMargin, -vMargin).adjusted(0.5, 0.5, -0.5, -0.5); const auto outerRect = innerRect.adjusted(-focusRingWidth, -focusRingWidth, focusRingWidth, focusRingWidth); @@ -1217,15 +1130,49 @@ void QMacStylePrivate::drawFocusRing(QPainter *p, const QRectF &targetRect, int focusRingPath.addEllipse(rbOuterRect); break; } - case Button_PopupButton: case Button_PullDown: - case Button_PushButton: + case Button_PushButton: { + QRectF focusRect; + auto *pb = static_cast<NSButton *>(cocoaControl(cw)); + const QRectF frameRect = cw.adjustedControlFrame(targetRect.adjusted(hMargin, vMargin, -hMargin, -vMargin)); + pb.frame = frameRect.toCGRect(); + focusRect = QRectF::fromCGRect([pb alignmentRectForFrame:pb.frame]); + if (cw.type == QMacStylePrivate::Button_PushButton) { + focusRect -= pushButtonShadowMargins[cw.size]; + if (cw.size == QStyleHelper::SizeMini) + focusRect.adjust(0, 3, 0, -3); + } else if (cw.type == QMacStylePrivate::Button_PullDown) { + focusRect -= pullDownButtonShadowMargins[cw.size]; + //fix focus ring drawn slightly off for pull downs + if (cw.size == QStyleHelper::SizeLarge) + focusRect = focusRect.adjusted(0, 0, 0, -1); + else if (cw.size == QStyleHelper::SizeMini) + focusRect = focusRect.adjusted(0, -1, 0, 0); + } + if (isBigSurOrAbove) + focusRect = focusRect.translated(0, 1); + const qreal innerRadius = cw.type == Button_PushButton ? 3 : 4; + const qreal outerRadius = innerRadius + focusRingWidth; + hOffset = focusRect.left(); + vOffset = focusRect.top(); + const auto innerRect = focusRect.translated(-focusRect.topLeft()); + const auto outerRect = innerRect.adjusted(-hMargin, -vMargin, hMargin, vMargin); + focusRingPath.addRoundedRect(innerRect, innerRadius, innerRadius); + focusRingPath.addRoundedRect(outerRect, outerRadius, outerRadius); + break; + } + case Button_PopupButton: case SegmentedControl_Single: { + QRectF focusRect = targetRect; + if (isBigSurOrAbove) + focusRect.translate(0, -1.5); + else if (QOperatingSystemVersion::current() >= QOperatingSystemVersion::MacOSCatalina) + focusRect.adjust(0, 0, 0, -1); const qreal innerRadius = cw.type == Button_PushButton ? 3 : 4; const qreal outerRadius = innerRadius + focusRingWidth; - hOffset = targetRect.left(); - vOffset = targetRect.top(); - const auto innerRect = targetRect.translated(-targetRect.topLeft()); + hOffset = focusRect.left(); + vOffset = focusRect.top(); + const auto innerRect = focusRect.translated(-focusRect.topLeft()); const auto outerRect = innerRect.adjusted(-hMargin, -vMargin, hMargin, vMargin); focusRingPath.addRoundedRect(innerRect, innerRadius, innerRadius); focusRingPath.addRoundedRect(outerRect, outerRadius, outerRadius); @@ -1274,7 +1221,7 @@ void QMacStylePrivate::drawFocusRing(QPainter *p, const QRectF &targetRect, int } auto focusRingColor = qt_mac_toQColor(NSColor.keyboardFocusIndicatorColor.CGColor); - if (!qt_mac_applicationIsInDarkMode()) { + if (!isDarkMode()) { // This color already has alpha ~ 0.25, this value is too small - the ring is // very pale and nothing like the native one. 0.39 makes it better (not ideal // anyway). The color seems to be correct in dark more without any modification. @@ -1496,7 +1443,7 @@ QStyleHelper::WidgetSizePolicy QMacStylePrivate::aquaSizeConstrain(const QStyleO if (guess_size) ret = qt_aqua_guess_size(widg, large, small, mini); - QSize *sz = 0; + QSize *sz = nullptr; if (ret == QStyleHelper::SizeSmall) sz = &small; else if (ret == QStyleHelper::SizeLarge) @@ -1577,15 +1524,18 @@ QRectF QMacStylePrivate::CocoaControl::adjustedControlFrame(const QRectF &rect) const auto frameSize = defaultFrameSize(); if (type == QMacStylePrivate::Button_SquareButton) { frameRect = rect.adjusted(3, 1, -3, -1) - .adjusted(focusRingWidth, focusRingWidth, -focusRingWidth, -focusRingWidth); + .adjusted(focusRingWidth, focusRingWidth, -focusRingWidth, -focusRingWidth) + .adjusted(-6.5, 0, 6.5, 0); } else if (type == QMacStylePrivate::Button_PushButton) { // Start from the style option's top-left corner. frameRect = QRectF(rect.topLeft(), QSizeF(rect.width(), frameSize.height())); if (size == QStyleHelper::SizeSmall) - frameRect = frameRect.translated(0, 1.5); + frameRect.translate(0, 0.5); else if (size == QStyleHelper::SizeMini) - frameRect = frameRect.adjusted(0, 0, -8, 0).translated(4, 4); + frameRect = frameRect.adjusted(0, 0, -8, 0).translated(4, -0.5); + frameRect = frameRect.adjusted(-pushButtonBevelRectOffsets[size], 0, + pushButtonBevelRectOffsets[size], 0); } else { // Center in the style option's rect. frameRect = QRectF(QPointF(0, (rect.height() - frameSize.height()) / 2.0), @@ -1598,6 +1548,9 @@ QRectF QMacStylePrivate::CocoaControl::adjustedControlFrame(const QRectF &rect) frameRect = frameRect.adjusted(0, 0, -4, 0).translated(2, 1); else if (size == QStyleHelper::SizeMini) frameRect = frameRect.adjusted(0, 0, -9, 0).translated(5, 0); + if (type == QMacStylePrivate::Button_PullDown) + frameRect = frameRect.adjusted(-pushButtonBevelRectOffsets[size], 0, + pushButtonBevelRectOffsets[size], 0); } else if (type == QMacStylePrivate::ComboBox) { frameRect = frameRect.adjusted(0, 0, -6, 0).translated(4, 0); } @@ -1652,7 +1605,7 @@ bool QMacStylePrivate::CocoaControl::getCocoaButtonTypeAndBezelStyle(NSButtonTyp *bezelStyle = NSBezelStyleShadowlessSquare; break; case Button_PushButton: - *buttonType = NSButtonTypePushOnPushOff; + *buttonType = NSButtonTypeMomentaryPushIn; *bezelStyle = NSBezelStyleRounded; break; default: @@ -1666,13 +1619,15 @@ QMacStylePrivate::CocoaControlType cocoaControlType(const QStyleOption *opt, con { if (const auto *btn = qstyleoption_cast<const QStyleOptionButton *>(opt)) { const bool hasMenu = btn->features & QStyleOptionButton::HasMenu; - // When the contents won't fit in a large sized button, - // and WA_MacNormalSize is not set, make the button square. + // Buttons that don't fit (or don't get filled by) the default bevel + // will be made of square type, unless WA_MacNormalSize is not set. // Threshold used to be at 34, not 32. - const auto maxNonSquareHeight = pushButtonDefaultHeight[QStyleHelper::SizeLarge]; + // Needs to be in sync with similar logic in CE_FocusFrame handling + QStyleHelper::WidgetSizePolicy sizePolicy = QStyleHelper::widgetSizePolicy(w, opt); + if (sizePolicy == QStyleHelper::SizeDefault) + sizePolicy = QStyleHelper::SizeLarge; const bool isSquare = (btn->features & QStyleOptionButton::Flat) - || (btn->rect.height() > maxNonSquareHeight - && !(w && w->testAttribute(Qt::WA_MacNormalSize))); + || (btn->rect.height() != pushButtonDefaultHeight[sizePolicy]); return (isSquare? QMacStylePrivate::Button_SquareButton : hasMenu ? QMacStylePrivate::Button_PullDown : QMacStylePrivate::Button_PushButton); @@ -1791,10 +1746,6 @@ QRectF QMacStylePrivate::comboboxEditBounds(const QRectF &outerBounds, const Coc QMacStylePrivate::QMacStylePrivate() : backingStoreNSView(nil) { - if (auto *ssf = QGuiApplicationPrivate::platformTheme()->font(QPlatformTheme::SmallFont)) - smallSystemFont = *ssf; - if (auto *msf = QGuiApplicationPrivate::platformTheme()->font(QPlatformTheme::MiniFont)) - miniSystemFont = *msf; } QMacStylePrivate::~QMacStylePrivate() @@ -1812,13 +1763,9 @@ NSView *QMacStylePrivate::cocoaControl(CocoaControl widget) const || widget.size == QStyleHelper::SizeDefault) return nil; - if (widget.type == Box) { - if (__builtin_available(macOS 10.14, *)) { - if (qt_mac_applicationIsInDarkMode()) { - // See render code in drawPrimitive(PE_FrameTabWidget) - widget.type = Box_Dark; - } - } + if (widget.type == Box && isDarkMode()) { + // See render code in drawPrimitive(PE_FrameTabWidget) + widget.type = Box_Dark; } NSView *bv = cocoaControls.value(widget, nil); @@ -2063,7 +2010,6 @@ QMacStyle::QMacStyle() QCoreApplication::sendEvent(o, &event); }); -#if QT_MACOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_14) Q_D(QMacStyle); // FIXME: Tie this logic into theme change, or even polish/unpolish if (QOperatingSystemVersion::current() >= QOperatingSystemVersion::MacOSMojave) { @@ -2074,7 +2020,6 @@ QMacStyle::QMacStyle() d->cocoaControls.clear(); }); } -#endif } QMacStyle::~QMacStyle() @@ -2095,6 +2040,14 @@ void QMacStyle::unpolish(QApplication *) void QMacStyle::polish(QWidget* w) { + Q_D(QMacStyle); + if (!d->smallSystemFont && QGuiApplicationPrivate::platformTheme()) { + if (auto *ssf = QGuiApplicationPrivate::platformTheme()->font(QPlatformTheme::SmallFont)) + d->smallSystemFont = *ssf; + else + d->smallSystemFont = QFont(); + } + if (false #if QT_CONFIG(menu) || qobject_cast<QMenu*>(w) @@ -2207,25 +2160,6 @@ int QMacStyle::pixelMetric(PixelMetric metric, const QStyleOption *opt, const QW case PM_FocusFrameHMargin: ret = qt_mac_aqua_get_metric(FocusRectOutset); break; - case PM_DialogButtonsSeparator: - ret = -5; - break; - case PM_DialogButtonsButtonHeight: { - QSize sz; - ret = d->aquaSizeConstrain(opt, 0, QStyle::CT_PushButton, QSize(-1, -1), &sz); - if (sz == QSize(-1, -1)) - ret = 32; - else - ret = sz.height(); - break; } - case PM_DialogButtonsButtonWidth: { - QSize sz; - ret = d->aquaSizeConstrain(opt, 0, QStyle::CT_PushButton, QSize(-1, -1), &sz); - if (sz == QSize(-1, -1)) - ret = 70; - else - ret = sz.width(); - break; } case PM_MenuBarHMargin: ret = 8; @@ -2552,10 +2486,13 @@ int QMacStyle::pixelMetric(PixelMetric metric, const QStyleOption *opt, const QW case PM_ToolBarFrameWidth: ret = 1; break; - case PM_ScrollView_ScrollBarOverlap: - ret = [NSScroller preferredScrollerStyle] == NSScrollerStyleOverlay ? - pixelMetric(PM_ScrollBarExtent, opt, widget) : 0; + case PM_ScrollView_ScrollBarOverlap: { + const QStyle *realStyle = widget ? widget->style() : proxy(); + ret = realStyle->styleHint(SH_ScrollBar_Transient, opt, widget) + ? realStyle->pixelMetric(PM_ScrollBarExtent, opt, widget) + : 0; break; + } default: ret = QCommonStyle::pixelMetric(metric, opt, widget); break; @@ -2735,7 +2672,7 @@ int QMacStyle::styleHint(StyleHint sh, const QStyleOption *opt, const QWidget *w break; case SH_FocusFrame_Mask: { ret = true; - if(QStyleHintReturnMask *mask = qstyleoption_cast<QStyleHintReturnMask*>(hret)) { + if (QStyleHintReturnMask *mask = qstyleoption_cast<QStyleHintReturnMask*>(hret)) { const uchar fillR = 192, fillG = 191, fillB = 190; QImage img; @@ -2863,7 +2800,7 @@ int QMacStyle::styleHint(StyleHint sh, const QStyleOption *opt, const QWidget *w case SH_ScrollBar_Transient: if ((qobject_cast<const QScrollBar *>(w) && w->parent() && qobject_cast<QAbstractScrollArea*>(w->parent()->parent())) -#ifndef QT_NO_ACCESSIBILITY +#if QT_CONFIG(accessibility) || (opt && QStyleHelper::hasAncestor(opt->styleObject, QAccessible::ScrollBar)) #endif ) { @@ -2888,6 +2825,9 @@ int QMacStyle::styleHint(StyleHint sh, const QStyleOption *opt, const QWidget *w case SH_Table_GridLineColor: ret = int(qt_mac_toQColor(NSColor.gridColor).rgba()); break; + case SH_TabBar_AllowWheelScrolling: + ret = false; + break; default: ret = QCommonStyle::styleHint(sh, opt, w, hret); break; @@ -3019,7 +2959,7 @@ void QMacStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, QPai } #if QT_CONFIG(tabwidget) QRegion region(tbb->rect); - region -= tbb->tabBarRect; + region -= tbb->tabBarRect.adjusted(3, 0, -3, 0); p->save(); p->setClipRegion(region); QStyleOptionTabWidgetFrame twf; @@ -3077,7 +3017,7 @@ void QMacStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, QPai auto adjustedRect = opt->rect; bool needTranslation = false; if (QOperatingSystemVersion::current() >= QOperatingSystemVersion::MacOSMojave - && !qt_mac_applicationIsInDarkMode()) { + && !isDarkMode()) { // In Aqua theme we have to use the 'default' NSBox (as opposite // to the 'custom' QDarkNSBox we use in dark theme). Since -drawRect: // does nothing in default NSBox, we call -displayRectIgnoringOpaticty:. @@ -3086,7 +3026,7 @@ void QMacStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, QPai // vertically and even worse, if QTabWidget has autoFillBackground // set, this background overpaints NSBox making it to disappear. // We trick our NSBox to render in a larger rectangle, so that - // the actuall result (which is again smaller than requested), + // the actual result (which is again smaller than requested), // more or less is what we really want. We'll have to adjust CTM // and translate accordingly. adjustedRect.adjust(0, 0, 6, 6); @@ -3125,14 +3065,14 @@ void QMacStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, QPai theStroker.setCapStyle(Qt::FlatCap); theStroker.setDashPattern(QVector<qreal>() << 1 << 2); path = theStroker.createStroke(path); - const auto dark = qt_mac_applicationIsInDarkMode() ? opt->palette.dark().color().darker() + const auto dark = isDarkMode() ? opt->palette.dark().color().darker() : QColor(0, 0, 0, 119); p->fillPath(path, dark); } break; case PE_FrameWindow: if (const QStyleOptionFrame *frame = qstyleoption_cast<const QStyleOptionFrame *>(opt)) { - if (w && w->inherits("QMdiSubWindow")) { + if (qobject_cast<const QMdiSubWindow*>(w)) { p->save(); p->setPen(QPen(frame->palette.dark().color(), frame->lineWidth)); p->setBrush(frame->palette.window()); @@ -3280,7 +3220,7 @@ void QMacStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, QPai NSButtonCell *triangleCell = static_cast<NSButtonCell *>(d->cocoaCell(cw)); [triangleCell setState:(opt->state & State_Open) ? NSControlStateValueOn : NSControlStateValueOff]; bool viewHasFocus = (w && w->hasFocus()) || (opt->state & State_HasFocus); - [triangleCell setBackgroundStyle:((opt->state & State_Selected) && viewHasFocus) ? NSBackgroundStyleDark : NSBackgroundStyleLight]; + [triangleCell setBackgroundStyle:((opt->state & State_Selected) && viewHasFocus) ? NSBackgroundStyleEmphasized : NSBackgroundStyleNormal]; d->setupNSGraphicsContext(cg, NO); @@ -3319,7 +3259,7 @@ void QMacStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, QPai static_cast<NSTextFieldCell *>(tf.cell).bezelStyle = isRounded ? NSTextFieldRoundedBezel : NSTextFieldSquareBezel; tf.frame = opt->rect.toCGRect(); d->drawNSViewInRect(tf, opt->rect, p, ^(CGContextRef, const CGRect &rect) { - if (!qt_mac_applicationIsInDarkMode()) { + if (!isDarkMode()) { // In 'Dark' mode controls are transparent, so we do not // over-paint the (potentially custom) color in the background. // In 'Light' mode we have to care about the correct @@ -3352,7 +3292,7 @@ void QMacStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, QPai case PE_PanelLineEdit: { const QStyleOptionFrame *panel = qstyleoption_cast<const QStyleOptionFrame *>(opt); - if (qt_mac_applicationIsInDarkMode() || (panel && panel->lineWidth <= 0)) { + if (isDarkMode() || (panel && panel->lineWidth <= 0)) { // QCommonStyle::drawPrimitive(PE_PanelLineEdit) fill the background with // a proper color, defined in opt->palette and then, if lineWidth > 0, it // calls QMacStyle::drawPrimitive(PE_FrameLineEdit). We use NSTextFieldCell @@ -3417,17 +3357,7 @@ void QMacStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, QPai } break; #endif // QT_CONFIG(tabbar) case PE_PanelStatusBar: { - // Fill the status bar with the titlebar gradient. - QLinearGradient linearGrad; - if (w ? qt_macWindowMainWindow(w->window()) : (opt->state & QStyle::State_Active)) { - linearGrad = titlebarGradientActive(); - } else { - linearGrad = titlebarGradientInactive(); - } - - linearGrad.setStart(0, opt->rect.top()); - linearGrad.setFinalStop(0, opt->rect.bottom()); - p->fillRect(opt->rect, linearGrad); + p->fillRect(opt->rect, opt->palette.window()); // Draw the black separator line at the top of the status bar. if (w ? qt_macWindowMainWindow(w->window()) : (opt->state & QStyle::State_Active)) @@ -3531,33 +3461,11 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter } break; - case CE_HeaderLabel: - if (const QStyleOptionHeader *header = qstyleoption_cast<const QStyleOptionHeader *>(opt)) { - p->save(); - QRect textr = header->rect; - if (!header->icon.isNull()) { - QIcon::Mode mode = QIcon::Disabled; - if (opt->state & State_Enabled) - mode = QIcon::Normal; - int iconExtent = proxy()->pixelMetric(PM_SmallIconSize); - QPixmap pixmap = header->icon.pixmap(QSize(iconExtent, iconExtent), p->device()->devicePixelRatio(), mode); - - QRect pixr = header->rect; - pixr.setY(header->rect.center().y() - (pixmap.height() / pixmap.devicePixelRatio() - 1) / 2); - proxy()->drawItemPixmap(p, pixr, Qt::AlignVCenter, pixmap); - textr.translate(pixmap.width() / pixmap.devicePixelRatio() + 2, 0); - } - - proxy()->drawItemText(p, textr, header->textAlignment | Qt::AlignVCenter, header->palette, - header->state & State_Enabled, header->text, QPalette::ButtonText); - p->restore(); - } - break; case CE_ToolButtonLabel: if (const QStyleOptionToolButton *tb = qstyleoption_cast<const QStyleOptionToolButton *>(opt)) { QStyleOptionToolButton myTb = *tb; myTb.state &= ~State_AutoRaise; -#ifndef QT_NO_ACCESSIBILITY +#if QT_CONFIG(accessibility) if (QStyleHelper::hasAncestor(opt->styleObject, QAccessible::ToolBar)) { QRect cr = tb->rect; int shiftX = 0; @@ -3597,12 +3505,13 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter // Draw the text if it's needed. if (tb->toolButtonStyle != Qt::ToolButtonIconOnly) { needText = true; + QSizeF size = pixmap.deviceIndependentSize(); if (tb->toolButtonStyle == Qt::ToolButtonTextUnderIcon) { - pr.setHeight(pixmap.size().height() / pixmap.devicePixelRatio() + 6); + pr.setHeight(size.height() + 6); cr.adjust(0, pr.bottom(), 0, -3); alignment |= Qt::AlignCenter; } else { - pr.setWidth(pixmap.width() / pixmap.devicePixelRatio() + 8); + pr.setWidth(size.width() + 8); cr.adjust(pr.right(), 0, 0, 0); alignment |= Qt::AlignLeft | Qt::AlignVCenter; } @@ -3646,7 +3555,7 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter QCommonStyle::drawControl(ce, &myTb, p, w); } } else -#endif // QT_NO_ACCESSIBILITY +#endif // QT_CONFIG(accessibility) { QCommonStyle::drawControl(ce, &myTb, p, w); } @@ -3689,14 +3598,27 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter const auto cw = QMacStylePrivate::CocoaControl(ct, cs); auto *pb = static_cast<NSButton *>(d->cocoaControl(cw)); // Ensure same size and location as we used to have with HITheme. - // This is more convoluted than we initialy thought. See for example + // This is more convoluted than we initially thought. See for example // differences between plain and menu button frames. const QRectF frameRect = cw.adjustedControlFrame(btn->rect); pb.frame = frameRect.toCGRect(); pb.enabled = isEnabled; + + // With the 'momentary push in' type this gives an impression of a + // button in a 'pressed' state (the 'momentary push in' does + // not show its state otherwise): [pb highlight:isPressed]; - pb.state = isHighlighted && !isPressed ? NSControlStateValueOn : NSControlStateValueOff; + + + if (cw.type == QMacStylePrivate::Button_SquareButton) { + pb.state = isHighlighted && !isPressed ? NSControlStateValueOn : NSControlStateValueOff; + } else { + // For default/checked button this will give the required + // button accent color: + pb.keyEquivalent = isHighlighted ? @"\r" : @""; + } + d->drawNSViewInRect(pb, frameRect, p, ^(CGContextRef, const CGRect &r) { [pb.cell drawBezelWithFrame:r inView:pb.superview]; }); @@ -3723,24 +3645,6 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter arrowOpt.rect = ar; proxy()->drawPrimitive(PE_IndicatorArrowDown, &arrowOpt, p, w); } - - - if (btn->state & State_HasFocus) { - // TODO Remove and use QFocusFrame instead. - const int hMargin = proxy()->pixelMetric(QStyle::PM_FocusFrameHMargin, btn, w); - const int vMargin = proxy()->pixelMetric(QStyle::PM_FocusFrameVMargin, btn, w); - QRectF focusRect; - if (cw.type == QMacStylePrivate::Button_SquareButton) { - focusRect = frameRect; - } else { - focusRect = QRectF::fromCGRect([pb alignmentRectForFrame:pb.frame]); - if (cw.type == QMacStylePrivate::Button_PushButton) - focusRect -= pushButtonShadowMargins[cw.size]; - else if (cw.type == QMacStylePrivate::Button_PullDown) - focusRect -= pullDownButtonShadowMargins[cw.size]; - } - d->drawFocusRing(p, focusRect, hMargin, vMargin, cw); - } } break; case CE_PushButtonLabel: @@ -3756,16 +3660,28 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter const bool hasText = !btn.text.isEmpty(); const bool isActive = btn.state & State_Active; const bool isPressed = btn.state & State_Sunken; + const bool isDefault = (btn.features & QStyleOptionButton::DefaultButton && !d->autoDefaultButton) + || d->autoDefaultButton == btn.styleObject; + // cocoaControlType evaluates the type based on the control's geometry, not on the + // label's geometry + const QRect oldRect = btn.rect; + if (w) + btn.rect = w->rect(); const auto ct = cocoaControlType(&btn, w); + btn.rect = oldRect; if (!hasMenu && ct != QMacStylePrivate::Button_SquareButton) { - if (isPressed - || (isActive && isEnabled - && ((btn.state & State_On) - || ((btn.features & QStyleOptionButton::DefaultButton) && !d->autoDefaultButton) - || d->autoDefaultButton == btn.styleObject))) - btn.palette.setColor(QPalette::ButtonText, Qt::white); + if (isPressed || (isActive && isEnabled && ((btn.state & State_On) || isDefault))) + btn.palette.setColor(QPalette::ButtonText, Qt::white); + } + + if (isEnabled && !isDarkMode() && QOperatingSystemVersion::current() > QOperatingSystemVersion::MacOSBigSur) { + if (!isDefault && !(btn.state & State_On)) { + // On macOS 12 it's a gray button, white text color (if set in the + // previous statement) would be almost invisible. + btn.palette.setColor(QPalette::ButtonText, Qt::black); + } } if ((!hasIcon && !hasMenu) || (hasIcon && !hasText)) { @@ -3775,7 +3691,11 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter QRect textRect = itemTextRect( btn.fontMetrics, freeContentRect, Qt::AlignCenter, isEnabled, btn.text); if (hasMenu) { - textRect.moveTo(w ? 15 : 11, textRect.top()); // Supports Qt Quick Controls + if (ct == QMacStylePrivate::Button_SquareButton) + textRect.moveTo(w ? 8 : 11, textRect.top()); + else + textRect.moveTo(w ? (15 - pushButtonBevelRectOffsets[d->effectiveAquaSizeConstrain(b, w)]) + : 11, textRect.top()); // Supports Qt Quick Controls } // Draw the icon: if (hasIcon) { @@ -3790,12 +3710,11 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter if (btn.state & State_On) state = QIcon::On; QPixmap pixmap = btn.icon.pixmap(btn.iconSize, p->device()->devicePixelRatio(), mode, state); - int pixmapWidth = pixmap.width() / pixmap.devicePixelRatio(); - int pixmapHeight = pixmap.height() / pixmap.devicePixelRatio(); - contentW += pixmapWidth + QMacStylePrivate::PushButtonContentPadding; + QSizeF pixmapSize = pixmap.deviceIndependentSize(); + contentW += pixmapSize.width() + QMacStylePrivate::PushButtonContentPadding; int iconLeftOffset = freeContentRect.x() + (freeContentRect.width() - contentW) / 2; - int iconTopOffset = freeContentRect.y() + (freeContentRect.height() - pixmapHeight) / 2; - QRect iconDestRect(iconLeftOffset, iconTopOffset, pixmapWidth, pixmapHeight); + int iconTopOffset = freeContentRect.y() + (freeContentRect.height() - pixmapSize.height()) / 2; + QRect iconDestRect(iconLeftOffset, iconTopOffset, pixmapSize.width(), pixmapSize.height()); QRect visualIconDestRect = visualRect(btn.direction, freeContentRect, iconDestRect); proxy()->drawItemPixmap(p, visualIconDestRect, Qt::AlignLeft | Qt::AlignVCenter, pixmap); int newOffset = iconDestRect.x() + iconDestRect.width() @@ -3867,16 +3786,11 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter // inFrame:withView:], -[drawRect:] or anything in between. Besides, // there's no public API do draw the pressed state, AFAICS. We'll use // a push NSButton instead and clip the CGContext. - // NOTE/TODO: this is not true. On 10.13 NSSegmentedControl works with - // some (black?) magic/magic dances, on 10.14 it simply works (was - // it fixed in AppKit?). But, indeed, we cannot make a tab 'pressed' - // with NSSegmentedControl (only selected), so we stay with buttons - // (mixing buttons and NSSegmentedControl for such a simple thing - // is too much work). const auto cs = d->effectiveAquaSizeConstrain(opt, w); // Extra hacks to get the proper pressed appreance when not selected or selected and inactive const bool needsInactiveHack = (!isActive && isSelected); + const bool isBigSurOrAbove = QOperatingSystemVersion::current() >= QOperatingSystemVersion::MacOSBigSur; const auto ct = !needsInactiveHack && (isSelected || tp == QStyleOptionTab::OnlyOneTab) ? QMacStylePrivate::Button_PushButton : QMacStylePrivate::Button_PopupButton; @@ -3885,6 +3799,12 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter auto *pb = static_cast<NSButton *>(d->cocoaControl(cw)); auto vOffset = isPopupButton ? 1 : 2; + if (isBigSurOrAbove) { + // Make it 1, otherwise, offset is very visible compared + // to selected tab (which is not a popup button). + vOffset = 1; + } + if (tabDirection == QMacStylePrivate::East) vOffset -= 1; const auto outerAdjust = isPopupButton ? 1 : 4; @@ -3901,16 +3821,35 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter frameRect = frameRect.adjusted(-innerAdjust, 0, outerAdjust, 0); else frameRect = frameRect.adjusted(-outerAdjust, 0, innerAdjust, 0); + + if (isSelected && isBigSurOrAbove) { + // 1 pixed of 'roundness' is still visible on the right + // (the left is OK, it's rounded). + frameRect = frameRect.adjusted(0, 0, 1, 0); + } + break; case QStyleOptionTab::Middle: frameRect = frameRect.adjusted(-innerAdjust, 0, innerAdjust, 0); + + if (isSelected && isBigSurOrAbove) { + // 1 pixel of 'roundness' is still visible on both + // sides - left and right. + frameRect = frameRect.adjusted(-1, 0, 1, 0); + } break; + case QStyleOptionTab::Moving: // Moving tab treated like End case QStyleOptionTab::End: // Pressed state hack: tweak adjustments in preparation for flip below if (isSelected || tabDirection == QMacStylePrivate::West) frameRect = frameRect.adjusted(-innerAdjust, 0, outerAdjust, 0); else frameRect = frameRect.adjusted(-outerAdjust, 0, innerAdjust, 0); + + if (isSelected && isBigSurOrAbove) { + // 1 pixel of 'roundness' is still visible on the left. + frameRect = frameRect.adjusted(-1, 0, 0, 0); + } break; case QStyleOptionTab::OnlyOneTab: frameRect = frameRect.adjusted(-outerAdjust, 0, outerAdjust, 0); @@ -3918,10 +3857,22 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter } pb.frame = frameRect.toCGRect(); + if (!isPopupButton) { + // Note: these days we use 'momentary push in' for Button_PushButton, + // but tabs are also rendered using NSButton/ButtonPushButton, and + // here we need 'push on/off' to make it work (tab highlight colors). + pb.buttonType = NSButtonTypePushOnPushOff; + } + pb.enabled = isEnabled; [pb highlight:isPressed]; + // Set off state when inactive. See needsInactiveHack for when it's selected - pb.state = (isActive && isSelected && !isPressed) ? NSControlStateValueOn : NSControlStateValueOff; + // On macOS 12, don't set the Off state for selected tabs as it draws a gray backgorund even when highlighted + if (QOperatingSystemVersion::current() > QOperatingSystemVersion::MacOSBigSur) + pb.state = (isActive && isSelected) ? NSControlStateValueOn : NSControlStateValueOff; + else + pb.state = (isActive && isSelected && !isPressed) ? NSControlStateValueOn : NSControlStateValueOff; const auto drawBezelBlock = ^(CGContextRef ctx, const CGRect &r) { CGContextClipToRect(ctx, opt->rect.toCGRect()); @@ -3958,7 +3909,10 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter NSPopUpArrowPosition oldPosition = NSPopUpArrowAtCenter; NSPopUpButtonCell *pbCell = nil; auto rAdjusted = r; - if (isPopupButton && tp == QStyleOptionTab::OnlyOneTab) { + if (isPopupButton && (tp == QStyleOptionTab::OnlyOneTab || isBigSurOrAbove)) { + // Note: starting from macOS BigSur NSPopupButton has this + // arrow 'button' in a different place and it became + // quite visible 'in between' inactive tabs. pbCell = static_cast<NSPopUpButtonCell *>(pb.cell); oldPosition = pbCell.arrowPosition; pbCell.arrowPosition = NSPopUpNoArrow; @@ -3966,6 +3920,10 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter // NSPopUpButton in this state is smaller. rAdjusted.origin.x -= 3; rAdjusted.size.width += 6; + if (isBigSurOrAbove) { + if (tp == QStyleOptionTab::End) + rAdjusted.origin.x -= 2; + } } } @@ -4029,32 +3987,12 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter } p->restore(); } - - // TODO Needs size adjustment to fit the focus ring - if (tabOpt->state & State_HasFocus) { - QMacStylePrivate::CocoaControlType focusRingType; - switch (tp) { - case QStyleOptionTab::Beginning: - focusRingType = verticalTabs ? QMacStylePrivate::SegmentedControl_Last - : QMacStylePrivate::SegmentedControl_First; - break; - case QStyleOptionTab::Middle: - focusRingType = QMacStylePrivate::SegmentedControl_Middle; - break; - case QStyleOptionTab::End: - focusRingType = verticalTabs ? QMacStylePrivate::SegmentedControl_First - : QMacStylePrivate::SegmentedControl_Last; - break; - case QStyleOptionTab::OnlyOneTab: - focusRingType = QMacStylePrivate::SegmentedControl_Single; - break; - } - } } break; case CE_TabBarTabLabel: if (const auto *tab = qstyleoption_cast<const QStyleOptionTab *>(opt)) { QStyleOptionTab myTab = *tab; + const auto foregroundRole = w ? w->foregroundRole() : QPalette::WindowText; const auto tabDirection = QMacStylePrivate::tabDirection(tab->shape); const bool verticalTabs = tabDirection == QMacStylePrivate::East || tabDirection == QMacStylePrivate::West; @@ -4068,11 +4006,11 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter if (!myTab.documentMode && (myTab.state & State_Selected) && (myTab.state & State_Active)) if (const auto *tabBar = qobject_cast<const QTabBar *>(w)) if (!tabBar->tabTextColor(tabBar->currentIndex()).isValid()) - myTab.palette.setColor(QPalette::WindowText, Qt::white); + myTab.palette.setColor(foregroundRole, Qt::white); if (myTab.documentMode && isDarkMode()) { bool active = (myTab.state & State_Selected) && (myTab.state & State_Active); - myTab.palette.setColor(QPalette::WindowText, active ? Qt::white : Qt::gray); + myTab.palette.setColor(foregroundRole, active ? Qt::white : Qt::gray); } int heightOffset = 0; @@ -4101,12 +4039,7 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter } // fill title bar background - QLinearGradient linearGrad; - linearGrad.setStart(QPointF(0, 0)); - linearGrad.setFinalStop(QPointF(0, 2 * effectiveRect.height())); - linearGrad.setColorAt(0, opt->palette.button().color()); - linearGrad.setColorAt(1, opt->palette.dark().color()); - p->fillRect(effectiveRect, linearGrad); + p->fillRect(effectiveRect, opt->palette.window()); // draw horizontal line at bottom p->setPen(opt->palette.dark().color()); @@ -4121,7 +4054,7 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter titleRect.width()); const auto text = p->fontMetrics().elidedText(dwOpt->title, Qt::ElideRight, titleRect.width()); - proxy()->drawItemText(p, titleRect, Qt::AlignCenter, dwOpt->palette, + proxy()->drawItemText(p, titleRect, Qt::AlignCenter | Qt::TextHideMnemonic, dwOpt->palette, dwOpt->state & State_Enabled, text, QPalette::WindowText); } p->restore(); @@ -4133,20 +4066,32 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter const auto *ffw = ff ? ff->widget() : nullptr; const auto ct = [=] { if (ffw) { - if (ffw->inherits("QCheckBox")) + if (qobject_cast<const QCheckBox*>(ffw)) return QMacStylePrivate::Button_CheckBox; - if (ffw->inherits("QRadioButton")) + if (qobject_cast<const QRadioButton*>(ffw)) return QMacStylePrivate::Button_RadioButton; - if (ffw->inherits("QLineEdit") || ffw->inherits("QTextEdit")) + if (qobject_cast<const QLineEdit*>(ffw) || qobject_cast<const QTextEdit*>(ffw)) return QMacStylePrivate::TextField; + if (const auto *pb = qobject_cast<const QPushButton *>(ffw)) { + // keep in sync with cocoaControlType + auto sizePolicy = QStyleHelper::widgetSizePolicy(ffw, opt); + if (sizePolicy == QStyleHelper::SizeDefault) + sizePolicy = QStyleHelper::SizeLarge; + if (pb->isFlat() + || (pb->rect().height() != pushButtonDefaultHeight[sizePolicy])) { + return QMacStylePrivate::Button_SquareButton; + } + if (pb->menu() != nullptr) + return QMacStylePrivate::Button_PullDown; + return QMacStylePrivate::Button_PushButton; + } } return QMacStylePrivate::Box; // Not really, just make it the default } (); - const auto cs = ffw ? (ffw->testAttribute(Qt::WA_MacMiniSize) ? QStyleHelper::SizeMini : - ffw->testAttribute(Qt::WA_MacSmallSize) ? QStyleHelper::SizeSmall : - QStyleHelper::SizeLarge) : - QStyleHelper::SizeLarge; + auto cs = QStyleHelper::widgetSizePolicy(ffw, opt); + if (cs == QStyleHelper::SizeDefault) + cs = QStyleHelper::SizeLarge; const int hMargin = proxy()->pixelMetric(QStyle::PM_FocusFrameHMargin, opt, w); const int vMargin = proxy()->pixelMetric(QStyle::PM_FocusFrameVMargin, opt, w); d->drawFocusRing(p, opt->rect, hMargin, vMargin, QMacStylePrivate::CocoaControl(ct, cs)); @@ -4254,13 +4199,12 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter } #endif QPixmap pixmap = mi->icon.pixmap(iconSize, p->device()->devicePixelRatio(), mode); - int pixw = pixmap.width() / pixmap.devicePixelRatio(); - int pixh = pixmap.height() / pixmap.devicePixelRatio(); QRect cr(xpos, mi->rect.y(), checkcol, mi->rect.height()); - QRect pmr(0, 0, pixw, pixh); + QSize size = pixmap.deviceIndependentSize().toSize(); + QRect pmr(QPoint(0, 0), size); pmr.moveCenter(cr.center()); p->drawPixmap(pmr.topLeft(), pixmap); - xpos += pixw + 6; + xpos += size.width() + 6; } QString s = mi->text; @@ -4289,9 +4233,25 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter if (!rightMarginText.isEmpty()) { p->setFont(qt_app_fonts_hash()->value("QMenuItem", p->font())); int xp = mi->rect.right() - tabwidth - macRightBorder + 2; - if (!isSubMenu) + if (isSubMenu) { + p->drawText(xp, yPos, tabwidth, mi->rect.height(), text_flags | Qt::AlignRight, rightMarginText); + } else { xp -= macItemHMargin + macItemFrame + 3; // Adjust for shortcut - p->drawText(xp, yPos, tabwidth, mi->rect.height(), text_flags | Qt::AlignRight, rightMarginText); + // try to render modifier part of shortcut string right aligned, key part left aligned + const QKeySequence seq = QKeySequence::fromString(rightMarginText, QKeySequence::NativeText); + if (seq.count() == 1) { // one-combo sequence, the right most character is the key + // we don't know which key of all menu items is the widest, so use the widest possible + const int maxKeyWidth = p->fontMetrics().maxWidth(); + const QChar key = rightMarginText.at(rightMarginText.length() - 1); + const QString modifiers = rightMarginText.left(rightMarginText.size() - 1); + p->drawText(xp + tabwidth - maxKeyWidth, yPos, maxKeyWidth, mi->rect.height(), text_flags, key); + // don't clip the shortcuts; maxKeyWidth might be more than what we have been allotted by the menu + p->drawText(xp, yPos, tabwidth - maxKeyWidth, mi->rect.height(), + text_flags | Qt::AlignRight | Qt::TextDontClip, modifiers); + } else { // draw the whole thing left-aligned for complex or unparsable cases + p->drawText(xp, yPos, tabwidth, mi->rect.height(), text_flags, rightMarginText); + } + } } if (!s.isEmpty()) { @@ -4333,8 +4293,10 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter // line-break the string if it doesn't fit the given rect. It's better to draw outside // the rect and possibly overlap something than to have part of the text disappear. [s.toNSString() drawAtPoint:CGPointMake(xpos, yPos) - withAttributes:@{ NSFontAttributeName:f, NSForegroundColorAttributeName:c, - NSObliquenessAttributeName: [NSNumber numberWithDouble: myFont.italic() ? 0.3 : 0.0]}]; + withAttributes:@{ NSFontAttributeName:f, NSForegroundColorAttributeName:c, + NSObliquenessAttributeName: [NSNumber numberWithDouble: myFont.italic() ? 0.3 : 0.0], + NSUnderlineStyleAttributeName: [NSNumber numberWithInt: myFont.underline() ? NSUnderlineStyleSingle + : NSUnderlineStyleNone]}]; d->restoreNSGraphicsContext(cgCtx); } else { @@ -4516,7 +4478,7 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter #ifndef QT_NO_TOOLBAR case CE_ToolBar: { const QStyleOptionToolBar *toolBar = qstyleoption_cast<const QStyleOptionToolBar *>(opt); - const bool isDarkMode = qt_mac_applicationIsInDarkMode(); + const bool darkMode = isDarkMode(); // Unified title and toolbar drawing. In this mode the cocoa platform plugin will // fill the top toolbar area part with a background gradient that "unifies" with @@ -4541,7 +4503,7 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter if (isEndOfUnifiedArea) { const int margin = qt_mac_aqua_get_metric(SeparatorSize); const auto separatorRect = QRect(opt->rect.left(), opt->rect.bottom(), opt->rect.width(), margin); - p->fillRect(separatorRect, isDarkMode ? darkModeSeparatorLine : opt->palette.dark().color()); + p->fillRect(separatorRect, darkMode ? darkModeSeparatorLine : opt->palette.dark().color()); } break; } @@ -4556,24 +4518,24 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter else linearGrad = QLinearGradient(opt->rect.left(), 0, opt->rect.right(), 0); - QColor mainWindowGradientBegin = isDarkMode ? darkMainWindowGradientBegin : lightMainWindowGradientBegin; - QColor mainWindowGradientEnd = isDarkMode ? darkMainWindowGradientEnd : lightMainWindowGradientEnd; + QColor mainWindowGradientBegin = darkMode ? darkMainWindowGradientBegin : lightMainWindowGradientBegin; + QColor mainWindowGradientEnd = darkMode ? darkMainWindowGradientEnd : lightMainWindowGradientEnd; linearGrad.setColorAt(0, mainWindowGradientBegin); linearGrad.setColorAt(1, mainWindowGradientEnd); p->fillRect(opt->rect, linearGrad); p->save(); - QRect toolbarRect = isDarkMode ? opt->rect.adjusted(0, 0, 0, 1) : opt->rect; + QRect toolbarRect = darkMode ? opt->rect.adjusted(0, 0, 0, 1) : opt->rect; if (opt->state & State_Horizontal) { - p->setPen(isDarkMode ? darkModeSeparatorLine : mainWindowGradientBegin.lighter(114)); + p->setPen(darkMode ? darkModeSeparatorLine : mainWindowGradientBegin.lighter(114)); p->drawLine(toolbarRect.topLeft(), toolbarRect.topRight()); - p->setPen(isDarkMode ? darkModeSeparatorLine :mainWindowGradientEnd.darker(114)); + p->setPen(darkMode ? darkModeSeparatorLine :mainWindowGradientEnd.darker(114)); p->drawLine(toolbarRect.bottomLeft(), toolbarRect.bottomRight()); } else { - p->setPen(isDarkMode ? darkModeSeparatorLine : mainWindowGradientBegin.lighter(114)); + p->setPen(darkMode ? darkModeSeparatorLine : mainWindowGradientBegin.lighter(114)); p->drawLine(toolbarRect.topLeft(), toolbarRect.bottomLeft()); - p->setPen(isDarkMode ? darkModeSeparatorLine : mainWindowGradientEnd.darker(114)); + p->setPen(darkMode ? darkModeSeparatorLine : mainWindowGradientEnd.darker(114)); p->drawLine(toolbarRect.topRight(), toolbarRect.bottomRight()); } p->restore(); @@ -4882,14 +4844,24 @@ QRect QMacStyle::subElementRect(SubElement sr, const QStyleOption *opt, = qstyleoption_cast<const QStyleOptionButton *>(opt)) { if ((buttonOpt->features & QStyleOptionButton::Flat)) break; // leave rect alone + if ((buttonOpt->features & QStyleOptionButton::CommandLinkButton)) { + rect = opt->rect; + if (controlSize == QStyleHelper::SizeLarge) + rect.adjust(+6, +4, -6, -8); + else if (controlSize == QStyleHelper::SizeSmall) + rect.adjust(+5, +4, -5, -6); + else + rect.adjust(+1, 0, -1, -2); + break; + } } rect = opt->rect; if (controlSize == QStyleHelper::SizeLarge) { - rect.adjust(+6, +4, -6, -8); + rect.adjust(0, +4, 0, -8); } else if (controlSize == QStyleHelper::SizeSmall) { - rect.adjust(+5, +4, -5, -6); + rect.adjust(0, +4, 0, -6); } else { - rect.adjust(+1, 0, -1, -2); + rect.adjust(0, 0, 0, -2); } break; case SE_RadioButtonLayoutItem: @@ -5128,7 +5100,7 @@ void QMacStyle::drawComplexControl(ComplexControl cc, const QStyleOptionComplex if (const QStyleOptionSlider *sb = qstyleoption_cast<const QStyleOptionSlider *>(opt)) { const bool drawTrack = sb->subControls & SC_ScrollBarGroove; - const bool drawKnob = sb->subControls & SC_ScrollBarSlider; + const bool drawKnob = sb->subControls & SC_ScrollBarSlider && sb->minimum != sb->maximum; if (!drawTrack && !drawKnob) break; @@ -5142,7 +5114,8 @@ void QMacStyle::drawComplexControl(ComplexControl cc, const QStyleOptionComplex const auto cocoaSize = d->effectiveAquaSizeConstrain(opt, widget); const CGFloat maxExpandScale = expandedKnobWidths[cocoaSize] / knobWidths[cocoaSize]; - const bool isTransient = proxy()->styleHint(SH_ScrollBar_Transient, opt, widget); + const QStyle *realStyle = widget ? widget->style() : proxy(); + const bool isTransient = realStyle->styleHint(SH_ScrollBar_Transient, opt, widget); if (!isTransient) d->stopAnimation(opt->styleObject); bool wasActive = false; @@ -5331,6 +5304,15 @@ void QMacStyle::drawComplexControl(ComplexControl cc, const QStyleOptionComplex const CGRect knobRect = [slider.cell knobRectFlipped:slider.isFlipped]; pressPoint.x = CGRectGetMidX(knobRect); pressPoint.y = CGRectGetMidY(knobRect); + + // The only way to tell a NSSlider/NSSliderCell to render as pressed + // is to start tracking. But this API has some weird behaviors that + // we have to account for. First of all, the pressed state will not + // be visually reflected unless we start tracking twice. And secondly + // if we don't track twice, the state of one render-pass will affect + // the render pass of other sliders, even if we set up the shared + // NSSlider with a new slider value. + [slider.cell startTrackingAt:pressPoint inView:slider]; [slider.cell startTrackingAt:pressPoint inView:slider]; } @@ -5360,7 +5342,7 @@ void QMacStyle::drawComplexControl(ComplexControl cc, const QStyleOptionComplex } #if 0 - // FIXME: Sadly, this part doesn't work. It seems to somehow polute the + // FIXME: Sadly, this part doesn't work. It seems to somehow pollute the // NSSlider's internal state and, when we need to use the "else" part, // the slider's frame is not in sync with its cell dimensions. const bool drawAllParts = drawKnob && drawBar && (!hasTicks || drawTicks); @@ -5377,8 +5359,6 @@ void QMacStyle::drawComplexControl(ComplexControl cc, const QStyleOptionComplex } else #endif { - if (!hasDoubleTicks) - fixStaleGeometry(slider); NSSliderCell *cell = slider.cell; const int numberOfTickMarks = slider.numberOfTickMarks; @@ -5437,8 +5417,12 @@ void QMacStyle::drawComplexControl(ComplexControl cc, const QStyleOptionComplex } }); - if (isPressed) + if (isPressed) { + // We stop twice to be on the safe side, even if one seems to be enough. + // See startTracking above for why we do this. [slider.cell stopTracking:pressPoint at:pressPoint inView:slider mouseIsUp:NO]; + [slider.cell stopTracking:pressPoint at:pressPoint inView:slider mouseIsUp:NO]; + } } break; #if QT_CONFIG(spinbox) @@ -5500,7 +5484,7 @@ void QMacStyle::drawComplexControl(ComplexControl cc, const QStyleOptionComplex const auto cw = QMacStylePrivate::CocoaControl(ct, cs); auto *cc = static_cast<NSControl *>(d->cocoaControl(cw)); cc.enabled = isEnabled; - QRectF frameRect = cw.adjustedControlFrame(combo->rect);; + QRectF frameRect = cw.adjustedControlFrame(combo->rect); if (cw.type == QMacStylePrivate::Button_PopupButton) { // Non-editable QComboBox auto *pb = static_cast<NSPopUpButton *>(cc); @@ -5573,16 +5557,7 @@ void QMacStyle::drawComplexControl(ComplexControl cc, const QStyleOptionComplex const auto frameAdjust = 1.0 / p->device()->devicePixelRatio(); const auto innerFrameRect = outerFrameRect.adjusted(frameAdjust, frameAdjust, -frameAdjust, 0); QPainterPath innerFramePath = d->windowPanelPath(innerFrameRect); - if (isActive) { - QLinearGradient g; - g.setStart(QPointF(0, 0)); - g.setFinalStop(QPointF(0, 2 * opt->rect.height())); - g.setColorAt(0, opt->palette.button().color()); - g.setColorAt(1, opt->palette.dark().color()); - p->fillPath(innerFramePath, g); - } else { - p->fillPath(innerFramePath, opt->palette.button()); - } + p->fillPath(innerFramePath, opt->palette.button()); if (titlebar->subControls & (SC_TitleBarCloseButton | SC_TitleBarMaxButton @@ -5645,8 +5620,8 @@ void QMacStyle::drawComplexControl(ComplexControl cc, const QStyleOptionComplex const bool rtl = groupBox.direction == Qt::RightToLeft; const int alignment = Qt::TextHideMnemonic | (rtl ? Qt::AlignRight : Qt::AlignLeft); const QFont savedFont = p->font(); - if (!flat) - p->setFont(d->smallSystemFont); + if (!flat && d->smallSystemFont) + p->setFont(*d->smallSystemFont); proxy()->drawItemText(p, rect, alignment, groupBox.palette, groupBox.state & State_Enabled, groupBox.text, QPalette::WindowText); if (!flat) p->setFont(savedFont); @@ -5656,7 +5631,7 @@ void QMacStyle::drawComplexControl(ComplexControl cc, const QStyleOptionComplex case CC_ToolButton: if (const QStyleOptionToolButton *tb = qstyleoption_cast<const QStyleOptionToolButton *>(opt)) { -#ifndef QT_NO_ACCESSIBILITY +#if QT_CONFIG(accessibility) if (QStyleHelper::hasAncestor(opt->styleObject, QAccessible::ToolBar)) { if (tb->subControls & SC_ToolButtonMenu) { QStyleOption arrowOpt = *tb; @@ -5682,7 +5657,7 @@ void QMacStyle::drawComplexControl(ComplexControl cc, const QStyleOptionComplex } proxy()->drawControl(CE_ToolButtonLabel, opt, p, widget); } else -#endif // QT_NO_ACCESSIBILITY +#endif // QT_CONFIG(accessibility) { auto bflags = tb->state; if (tb->subControls & SC_ToolButton) @@ -5929,7 +5904,7 @@ QRect QMacStyle::subControlRect(ComplexControl cc, const QStyleOptionComplex *op qreal controlsSpacing = lastButtonRect.right() + titleBarButtonSpacing; if (!titlebar->icon.isNull()) { const auto iconSize = proxy()->pixelMetric(PM_SmallIconSize); - const auto actualIconSize = titlebar->icon.actualSize(QSize(iconSize, iconSize)).width();; + const auto actualIconSize = titlebar->icon.actualSize(QSize(iconSize, iconSize)).width(); controlsSpacing += actualIconSize + titleBarIconTitleSpacing; } @@ -6010,7 +5985,9 @@ QRect QMacStyle::subControlRect(ComplexControl cc, const QStyleOptionComplex *op const int margin = flat || hasNoText ? 0 : 9; ret = groupBox->rect.adjusted(margin, 0, -margin, 0); - const QFontMetricsF fm = flat || fontIsSet ? QFontMetricsF(groupBox->fontMetrics) : QFontMetricsF(d->smallSystemFont); + const QFontMetricsF fm = flat || fontIsSet || !d->smallSystemFont + ? QFontMetricsF(groupBox->fontMetrics) + : QFontMetricsF(*d->smallSystemFont); const QSizeF s = fm.size(Qt::AlignHCenter | Qt::AlignVCenter, qt_mac_removeMnemonics(groupBox->text), 0, nullptr); const int tw = qCeil(s.width()); const int h = qCeil(fm.height()); @@ -6164,7 +6141,7 @@ QRect QMacStyle::subControlRect(ComplexControl cc, const QStyleOptionComplex *op case CC_ToolButton: ret = QCommonStyle::subControlRect(cc, opt, sc, widget); if (sc == SC_ToolButtonMenu) { -#ifndef QT_NO_ACCESSIBILITY +#if QT_CONFIG(accessibility) if (QStyleHelper::hasAncestor(opt->styleObject, QAccessible::ToolBar)) ret.adjust(-toolButtonArrowMargin, 0, 0, 0); #endif @@ -6320,14 +6297,16 @@ QSize QMacStyle::sizeFromContents(ContentsType ct, const QStyleOption *opt, break; #endif case QStyle::CT_PushButton: { - if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(opt)) + bool isFlat = false; + if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(opt)) { if (btn->features & QStyleOptionButton::CommandLinkButton) return QCommonStyle::sizeFromContents(ct, opt, sz, widget); + isFlat = btn->features & QStyleOptionButton::Flat; + } // By default, we fit the contents inside a normal rounded push button. // Do this by add enough space around the contents so that rounded // borders (including highlighting when active) will show. - // TODO Use QFocusFrame and get rid of these horrors. QSize macsz; const auto controlSize = d->effectiveAquaSizeConstrain(opt, widget, CT_PushButton, sz, &macsz); // FIXME See comment in CT_PushButton case in qt_aqua_get_known_size(). @@ -6338,12 +6317,24 @@ QSize QMacStyle::sizeFromContents(ContentsType ct, const QStyleOption *opt, // All values as measured from HIThemeGetButtonBackgroundBounds() if (controlSize != QStyleHelper::SizeMini) sz.rwidth() += 12; // We like 12 over here. - if (controlSize == QStyleHelper::SizeLarge && sz.height() > 16) - sz.rheight() += pushButtonDefaultHeight[QStyleHelper::SizeLarge] - 16; - else if (controlSize == QStyleHelper::SizeMini) - sz.setHeight(24); // FIXME Our previous HITheme-based logic returned this. - else - sz.setHeight(pushButtonDefaultHeight[controlSize]); + + if (controlSize == QStyleHelper::SizeLarge) { + if (!isFlat) + sz.rwidth() -= 12; + if (sz.height() > 16) + sz.rheight() += pushButtonDefaultHeight[QStyleHelper::SizeLarge] - 16; + else + sz.setHeight(pushButtonDefaultHeight[QStyleHelper::SizeLarge]); + } else { + if (!isFlat) + sz.rwidth() -= 10; + if (controlSize == QStyleHelper::SizeMini) + sz.setHeight(24); + else + sz.setHeight(pushButtonDefaultHeight[QStyleHelper::SizeSmall]); + } + if (isFlat) + sz.rwidth() -= 13; break; } case QStyle::CT_MenuItem: @@ -6414,9 +6405,9 @@ QSize QMacStyle::sizeFromContents(ContentsType ct, const QStyleOption *opt, if (const auto *cb = qstyleoption_cast<const QStyleOptionComboBox *>(opt)) { const auto controlSize = d->effectiveAquaSizeConstrain(opt, widget); if (!cb->editable) { - // Same as CT_PushButton, because we have to fit the focus + // See CT_PushButton; we have to fit the focus // ring and a non-editable combo box is a NSPopUpButton. - sz.rwidth() += QMacStylePrivate::PushButtonLeftOffset + QMacStylePrivate::PushButtonRightOffset + 12; + sz.rwidth() += QMacStylePrivate::PushButtonLeftOffset + QMacStylePrivate::PushButtonRightOffset + 24; // All values as measured from HIThemeGetButtonBackgroundBounds() if (controlSize != QStyleHelper::SizeMini) sz.rwidth() += 12; // We like 12 over here. @@ -6509,7 +6500,7 @@ QSize QMacStyle::sizeFromContents(ContentsType ct, const QStyleOption *opt, void QMacStyle::drawItemText(QPainter *p, const QRect &r, int flags, const QPalette &pal, bool enabled, const QString &text, QPalette::ColorRole textRole) const { - if(flags & Qt::TextShowMnemonic) + if (flags & Qt::TextShowMnemonic) flags |= Qt::TextHideMnemonic; QCommonStyle::drawItemText(p, r, flags, pal, enabled, text, textRole); } @@ -6517,8 +6508,8 @@ void QMacStyle::drawItemText(QPainter *p, const QRect &r, int flags, const QPale bool QMacStyle::event(QEvent *e) { Q_D(QMacStyle); - if(e->type() == QEvent::FocusIn) { - QWidget *f = 0; + if (e->type() == QEvent::FocusIn) { + QWidget *f = nullptr; QWidget *focusWidget = QApplication::focusWidget(); #if QT_CONFIG(graphicsview) if (QGraphicsView *graphicsView = qobject_cast<QGraphicsView *>(focusWidget)) { @@ -6540,14 +6531,14 @@ bool QMacStyle::event(QEvent *e) f = focusWidget; } if (f) { - if(!d->focusWidget) + if (!d->focusWidget) d->focusWidget = new QFocusFrame(f); d->focusWidget->setWidget(f); - } else if(d->focusWidget) { + } else if (d->focusWidget) { d->focusWidget->setWidget(0); } - } else if(e->type() == QEvent::FocusOut) { - if(d->focusWidget) + } else if (e->type() == QEvent::FocusOut) { + if (d->focusWidget) d->focusWidget->setWidget(0); } return false; @@ -6561,7 +6552,7 @@ QIcon QMacStyle::standardIcon(StandardPixmap standardIcon, const QStyleOption *o return QCommonStyle::standardIcon(standardIcon, opt, widget); case SP_ToolBarHorizontalExtensionButton: case SP_ToolBarVerticalExtensionButton: { - QPixmap pixmap(QLatin1String(":/qt-project.org/styles/macstyle/images/toolbar-ext.png")); + QPixmap pixmap(QLatin1String(":/qt-project.org/styles/macstyle/images/toolbar-ext-macstyle.png")); if (standardIcon == SP_ToolBarVerticalExtensionButton) { QPixmap pix2(pixmap.height(), pixmap.width()); pix2.setDevicePixelRatio(pixmap.devicePixelRatio()); diff --git a/src/plugins/styles/mac/qmacstyle_mac_p.h b/src/plugins/styles/mac/qmacstyle_mac_p.h index 88f104cccf..99c8d1fec4 100644 --- a/src/plugins/styles/mac/qmacstyle_mac_p.h +++ b/src/plugins/styles/mac/qmacstyle_mac_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWidgets module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// 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 QMACSTYLE_MAC_P_H #define QMACSTYLE_MAC_P_H @@ -77,28 +41,28 @@ public: void polish(QPalette &pal); void drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, QPainter *p, - const QWidget *w = 0) const; + const QWidget *w = nullptr) const; void drawControl(ControlElement element, const QStyleOption *opt, QPainter *p, - const QWidget *w = 0) const; - QRect subElementRect(SubElement r, const QStyleOption *opt, const QWidget *widget = 0) const; + const QWidget *w = nullptr) const; + QRect subElementRect(SubElement r, const QStyleOption *opt, const QWidget *widget = nullptr) const; void drawComplexControl(ComplexControl cc, const QStyleOptionComplex *opt, QPainter *p, - const QWidget *w = 0) const; + const QWidget *w = nullptr) const; SubControl hitTestComplexControl(ComplexControl cc, const QStyleOptionComplex *opt, - const QPoint &pt, const QWidget *w = 0) const; + const QPoint &pt, const QWidget *w = nullptr) const; QRect subControlRect(ComplexControl cc, const QStyleOptionComplex *opt, SubControl sc, - const QWidget *w = 0) const; + const QWidget *w = nullptr) const; QSize sizeFromContents(ContentsType ct, const QStyleOption *opt, - const QSize &contentsSize, const QWidget *w = 0) const; + const QSize &contentsSize, const QWidget *w = nullptr) const; - int pixelMetric(PixelMetric pm, const QStyleOption *opt = 0, const QWidget *widget = 0) const; + int pixelMetric(PixelMetric pm, const QStyleOption *opt = 0, const QWidget *widget = nullptr) const; QPalette standardPalette() const; - virtual int styleHint(StyleHint sh, const QStyleOption *opt = 0, const QWidget *w = 0, - QStyleHintReturn *shret = 0) const; + virtual int styleHint(StyleHint sh, const QStyleOption *opt = 0, const QWidget *w = nullptr, + QStyleHintReturn *shret = nullptr) const; QPixmap standardPixmap(StandardPixmap sp, const QStyleOption *opt, - const QWidget *widget = 0) const; + const QWidget *widget = nullptr) const; QPixmap generatedIconPixmap(QIcon::Mode iconMode, const QPixmap &pixmap, const QStyleOption *opt) const; @@ -108,11 +72,11 @@ public: bool event(QEvent *e); - QIcon standardIcon(StandardPixmap standardIcon, const QStyleOption *opt = 0, - const QWidget *widget = 0) const; + QIcon standardIcon(StandardPixmap standardIcon, const QStyleOption *opt = nullptr, + const QWidget *widget = nullptr) const; int layoutSpacing(QSizePolicy::ControlType control1, QSizePolicy::ControlType control2, - Qt::Orientation orientation, const QStyleOption *option = 0, - const QWidget *widget = 0) const; + Qt::Orientation orientation, const QStyleOption *option = nullptr, + const QWidget *widget = nullptr) const; private: Q_DISABLE_COPY_MOVE(QMacStyle) diff --git a/src/plugins/styles/mac/qmacstyle_mac_p_p.h b/src/plugins/styles/mac/qmacstyle_mac_p_p.h index 6305a0ea1f..0dcfce2f0c 100644 --- a/src/plugins/styles/mac/qmacstyle_mac_p_p.h +++ b/src/plugins/styles/mac/qmacstyle_mac_p_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWidgets module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// 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 QMACSTYLE_MAC_P_P_H @@ -48,7 +12,6 @@ #include <QtCore/qlist.h> #include <QtCore/qmap.h> #include <QtCore/qmath.h> -#include <QtCore/qpair.h> #include <QtCore/qpointer.h> #include <QtCore/qtextstream.h> @@ -298,8 +261,7 @@ public: mutable QHash<CocoaControl, NSView *> cocoaControls; mutable QHash<CocoaControl, NSCell *> cocoaCells; - QFont smallSystemFont; - QFont miniSystemFont; + std::optional<QFont> smallSystemFont; QMacKeyValueObserver appearanceObserver; }; diff --git a/src/plugins/styles/windowsvista/CMakeLists.txt b/src/plugins/styles/modernwindows/CMakeLists.txt index 224b7f1da1..985bce3a2d 100644 --- a/src/plugins/styles/windowsvista/CMakeLists.txt +++ b/src/plugins/styles/modernwindows/CMakeLists.txt @@ -1,27 +1,27 @@ -# Generated from windowsvista.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## QWindowsVistaStylePlugin Plugin: ##################################################################### -qt_internal_add_plugin(QWindowsVistaStylePlugin - OUTPUT_NAME qwindowsvistastyle - TYPE styles +qt_internal_add_plugin(QModernWindowsStylePlugin + OUTPUT_NAME qmodernwindowsstyle + PLUGIN_TYPE styles SOURCES main.cpp qwindowsvistastyle.cpp qwindowsvistastyle_p.h + qwindows11style.cpp qwindows11style_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 uxtheme - PUBLIC_LIBRARIES Qt::Core Qt::Gui + Qt::GuiPrivate Qt::WidgetsPrivate ) - -#### Keys ignored in scope 1:.:.:windowsvista.pro:<TRUE>: -# DISTFILES = "windowsvistastyle.json" diff --git a/src/plugins/styles/modernwindows/main.cpp b/src/plugins/styles/modernwindows/main.cpp new file mode 100644 index 0000000000..a4d8e60385 --- /dev/null +++ b/src/plugins/styles/modernwindows/main.cpp @@ -0,0 +1,36 @@ +// Copyright (C) 2017 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 <QtWidgets/private/qtwidgetsglobal_p.h> +#include <QtWidgets/qstyleplugin.h> +#include <QtCore/qoperatingsystemversion.h> +#include "qwindowsvistastyle_p.h" +#include "qwindows11style_p.h" + +QT_BEGIN_NAMESPACE + +class QModernWindowsStylePlugin : public QStylePlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QStyleFactoryInterface" FILE "modernwindowsstyles.json") +public: + QStyle *create(const QString &key) override; +}; + +QStyle *QModernWindowsStylePlugin::create(const QString &key) +{ + bool isWin11OrAbove = QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows11; + if (isWin11OrAbove && key.compare(QLatin1String("windows11"), Qt::CaseInsensitive) == 0) { + return new QWindows11Style(); + } else if (!isWin11OrAbove && key.compare(QLatin1String("windows11"), Qt::CaseInsensitive) == 0) { + qWarning("QWindows11Style: Style is only supported on Windows11 and above"); + return new QWindowsVistaStyle(); + } else if (key.compare(QLatin1String("windowsvista"), Qt::CaseInsensitive) == 0) { + return new QWindowsVistaStyle(); + } + return nullptr; +} + +QT_END_NAMESPACE + +#include "main.moc" diff --git a/src/plugins/styles/modernwindows/modernwindowsstyles.json b/src/plugins/styles/modernwindows/modernwindowsstyles.json new file mode 100644 index 0000000000..e9cef558e8 --- /dev/null +++ b/src/plugins/styles/modernwindows/modernwindowsstyles.json @@ -0,0 +1,3 @@ +{ + "Keys": [ "windowsvista", "windows11" ] +} diff --git a/src/plugins/styles/modernwindows/qwindows11style.cpp b/src/plugins/styles/modernwindows/qwindows11style.cpp new file mode 100644 index 0000000000..63663c74aa --- /dev/null +++ b/src/plugins/styles/modernwindows/qwindows11style.cpp @@ -0,0 +1,2174 @@ +// 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 "qwindows11style_p.h" +#include <qstylehints.h> +#include <private/qstyleanimation_p.h> +#include <private/qstylehelper_p.h> +#include <private/qapplication_p.h> +#include <qstyleoption.h> +#include <qpainter.h> +#include <QGraphicsDropShadowEffect> +#include <QLatin1StringView> +#include <QtWidgets/qcombobox.h> +#include <QtWidgets/qcommandlinkbutton.h> +#include <QtWidgets/qgraphicsview.h> +#include <QtWidgets/qlistview.h> +#include <QtWidgets/qmenu.h> +#if QT_CONFIG(mdiarea) +#include <QtWidgets/qmdiarea.h> +#endif +#include <QtWidgets/qtextedit.h> +#include <QtWidgets/qtreeview.h> + +#include "qdrawutil.h" +#include <chrono> + +QT_BEGIN_NAMESPACE + +const static int topLevelRoundingRadius = 8; //Radius for toplevel items like popups for round corners +const static int secondLevelRoundingRadius = 4; //Radius for second level items like hovered menu item round corners +constexpr QLatin1StringView originalWidthProperty("_q_windows11_style_original_width"); + +enum WINUI3Color { + subtleHighlightColor, //Subtle highlight based on alpha used for hovered elements + subtlePressedColor, //Subtle highlight based on alpha used for pressed elements + frameColorLight, //Color of frame around flyouts and controls except for Checkbox and Radiobutton + frameColorStrong, //Color of frame around Checkbox and Radiobuttons + controlStrongFill, //Color of controls with strong filling such as the right side of a slider + controlStrokeSecondary, + controlStrokePrimary, + controlFillTertiary, //Color of filled sunken controls + controlFillSecondary, //Color of filled hovered controls + menuPanelFill, //Color of menu panel + textOnAccentPrimary, //Color of text on controls filled in accent color + textOnAccentSecondary, //Color of text of sunken controls in accent color + controlTextSecondary, //Color of text of sunken controls + controlStrokeOnAccentSecondary, //Color of frame around Buttons in accent color + controlFillSolid, //Color for solid fill + surfaceStroke, //Color of MDI window frames +}; + +const static QColor WINUI3ColorsLight [] { + QColor(0x00,0x00,0x00,0x09), //subtleHighlightColor + QColor(0x00,0x00,0x00,0x06), //subtlePressedColor + QColor(0x00,0x00,0x00,0x0F), //frameColorLight + QColor(0x00,0x00,0x00,0x9c), //frameColorStrong + QColor(0x00,0x00,0x00,0x72), //controlStrongFill + QColor(0x00,0x00,0x00,0x29), //controlStrokeSecondary + QColor(0x00,0x00,0x00,0x14), //controlStrokePrimary + QColor(0xF9,0xF9,0xF9,0x00), //controlFillTertiary + QColor(0xF9,0xF9,0xF9,0x80), //controlFillSecondary + QColor(0xFF,0xFF,0xFF,0xFF), //menuPanelFill + QColor(0xFF,0xFF,0xFF,0xFF), //textOnAccentPrimary + QColor(0xFF,0xFF,0xFF,0x7F), //textOnAccentSecondary + QColor(0x00,0x00,0x00,0x7F), //controlTextSecondary + QColor(0x00,0x00,0x00,0x66), //controlStrokeOnAccentSecondary + QColor(0xFF,0xFF,0xFF,0xFF), //controlFillSolid + QColor(0x75,0x75,0x75,0x66), //surfaceStroke +}; + +const static QColor WINUI3ColorsDark[] { + QColor(0xFF,0xFF,0xFF,0x0F), //subtleHighlightColor + QColor(0xFF,0xFF,0xFF,0x0A), //subtlePressedColor + QColor(0xFF,0xFF,0xFF,0x12), //frameColorLight + QColor(0xFF,0xFF,0xFF,0x8B), //frameColorStrong + QColor(0xFF,0xFF,0xFF,0x8B), //controlStrongFill + QColor(0xFF,0xFF,0xFF,0x18), //controlStrokeSecondary + QColor(0xFF,0xFF,0xFF,0x12), //controlStrokePrimary + QColor(0xF9,0xF9,0xF9,0x00), //controlFillTertiary + QColor(0xF9,0xF9,0xF9,0x80), //controlFillSecondary + QColor(0x0F,0x0F,0x0F,0xFF), //menuPanelFill + QColor(0x00,0x00,0x00,0xFF), //textOnAccentPrimary + QColor(0x00,0x00,0x00,0x80), //textOnAccentSecondary + QColor(0xFF,0xFF,0xFF,0x87), //controlTextSecondary + QColor(0xFF,0xFF,0xFF,0x14), //controlStrokeOnAccentSecondary + QColor(0x45,0x45,0x45,0xFF), //controlFillSolid + QColor(0x75,0x75,0x75,0x66), //surfaceStroke +}; + +const static QColor* WINUI3Colors[] { + WINUI3ColorsLight, + WINUI3ColorsDark +}; + +const QColor shellCloseButtonColor(0xC4,0x2B,0x1C,0xFF); //Color of close Button in Titlebar + +#if QT_CONFIG(toolbutton) +static void drawArrow(const QStyle *style, const QStyleOptionToolButton *toolbutton, + const QRect &rect, QPainter *painter, const QWidget *widget = nullptr) +{ + QStyle::PrimitiveElement pe; + switch (toolbutton->arrowType) { + case Qt::LeftArrow: + pe = QStyle::PE_IndicatorArrowLeft; + break; + case Qt::RightArrow: + pe = QStyle::PE_IndicatorArrowRight; + break; + case Qt::UpArrow: + pe = QStyle::PE_IndicatorArrowUp; + break; + case Qt::DownArrow: + pe = QStyle::PE_IndicatorArrowDown; + break; + default: + return; + } + QStyleOption arrowOpt = *toolbutton; + arrowOpt.rect = rect; + style->drawPrimitive(pe, &arrowOpt, painter, widget); +} +#endif // QT_CONFIG(toolbutton) +/*! + \class QWindows11Style + \brief The QWindows11Style class provides a look and feel suitable for applications on Microsoft Windows 11. + \since 6.6 + \ingroup appearance + \inmodule QtWidgets + \internal + + \warning This style is only available on the Windows 11 platform and above. + + \sa QWindows11Style QWindowsVistaStyle, QMacStyle, QFusionStyle +*/ + +/*! + Constructs a QWindows11Style object. +*/ +QWindows11Style::QWindows11Style() : QWindowsVistaStyle(*new QWindows11StylePrivate) +{ + highContrastTheme = QGuiApplicationPrivate::styleHints->colorScheme() == Qt::ColorScheme::Unknown; + colorSchemeIndex = QGuiApplicationPrivate::styleHints->colorScheme() == Qt::ColorScheme::Light ? 0 : 1; +} + +/*! + \internal + Constructs a QWindows11Style object. +*/ +QWindows11Style::QWindows11Style(QWindows11StylePrivate &dd) : QWindowsVistaStyle(dd) +{ + highContrastTheme = QGuiApplicationPrivate::styleHints->colorScheme() == Qt::ColorScheme::Unknown; + colorSchemeIndex = QGuiApplicationPrivate::styleHints->colorScheme() == Qt::ColorScheme::Light ? 0 : 1; +} + +/*! + Destructor. +*/ +QWindows11Style::~QWindows11Style() = default; + +/*! + \internal + see drawPrimitive for comments on the animation support + + */ +void QWindows11Style::drawComplexControl(ComplexControl control, const QStyleOptionComplex *option, + QPainter *painter, const QWidget *widget) const +{ + QWindows11StylePrivate *d = const_cast<QWindows11StylePrivate*>(d_func()); + + State state = option->state; + SubControls sub = option->subControls; + State flags = option->state; + if (widget && widget->testAttribute(Qt::WA_UnderMouse) && widget->isActiveWindow()) + flags |= State_MouseOver; + + painter->save(); + painter->setRenderHint(QPainter::Antialiasing); + if (d->transitionsEnabled()) { + if (control == CC_Slider) { + if (const auto *slider = qstyleoption_cast<const QStyleOptionSlider *>(option)) { + QObject *styleObject = option->styleObject; // Can be widget or qquickitem + + QRectF thumbRect = proxy()->subControlRect(CC_Slider, option, SC_SliderHandle, widget); + auto center = thumbRect.center(); + const qreal outerRadius = qMin(8.0, (slider->orientation == Qt::Horizontal ? thumbRect.height() / 2.0 : thumbRect.width() / 2.0) - 1); + + thumbRect.setWidth(outerRadius); + thumbRect.setHeight(outerRadius); + thumbRect.moveCenter(center); + QPointF cursorPos = widget ? widget->mapFromGlobal(QCursor::pos()) : QPointF(); + bool isInsideHandle = thumbRect.contains(cursorPos); + + bool oldIsInsideHandle = styleObject->property("_q_insidehandle").toBool(); + int oldState = styleObject->property("_q_stylestate").toInt(); + int oldActiveControls = styleObject->property("_q_stylecontrols").toInt(); + + QRectF oldRect = styleObject->property("_q_stylerect").toRect(); + styleObject->setProperty("_q_insidehandle", isInsideHandle); + styleObject->setProperty("_q_stylestate", int(option->state)); + styleObject->setProperty("_q_stylecontrols", int(option->activeSubControls)); + styleObject->setProperty("_q_stylerect", option->rect); + if (option->styleObject->property("_q_end_radius").isNull()) + option->styleObject->setProperty("_q_end_radius", outerRadius * 0.43); + + bool doTransition = (((state & State_Sunken) != (oldState & State_Sunken) + || ((oldIsInsideHandle) != (isInsideHandle)) + || oldActiveControls != int(option->activeSubControls)) + && state & State_Enabled); + + if (oldRect != option->rect) { + doTransition = false; + d->stopAnimation(styleObject); + styleObject->setProperty("_q_inner_radius", outerRadius * 0.43); + } + + if (doTransition) { + QNumberStyleAnimation *t = new QNumberStyleAnimation(styleObject); + t->setStartValue(styleObject->property("_q_inner_radius").toFloat()); + if (state & State_Sunken) + t->setEndValue(outerRadius * 0.29); + else if (isInsideHandle) + t->setEndValue(outerRadius * 0.71); + else + t->setEndValue(outerRadius * 0.43); + + styleObject->setProperty("_q_end_radius", t->endValue()); + + t->setStartTime(d->animationTime()); + t->setDuration(150); + d->startAnimation(t); + } + } + } + } + + switch (control) { +#if QT_CONFIG(spinbox) + case CC_SpinBox: + if (const QStyleOptionSpinBox *sb = qstyleoption_cast<const QStyleOptionSpinBox *>(option)) { + if (sb->frame && (sub & SC_SpinBoxFrame)) { + painter->save(); + QRegion clipRegion = option->rect; + clipRegion -= option->rect.adjusted(2, 2, -2, -2); + painter->setClipRegion(clipRegion); + QColor lineColor = state & State_HasFocus ? option->palette.accent().color() : QColor(0,0,0); + painter->setPen(QPen(lineColor)); + painter->drawLine(option->rect.bottomLeft() + QPointF(7,-0.5), option->rect.bottomRight() + QPointF(-7,-0.5)); + painter->restore(); + } + QRectF frameRect = option->rect; + frameRect.adjust(0.5,0.5,-0.5,-0.5); + QBrush fillColor = option->palette.brush(QPalette::Base); + painter->setBrush(fillColor); + painter->setPen(QPen(highContrastTheme == true ? sb->palette.buttonText().color() : WINUI3Colors[colorSchemeIndex][frameColorLight])); + painter->drawRoundedRect(frameRect.adjusted(2,2,-2,-2), secondLevelRoundingRadius, secondLevelRoundingRadius); + QPoint mousePos = widget ? widget->mapFromGlobal(QCursor::pos()) : QPoint(); + QColor hoverColor = WINUI3Colors[colorSchemeIndex][subtleHighlightColor]; + if (sub & SC_SpinBoxEditField) { + QRect rect = proxy()->subControlRect(CC_SpinBox, option, SC_SpinBoxEditField, widget).adjusted(0, 0, 0, 1); + if (rect.contains(mousePos) && !(state & State_HasFocus)) { + QBrush fillColor = QBrush(WINUI3Colors[colorSchemeIndex][subtleHighlightColor]); + painter->setBrush(fillColor); + painter->setPen(Qt::NoPen); + painter->drawRoundedRect(option->rect.adjusted(2,2,-2,-2), secondLevelRoundingRadius, secondLevelRoundingRadius); + } + } + if (sub & SC_SpinBoxUp) { + QRect rect = proxy()->subControlRect(CC_SpinBox, option, SC_SpinBoxUp, widget).adjusted(0, 0, 0, 1); + float scale = rect.width() >= 16 ? 1.0 : rect.width()/16.0; + if (rect.contains(mousePos)) { + painter->setPen(Qt::NoPen); + painter->setBrush(QBrush(hoverColor)); + painter->drawRoundedRect(rect.adjusted(1,1,-1,-1),secondLevelRoundingRadius, secondLevelRoundingRadius); + } + painter->save(); + painter->translate(rect.center()); + painter->scale(scale,scale); + painter->translate(-rect.center()); + painter->setFont(assetFont); + painter->setPen(sb->palette.buttonText().color()); + painter->setBrush(Qt::NoBrush); + painter->drawText(rect,"\uE018", Qt::AlignVCenter | Qt::AlignHCenter); + painter->restore(); + } + if (sub & SC_SpinBoxDown) { + QRect rect = proxy()->subControlRect(CC_SpinBox, option, SC_SpinBoxDown, widget); + float scale = rect.width() >= 16 ? 1.0 : rect.width()/16.0; + if (rect.contains(mousePos)) { + painter->setPen(Qt::NoPen); + painter->setBrush(QBrush(hoverColor)); + painter->drawRoundedRect(rect.adjusted(1,1,-1,-1), secondLevelRoundingRadius, secondLevelRoundingRadius); + } + painter->save(); + painter->translate(rect.center()); + painter->scale(scale,scale); + painter->translate(-rect.center()); + painter->setFont(assetFont); + painter->setPen(sb->palette.buttonText().color()); + painter->setBrush(Qt::NoBrush); + painter->drawText(rect,"\uE019", Qt::AlignVCenter | Qt::AlignHCenter); + painter->restore(); + } + } + break; +#endif // QT_CONFIG(spinbox) +#if QT_CONFIG(slider) + case CC_Slider: + if (const auto *slider = qstyleoption_cast<const QStyleOptionSlider *>(option)) { + QRectF slrect = slider->rect; + QRegion tickreg = slrect.toRect(); + + if (sub & SC_SliderGroove) { + QRectF rect = proxy()->subControlRect(CC_Slider, option, SC_SliderGroove, widget); + QRectF handleRect = proxy()->subControlRect(CC_Slider, option, SC_SliderHandle, widget); + QPointF handlePos = handleRect.center(); + QRectF leftRect; + QRectF rightRect; + + if (slider->orientation == Qt::Horizontal) { + rect = QRect(slrect.left(), rect.center().y() - 2, slrect.width() - 5, 4); + leftRect = QRect(rect.left(), rect.top(), (handlePos.x() - rect.left()), rect.height()); + rightRect = QRect(handlePos.x(), rect.top(), (rect.width() - handlePos.x()), rect.height()); + } else { + rect = QRect(rect.center().x() - 2, slrect.top(), 4, slrect.height() - 5); + rightRect = QRect(rect.left(), rect.top(), rect.width(), (handlePos.y() - rect.top())); + leftRect = QRect(rect.left(), handlePos.y(), rect.width(), (rect.height() - handlePos.y())); + } + + painter->setPen(Qt::NoPen); + painter->setBrush(option->palette.accent()); + painter->drawRoundedRect(leftRect,1,1); + painter->setBrush(QBrush(WINUI3Colors[colorSchemeIndex][controlStrongFill])); + painter->drawRoundedRect(rightRect,1,1); + + painter->setPen(QPen(highContrastTheme == true ? slider->palette.buttonText().color() : WINUI3Colors[colorSchemeIndex][frameColorLight])); + painter->setBrush(Qt::NoBrush); + painter->drawRoundedRect(leftRect,1.5,1.5); + painter->drawRoundedRect(rightRect,1.5,1.5); + + tickreg -= rect.toRect(); + } + 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(slider->palette.text().color()); + QVarLengthArray<QLine, 32> 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) { + if (const auto *slider = qstyleoption_cast<const QStyleOptionSlider *>(option)) { + const QRectF rect = proxy()->subControlRect(CC_Slider, option, SC_SliderHandle, widget); + const QPointF center = rect.center(); + + const QNumberStyleAnimation* animation = qobject_cast<QNumberStyleAnimation*>(d->animation(option->styleObject)); + + if (animation != nullptr) + option->styleObject->setProperty("_q_inner_radius", animation->currentValue()); + else + option->styleObject->setProperty("_q_inner_radius", option->styleObject->property("_q_end_radius")); + + const qreal outerRadius = qMin(8.0,(slider->orientation == Qt::Horizontal ? rect.height() / 2.0 : rect.width() / 2.0) - 1); + const float innerRadius = option->styleObject->property("_q_inner_radius").toFloat(); + painter->setRenderHint(QPainter::Antialiasing, true); + painter->setPen(Qt::NoPen); + painter->setBrush(QBrush(WINUI3Colors[colorSchemeIndex][controlFillSolid])); + painter->drawEllipse(center, outerRadius, outerRadius); + painter->setBrush(option->palette.accent()); + painter->drawEllipse(center, innerRadius, innerRadius); + + painter->setPen(QPen(WINUI3Colors[colorSchemeIndex][controlStrokeSecondary])); + painter->setBrush(Qt::NoBrush); + painter->drawEllipse(center, outerRadius + 0.5, outerRadius + 0.5); + painter->drawEllipse(center, innerRadius + 0.5, innerRadius + 0.5); + } + } + 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(combobox) + case CC_ComboBox: + if (const QStyleOptionComboBox *combobox = qstyleoption_cast<const QStyleOptionComboBox *>(option)) { + QBrush fillColor = combobox->palette.brush(QPalette::Base); + QRectF rect = option->rect.adjusted(2,2,-2,-2); + painter->setBrush(fillColor); + painter->setPen(Qt::NoPen); + painter->drawRoundedRect(rect, secondLevelRoundingRadius, secondLevelRoundingRadius); + + // In case the QComboBox is hovered overdraw the background with a alpha mask to + // highlight the QComboBox. + if (state & State_MouseOver) { + fillColor = QBrush(WINUI3Colors[colorSchemeIndex][subtleHighlightColor]); + painter->setBrush(fillColor); + painter->setPen(Qt::NoPen); + painter->drawRoundedRect(rect, secondLevelRoundingRadius, secondLevelRoundingRadius); + } + + rect.adjust(0.5,0.5,-0.5,-0.5); + painter->setBrush(Qt::NoBrush); + painter->setPen(highContrastTheme == true ? combobox->palette.buttonText().color() : WINUI3Colors[colorSchemeIndex][frameColorLight]); + painter->drawRoundedRect(rect, secondLevelRoundingRadius, secondLevelRoundingRadius); + if (sub & SC_ComboBoxArrow) { + QRectF rect = proxy()->subControlRect(CC_ComboBox, option, SC_ComboBoxArrow, widget).adjusted(-4, 0, -4, 1); + painter->setFont(assetFont); + painter->setPen(combobox->palette.text().color()); + painter->drawText(rect,"\uE019", Qt::AlignVCenter | Qt::AlignHCenter); + } + if (combobox->editable) { + QColor lineColor = state & State_HasFocus ? option->palette.accent().color() : QColor(0,0,0); + painter->setPen(QPen(lineColor)); + painter->drawLine(rect.bottomLeft() + QPoint(2,1), rect.bottomRight() + QPoint(-2,1)); + if (state & State_HasFocus) + painter->drawLine(rect.bottomLeft() + QPoint(3,2), rect.bottomRight() + QPoint(-3,2)); + } + } + break; +#endif // QT_CONFIG(combobox) + case QStyle::CC_ScrollBar: + if (const QStyleOptionSlider *scrollbar = qstyleoption_cast<const QStyleOptionSlider *>(option)) { + QRectF rect = scrollbar->rect; + QPointF center = rect.center(); + + if (scrollbar->orientation == Qt::Vertical && rect.width()>24) + rect.marginsRemoved(QMargins(0,2,2,2)); + else if (scrollbar->orientation == Qt::Horizontal && rect.height()>24) + rect.marginsRemoved(QMargins(2,0,2,2)); + + if (state & State_MouseOver) { + if (scrollbar->orientation == Qt::Vertical && rect.width()>24) + rect.setWidth(rect.width()/2); + else if (scrollbar->orientation == Qt::Horizontal && rect.height()>24) + rect.setHeight(rect.height()/2); + rect.moveCenter(center); + painter->setBrush(scrollbar->palette.base()); + painter->setPen(Qt::NoPen); + painter->drawRoundedRect(rect, topLevelRoundingRadius, topLevelRoundingRadius); + + painter->setBrush(Qt::NoBrush); + painter->setPen(WINUI3Colors[colorSchemeIndex][frameColorLight]); + painter->drawRoundedRect(rect.marginsRemoved(QMarginsF(0.5,0.5,0.5,0.5)), topLevelRoundingRadius + 0.5, topLevelRoundingRadius + 0.5); + } + if (sub & SC_ScrollBarSlider) { + QRectF rect = proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarSlider, widget); + QPointF center = rect.center(); + if (flags & State_MouseOver) { + if (scrollbar->orientation == Qt::Vertical) + rect.setWidth(rect.width()/2); + else + rect.setHeight(rect.height()/2); + } + else { + if (scrollbar->orientation == Qt::Vertical) + rect.setWidth(1); + else + rect.setHeight(1); + + } + rect.moveCenter(center); + painter->setBrush(Qt::gray); + painter->setPen(Qt::NoPen); + painter->drawRoundedRect(rect, secondLevelRoundingRadius, secondLevelRoundingRadius); + } + if (sub & SC_ScrollBarAddLine) { + QRectF rect = proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarAddLine, widget); + if (flags & State_MouseOver) { + painter->setFont(QFont("Segoe Fluent Icons",6)); + painter->setPen(Qt::gray); + if (scrollbar->orientation == Qt::Vertical) + painter->drawText(rect,"\uEDDC", Qt::AlignVCenter | Qt::AlignHCenter); + else + painter->drawText(rect,"\uEDDA", Qt::AlignVCenter | Qt::AlignHCenter); + } + } + if (sub & SC_ScrollBarSubLine) { + QRectF rect = proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarSubLine, widget); + if (flags & State_MouseOver) { + painter->setPen(Qt::gray); + if (scrollbar->orientation == Qt::Vertical) + painter->drawText(rect,"\uEDDB", Qt::AlignVCenter | Qt::AlignHCenter); + else + painter->drawText(rect,"\uEDD9", Qt::AlignVCenter | Qt::AlignHCenter); + } + } + } + break; + case CC_TitleBar: + if (const auto* titlebar = qstyleoption_cast<const QStyleOptionTitleBar*>(option)) { + painter->setPen(Qt::NoPen); + painter->setPen(QPen(WINUI3Colors[colorSchemeIndex][surfaceStroke])); + painter->setBrush(titlebar->palette.button()); + painter->drawRect(titlebar->rect); + + // draw title + QRect textRect = proxy()->subControlRect(CC_TitleBar, titlebar, SC_TitleBarLabel, widget); + painter->setPen(titlebar->palette.text().color()); + // Note workspace also does elliding but it does not use the correct font + QString title = painter->fontMetrics().elidedText(titlebar->text, Qt::ElideRight, textRect.width() - 14); + painter->drawText(textRect.adjusted(1, 1, 1, 1), title, QTextOption(Qt::AlignHCenter | Qt::AlignVCenter)); + + QFont buttonFont = QFont(assetFont); + buttonFont.setPointSize(8); + // min button + if ((titlebar->subControls & SC_TitleBarMinButton) && (titlebar->titleBarFlags & Qt::WindowMinimizeButtonHint) && + !(titlebar->titleBarState& Qt::WindowMinimized)) { + const QRect minButtonRect = proxy()->subControlRect(CC_TitleBar, titlebar, SC_TitleBarMinButton, widget); + if (minButtonRect.isValid()) { + bool hover = (titlebar->activeSubControls & SC_TitleBarMinButton) && (titlebar->state & State_MouseOver); + if (hover) + painter->fillRect(minButtonRect,WINUI3Colors[colorSchemeIndex][subtleHighlightColor]); + const QString textToDraw("\uE921"); + painter->setPen(QPen(titlebar->palette.text().color())); + painter->setFont(buttonFont); + painter->drawText(minButtonRect, Qt::AlignVCenter | Qt::AlignHCenter, textToDraw); + } + } + // max button + if ((titlebar->subControls & SC_TitleBarMaxButton) && (titlebar->titleBarFlags & Qt::WindowMaximizeButtonHint) && + !(titlebar->titleBarState & Qt::WindowMaximized)) { + const QRectF maxButtonRect = proxy()->subControlRect(CC_TitleBar, titlebar, SC_TitleBarMaxButton, widget); + if (maxButtonRect.isValid()) { + bool hover = (titlebar->activeSubControls & SC_TitleBarMaxButton) && (titlebar->state & State_MouseOver); + if (hover) + painter->fillRect(maxButtonRect,WINUI3Colors[colorSchemeIndex][subtleHighlightColor]); + const QString textToDraw("\uE922"); + painter->setPen(QPen(titlebar->palette.text().color())); + painter->setFont(buttonFont); + painter->drawText(maxButtonRect, Qt::AlignVCenter | Qt::AlignHCenter, textToDraw); + } + } + + // close button + if ((titlebar->subControls & SC_TitleBarCloseButton) && (titlebar->titleBarFlags & Qt::WindowSystemMenuHint)) { + const QRect closeButtonRect = proxy()->subControlRect(CC_TitleBar, titlebar, SC_TitleBarCloseButton, widget); + if (closeButtonRect.isValid()) { + bool hover = (titlebar->activeSubControls & SC_TitleBarCloseButton) && (titlebar->state & State_MouseOver); + if (hover) + painter->fillRect(closeButtonRect,shellCloseButtonColor); + const QString textToDraw("\uE8BB"); + painter->setPen(QPen(hover ? titlebar->palette.highlightedText().color() : titlebar->palette.text().color())); + painter->setFont(buttonFont); + painter->drawText(closeButtonRect, Qt::AlignVCenter | Qt::AlignHCenter, textToDraw); + } + } + + // normalize button + if ((titlebar->subControls & SC_TitleBarNormalButton) && + (((titlebar->titleBarFlags & Qt::WindowMinimizeButtonHint) && + (titlebar->titleBarState & Qt::WindowMinimized)) || + ((titlebar->titleBarFlags & Qt::WindowMaximizeButtonHint) && + (titlebar->titleBarState & Qt::WindowMaximized)))) { + const QRect normalButtonRect = proxy()->subControlRect(CC_TitleBar, titlebar, SC_TitleBarNormalButton, widget); + if (normalButtonRect.isValid()) { + bool hover = (titlebar->activeSubControls & SC_TitleBarNormalButton) && (titlebar->state & State_MouseOver); + if (hover) + painter->fillRect(normalButtonRect,WINUI3Colors[colorSchemeIndex][subtleHighlightColor]); + const QString textToDraw("\uE923"); + painter->setPen(QPen(titlebar->palette.text().color())); + painter->setFont(buttonFont); + painter->drawText(normalButtonRect, Qt::AlignVCenter | Qt::AlignHCenter, textToDraw); + } + } + + // context help button + if (titlebar->subControls & SC_TitleBarContextHelpButton + && (titlebar->titleBarFlags & Qt::WindowContextHelpButtonHint)) { + const QRect contextHelpButtonRect = proxy()->subControlRect(CC_TitleBar, titlebar, SC_TitleBarContextHelpButton, widget); + if (contextHelpButtonRect.isValid()) { + bool hover = (titlebar->activeSubControls & SC_TitleBarCloseButton) && (titlebar->state & State_MouseOver); + if (hover) + painter->fillRect(contextHelpButtonRect,WINUI3Colors[colorSchemeIndex][subtleHighlightColor]); + const QString textToDraw("\uE897"); + painter->setPen(QPen(titlebar->palette.text().color())); + painter->setFont(buttonFont); + painter->drawText(contextHelpButtonRect, Qt::AlignVCenter | Qt::AlignHCenter, textToDraw); + } + } + + // shade button + if (titlebar->subControls & SC_TitleBarShadeButton && (titlebar->titleBarFlags & Qt::WindowShadeButtonHint)) { + const QRect shadeButtonRect = proxy()->subControlRect(CC_TitleBar, titlebar, SC_TitleBarShadeButton, widget); + if (shadeButtonRect.isValid()) { + bool hover = (titlebar->activeSubControls & SC_TitleBarShadeButton) && (titlebar->state & State_MouseOver); + if (hover) + painter->fillRect(shadeButtonRect,WINUI3Colors[colorSchemeIndex][subtleHighlightColor]); + const QString textToDraw("\uE010"); + painter->setPen(QPen(titlebar->palette.text().color())); + painter->setFont(buttonFont); + painter->drawText(shadeButtonRect, Qt::AlignVCenter | Qt::AlignHCenter, textToDraw); + } + } + + // unshade button + if (titlebar->subControls & SC_TitleBarUnshadeButton && (titlebar->titleBarFlags & Qt::WindowShadeButtonHint)) { + const QRect unshadeButtonRect = proxy()->subControlRect(CC_TitleBar, titlebar, SC_TitleBarUnshadeButton, widget); + if (unshadeButtonRect.isValid()) { + bool hover = (titlebar->activeSubControls & SC_TitleBarUnshadeButton) && (titlebar->state & State_MouseOver); + if (hover) + painter->fillRect(unshadeButtonRect,WINUI3Colors[colorSchemeIndex][subtleHighlightColor]); + const QString textToDraw("\uE011"); + painter->setPen(QPen(titlebar->palette.text().color())); + painter->setFont(buttonFont); + painter->drawText(unshadeButtonRect, Qt::AlignVCenter | Qt::AlignHCenter, textToDraw); + } + } + + // window icon for system menu + if ((titlebar->subControls & SC_TitleBarSysMenu) && (titlebar->titleBarFlags & Qt::WindowSystemMenuHint)) { + const QRect iconRect = proxy()->subControlRect(CC_TitleBar, titlebar, SC_TitleBarSysMenu, widget); + if (iconRect.isValid()) { + if (!titlebar->icon.isNull()) { + titlebar->icon.paint(painter, iconRect); + } else { + QStyleOption tool = *titlebar; + QPixmap pm = proxy()->standardIcon(SP_TitleBarMenuButton, &tool, widget).pixmap(16, 16); + tool.rect = iconRect; + painter->save(); + proxy()->drawItemPixmap(painter, iconRect, Qt::AlignCenter, pm); + painter->restore(); + } + } + } + } + break; + default: + QWindowsVistaStyle::drawComplexControl(control, option, painter, widget); + } + painter->restore(); +} + +void QWindows11Style::drawPrimitive(PrimitiveElement element, const QStyleOption *option, + QPainter *painter, + const QWidget *widget) const { + QWindows11StylePrivate *d = const_cast<QWindows11StylePrivate*>(d_func()); + + int state = option->state; + painter->save(); + painter->setRenderHint(QPainter::Antialiasing); + if (d->transitionsEnabled() && (element == PE_IndicatorCheckBox || element == PE_IndicatorRadioButton)) { + QObject *styleObject = option->styleObject; // Can be widget or qquickitem + if (styleObject) { + int oldState = styleObject->property("_q_stylestate").toInt(); + styleObject->setProperty("_q_stylestate", int(option->state)); + styleObject->setProperty("_q_stylerect", option->rect); + bool doTransition = (((state & State_Sunken) != (oldState & State_Sunken) + || ((state & State_MouseOver) != (oldState & State_MouseOver)) + || (state & State_On) != (oldState & State_On)) + && state & State_Enabled); + if (doTransition) { + if (element == PE_IndicatorRadioButton) { + QNumberStyleAnimation *t = new QNumberStyleAnimation(styleObject); + t->setStartValue(styleObject->property("_q_inner_radius").toFloat()); + t->setEndValue(7.0f); + if (option->state & State_Sunken) + t->setEndValue(2.0f); + else if (option->state & State_MouseOver && !(option->state & State_On)) + t->setEndValue(7.0f); + else if (option->state & State_MouseOver && (option->state & State_On)) + t->setEndValue(5.0f); + else if (option->state & State_On) + t->setEndValue(4.0f); + styleObject->setProperty("_q_end_radius", t->endValue()); + t->setStartTime(d->animationTime()); + t->setDuration(150); + d->startAnimation(t); + } + else if (element == PE_IndicatorCheckBox) { + if ((oldState & State_Off && state & State_On) || (oldState & State_NoChange && state & State_On)) { + QNumberStyleAnimation *t = new QNumberStyleAnimation(styleObject); + t->setStartValue(0.0f); + t->setEndValue(1.0f); + t->setStartTime(d->animationTime()); + t->setDuration(150); + d->startAnimation(t); + } + } + } + } + } else if (!d->transitionsEnabled() && element == PE_IndicatorRadioButton) { + QObject *styleObject = option->styleObject; // Can be widget or qquickitem + if (styleObject) { + styleObject->setProperty("_q_end_radius",7.0); + if (option->state & State_Sunken) + styleObject->setProperty("_q_end_radius",2.0); + else if (option->state & State_MouseOver && !(option->state & State_On)) + styleObject->setProperty("_q_end_radius",7.0); + else if (option->state & State_MouseOver && (option->state & State_On)) + styleObject->setProperty("_q_end_radius",5.0); + else if (option->state & State_On) + styleObject->setProperty("_q_end_radius",4.0); + } + } + + switch (element) { + case PE_PanelTipLabel: { + QRectF tipRect = option->rect.marginsRemoved(QMargins(1,1,1,1)); + painter->setPen(Qt::NoPen); + painter->setBrush(option->palette.toolTipBase()); + painter->drawRoundedRect(tipRect, secondLevelRoundingRadius, secondLevelRoundingRadius); + + painter->setPen(highContrastTheme == true ? option->palette.buttonText().color() : WINUI3Colors[colorSchemeIndex][frameColorLight]); + painter->setBrush(Qt::NoBrush); + painter->drawRoundedRect(tipRect.marginsAdded(QMarginsF(0.5,0.5,0.5,0.5)), secondLevelRoundingRadius, secondLevelRoundingRadius); + break; + } + case PE_FrameTabWidget: + if (const QStyleOptionTabWidgetFrame *frame = qstyleoption_cast<const QStyleOptionTabWidgetFrame *>(option)) { + QRectF frameRect = frame->rect.marginsRemoved(QMargins(0,0,0,0)); + painter->setPen(Qt::NoPen); + painter->setBrush(frame->palette.base()); + painter->drawRoundedRect(frameRect, secondLevelRoundingRadius, secondLevelRoundingRadius); + + painter->setPen(highContrastTheme == true ? frame->palette.buttonText().color() : WINUI3Colors[colorSchemeIndex][frameColorLight]); + painter->setBrush(Qt::NoBrush); + painter->drawRoundedRect(frameRect.marginsRemoved(QMarginsF(0.5,0.5,0.5,0.5)), secondLevelRoundingRadius, secondLevelRoundingRadius); + } + break; + case PE_FrameGroupBox: + if (const QStyleOptionFrame *frame = qstyleoption_cast<const QStyleOptionFrame *>(option)) { + QRectF frameRect = frame->rect; + frameRect.adjust(0.5,0.5,-0.5,-0.5); + painter->setPen(highContrastTheme == true ? frame->palette.buttonText().color() : WINUI3Colors[colorSchemeIndex][frameColorStrong]); + painter->setBrush(Qt::NoBrush); + if (frame->features & QStyleOptionFrame::Flat) { + QRect fr = frame->rect; + QPoint p1(fr.x(), fr.y() + 1); + QPoint p2(fr.x() + fr.width(), p1.y()); + painter->drawLine(p1,p2); + } else { + painter->drawRoundedRect(frameRect.marginsRemoved(QMargins(1,1,1,1)), secondLevelRoundingRadius, secondLevelRoundingRadius); + } + } + break; + case PE_IndicatorCheckBox: + { + QNumberStyleAnimation* animation = qobject_cast<QNumberStyleAnimation*>(d->animation(option->styleObject)); + QFontMetrics fm(assetFont); + + QRectF rect = option->rect; + QPointF center = QPointF(rect.x() + rect.width() / 2, rect.y() + rect.height() / 2); + rect.setWidth(15); + rect.setHeight(15); + rect.moveCenter(center); + + float clipWidth = animation != nullptr ? animation->currentValue() : 1.0f; + QRectF clipRect = fm.boundingRect("\uE001"); + clipRect.moveCenter(center); + clipRect.setLeft(rect.x() + (rect.width() - clipRect.width()) / 2.0); + clipRect.setWidth(clipWidth * clipRect.width()); + + + QBrush fillBrush = (option->state & State_On || option->state & State_NoChange) ? option->palette.accent() : option->palette.window(); + if (state & State_MouseOver && (option->state & State_On || option->state & State_NoChange)) + fillBrush.setColor(fillBrush.color().lighter(107)); + else if (state & State_MouseOver && !(option->state & State_On || option->state & State_NoChange)) + fillBrush.setColor(fillBrush.color().darker(107)); + painter->setPen(Qt::NoPen); + painter->setBrush(fillBrush); + painter->drawRoundedRect(rect, secondLevelRoundingRadius, secondLevelRoundingRadius, Qt::AbsoluteSize); + + painter->setPen(QPen(highContrastTheme == true ? option->palette.buttonText().color() : WINUI3Colors[colorSchemeIndex][frameColorStrong])); + painter->setBrush(Qt::NoBrush); + painter->drawRoundedRect(rect, secondLevelRoundingRadius + 0.5, secondLevelRoundingRadius + 0.5, Qt::AbsoluteSize); + + painter->setFont(assetFont); + painter->setPen(option->palette.highlightedText().color()); + painter->setBrush(option->palette.highlightedText().color()); + if (option->state & State_On) + painter->drawText(clipRect, Qt::AlignVCenter | Qt::AlignLeft,"\uE001"); + else if (option->state & State_NoChange) + painter->drawText(rect, Qt::AlignVCenter | Qt::AlignHCenter,"\uE108"); + } + break; + + case PE_IndicatorRadioButton: + { + if (option->styleObject->property("_q_end_radius").isNull()) + option->styleObject->setProperty("_q_end_radius", option->state & State_On ? 4.0f :7.0f); + QNumberStyleAnimation* animation = qobject_cast<QNumberStyleAnimation*>(d->animation(option->styleObject)); + if (animation != nullptr) + option->styleObject->setProperty("_q_inner_radius", animation->currentValue()); + else + option->styleObject->setProperty("_q_inner_radius", option->styleObject->property("_q_end_radius")); + int innerRadius = option->styleObject->property("_q_inner_radius").toFloat(); + + QRectF rect = option->rect; + QPointF center = QPoint(rect.x() + rect.width() / 2, rect.y() + rect.height() / 2); + rect.setWidth(15); + rect.setHeight(15); + rect.moveCenter(center); + QRectF innerRect = rect; + innerRect.setWidth(8); + innerRect.setHeight(8); + innerRect.moveCenter(center); + + painter->setPen(Qt::NoPen); + painter->setBrush(option->palette.accent()); + if (option->state & State_MouseOver && option->state & State_Enabled) + painter->setBrush(QBrush(option->palette.accent().color().lighter(107))); + painter->drawEllipse(center, 7, 7); + + painter->setPen(QPen(WINUI3Colors[colorSchemeIndex][frameColorStrong])); + painter->setBrush(Qt::NoBrush); + painter->drawEllipse(center, 7.5, 7.5); + + painter->setPen(Qt::NoPen); + painter->setBrush(QBrush(option->palette.window())); + painter->drawEllipse(center,innerRadius, innerRadius); + + painter->setPen(QPen(WINUI3Colors[colorSchemeIndex][frameColorStrong])); + painter->setBrush(Qt::NoBrush); + painter->drawEllipse(center,innerRadius + 0.5, innerRadius + 0.5); + + painter->setPen(Qt::NoPen); + painter->setBrush(QBrush(option->palette.window())); + if (option->state & State_MouseOver && option->state & State_Enabled) + painter->setBrush(QBrush(option->palette.window().color().darker(107))); + painter->drawEllipse(center,innerRadius, innerRadius); + } + break; + case PE_PanelButtonBevel:{ + QRectF rect = option->rect.marginsRemoved(QMargins(2,2,2,2)); + rect.adjust(-0.5,-0.5,0.5,0.5); + painter->setBrush(Qt::NoBrush); + painter->setPen(QPen(WINUI3Colors[colorSchemeIndex][controlStrokePrimary])); + painter->drawRoundedRect(rect, secondLevelRoundingRadius, secondLevelRoundingRadius); + + rect = option->rect.marginsRemoved(QMargins(2,2,2,2)); + painter->setPen(Qt::NoPen); + if (!(state & (State_Raised))) + painter->setBrush(WINUI3Colors[colorSchemeIndex][controlFillTertiary]); + else if (state & State_MouseOver) + painter->setBrush(WINUI3Colors[colorSchemeIndex][controlFillSecondary]); + else + painter->setBrush(option->palette.button()); + painter->drawRoundedRect(rect, secondLevelRoundingRadius, secondLevelRoundingRadius); + painter->setPen(QPen(WINUI3Colors[colorSchemeIndex][controlStrokeSecondary])); + if (state & State_Raised) + painter->drawLine(rect.bottomLeft() + QPoint(2,1), rect.bottomRight() + QPoint(-2,1)); + } + break; + case PE_FrameDefaultButton: + painter->setPen(option->palette.accent().color()); + painter->setBrush(Qt::NoBrush); + painter->drawRoundedRect(option->rect, secondLevelRoundingRadius, secondLevelRoundingRadius); + break; + case QStyle::PE_FrameMenu: + break; + case QStyle::PE_PanelMenu: { + QRect rect = option->rect; + QPen pen(WINUI3Colors[colorSchemeIndex][frameColorLight]); + painter->save(); + painter->setPen(pen); + painter->setBrush(QBrush(WINUI3Colors[colorSchemeIndex][menuPanelFill])); + painter->setRenderHint(QPainter::Antialiasing); + painter->drawRoundedRect(rect.marginsRemoved(QMargins(2,2,12,2)), topLevelRoundingRadius, topLevelRoundingRadius); + painter->restore(); + break; + } + case PE_PanelLineEdit: + if (widget && widget->objectName() == "qt_spinbox_lineedit") + break; + if (const auto *panel = qstyleoption_cast<const QStyleOptionFrame *>(option)) { + QRectF frameRect = option->rect; + frameRect.adjust(0.5,0.5,-0.5,-0.5); + QBrush fillColor = option->palette.brush(QPalette::Base); + painter->setBrush(fillColor); + painter->setPen(Qt::NoPen); + painter->drawRoundedRect(frameRect, secondLevelRoundingRadius, secondLevelRoundingRadius); + // In case the QLineEdit is hovered overdraw the background with a alpha mask to + // highlight the QLineEdit. + if (state & State_MouseOver && !(state & State_HasFocus)) { + fillColor = QBrush(WINUI3Colors[colorSchemeIndex][subtleHighlightColor]); + painter->setBrush(fillColor); + painter->setPen(Qt::NoPen); + painter->drawRoundedRect(frameRect, secondLevelRoundingRadius, secondLevelRoundingRadius); + } + if (panel->lineWidth > 0) + proxy()->drawPrimitive(PE_FrameLineEdit, panel, painter, widget); + } + break; + case PE_FrameLineEdit: { + painter->setBrush(Qt::NoBrush); + painter->setPen(highContrastTheme == true ? option->palette.buttonText().color() : QPen(WINUI3Colors[colorSchemeIndex][frameColorLight])); + painter->drawRoundedRect(option->rect, secondLevelRoundingRadius, secondLevelRoundingRadius); + QRegion clipRegion = option->rect; + clipRegion -= option->rect.adjusted(2, 2, -2, -2); + painter->setClipRegion(clipRegion); + QColor lineColor = state & State_HasFocus ? option->palette.accent().color() : QColor(0,0,0); + painter->setPen(QPen(lineColor)); + painter->drawLine(option->rect.bottomLeft() + QPointF(1,0.5), option->rect.bottomRight() + QPointF(-1,0.5)); + } + break; + case PE_Frame: { + if (const auto *frame = qstyleoption_cast<const QStyleOptionFrame *>(option)) { + if (frame->frameShape == QFrame::NoFrame) + break; + QRectF rect = option->rect.adjusted(1,1,-1,-1); + if (widget && widget->inherits("QComboBoxPrivateContainer")) { + painter->setPen(Qt::NoPen); + painter->setBrush(WINUI3Colors[colorSchemeIndex][menuPanelFill]); + painter->drawRoundedRect(rect, secondLevelRoundingRadius, secondLevelRoundingRadius); + + } + painter->setBrush(option->palette.base()); + painter->setPen(Qt::NoPen); + painter->drawRoundedRect(rect, secondLevelRoundingRadius, secondLevelRoundingRadius); + + painter->setBrush(Qt::NoBrush); + painter->setPen(QPen(WINUI3Colors[colorSchemeIndex][frameColorLight])); + painter->drawRoundedRect(rect.marginsRemoved(QMarginsF(0.5,0.5,0.5,0.5)), secondLevelRoundingRadius, secondLevelRoundingRadius); + + if (qobject_cast<const QTextEdit *>(widget)) { + QRegion clipRegion = option->rect; + QColor lineColor = state & State_HasFocus ? option->palette.accent().color() : QColor(0,0,0,255); + painter->setPen(QPen(lineColor)); + painter->drawLine(option->rect.bottomLeft() + QPoint(1,-1), option->rect.bottomRight() + QPoint(-1,-1)); + } + } + break; + } + case QStyle::PE_PanelItemViewRow: + if (const QStyleOptionViewItem *vopt = qstyleoption_cast<const QStyleOptionViewItem *>(option)) { + if ((vopt->state & State_Selected || vopt->state & State_MouseOver) && vopt->showDecorationSelected) { + painter->setBrush(WINUI3Colors[colorSchemeIndex][subtleHighlightColor]); + painter->setPen(Qt::NoPen); + painter->drawRoundedRect(vopt->rect.marginsRemoved(QMargins(0,2,-2,2)),2,2); + const int offset = qobject_cast<const QTreeView *>(widget) ? 2 : 0; + if (vopt->viewItemPosition == QStyleOptionViewItem::Beginning && option->state & State_Selected) { + painter->setPen(QPen(option->palette.accent().color())); + painter->drawLine(option->rect.x(),option->rect.y()+offset,option->rect.x(),option->rect.y() + option->rect.height()-2); + painter->drawLine(option->rect.x()+1,option->rect.y()+2,option->rect.x()+1,option->rect.y() + option->rect.height()-2); + } + } + } + break; + case QStyle::PE_Widget: { +#if QT_CONFIG(dialogbuttonbox) + const QDialogButtonBox *buttonBox = nullptr; + if (qobject_cast<const QMessageBox *> (widget)) + buttonBox = widget->findChild<const QDialogButtonBox *>(QLatin1String("qt_msgbox_buttonbox")); +#if QT_CONFIG(inputdialog) + else if (qobject_cast<const QInputDialog *> (widget)) + buttonBox = widget->findChild<const QDialogButtonBox *>(QLatin1String("qt_inputdlg_buttonbox")); +#endif // QT_CONFIG(inputdialog) + if (buttonBox) { + painter->fillRect(option->rect,option->palette.window()); + } +#endif + break; + } + case QStyle::PE_FrameWindow: + if (const auto *frm = qstyleoption_cast<const QStyleOptionFrame *>(option)) { + + QRectF rect= option->rect; + int fwidth = int((frm->lineWidth + frm->midLineWidth) / QWindowsStylePrivate::nativeMetricScaleFactor(widget)); + + QRectF bottomLeftCorner = QRectF(rect.left() + 1.0, + rect.bottom() - 1.0 - secondLevelRoundingRadius, + secondLevelRoundingRadius, + secondLevelRoundingRadius); + QRectF bottomRightCorner = QRectF(rect.right() - 1.0 - secondLevelRoundingRadius, + rect.bottom() - 1.0 - secondLevelRoundingRadius, + secondLevelRoundingRadius, + secondLevelRoundingRadius); + + //Draw Mask + if (widget != nullptr) { + QBitmap mask(widget->width(), widget->height()); + mask.clear(); + + QPainter maskPainter(&mask); + maskPainter.setRenderHint(QPainter::Antialiasing); + maskPainter.setBrush(Qt::color1); + maskPainter.setPen(Qt::NoPen); + maskPainter.drawRoundedRect(option->rect,secondLevelRoundingRadius,secondLevelRoundingRadius); + const_cast<QWidget*>(widget)->setMask(mask); + } + + //Draw Window + painter->setPen(QPen(frm->palette.base(), fwidth)); + painter->drawLine(QPointF(rect.left(), rect.top()), + QPointF(rect.left(), rect.bottom() - fwidth)); + painter->drawLine(QPointF(rect.left() + fwidth, rect.bottom()), + QPointF(rect.right() - fwidth, rect.bottom())); + painter->drawLine(QPointF(rect.right(), rect.top()), + QPointF(rect.right(), rect.bottom() - fwidth)); + + painter->setPen(QPen(WINUI3Colors[colorSchemeIndex][surfaceStroke])); + painter->drawLine(QPointF(rect.left() + 0.5, rect.top() + 0.5), + QPointF(rect.left() + 0.5, rect.bottom() - 0.5 - secondLevelRoundingRadius)); + painter->drawLine(QPointF(rect.left() + 0.5 + secondLevelRoundingRadius, rect.bottom() - 0.5), + QPointF(rect.right() - 0.5 - secondLevelRoundingRadius, rect.bottom() - 0.5)); + painter->drawLine(QPointF(rect.right() - 0.5, rect.top() + 1.5), + QPointF(rect.right() - 0.5, rect.bottom() - 0.5 - secondLevelRoundingRadius)); + + painter->setPen(Qt::NoPen); + painter->setBrush(frm->palette.base()); + painter->drawPie(bottomRightCorner.marginsAdded(QMarginsF(2.5,2.5,0.0,0.0)), + 270 * 16,90 * 16); + painter->drawPie(bottomLeftCorner.marginsAdded(QMarginsF(0.0,2.5,2.5,0.0)), + -90 * 16,-90 * 16); + + painter->setPen(QPen(WINUI3Colors[colorSchemeIndex][surfaceStroke])); + painter->setBrush(Qt::NoBrush); + painter->drawArc(bottomRightCorner, + 0 * 16,-90 * 16); + painter->drawArc(bottomLeftCorner, + -90 * 16,-90 * 16); + } + break; + default: + QWindowsVistaStyle::drawPrimitive(element, option, painter, widget); + } + painter->restore(); +} + +/*! + \internal +*/ +void QWindows11Style::drawControl(ControlElement element, const QStyleOption *option, + QPainter *painter, const QWidget *widget) const +{ + Q_D(const QWindows11Style); + QRect rect(option->rect); + State flags = option->state; + + painter->save(); + painter->setRenderHint(QPainter::Antialiasing); + switch (element) { + case QStyle::CE_TabBarTabShape: + if (const QStyleOptionTab *tab = qstyleoption_cast<const QStyleOptionTab *>(option)) { + QRectF tabRect = tab->rect.marginsRemoved(QMargins(2,2,0,0)); + painter->setPen(Qt::NoPen); + painter->setBrush(tab->palette.base()); + if (tab->state & State_MouseOver){ + painter->setBrush(WINUI3Colors[colorSchemeIndex][subtleHighlightColor]); + } else if (tab->state & State_Selected) { + painter->setBrush(tab->palette.base()); + } else { + painter->setBrush(tab->palette.window()); + } + painter->drawRoundedRect(tabRect,2,2); + + painter->setBrush(Qt::NoBrush); + painter->setPen(highContrastTheme == true ? tab->palette.buttonText().color() : WINUI3Colors[colorSchemeIndex][frameColorLight]); + painter->drawRoundedRect(tabRect.adjusted(0.5,0.5,-0.5,-0.5),2,2); + + } + break; + case CE_ToolButtonLabel: + if (const QStyleOptionToolButton *toolbutton + = qstyleoption_cast<const QStyleOptionToolButton *>(option)) { + QRect rect = toolbutton->rect; + int shiftX = 0; + int shiftY = 0; + if (toolbutton->state & (State_Sunken | State_On)) { + shiftX = proxy()->pixelMetric(PM_ButtonShiftHorizontal, toolbutton, widget); + shiftY = proxy()->pixelMetric(PM_ButtonShiftVertical, toolbutton, widget); + } + // Arrow type always overrules and is always shown + bool hasArrow = toolbutton->features & QStyleOptionToolButton::Arrow; + if (((!hasArrow && toolbutton->icon.isNull()) && !toolbutton->text.isEmpty()) + || toolbutton->toolButtonStyle == Qt::ToolButtonTextOnly) { + int alignment = Qt::AlignCenter | Qt::TextShowMnemonic; + if (!proxy()->styleHint(SH_UnderlineShortcut, toolbutton, widget)) + alignment |= Qt::TextHideMnemonic; + rect.translate(shiftX, shiftY); + painter->setFont(toolbutton->font); + const QString text = d->toolButtonElideText(toolbutton, rect, alignment); + if (toolbutton->state & State_Raised || toolbutton->palette.isBrushSet(QPalette::Current, QPalette::ButtonText)) + painter->setPen(QPen(toolbutton->palette.buttonText().color())); + else + painter->setPen(QPen(WINUI3Colors[colorSchemeIndex][controlTextSecondary])); + proxy()->drawItemText(painter, rect, alignment, toolbutton->palette, + toolbutton->state & State_Enabled, text); + } else { + QPixmap pm; + QSize pmSize = toolbutton->iconSize; + if (!toolbutton->icon.isNull()) { + QIcon::State state = toolbutton->state & State_On ? QIcon::On : QIcon::Off; + QIcon::Mode mode; + if (!(toolbutton->state & State_Enabled)) + mode = QIcon::Disabled; + else if ((toolbutton->state & State_MouseOver) && (toolbutton->state & State_AutoRaise)) + mode = QIcon::Active; + else + mode = QIcon::Normal; + pm = toolbutton->icon.pixmap(toolbutton->rect.size().boundedTo(toolbutton->iconSize), painter->device()->devicePixelRatio(), + mode, state); + pmSize = pm.size() / pm.devicePixelRatio(); + } + + if (toolbutton->toolButtonStyle != Qt::ToolButtonIconOnly) { + painter->setFont(toolbutton->font); + QRect pr = rect, + tr = rect; + int alignment = Qt::TextShowMnemonic; + if (!proxy()->styleHint(SH_UnderlineShortcut, toolbutton, widget)) + alignment |= Qt::TextHideMnemonic; + + if (toolbutton->toolButtonStyle == Qt::ToolButtonTextUnderIcon) { + pr.setHeight(pmSize.height() + 4); //### 4 is currently hardcoded in QToolButton::sizeHint() + tr.adjust(0, pr.height() - 1, 0, -1); + pr.translate(shiftX, shiftY); + if (!hasArrow) { + proxy()->drawItemPixmap(painter, pr, Qt::AlignCenter, pm); + } else { + drawArrow(proxy(), toolbutton, pr, painter, widget); + } + alignment |= Qt::AlignCenter; + } else { + pr.setWidth(pmSize.width() + 4); //### 4 is currently hardcoded in QToolButton::sizeHint() + tr.adjust(pr.width(), 0, 0, 0); + pr.translate(shiftX, shiftY); + if (!hasArrow) { + proxy()->drawItemPixmap(painter, QStyle::visualRect(toolbutton->direction, rect, pr), Qt::AlignCenter, pm); + } else { + drawArrow(proxy(), toolbutton, pr, painter, widget); + } + alignment |= Qt::AlignLeft | Qt::AlignVCenter; + } + tr.translate(shiftX, shiftY); + const QString text = d->toolButtonElideText(toolbutton, tr, alignment); + if (toolbutton->state & State_Raised || toolbutton->palette.isBrushSet(QPalette::Current, QPalette::ButtonText)) + painter->setPen(QPen(toolbutton->palette.buttonText().color())); + else + painter->setPen(QPen(WINUI3Colors[colorSchemeIndex][controlTextSecondary])); + proxy()->drawItemText(painter, QStyle::visualRect(toolbutton->direction, rect, tr), alignment, toolbutton->palette, + toolbutton->state & State_Enabled, text); + } else { + rect.translate(shiftX, shiftY); + if (hasArrow) { + drawArrow(proxy(), toolbutton, rect, painter, widget); + } else { + proxy()->drawItemPixmap(painter, rect, Qt::AlignCenter, pm); + } + } + } + } + break; + case QStyle::CE_ShapedFrame: + if (const QStyleOptionFrame *f = qstyleoption_cast<const QStyleOptionFrame *>(option)) { + int frameShape = f->frameShape; + int frameShadow = QFrame::Plain; + if (f->state & QStyle::State_Sunken) + frameShadow = QFrame::Sunken; + else if (f->state & QStyle::State_Raised) + frameShadow = QFrame::Raised; + + int lw = f->lineWidth; + int mlw = f->midLineWidth; + + switch (frameShape) { + case QFrame::Box: + if (frameShadow == QFrame::Plain) + qDrawPlainRoundedRect(painter, f->rect, secondLevelRoundingRadius, secondLevelRoundingRadius, highContrastTheme == true ? f->palette.buttonText().color() : WINUI3Colors[colorSchemeIndex][frameColorStrong], lw); + else + qDrawShadeRect(painter, f->rect, f->palette, frameShadow == QFrame::Sunken, lw, mlw); + break; + case QFrame::Panel: + if (frameShadow == QFrame::Plain) + qDrawPlainRoundedRect(painter, f->rect, secondLevelRoundingRadius, secondLevelRoundingRadius, highContrastTheme == true ? f->palette.buttonText().color() : WINUI3Colors[colorSchemeIndex][frameColorStrong], lw); + else + qDrawShadePanel(painter, f->rect, f->palette, frameShadow == QFrame::Sunken, lw); + break; + default: + QWindowsVistaStyle::drawControl(element, option, painter, widget); + } + } + break; + case QStyle::CE_ProgressBarGroove:{ + if (const QStyleOptionProgressBar* progbaropt = qstyleoption_cast<const QStyleOptionProgressBar*>(option)) { + QRect rect = subElementRect(SE_ProgressBarContents, progbaropt, widget); + QPointF center = rect.center(); + if (progbaropt->state & QStyle::State_Horizontal) { + rect.setHeight(1); + rect.moveTop(center.y()); + } else { + rect.setWidth(1); + rect.moveLeft(center.x()); + } + painter->setPen(Qt::NoPen); + painter->setBrush(Qt::gray); + painter->drawRect(rect); + } + break; + } + case QStyle::CE_ProgressBarContents: + if (const QStyleOptionProgressBar* progbaropt = qstyleoption_cast<const QStyleOptionProgressBar*>(option)) { + const qreal progressBarThickness = 3; + const qreal progressBarHalfThickness = progressBarThickness / 2.0; + QRectF rect = subElementRect(SE_ProgressBarContents, progbaropt, widget); + QRectF originalRect = rect; + QPointF center = rect.center(); + bool isIndeterminate = progbaropt->maximum == 0 && progbaropt->minimum == 0; + float fillPercentage = 0; + const Qt::Orientation orientation = (progbaropt->state & QStyle::State_Horizontal) ? Qt::Horizontal : Qt::Vertical; + const qreal offset = (orientation == Qt::Horizontal && int(rect.height()) % 2 == 0) + || (orientation == Qt::Vertical && int(rect.width()) % 2 == 0) ? 0.5 : 0.0; + + if (!isIndeterminate) { + fillPercentage = ((float(progbaropt->progress) - float(progbaropt->minimum)) / (float(progbaropt->maximum) - float(progbaropt->minimum))); + if (orientation == Qt::Horizontal) { + rect.setHeight(progressBarThickness); + rect.moveTop(center.y() - progressBarHalfThickness - offset); + rect.setWidth(rect.width() * fillPercentage); + } else { + float oldHeight = rect.height(); + rect.setWidth(progressBarThickness); + rect.moveLeft(center.x() - progressBarHalfThickness - offset); + rect.moveTop(oldHeight * (1.0f - fillPercentage)); + rect.setHeight(oldHeight * fillPercentage); + } + } else { + auto elapsedTime = std::chrono::time_point_cast<std::chrono::milliseconds>(std::chrono::system_clock::now()); + fillPercentage = (elapsedTime.time_since_epoch().count() % 5000)/(5000.0f*0.75); + if (orientation == Qt::Horizontal) { + float barBegin = qMin(qMax(fillPercentage-0.25,0.0) * rect.width(), float(rect.width())); + float barEnd = qMin(fillPercentage * rect.width(), float(rect.width())); + rect = QRect(QPoint(rect.left() + barBegin, rect.top()), QPoint(rect.left() + barEnd, rect.bottom())); + rect.setHeight(progressBarThickness); + rect.moveTop(center.y() - progressBarHalfThickness - offset); + } else { + float barBegin = qMin(qMax(fillPercentage-0.25,0.0) * rect.height(), float(rect.height())); + float barEnd = qMin(fillPercentage * rect.height(), float(rect.height())); + rect = QRect(QPoint(rect.left(), rect.bottom() - barEnd), QPoint(rect.right(), rect.bottom() - barBegin)); + rect.setWidth(progressBarThickness); + rect.moveLeft(center.x() - progressBarHalfThickness - offset); + } + const_cast<QWidget*>(widget)->update(); + } + if (progbaropt->invertedAppearance && orientation == Qt::Horizontal) + rect.moveLeft(originalRect.width() * (1.0 - fillPercentage)); + else if (progbaropt->invertedAppearance && orientation == Qt::Vertical) + rect.moveBottom(originalRect.height() * fillPercentage); + painter->setPen(Qt::NoPen); + painter->setBrush(progbaropt->palette.accent()); + painter->drawRoundedRect(rect, secondLevelRoundingRadius, secondLevelRoundingRadius); + } + break; + case QStyle::CE_ProgressBarLabel: + if (const QStyleOptionProgressBar* progbaropt = qstyleoption_cast<const QStyleOptionProgressBar*>(option)) { + QRect rect = subElementRect(SE_ProgressBarLabel, progbaropt, widget); + painter->setPen(progbaropt->palette.text().color()); + painter->drawText(rect, progbaropt->text,Qt::AlignVCenter|Qt::AlignLeft); + } + break; + case CE_PushButtonLabel: + if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(option)) { + QRect textRect = btn->rect; + + int tf = Qt::AlignVCenter|Qt::TextShowMnemonic; + if (!proxy()->styleHint(SH_UnderlineShortcut, btn, widget)) + tf |= Qt::TextHideMnemonic; + + if (btn->features & QStyleOptionButton::HasMenu) { + int indicatorSize = proxy()->pixelMetric(PM_MenuButtonIndicator, btn, widget); + if (btn->direction == Qt::LeftToRight) + textRect = textRect.adjusted(0, 0, -indicatorSize, 0); + else + textRect = textRect.adjusted(indicatorSize, 0, 0, 0); + } + if (!btn->icon.isNull()) { + //Center both icon and text + QIcon::Mode mode = btn->state & State_Enabled ? QIcon::Normal : QIcon::Disabled; + if (mode == QIcon::Normal && btn->state & State_HasFocus) + mode = QIcon::Active; + QIcon::State state = QIcon::Off; + if (btn->state & State_On) + state = QIcon::On; + + QPixmap pixmap = btn->icon.pixmap(btn->iconSize, painter->device()->devicePixelRatio(), mode, state); + int pixmapWidth = pixmap.width() / pixmap.devicePixelRatio(); + int pixmapHeight = pixmap.height() / pixmap.devicePixelRatio(); + int labelWidth = pixmapWidth; + int labelHeight = pixmapHeight; + int iconSpacing = 4;//### 4 is currently hardcoded in QPushButton::sizeHint() + if (!btn->text.isEmpty()) { + int textWidth = btn->fontMetrics.boundingRect(option->rect, tf, btn->text).width(); + labelWidth += (textWidth + iconSpacing); + } + + QRect iconRect = QRect(textRect.x() + (textRect.width() - labelWidth) / 2, + textRect.y() + (textRect.height() - labelHeight) / 2, + pixmapWidth, pixmapHeight); + + iconRect = visualRect(btn->direction, textRect, iconRect); + + if (btn->direction == Qt::RightToLeft) { + tf |= Qt::AlignRight; + textRect.setRight(iconRect.left() - iconSpacing / 2); + } else { + tf |= Qt::AlignLeft; //left align, we adjust the text-rect instead + textRect.setLeft(iconRect.left() + iconRect.width() + iconSpacing / 2); + } + + if (btn->state & (State_On | State_Sunken)) + iconRect.translate(proxy()->pixelMetric(PM_ButtonShiftHorizontal, option, widget), + proxy()->pixelMetric(PM_ButtonShiftVertical, option, widget)); + painter->drawPixmap(iconRect, pixmap); + } else { + tf |= Qt::AlignHCenter; + } + + + if (btn->state & State_Sunken) + painter->setPen(flags & State_On ? QPen(WINUI3Colors[colorSchemeIndex][textOnAccentSecondary]) : QPen(WINUI3Colors[colorSchemeIndex][controlTextSecondary])); + else + painter->setPen(flags & State_On ? QPen(WINUI3Colors[colorSchemeIndex][textOnAccentPrimary]) : QPen(btn->palette.buttonText().color())); + proxy()->drawItemText(painter, textRect, tf, option->palette,btn->state & State_Enabled, btn->text); + } + break; + case CE_PushButtonBevel: + if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(option)) { + if (btn->features.testFlag(QStyleOptionButton::Flat)) { + painter->setPen(Qt::NoPen); + if (flags & (State_Sunken | State_On)) { + painter->setBrush(WINUI3Colors[colorSchemeIndex][subtlePressedColor]); + } + else if (flags & State_MouseOver) { + painter->setBrush(WINUI3Colors[colorSchemeIndex][subtleHighlightColor]); + } + painter->drawRoundedRect(rect, secondLevelRoundingRadius, secondLevelRoundingRadius); + } else { + QRectF rect = btn->rect.marginsRemoved(QMargins(2,2,2,2)); + painter->setPen(Qt::NoPen); + painter->setBrush(flags & State_On ? option->palette.accent() : option->palette.button()); + painter->drawRoundedRect(rect, secondLevelRoundingRadius, secondLevelRoundingRadius); + if (flags.testFlags(State_Sunken | State_MouseOver)) { + if (flags & (State_Sunken)) + painter->setBrush(flags & State_On ? option->palette.accent().color().lighter(120) : WINUI3Colors[colorSchemeIndex][controlFillTertiary]); + else if (flags & State_MouseOver) + painter->setBrush(flags & State_On ? option->palette.accent().color().lighter(110) : WINUI3Colors[colorSchemeIndex][controlFillSecondary]); + painter->drawRoundedRect(rect, secondLevelRoundingRadius, secondLevelRoundingRadius); + } + + rect.adjust(0.5,0.5,-0.5,-0.5); + painter->setBrush(Qt::NoBrush); + painter->setPen(btn->features.testFlag(QStyleOptionButton::DefaultButton) ? QPen(option->palette.accent().color()) : QPen(WINUI3Colors[colorSchemeIndex][controlStrokePrimary])); + painter->drawRoundedRect(rect, secondLevelRoundingRadius, secondLevelRoundingRadius); + + painter->setPen(btn->features.testFlag(QStyleOptionButton::DefaultButton) ? QPen(WINUI3Colors[colorSchemeIndex][controlStrokeOnAccentSecondary]) : QPen(WINUI3Colors[colorSchemeIndex][controlStrokeSecondary])); + if (flags & State_Raised) + painter->drawLine(rect.bottomLeft() + QPointF(4.0,0.0), rect.bottomRight() + QPointF(-4,0.0)); + } + } + break; + case CE_MenuBarItem: + if (const auto *mbi = qstyleoption_cast<const QStyleOptionMenuItem *>(option)) { + constexpr int hPadding = 11; + constexpr int topPadding = 4; + constexpr int bottomPadding = 6; + bool active = mbi->state & State_Selected; + bool hasFocus = mbi->state & State_HasFocus; + bool down = mbi->state & State_Sunken; + bool enabled = mbi->state & State_Enabled; + QStyleOptionMenuItem newMbi = *mbi; + newMbi.font.setPointSize(10); + if (enabled && (active || hasFocus)) { + if (active && down) + painter->setBrushOrigin(painter->brushOrigin() + QPoint(1, 1)); + if (active && hasFocus) { + painter->setBrush(WINUI3Colors[colorSchemeIndex][subtleHighlightColor]); + painter->setPen(Qt::NoPen); + QRect rect = mbi->rect.marginsRemoved(QMargins(5,0,5,0)); + painter->drawRoundedRect(rect,secondLevelRoundingRadius,secondLevelRoundingRadius,Qt::AbsoluteSize); + } + } + newMbi.rect.adjust(hPadding,topPadding,-hPadding,-bottomPadding); + painter->setFont(newMbi.font); + QCommonStyle::drawControl(element, &newMbi, painter, widget); + } + break; + +#if QT_CONFIG(menu) + case CE_MenuEmptyArea: + break; + + case CE_MenuItem: + if (const auto *menuitem = qstyleoption_cast<const QStyleOptionMenuItem *>(option)) { + int x, y, w, h; + menuitem->rect.getRect(&x, &y, &w, &h); + int tab = menuitem->reservedShortcutWidth; + bool dis = !(menuitem->state & State_Enabled); + bool checked = menuitem->checkType != QStyleOptionMenuItem::NotCheckable + ? menuitem->checked : false; + bool act = menuitem->state & State_Selected; + + // windows always has a check column, regardless whether we have an icon or not + int checkcol = qMax<int>(menuitem->maxIconWidth, 32); + + QBrush fill = (act == true && dis == false) ? QBrush(WINUI3Colors[colorSchemeIndex][subtleHighlightColor]) : menuitem->palette.brush(QPalette::Button); + painter->setBrush(fill); + painter->setPen(Qt::NoPen); + QRect rect = menuitem->rect; + rect = rect.marginsRemoved(QMargins(2,2,2,2)); + if (act && dis == false) + painter->drawRoundedRect(rect,secondLevelRoundingRadius,secondLevelRoundingRadius,Qt::AbsoluteSize); + + if (menuitem->menuItemType == QStyleOptionMenuItem::Separator){ + int yoff = 4; + painter->setPen(highContrastTheme == true ? menuitem->palette.buttonText().color() : WINUI3Colors[colorSchemeIndex][frameColorLight]); + painter->drawLine(x, y + yoff, x + w, y + yoff ); + break; + } + + QRect vCheckRect = visualRect(option->direction, menuitem->rect, QRect(menuitem->rect.x(), menuitem->rect.y(), checkcol, menuitem->rect.height())); + if (!menuitem->icon.isNull() && checked) { + if (act) { + qDrawShadePanel(painter, vCheckRect, + menuitem->palette, true, 1, + &menuitem->palette.brush(QPalette::Button)); + } else { + QBrush fill(menuitem->palette.light().color(), Qt::Dense4Pattern); + qDrawShadePanel(painter, vCheckRect, menuitem->palette, true, 1, &fill); + } + } + // On Windows Style, if we have a checkable item and an icon we + // draw the icon recessed to indicate an item is checked. If we + // have no icon, we draw a checkmark instead. + if (!menuitem->icon.isNull()) { + QIcon::Mode mode = dis ? QIcon::Disabled : QIcon::Normal; + if (act && !dis) + mode = QIcon::Active; + QPixmap pixmap; + if (checked) + pixmap = menuitem->icon.pixmap(proxy()->pixelMetric(PM_SmallIconSize, option, widget), mode, QIcon::On); + else + pixmap = menuitem->icon.pixmap(proxy()->pixelMetric(PM_SmallIconSize, option, widget), mode); + QRect pmr(QPoint(0, 0), pixmap.deviceIndependentSize().toSize()); + pmr.moveCenter(vCheckRect.center()); + painter->setPen(menuitem->palette.text().color()); + painter->drawPixmap(pmr.topLeft(), pixmap); + } else if (checked) { + QStyleOptionMenuItem newMi = *menuitem; + newMi.state = State_None; + if (!dis) + newMi.state |= State_Enabled; + if (act) + newMi.state |= State_On | State_Selected; + newMi.rect = visualRect(option->direction, menuitem->rect, QRect(menuitem->rect.x() + QWindowsStylePrivate::windowsItemFrame, + menuitem->rect.y() + QWindowsStylePrivate::windowsItemFrame, + checkcol - 2 * QWindowsStylePrivate::windowsItemFrame, + menuitem->rect.height() - 2 * QWindowsStylePrivate::windowsItemFrame)); + + QColor discol; + if (dis) { + discol = menuitem->palette.text().color(); + painter->setPen(discol); + } + int xm = int(QWindowsStylePrivate::windowsItemFrame) + checkcol / 4 + int(QWindowsStylePrivate::windowsItemHMargin); + int xpos = menuitem->rect.x() + xm; + QRect textRect(xpos, y + QWindowsStylePrivate::windowsItemVMargin, + w - xm - QWindowsStylePrivate::windowsRightBorder - tab + 1, h - 2 * QWindowsStylePrivate::windowsItemVMargin); + QRect vTextRect = visualRect(option->direction, menuitem->rect, textRect); + + painter->save(); + painter->setFont(assetFont); + int text_flags = Qt::AlignVCenter | Qt::TextShowMnemonic | Qt::TextDontClip | Qt::TextSingleLine; + if (!proxy()->styleHint(SH_UnderlineShortcut, menuitem, widget)) + text_flags |= Qt::TextHideMnemonic; + text_flags |= Qt::AlignLeft; + + const QString textToDraw("\uE001"); + painter->setPen(option->palette.text().color()); + painter->drawText(vTextRect, text_flags, textToDraw); + painter->restore(); + } + painter->setPen(act ? menuitem->palette.highlightedText().color() : menuitem->palette.buttonText().color()); + + QColor discol = menuitem->palette.text().color(); + if (dis) { + discol = menuitem->palette.color(QPalette::Disabled, QPalette::WindowText); + painter->setPen(discol); + } + + int xm = int(QWindowsStylePrivate::windowsItemFrame) + checkcol + int(QWindowsStylePrivate::windowsItemHMargin); + int xpos = menuitem->rect.x() + xm; + QRect textRect(xpos, y + QWindowsStylePrivate::windowsItemVMargin, + w - xm - QWindowsStylePrivate::windowsRightBorder - tab + 1, h - 2 * QWindowsStylePrivate::windowsItemVMargin); + QRect vTextRect = visualRect(option->direction, menuitem->rect, textRect); + QStringView s(menuitem->text); + if (!s.isEmpty()) { // draw text + painter->save(); + qsizetype t = s.indexOf(u'\t'); + int text_flags = Qt::AlignVCenter | Qt::TextShowMnemonic | Qt::TextDontClip | Qt::TextSingleLine; + if (!proxy()->styleHint(SH_UnderlineShortcut, menuitem, widget)) + text_flags |= Qt::TextHideMnemonic; + text_flags |= Qt::AlignLeft; + if (t >= 0) { + QRect vShortcutRect = visualRect(option->direction, menuitem->rect, + QRect(textRect.topRight(), QPoint(menuitem->rect.right(), textRect.bottom()))); + const QString textToDraw = s.mid(t + 1).toString(); + if (dis && !act && proxy()->styleHint(SH_EtchDisabledText, option, widget)) { + painter->setPen(menuitem->palette.light().color()); + painter->drawText(vShortcutRect.adjusted(1, 1, 1, 1), text_flags, textToDraw); + painter->setPen(discol); + } + painter->setPen(menuitem->palette.color(QPalette::Disabled, QPalette::Text)); + painter->drawText(vShortcutRect, text_flags, textToDraw); + s = s.left(t); + } + QFont font = menuitem->font; + if (menuitem->menuItemType == QStyleOptionMenuItem::DefaultItem) + font.setBold(true); + painter->setFont(font); + const QString textToDraw = s.left(t).toString(); + painter->setPen(discol); + painter->drawText(vTextRect, text_flags, textToDraw); + painter->restore(); + } + if (menuitem->menuItemType == QStyleOptionMenuItem::SubMenu) {// draw sub menu arrow + int dim = (h - 2 * QWindowsStylePrivate::windowsItemFrame) / 2; + xpos = x + w - QWindowsStylePrivate::windowsArrowHMargin - QWindowsStylePrivate::windowsItemFrame - dim; + QRect vSubMenuRect = visualRect(option->direction, menuitem->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()); + painter->save(); + painter->setFont(assetFont); + int text_flags = Qt::AlignVCenter | Qt::TextShowMnemonic | Qt::TextDontClip | Qt::TextSingleLine; + if (!proxy()->styleHint(SH_UnderlineShortcut, menuitem, widget)) + text_flags |= Qt::TextHideMnemonic; + text_flags |= Qt::AlignLeft; + const QString textToDraw("\uE013"); + painter->setPen(option->palette.text().color()); + painter->drawText(vSubMenuRect, text_flags, textToDraw); + painter->restore(); + } + } + break; +#endif // QT_CONFIG(menu) + case CE_MenuBarEmptyArea: { + break; + } + case CE_HeaderEmptyArea: + break; + case CE_HeaderSection: { + if (const QStyleOptionHeader *header = qstyleoption_cast<const QStyleOptionHeader *>(option)) { + painter->setPen(Qt::NoPen); + painter->setBrush(header->palette.button()); + painter->drawRect(header->rect); + + painter->setPen(highContrastTheme == true ? header->palette.buttonText().color() : WINUI3Colors[colorSchemeIndex][frameColorLight]); + painter->setBrush(Qt::NoBrush); + + if (header->position == QStyleOptionHeader::OnlyOneSection) { + break; + } + else if (header->position == QStyleOptionHeader::Beginning) { + painter->drawLine(QPointF(option->rect.topRight()) + QPointF(0.5,0.0), + QPointF(option->rect.bottomRight()) + QPointF(0.5,0.0)); + } + else if (header->position == QStyleOptionHeader::End) { + painter->drawLine(QPointF(option->rect.topLeft()) - QPointF(0.5,0.0), + QPointF(option->rect.bottomLeft()) - QPointF(0.5,0.0)); + } else { + painter->drawLine(QPointF(option->rect.topRight()) + QPointF(0.5,0.0), + QPointF(option->rect.bottomRight()) + QPointF(0.5,0.0)); + painter->drawLine(QPointF(option->rect.topLeft()) - QPointF(0.5,0.0), + QPointF(option->rect.bottomLeft()) - QPointF(0.5,0.0)); + } + painter->drawLine(QPointF(option->rect.bottomLeft()) + QPointF(0.0,0.5), + QPointF(option->rect.bottomRight()) + QPointF(0.0,0.5)); + } + break; + } + case QStyle::CE_ItemViewItem: { + if (const QStyleOptionViewItem *vopt = qstyleoption_cast<const QStyleOptionViewItem *>(option)) { + if (const QAbstractItemView *view = qobject_cast<const QAbstractItemView *>(widget)) { + QRect checkRect = proxy()->subElementRect(SE_ItemViewItemCheckIndicator, vopt, widget); + QRect iconRect = proxy()->subElementRect(SE_ItemViewItemDecoration, vopt, widget); + QRect textRect = proxy()->subElementRect(SE_ItemViewItemText, vopt, widget); + + QRect rect = vopt->rect; + + painter->setPen(highContrastTheme == true ? vopt->palette.buttonText().color() : WINUI3Colors[colorSchemeIndex][frameColorLight]); + if (vopt->viewItemPosition == QStyleOptionViewItem::OnlyOne || vopt->viewItemPosition == QStyleOptionViewItem::Invalid) { + } else if (vopt->viewItemPosition == QStyleOptionViewItem::Beginning) { + painter->drawLine(QPointF(option->rect.topRight()) + QPointF(0.5,0.0), + QPointF(option->rect.bottomRight()) + QPointF(0.5,0.0)); + } else if (vopt->viewItemPosition == QStyleOptionViewItem::End) { + painter->drawLine(QPointF(option->rect.topLeft()) - QPointF(0.5,0.0), + QPointF(option->rect.bottomLeft()) - QPointF(0.5,0.0)); + } else { + painter->drawLine(QPointF(option->rect.topRight()) + QPointF(0.5,0.0), + QPointF(option->rect.bottomRight()) + QPointF(0.5,0.0)); + painter->drawLine(QPointF(option->rect.topLeft()) - QPointF(0.5,0.0), + QPointF(option->rect.bottomLeft()) - QPointF(0.5,0.0)); + } + painter->setPen(QPen(option->palette.buttonText().color())); + + const bool isTreeView = qobject_cast<const QTreeView *>(widget); + + if ((vopt->state & State_Selected || vopt->state & State_MouseOver) && !(isTreeView && vopt->state & State_MouseOver) && vopt->showDecorationSelected) { + painter->setBrush(WINUI3Colors[colorSchemeIndex][subtleHighlightColor]); + QWidget *editorWidget = view ? view->indexWidget(view->currentIndex()) : nullptr; + if (editorWidget) { + QPalette pal = editorWidget->palette(); + QColor editorBgColor = vopt->backgroundBrush == Qt::NoBrush ? vopt->palette.color(widget->backgroundRole()) : vopt->backgroundBrush.color(); + editorBgColor.setAlpha(255); + pal.setColor(editorWidget->backgroundRole(),editorBgColor); + editorWidget->setPalette(pal); + } + } else { + painter->setBrush(vopt->backgroundBrush); + } + painter->setPen(Qt::NoPen); + + if (vopt->viewItemPosition == QStyleOptionViewItem::OnlyOne || vopt->viewItemPosition == QStyleOptionViewItem::Invalid) { + painter->drawRoundedRect(vopt->rect.marginsRemoved(QMargins(2,2,2,2)),secondLevelRoundingRadius,secondLevelRoundingRadius); + } else if (vopt->viewItemPosition == QStyleOptionViewItem::Beginning) { + painter->drawRoundedRect(rect.marginsRemoved(QMargins(2,2,0,2)),secondLevelRoundingRadius,secondLevelRoundingRadius); + } else if (vopt->viewItemPosition == QStyleOptionViewItem::End) { + painter->drawRoundedRect(vopt->rect.marginsRemoved(QMargins(0,2,2,2)),secondLevelRoundingRadius,secondLevelRoundingRadius); + } else { + painter->drawRect(vopt->rect.marginsRemoved(QMargins(0,2,0,2))); + } + + // draw the check mark + if (vopt->features & QStyleOptionViewItem::HasCheckIndicator) { + QStyleOptionViewItem option(*vopt); + option.rect = checkRect; + option.state = option.state & ~QStyle::State_HasFocus; + + switch (vopt->checkState) { + case Qt::Unchecked: + option.state |= QStyle::State_Off; + break; + case Qt::PartiallyChecked: + option.state |= QStyle::State_NoChange; + break; + case Qt::Checked: + option.state |= QStyle::State_On; + break; + } + proxy()->drawPrimitive(QStyle::PE_IndicatorItemViewItemCheck, &option, painter, widget); + } + + // draw the icon + QIcon::Mode mode = QIcon::Normal; + if (!(vopt->state & QStyle::State_Enabled)) + mode = QIcon::Disabled; + else if (vopt->state & QStyle::State_Selected) + mode = QIcon::Selected; + QIcon::State state = vopt->state & QStyle::State_Open ? QIcon::On : QIcon::Off; + vopt->icon.paint(painter, iconRect, vopt->decorationAlignment, mode, state); + + painter->setPen(QPen(option->palette.buttonText().color())); + if (!view || !view->isPersistentEditorOpen(vopt->index)) + d->viewItemDrawText(painter, vopt, textRect); + if (vopt->state & State_Selected + && (vopt->viewItemPosition == QStyleOptionViewItem::Beginning + || vopt->viewItemPosition == QStyleOptionViewItem::OnlyOne + || vopt->viewItemPosition == QStyleOptionViewItem::Invalid)) { + if (const QListView *lv = qobject_cast<const QListView *>(widget); + lv && lv->viewMode() != QListView::IconMode) { + painter->setPen(QPen(vopt->palette.accent().color())); + painter->drawLine(option->rect.x(), option->rect.y() + 2, + option->rect.x(),option->rect.y() + option->rect.height() - 2); + painter->drawLine(option->rect.x() + 1, option->rect.y() + 2, + option->rect.x() + 1,option->rect.y() + option->rect.height() - 2); + } + } + } + } + break; + } + default: + QWindowsVistaStyle::drawControl(element, option, painter, widget); + } + painter->restore(); +} + +int QWindows11Style::styleHint(StyleHint hint, const QStyleOption *opt, + const QWidget *widget, QStyleHintReturn *returnData) const { + switch (hint) { + case SH_GroupBox_TextLabelColor: + if (opt!=nullptr && widget!=nullptr) + return opt->palette.text().color().rgba(); + return 0; + case QStyle::SH_ItemView_ShowDecorationSelected: + return 1; + case QStyle::SH_Slider_AbsoluteSetButtons: + return Qt::LeftButton; + case QStyle::SH_Slider_PageSetButtons: + return 0; + default: + return QWindowsVistaStyle::styleHint(hint, opt, widget, returnData); + } +} + +QRect QWindows11Style::subElementRect(QStyle::SubElement element, const QStyleOption *option, + const QWidget *widget) const +{ + QRect ret; + switch (element) { + case QStyle::SE_LineEditContents: + ret = option->rect.adjusted(8,0,-8,0); + break; + case QStyle::SE_ItemViewItemText: + if (const auto *item = qstyleoption_cast<const QStyleOptionViewItem *>(option)) { + const int decorationOffset = item->features.testFlag(QStyleOptionViewItem::HasDecoration) ? item->decorationSize.width() : 0; + if (widget && widget->parentWidget() + && widget->parentWidget()->inherits("QComboBoxPrivateContainer")) { + ret = option->rect.adjusted(decorationOffset + 5, 0, -5, 0); + } else { + ret = QWindowsVistaStyle::subElementRect(element, option, widget); + } + } else { + ret = QWindowsVistaStyle::subElementRect(element, option, widget); + } + break; + default: + ret = QWindowsVistaStyle::subElementRect(element, option, widget); + } + return ret; +} + +/*! + \internal + */ +QRect QWindows11Style::subControlRect(ComplexControl control, const QStyleOptionComplex *option, + SubControl subControl, const QWidget *widget) const +{ + QRect ret; + + switch (control) { +#if QT_CONFIG(spinbox) + case CC_SpinBox: + if (const QStyleOptionSpinBox *spinbox = qstyleoption_cast<const QStyleOptionSpinBox *>(option)) { + QSize bs; + int fw = spinbox->frame ? proxy()->pixelMetric(PM_SpinBoxFrameWidth, spinbox, widget) : 0; + bs.setHeight(qMax(8, spinbox->rect.height() - fw)); + bs.setWidth(qMin(24.0, spinbox->rect.width()*(1.0/4.0))); + int y = fw + spinbox->rect.y(); + int x, lx, rx; + x = spinbox->rect.x() + spinbox->rect.width() - fw - 2 * bs.width(); + lx = fw; + rx = x - fw; + switch (subControl) { + case SC_SpinBoxUp: + if (spinbox->buttonSymbols == QAbstractSpinBox::NoButtons) + return QRect(); + ret = QRect(x, y, bs.width(), bs.height()); + break; + case SC_SpinBoxDown: + if (spinbox->buttonSymbols == QAbstractSpinBox::NoButtons) + return QRect(); + ret = QRect(x + bs.width(), y, bs.width(), bs.height()); + break; + case SC_SpinBoxEditField: + if (spinbox->buttonSymbols == QAbstractSpinBox::NoButtons) { + ret = QRect(lx, fw, spinbox->rect.width() - 2*fw, spinbox->rect.height() - 2*fw); + } else { + ret = QRect(lx, fw, rx, spinbox->rect.height() - 2*fw); + } + break; + case SC_SpinBoxFrame: + ret = spinbox->rect; + default: + break; + } + ret = visualRect(spinbox->direction, spinbox->rect, ret); + } + break; + case CC_TitleBar: + if (const QStyleOptionTitleBar *titlebar = qstyleoption_cast<const QStyleOptionTitleBar *>(option)) { + SubControl sc = subControl; + ret = QCommonStyle::subControlRect(control, option, subControl, widget); + static constexpr int indent = 3; + static constexpr int controlWidthMargin = 2; + const int controlHeight = titlebar->rect.height(); + const int controlWidth = 46; + const int iconSize = proxy()->pixelMetric(QStyle::PM_TitleBarButtonIconSize, option, widget); + int offset = -(controlWidthMargin + indent); + + bool isMinimized = titlebar->titleBarState & Qt::WindowMinimized; + bool isMaximized = titlebar->titleBarState & Qt::WindowMaximized; + + switch (sc) { + case SC_TitleBarLabel: + if (titlebar->titleBarFlags & (Qt::WindowTitleHint | Qt::WindowSystemMenuHint)) { + ret = titlebar->rect; + if (titlebar->titleBarFlags & Qt::WindowSystemMenuHint) + ret.adjust(iconSize + controlWidthMargin + indent, 0, -controlWidth, 0); + if (titlebar->titleBarFlags & Qt::WindowMinimizeButtonHint) + ret.adjust(0, 0, -controlWidth, 0); + if (titlebar->titleBarFlags & Qt::WindowMaximizeButtonHint) + ret.adjust(0, 0, -controlWidth, 0); + if (titlebar->titleBarFlags & Qt::WindowShadeButtonHint) + ret.adjust(0, 0, -controlWidth, 0); + if (titlebar->titleBarFlags & Qt::WindowContextHelpButtonHint) + ret.adjust(0, 0, -controlWidth, 0); + } + break; + case SC_TitleBarContextHelpButton: + if (titlebar->titleBarFlags & Qt::WindowContextHelpButtonHint) + offset += controlWidth; + Q_FALLTHROUGH(); + case SC_TitleBarMinButton: + if (!isMinimized && (titlebar->titleBarFlags & Qt::WindowMinimizeButtonHint)) + offset += controlWidth; + else if (sc == SC_TitleBarMinButton) + break; + Q_FALLTHROUGH(); + case SC_TitleBarNormalButton: + if (isMinimized && (titlebar->titleBarFlags & Qt::WindowMinimizeButtonHint)) + offset += controlWidth; + else if (isMaximized && (titlebar->titleBarFlags & Qt::WindowMaximizeButtonHint)) + offset += controlWidth; + else if (sc == SC_TitleBarNormalButton) + break; + Q_FALLTHROUGH(); + case SC_TitleBarMaxButton: + if (!isMaximized && (titlebar->titleBarFlags & Qt::WindowMaximizeButtonHint)) + offset += controlWidth; + else if (sc == SC_TitleBarMaxButton) + break; + Q_FALLTHROUGH(); + case SC_TitleBarShadeButton: + if (!isMinimized && (titlebar->titleBarFlags & Qt::WindowShadeButtonHint)) + offset += controlWidth; + else if (sc == SC_TitleBarShadeButton) + break; + Q_FALLTHROUGH(); + case SC_TitleBarUnshadeButton: + if (isMinimized && (titlebar->titleBarFlags & Qt::WindowShadeButtonHint)) + offset += controlWidth; + else if (sc == SC_TitleBarUnshadeButton) + break; + Q_FALLTHROUGH(); + case SC_TitleBarCloseButton: + if (titlebar->titleBarFlags & Qt::WindowSystemMenuHint) + offset += controlWidth; + else if (sc == SC_TitleBarCloseButton) + break; + ret.setRect(titlebar->rect.right() - offset, titlebar->rect.top(), + controlWidth, controlHeight); + break; + case SC_TitleBarSysMenu: + if (titlebar->titleBarFlags & Qt::WindowSystemMenuHint) { + ret.setRect(titlebar->rect.left() + controlWidthMargin + indent, titlebar->rect.top() + iconSize/2, + iconSize, iconSize); + } + break; + default: + break; + } + if (widget && isMinimized && titlebar->rect.width() < offset) + const_cast<QWidget*>(widget)->resize(controlWidthMargin + indent + offset + iconSize + controlWidthMargin, controlWidth); + ret = visualRect(titlebar->direction, titlebar->rect, ret); + } + break; +#endif // Qt_NO_SPINBOX + case CC_ScrollBar: + { + ret = QCommonStyle::subControlRect(control, option, subControl, widget); + + switch (subControl) { + case QStyle::SC_ScrollBarAddLine: + if (const QStyleOptionSlider *scrollbar = qstyleoption_cast<const QStyleOptionSlider *>(option)) { + if (scrollbar->orientation == Qt::Vertical) { + ret = ret.adjusted(2,2,-2,-3); + } else { + ret = ret.adjusted(3,2,-2,-2); + } + } + break; + case QStyle::SC_ScrollBarSubLine: + if (const QStyleOptionSlider *scrollbar = qstyleoption_cast<const QStyleOptionSlider *>(option)) { + if (scrollbar->orientation == Qt::Vertical) { + ret = ret.adjusted(2,2,-2,-3); + } else { + ret = ret.adjusted(3,2,-2,-2); + } + } + break; + default: + break; + } + break; + } + default: + ret = QWindowsVistaStyle::subControlRect(control, option, subControl, widget); + } + return ret; +} + +/*! + \internal + */ +QSize QWindows11Style::sizeFromContents(ContentsType type, const QStyleOption *option, + const QSize &size, const QWidget *widget) const +{ + QSize contentSize(size); + + switch (type) { + + case CT_Menu: + contentSize += QSize(10, 0); + break; + +#if QT_CONFIG(menubar) + case CT_MenuBarItem: + if (!contentSize.isEmpty()) { + constexpr int hMargin = 2 * 6; + constexpr int hPadding = 2 * 11; + constexpr int itemHeight = 32; + contentSize.setWidth(contentSize.width() + hMargin + hPadding); + contentSize.setHeight(itemHeight); + } + break; +#endif + + default: + contentSize = QWindowsVistaStyle::sizeFromContents(type, option, size, widget); + break; + } + + return contentSize; +} + + +/*! + \internal + */ +int QWindows11Style::pixelMetric(PixelMetric metric, const QStyleOption *option, const QWidget *widget) const +{ + int res = 0; + + switch (metric) { + case QStyle::PM_IndicatorWidth: + case QStyle::PM_IndicatorHeight: + case QStyle::PM_ExclusiveIndicatorWidth: + case QStyle::PM_ExclusiveIndicatorHeight: + res = 16; + break; + case QStyle::PM_SliderLength: + res = int(QStyleHelper::dpiScaled(16, option)); + break; + case QStyle::PM_TitleBarButtonIconSize: + res = 16; + break; + case QStyle::PM_TitleBarButtonSize: + res = 32; + break; + case QStyle::PM_ScrollBarExtent: + res = 12; + break; + default: + res = QWindowsVistaStyle::pixelMetric(metric, option, widget); + } + + return res; +} + +void QWindows11Style::polish(QWidget* widget) +{ + QWindowsVistaStyle::polish(widget); + const bool isScrollBar = qobject_cast<QScrollBar *>(widget); + if (isScrollBar || qobject_cast<QMenu *>(widget) || widget->inherits("QComboBoxPrivateContainer")) { + bool wasCreated = widget->testAttribute(Qt::WA_WState_Created); + bool layoutDirection = widget->testAttribute(Qt::WA_RightToLeft); + widget->setAttribute(Qt::WA_OpaquePaintEvent,false); + widget->setAttribute(Qt::WA_TranslucentBackground); + widget->setWindowFlag(Qt::FramelessWindowHint); + widget->setWindowFlag(Qt::NoDropShadowWindowHint); + widget->setAttribute(Qt::WA_RightToLeft, layoutDirection); + widget->setAttribute(Qt::WA_WState_Created, wasCreated); + auto pal = widget->palette(); + pal.setColor(widget->backgroundRole(), Qt::transparent); + widget->setPalette(pal); + if (!isScrollBar) { // for menus and combobox containers... + QGraphicsDropShadowEffect* dropshadow = new QGraphicsDropShadowEffect(widget); + dropshadow->setBlurRadius(3); + dropshadow->setXOffset(3); + dropshadow->setYOffset(3); + widget->setGraphicsEffect(dropshadow); + } + } else if (QComboBox* cb = qobject_cast<QComboBox*>(widget)) { + if (cb->isEditable()) { + QLineEdit *le = cb->lineEdit(); + le->setFrame(false); + } + } else if (widget->inherits("QAbstractSpinBox")) { + const int minWidth = 2 * 24 + 40; + const int originalWidth = widget->size().width(); + if (originalWidth < minWidth) { + widget->resize(minWidth, widget->size().height()); + widget->setProperty(originalWidthProperty.constData(), originalWidth); + } + } else if (widget->inherits("QAbstractButton") || widget->inherits("QToolButton")) { + widget->setAutoFillBackground(false); + } else if (qobject_cast<QGraphicsView *>(widget) && !qobject_cast<QTextEdit *>(widget)) { + QPalette pal = widget->palette(); + pal.setColor(QPalette::Base, pal.window().color()); + widget->setPalette(pal); + } else if (const auto *scrollarea = qobject_cast<QAbstractScrollArea *>(widget); + scrollarea +#if QT_CONFIG(mdiarea) + && !qobject_cast<QMdiArea *>(widget) +#endif + ) { + QPalette pal = scrollarea->viewport()->palette(); + const QPalette originalPalette = pal; + pal.setColor(scrollarea->viewport()->backgroundRole(), Qt::transparent); + scrollarea->viewport()->setPalette(pal); + scrollarea->viewport()->setProperty("_q_original_background_palette", originalPalette); + } else if (qobject_cast<QCommandLinkButton *>(widget)) { + widget->setProperty("_qt_usingVistaStyle",false); + QPalette pal = widget->palette(); + pal.setColor(QPalette::ButtonText, pal.text().color()); + pal.setColor(QPalette::BrightText, pal.text().color()); + widget->setPalette(pal); + } +} + +void QWindows11Style::unpolish(QWidget *widget) +{ + QWindowsVistaStyle::unpolish(widget); + if (const auto *scrollarea = qobject_cast<QAbstractScrollArea *>(widget); + scrollarea +#if QT_CONFIG(mdiarea) + && !qobject_cast<QMdiArea *>(widget) +#endif + ) { + const QPalette pal = scrollarea->viewport()->property("_q_original_background_palette").value<QPalette>(); + scrollarea->viewport()->setPalette(pal); + scrollarea->viewport()->setProperty("_q_original_background_palette", QVariant()); + } + if (widget->inherits("QAbstractSpinBox")) { + const QVariant originalWidth = widget->property(originalWidthProperty.constData()); + if (originalWidth.isValid()) { + widget->resize(originalWidth.toInt(), widget->size().height()); + widget->setProperty(originalWidthProperty.constData(), QVariant()); + } + } +} + +/* +The colors for Windows 11 are taken from the official WinUI3 Figma style at +https://www.figma.com/community/file/1159947337437047524 +*/ +#define SET_IF_UNRESOLVED(GROUP, ROLE, VALUE) \ + if (!result.isBrushSet(QPalette::Inactive, ROLE) || styleSheetChanged) \ + result.setColor(GROUP, ROLE, VALUE) + +static void populateLightSystemBasePalette(QPalette &result) +{ + static QString oldStyleSheet; + const bool styleSheetChanged = oldStyleSheet != qApp->styleSheet(); + + const QColor textColor = QColor(0x00,0x00,0x00,0xE4); + const QColor btnFace = QColor(0xFF,0xFF,0xFF,0xB3); + const QColor btnHighlight = result.accent().color(); + const QColor btnColor = result.button().color(); + + SET_IF_UNRESOLVED(QPalette::All, QPalette::Highlight, btnHighlight); + SET_IF_UNRESOLVED(QPalette::All, QPalette::WindowText, textColor); + SET_IF_UNRESOLVED(QPalette::All, QPalette::Button, btnFace); + SET_IF_UNRESOLVED(QPalette::All, QPalette::Light, btnColor.lighter(150)); + SET_IF_UNRESOLVED(QPalette::All, QPalette::Dark, btnColor.darker(200)); + SET_IF_UNRESOLVED(QPalette::All, QPalette::Mid, btnColor.darker(150)); + SET_IF_UNRESOLVED(QPalette::All, QPalette::Text, textColor); + SET_IF_UNRESOLVED(QPalette::All, QPalette::BrightText, btnHighlight); + SET_IF_UNRESOLVED(QPalette::All, QPalette::Base, btnFace); + SET_IF_UNRESOLVED(QPalette::All, QPalette::Window, QColor(0xF3,0xF3,0xF3,0xFF)); + SET_IF_UNRESOLVED(QPalette::All, QPalette::ButtonText, textColor); + SET_IF_UNRESOLVED(QPalette::All, QPalette::Midlight, btnColor.lighter(125)); + SET_IF_UNRESOLVED(QPalette::All, QPalette::Shadow, Qt::black); + SET_IF_UNRESOLVED(QPalette::All, QPalette::ToolTipBase, result.window().color()); + SET_IF_UNRESOLVED(QPalette::All, QPalette::ToolTipText, result.windowText().color()); + + if (result.midlight() == result.button()) + result.setColor(QPalette::Midlight, btnColor.lighter(110)); + oldStyleSheet = qApp->styleSheet(); +} + +/*! + \internal + */ +void QWindows11Style::polish(QPalette& result) +{ + highContrastTheme = QGuiApplication::styleHints()->colorScheme() == Qt::ColorScheme::Unknown; + colorSchemeIndex = QGuiApplication::styleHints()->colorScheme() == Qt::ColorScheme::Light ? 0 : 1; + + if (!highContrastTheme && colorSchemeIndex == 0) + populateLightSystemBasePalette(result); + + const bool styleSheetChanged = false; // so the macro works + + SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::Button, result.button().color()); + SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::Window, result.window().color()); + SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::Light, result.light().color()); + SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::Dark, result.dark().color()); + SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::Accent, result.accent().color()); + SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::Highlight, result.highlight().color()); + SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::HighlightedText, result.highlightedText().color()); + SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::Text, result.text().color()); + SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::WindowText, result.windowText().color()); + + if (highContrastTheme) + result.setColor(QPalette::Active, QPalette::HighlightedText, result.windowText().color()); +} + +#undef SET_IF_UNRESOLVED + +QT_END_NAMESPACE diff --git a/src/plugins/styles/modernwindows/qwindows11style_p.h b/src/plugins/styles/modernwindows/qwindows11style_p.h new file mode 100644 index 0000000000..9c54afd967 --- /dev/null +++ b/src/plugins/styles/modernwindows/qwindows11style_p.h @@ -0,0 +1,70 @@ +// 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 + +#ifndef QWINDOWS11STYLE_P_H +#define QWINDOWS11STYLE_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 <QtWidgets/private/qtwidgetsglobal_p.h> +#include <qwindowsvistastyle_p_p.h> + +QT_BEGIN_NAMESPACE + +class QWindows11StylePrivate; +class QWindows11Style; + +class QWindows11Style : public QWindowsVistaStyle +{ + Q_OBJECT +public: + QWindows11Style(); + ~QWindows11Style() override; + void drawComplexControl(ComplexControl control, const QStyleOptionComplex *option, + QPainter *painter, const QWidget *widget) const override; + void drawPrimitive(PrimitiveElement element, const QStyleOption *option, + QPainter *painter, const QWidget *widget) const override; + QRect subElementRect(QStyle::SubElement element, const QStyleOption *option, + const QWidget *widget = nullptr) const override; + QRect subControlRect(ComplexControl control, const QStyleOptionComplex *option, + SubControl subControl, const QWidget *widget) const override; + void drawControl(ControlElement element, const QStyleOption *option, + QPainter *painter, const QWidget *widget) const override; + int styleHint(StyleHint hint, const QStyleOption *opt = nullptr, + const QWidget *widget = nullptr, QStyleHintReturn *returnData = nullptr) const override; + void polish(QWidget* widget) override; + + QSize sizeFromContents(ContentsType type, const QStyleOption *option, + const QSize &size, const QWidget *widget) const override; + int pixelMetric(PixelMetric metric, const QStyleOption *option = nullptr, + const QWidget *widget = nullptr) const override; + void polish(QPalette &pal) override; + void unpolish(QWidget *widget) override; +protected: + QWindows11Style(QWindows11StylePrivate &dd); +private: + Q_DISABLE_COPY_MOVE(QWindows11Style) + Q_DECLARE_PRIVATE(QWindows11Style) + friend class QStyleFactory; + + bool highContrastTheme = false; + int colorSchemeIndex = 0; + const QFont assetFont = QFont("Segoe Fluent Icons"); //Font to load icons from +}; + +class QWindows11StylePrivate : public QWindowsVistaStylePrivate { + Q_DECLARE_PUBLIC(QWindows11Style) +}; + +QT_END_NAMESPACE + +#endif // QWINDOWS11STYLE_P_H diff --git a/src/plugins/styles/modernwindows/qwindowsthemedata.cpp b/src/plugins/styles/modernwindows/qwindowsthemedata.cpp new file mode 100644 index 0000000000..44569e054d --- /dev/null +++ b/src/plugins/styles/modernwindows/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/modernwindows/qwindowsthemedata_p.h b/src/plugins/styles/modernwindows/qwindowsthemedata_p.h new file mode 100644 index 0000000000..5de2bcbdea --- /dev/null +++ b/src/plugins/styles/modernwindows/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 <qwidget.h> +#include <qt_windows.h> +#include <uxtheme.h> +#include <vssym32.h> +#include <limits.h> + + +// 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/modernwindows/qwindowsvistaanimation.cpp b/src/plugins/styles/modernwindows/qwindowsvistaanimation.cpp new file mode 100644 index 0000000000..ac9a5ad8c0 --- /dev/null +++ b/src/plugins/styles/modernwindows/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/modernwindows/qwindowsvistaanimation_p.h b/src/plugins/styles/modernwindows/qwindowsvistaanimation_p.h new file mode 100644 index 0000000000..817fa5f4b5 --- /dev/null +++ b/src/plugins/styles/modernwindows/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 <private/qstyleanimation_p.h> +#include <QtWidgets/private/qwindowsstyle_p.h> + +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/modernwindows/qwindowsvistastyle.cpp b/src/plugins/styles/modernwindows/qwindowsvistastyle.cpp new file mode 100644 index 0000000000..beee1d6f31 --- /dev/null +++ b/src/plugins/styles/modernwindows/qwindowsvistastyle.cpp @@ -0,0 +1,5012 @@ +// 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 "qwindowsvistastyle_p.h" +#include "qwindowsvistastyle_p_p.h" +#include "qwindowsvistaanimation_p.h" +#include <qoperatingsystemversion.h> +#include <qscreen.h> +#include <qstylehints.h> +#include <qwindow.h> +#include <private/qstyleanimation_p.h> +#include <private/qstylehelper_p.h> +#include <qpa/qplatformnativeinterface.h> +#include <private/qapplication_p.h> +#include <private/qsystemlibrary_p.h> +#include <private/qwindowsthemecache_p.h> + +#include "qdrawutil.h" // for now +#include <qbackingstore.h> + + +QT_BEGIN_NAMESPACE + +using namespace Qt::StringLiterals; + +static const int windowsItemFrame = 2; // menu item frame width +static const int windowsItemHMargin = 3; // menu item hor text margin +static const int windowsItemVMargin = 4; // menu item ver text margin +static const int windowsArrowHMargin = 6; // arrow horizontal margin +static const int windowsRightBorder = 15; // right border on windows + +#ifndef TMT_CONTENTMARGINS +# define TMT_CONTENTMARGINS 3602 +#endif +#ifndef TMT_SIZINGMARGINS +# define TMT_SIZINGMARGINS 3601 +#endif +#ifndef LISS_NORMAL +# define LISS_NORMAL 1 +# define LISS_HOT 2 +# define LISS_SELECTED 3 +# define LISS_DISABLED 4 +# define LISS_SELECTEDNOTFOCUS 5 +# define LISS_HOTSELECTED 6 +#endif +#ifndef BP_COMMANDLINK +# define BP_COMMANDLINK 6 +# define BP_COMMANDLINKGLYPH 7 +# define CMDLGS_NORMAL 1 +# define CMDLGS_HOT 2 +# define CMDLGS_PRESSED 3 +# define CMDLGS_DISABLED 4 +#endif + +// QWindowsVistaStylePrivate ------------------------------------------------------------------------- +// Static initializations +HWND QWindowsVistaStylePrivate::m_vistaTreeViewHelper = nullptr; +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<const QStyleOptionProgressBar *>(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<QWindowsApplication *>(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) +{ + 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()); + 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); +} + +/* \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 QWindowsStylePrivate::pixelMetricFromSystemDp(QStyle::PM_TitleBarHeight, option, widget); + 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() +{ + QWindowsThemeCache::clearAllThemeCaches(); + QWindowsVistaStylePrivate::cleanupVistaTreeViewTheming(); +} + +HTHEME QWindowsVistaStylePrivate::createTheme(int theme, HWND hwnd) +{ + if (theme == VistaTreeViewTheme && QWindowsVistaStylePrivate::initVistaTreeViewTheming()) + hwnd = QWindowsVistaStylePrivate::m_vistaTreeViewHelper; + return QWindowsThemeCache::createTheme(theme, hwnd); +} + +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<HDC>(nativeInterface->nativeResourceForBackingStore(QByteArrayLiteral("getDC"), backingStore)); + } + return nullptr; +} + +QString QWindowsVistaStylePrivate::themeName(int theme) +{ + return QWindowsThemeCache::themeName(theme); +} + +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<QAbstractSpinBox*>(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<QWidget *>(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<HWND>(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<void **>(&bufferPixels), nullptr, 0); + GdiFlush(); + nullBitmap = static_cast<HBITMAP>(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) { + QVarLengthArray<char> buf(256); + RGNDATA *rd = reinterpret_cast<RGNDATA *>(buf.data()); + if (GetRegionData(dest, buf.size(), rd) == 0) { + const auto numBytes = GetRegionData(dest, 0, nullptr); + if (numBytes > 0) { + buf.resize(numBytes); + rd = reinterpret_cast<RGNDATA *>(buf.data()); + if (GetRegionData(dest, numBytes, rd) == 0) + rd = nullptr; + } else { + rd = nullptr; + } + } + if (rd) { + RECT *r = reinterpret_cast<RECT *>(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; + } + } + } + + 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<const DWORD *>(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<DWORD *>(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<DWORD *>(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<const QWidget *>(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<void *>(&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<void *>(&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; +} + +/*! + \internal + + Animations are started at a frame that is based on the current time, + which makes it impossible to run baseline tests with this style. Allow + overriding through a dynamic property. +*/ +QTime QWindowsVistaStylePrivate::animationTime() const +{ + Q_Q(const QWindowsVistaStyle); + static bool animationTimeOverride = q->dynamicPropertyNames().contains("_qt_animation_time"); + if (animationTimeOverride) + return q->property("_qt_animation_time").toTime(); + return QTime::currentTime(); +} + +/* \internal + Checks and returns the style object +*/ +inline QObject *styleObject(const QStyleOption *option) { + return option ? option->styleObject : nullptr; +} + +/* \internal + Checks if we can animate on a style option +*/ +bool canAnimate(const QStyleOption *option) { + return option + && option->styleObject + && !option->styleObject->property("_q_no_animation").toBool(); +} + +static inline QImage createAnimationBuffer(const QStyleOption *option, const QWidget *widget) +{ + const qreal devicePixelRatio = widget + ? widget->devicePixelRatioF() : qApp->devicePixelRatio(); + QImage result(option->rect.size() * devicePixelRatio, QImage::Format_ARGB32_Premultiplied); + result.setDevicePixelRatio(devicePixelRatio); + result.fill(0); + return result; +} + +/* \internal + Used by animations to clone a styleoption and shift its offset +*/ +QStyleOption *clonedAnimationStyleOption(const QStyleOption*option) { + QStyleOption *styleOption = nullptr; + if (const QStyleOptionSlider *slider = qstyleoption_cast<const QStyleOptionSlider*>(option)) + styleOption = new QStyleOptionSlider(*slider); + else if (const QStyleOptionSpinBox *spinbox = qstyleoption_cast<const QStyleOptionSpinBox*>(option)) + styleOption = new QStyleOptionSpinBox(*spinbox); + else if (const QStyleOptionGroupBox *groupBox = qstyleoption_cast<const QStyleOptionGroupBox*>(option)) + styleOption = new QStyleOptionGroupBox(*groupBox); + else if (const QStyleOptionComboBox *combo = qstyleoption_cast<const QStyleOptionComboBox*>(option)) + styleOption = new QStyleOptionComboBox(*combo); + else if (const QStyleOptionButton *button = qstyleoption_cast<const QStyleOptionButton*>(option)) + styleOption = new QStyleOptionButton(*button); + else + styleOption = new QStyleOption(*option); + styleOption->rect = QRect(QPoint(0,0), option->rect.size()); + return styleOption; +} + +/* \internal + Used by animations to delete cloned styleoption +*/ +void deleteClonedAnimationStyleOption(const QStyleOption *option) +{ + if (const QStyleOptionSlider *slider = qstyleoption_cast<const QStyleOptionSlider*>(option)) + delete slider; + else if (const QStyleOptionSpinBox *spinbox = qstyleoption_cast<const QStyleOptionSpinBox*>(option)) + delete spinbox; + else if (const QStyleOptionGroupBox *groupBox = qstyleoption_cast<const QStyleOptionGroupBox*>(option)) + delete groupBox; + else if (const QStyleOptionComboBox *combo = qstyleoption_cast<const QStyleOptionComboBox*>(option)) + delete combo; + else if (const QStyleOptionButton *button = qstyleoption_cast<const QStyleOptionButton*>(option)) + delete button; + else + delete option; +} + +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; +} + +#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; +} + +// 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) + +/* + 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; + break; + default : + retVal = true; + } + return retVal; +} + +//convert Qt state flags to uxtheme button states +static int buttonStateId(int flags, int partId) +{ + int stateId = 0; + if (partId == BP_RADIOBUTTON || partId == BP_CHECKBOX) { + if (!(flags & QStyle::State_Enabled)) + stateId = RBS_UNCHECKEDDISABLED; + else if (flags & QStyle::State_Sunken) + stateId = RBS_UNCHECKEDPRESSED; + else if (flags & QStyle::State_MouseOver) + stateId = RBS_UNCHECKEDHOT; + else + stateId = RBS_UNCHECKEDNORMAL; + + if (flags & QStyle::State_On) + stateId += RBS_CHECKEDNORMAL-1; + + } else if (partId == BP_PUSHBUTTON) { + if (!(flags & QStyle::State_Enabled)) + stateId = PBS_DISABLED; + else if (flags & (QStyle::State_Sunken | QStyle::State_On)) + stateId = PBS_PRESSED; + else if (flags & QStyle::State_MouseOver) + stateId = PBS_HOT; + else + stateId = PBS_NORMAL; + } else { + Q_ASSERT(1); + } + return stateId; +} + +static inline bool supportsStateTransition(QStyle::PrimitiveElement element, + const QStyleOption *option, + const QWidget *widget) +{ + bool result = false; + switch (element) { + case QStyle::PE_IndicatorRadioButton: + case QStyle::PE_IndicatorCheckBox: + result = true; + break; + // QTBUG-40634, do not animate when color is set in palette for PE_PanelLineEdit. + case QStyle::PE_FrameLineEdit: + result = !QWindowsVistaStylePrivate::isLineEditBaseColorSet(option, widget); + break; + default: + break; + } + 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) +{ +} + +/*! + \internal + Constructs a QWindowsStyle object. +*/ +QWindowsVistaStyle::QWindowsVistaStyle(QWindowsVistaStylePrivate &dd) : QWindowsStyle(dd) +{ +} + +/*! + Destructor. +*/ +QWindowsVistaStyle::~QWindowsVistaStyle() = default; + + +/*! + \internal + + Animations are used for some state transitions on specific widgets. + + Only one running animation can exist for a widget at any specific + time. Animations can be added through + QWindowsVistaStylePrivate::startAnimation(Animation *) and any + existing animation on a widget can be retrieved with + QWindowsVistaStylePrivate::widgetAnimation(Widget *). + + Once an animation has been started, + QWindowsVistaStylePrivate::timerEvent(QTimerEvent *) will + continuously call update() on the widget until it is stopped, + meaning that drawPrimitive will be called many times until the + transition has completed. During this time, the result will be + retrieved by the Animation::paint(...) function and not by the style + itself. + + To determine if a transition should occur, the style needs to know + the previous state of the widget as well as the current one. This is + solved by updating dynamic properties on the widget every time the + function is called. + + Transitions interrupting existing transitions should always be + smooth, so whenever a hover-transition is started on a pulsating + button, it uses the current frame of the pulse-animation as the + starting image for the hover transition. + + */ +void QWindowsVistaStyle::drawPrimitive(PrimitiveElement element, const QStyleOption *option, + QPainter *painter, const QWidget *widget) const +{ + if (!QWindowsVistaStylePrivate::useVista()) { + QWindowsStyle::drawPrimitive(element, option, painter, widget); + return; + } + + QWindowsVistaStylePrivate *d = const_cast<QWindowsVistaStylePrivate*>(d_func()); + + int state = option->state; + QRect rect = option->rect; + + 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); + + bool doTransition = oldState && + ((state & State_Sunken) != (oldState & State_Sunken) || + (state & State_On) != (oldState & State_On) || + (state & State_MouseOver) != (oldState & State_MouseOver)); + + if (oldRect != newRect || + (state & State_Enabled) != (oldState & State_Enabled) || + (state & State_Active) != (oldState & State_Active)) + d->stopAnimation(styleObject); + + if (state & State_ReadOnly && element == PE_FrameLineEdit) // Do not animate read only line edits + doTransition = false; + + if (doTransition) { + QStyleOption *styleOption = clonedAnimationStyleOption(option); + styleOption->state = QStyle::State(oldState); + + QWindowsVistaAnimation *animate = qobject_cast<QWindowsVistaAnimation *>(d->animation(styleObject)); + QWindowsVistaTransition *transition = new QWindowsVistaTransition(styleObject); + + // 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); + + QImage endImage = createAnimationBuffer(option, widget); + QPainter endPainter(&endImage); + + // 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); + + 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); + + transition->setEndImage(endImage); + + 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; + + if (oldState & State_HasFocus) + fromState = ETS_SELECTED; + else if (oldState & State_MouseOver) + fromState = ETS_HOT; + else + fromState = ETS_NORMAL; + + if (state & State_HasFocus) + toState = ETS_SELECTED; + else if (state & State_MouseOver) + toState = ETS_HOT; + 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; + + fromState = buttonStateId(oldState, partId); + toState = buttonStateId(option->state, partId); + } + + // Retrieve the transition time between the states from the system. + if (theme + && SUCCEEDED(GetThemeTransitionDuration(theme, partId, fromState, toState, + TMT_TRANSITIONDURATIONS, &duration))) { + transition->setDuration(int(duration)); + } + transition->setStartTime(d->animationTime()); + + deleteClonedAnimationStyleOption(styleOption); + d->startAnimation(transition); + } + styleObject->setProperty("_q_no_animation", false); + } + } + + 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<const QStyleOptionButton *>(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 auto *header = qstyleoption_cast<const QStyleOptionHeader *>(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 + QWindowsThemeData theme(widget, painter, + QWindowsVistaStylePrivate::HeaderTheme, + HP_HEADERSORTARROW, stateId, option->rect); + d->drawBackground(theme); + return; + } + break; + + case PE_IndicatorCheckBox: + if (auto *animate = + qobject_cast<QWindowsVistaAnimation *>(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: + if (QWindowsVistaAnimation *animate = + qobject_cast<QWindowsVistaAnimation *>(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_IndicatorRadioButton: + if (QWindowsVistaAnimation *animate = + qobject_cast<QWindowsVistaAnimation *>(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: +#if QT_CONFIG(accessibility) + if (QStyleHelper::isInstanceOf(option->styleObject, QAccessible::EditableText) + || QStyleHelper::isInstanceOf(option->styleObject, QAccessible::StaticText) || +#else + if ( +#endif + (widget && widget->inherits("QTextEdit"))) { + painter->save(); + int stateId = ETS_NORMAL; + if (!(state & State_Enabled)) + stateId = ETS_DISABLED; + else if (state & State_ReadOnly) + stateId = ETS_READONLY; + else if (state & State_HasFocus) + stateId = ETS_SELECTED; + 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); + QRegion clipRegion = option->rect; + QRegion content = option->rect.adjusted(borderSize, borderSize, -borderSize, -borderSize); + clipRegion ^= content; + painter->setClipRegion(clipRegion); + d->drawBackground(theme); + painter->restore(); + return; + } else { + 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; + } + + case PE_PanelMenuBar: + break; + +#if QT_CONFIG(dockwidget) + case PE_IndicatorDockWidgetResizeHandle: + return; + + case PE_FrameDockWidget: + if (const auto *frm = qstyleoption_cast<const QStyleOptionFrame *>(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<const QStyleOptionTabWidgetFrame *>(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<const QStyleOptionFrame *>(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 auto *panel = qstyleoption_cast<const QStyleOptionFrame *>(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 (option->state & State_ReadOnly) + stateId = EBS_READONLY; + else if (option->state & State_MouseOver) + stateId = EBS_HOT; + + 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) { + d->drawBackground(theme); + } else { + QBrush fillColor = option->palette.brush(QPalette::Base); + if (!isEnabled) { + PROPERTYORIGIN origin = PO_NOTFOUND; + GetThemePropertyOrigin(theme.handle(), theme.partId, theme.stateId, TMT_FILLCOLOR, &origin); + // Use only if the fill property comes from our part + if ((origin == PO_PART || origin == PO_STATE)) { + COLORREF bgRef; + GetThemeColor(theme.handle(), partId, stateId, TMT_FILLCOLOR, &bgRef); + fillColor = QBrush(qRgb(GetRValue(bgRef), GetGValue(bgRef), GetBValue(bgRef))); + } + } + painter->fillRect(option->rect, fillColor); + } + } + if (panel->lineWidth > 0) + proxy()->drawPrimitive(PE_FrameLineEdit, panel, painter, widget); + } + 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 *animate = qobject_cast<QWindowsVistaAnimation *>(d->animation(styleObject(option)))) { + animate->paint(painter, option); + } else { + if (QWindowsVistaStylePrivate::isItemViewDelegateLineEdit(widget)) { + // we try to check if this lineedit is a delegate on a QAbstractItemView-derived class. + QPen oldPen = painter->pen(); + // Inner white border + painter->setPen(QPen(option->palette.base().color(), 1)); + painter->drawRect(option->rect.adjusted(1, 1, -2, -2)); + // Outer dark border + 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; + if (!(state & State_Enabled)) + stateId = ETS_DISABLED; + else if (state & State_ReadOnly) + stateId = ETS_READONLY; + else if (state & State_HasFocus) + stateId = ETS_SELECTED; + else if (state & State_MouseOver) + stateId = ETS_HOT; + QWindowsThemeData theme(widget, painter, + QWindowsVistaStylePrivate::EditTheme, + EP_EDITBORDER_NOSCROLL, stateId, option->rect); + theme.noContent = true; + painter->save(); + QRegion clipRegion = option->rect; + clipRegion -= option->rect.adjusted(2, 2, -2, -2); + painter->setClipRegion(clipRegion); + d->drawBackground(theme); + painter->restore(); + } + 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<const QStyleOptionFrame *>(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<const QStyleOptionTabBarBase *>(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_Widget: { +#if QT_CONFIG(dialogbuttonbox) + const QDialogButtonBox *buttonBox = nullptr; + if (qobject_cast<const QMessageBox *> (widget)) + buttonBox = widget->findChild<const QDialogButtonBox *>(QLatin1String("qt_msgbox_buttonbox")); +#if QT_CONFIG(inputdialog) + else if (qobject_cast<const QInputDialog *> (widget)) + buttonBox = widget->findChild<const QDialogButtonBox *>(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); + + //draw bottom panel part + QRect buttonRect = option->rect; + buttonRect.setTop(buttonBox->geometry().top()); + theme.rect = buttonRect; + theme.partId = TDLG_SECONDARYPANEL; + d->drawBackground(theme); + } +#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<const QAbstractItemView *>(widget)) { + newStyle = !qobject_cast<const QTableView*>(view); + selectionBehavior = view->selectionBehavior(); + selectionMode = view->selectionMode(); +#if QT_CONFIG(accessibility) + } else if (!widget) { + newStyle = !QStyleHelper::hasAncestor(option->styleObject, QAccessible::MenuItem) ; +#endif + } + + if (newStyle && (vopt = qstyleoption_cast<const QStyleOptionViewItem *>(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()); + + 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()); + + QSize sectionSize = itemRect.size(); + if (vopt->showDecorationSelected) + sectionSize = vopt->rect.size(); + + 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 (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); + + QWindowsThemeData theme(widget, &pixmapPainter, + QWindowsVistaStylePrivate::VistaTreeViewTheme, + LVP_LISTITEM, state, QRect(0, 0, sectionSize.width(), sectionSize.height())); + + if (!theme.isValid()) + break; + + d->drawBackground(theme); + QPixmapCache::insert(key, pixmap); + } + } + + 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); + } + } + return; + } + break; + } + + default: + break; + } + + QWindowsThemeData theme(widget, painter, themeNumber, partId, stateId, rect); + + 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<QWindowsVistaStylePrivate*>(d_func()); + + int ret = 0; + switch (hint) { + case SH_EtchDisabledText: + ret = (qobject_cast<const QLabel*>(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<QStyleHintReturnMask *>(returnData); + const auto *titlebar = qstyleoption_cast<const QStyleOptionTitleBar *>(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<const QStyleOptionRubberBand *>(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<QStyleHintReturnMask*>(returnData)) { + ret = true; + QWindowsThemeData themeData(widget, nullptr, + QWindowsVistaStylePrivate::ToolTipTheme, + TTP_STANDARD, TTSS_NORMAL, option->rect); + mask->region = d->region(themeData); + } + } + 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: + ret = QWindowsStyle::styleHint(hint, option, widget, returnData); + break; + } + + return ret; +} + + +/*! + \internal + + see drawPrimitive for comments on the animation support + */ +void QWindowsVistaStyle::drawControl(ControlElement element, const QStyleOption *option, + QPainter *painter, const QWidget *widget) const +{ + if (!QWindowsVistaStylePrivate::useVista()) { + QWindowsStyle::drawControl(element, option, painter, widget); + return; + } + + QWindowsVistaStylePrivate *d = const_cast<QWindowsVistaStylePrivate*>(d_func()); + + bool selected = option->state & State_Selected; + bool pressed = option->state & State_Sunken; + bool disabled = !(option->state & State_Enabled); + + int state = option->state; + int themeNumber = -1; + + QRect rect(option->rect); + State flags = option->state; + int partId = 0; + int stateId = 0; + + if (d->transitionsEnabled() && canAnimate(option)) { + if (element == CE_PushButtonBevel) { + QRect oldRect; + QRect newRect; + + QObject *styleObject = option->styleObject; + + 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); + + bool wasDefault = false; + bool isDefault = false; + if (const QStyleOptionButton *button = qstyleoption_cast<const QStyleOptionButton *>(option)) { + wasDefault = styleObject->property("_q_isdefault").toBool(); + isDefault = button->features & QStyleOptionButton::DefaultButton; + styleObject->setProperty("_q_isdefault", isDefault); + } + + bool doTransition = ((state & State_Sunken) != (oldState & State_Sunken) || + (state & State_On) != (oldState & State_On) || + (state & State_MouseOver) != (oldState & State_MouseOver)); + + if (oldRect != newRect || (wasDefault && !isDefault)) { + doTransition = false; + d->stopAnimation(styleObject); + } + + if (doTransition) { + styleObject->setProperty("_q_no_animation", true); + + QWindowsVistaTransition *t = new QWindowsVistaTransition(styleObject); + QWindowsVistaAnimation *anim = qobject_cast<QWindowsVistaAnimation *>(d->animation(styleObject)); + QStyleOption *styleOption = clonedAnimationStyleOption(option); + styleOption->state = QStyle::State(oldState); + + QImage startImage = createAnimationBuffer(option, widget); + QPainter startPainter(&startImage); + + // Use current state of existing animation if already one is running + if (!anim) { + proxy()->drawControl(element, styleOption, &startPainter, widget); + } else { + anim->paint(&startPainter, styleOption); + d->stopAnimation(styleObject); + } + + t->setStartImage(startImage); + QImage endImage = createAnimationBuffer(option, widget); + QPainter endPainter(&endImage); + styleOption->state = option->state; + proxy()->drawControl(element, styleOption, &endPainter, widget); + t->setEndImage(endImage); + + + DWORD duration = 0; + const HTHEME theme = OpenThemeData(nullptr, L"Button"); + + int fromState = buttonStateId(oldState, BP_PUSHBUTTON); + int toState = buttonStateId(option->state, BP_PUSHBUTTON); + if (GetThemeTransitionDuration(theme, BP_PUSHBUTTON, fromState, toState, TMT_TRANSITIONDURATIONS, &duration) == S_OK) + t->setDuration(int(duration)); + else + t->setDuration(0); + t->setStartTime(d->animationTime()); + styleObject->setProperty("_q_no_animation", false); + + deleteClonedAnimationStyleOption(styleOption); + d->startAnimation(t); + } + + QWindowsVistaAnimation *anim = qobject_cast<QWindowsVistaAnimation *>(d->animation(styleObject)); + if (anim) { + anim->paint(painter, option); + return; + } + + } + } + + bool hMirrored = false; + bool vMirrored = false; + int rotate = 0; + + switch (element) { + case CE_PushButtonBevel: + if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(option)) { + themeNumber = QWindowsVistaStylePrivate::ButtonTheme; + partId = BP_PUSHBUTTON; + if (btn->features & QStyleOptionButton::CommandLinkButton) + partId = BP_COMMANDLINK; + bool justFlat = (btn->features & QStyleOptionButton::Flat) && !(flags & (State_On|State_Sunken)); + if (!(flags & State_Enabled) && !(btn->features & QStyleOptionButton::Flat)) + stateId = PBS_DISABLED; + else if (justFlat) + ; + else if (flags & (State_Sunken | State_On)) + stateId = PBS_PRESSED; + else if (flags & State_MouseOver) + stateId = PBS_HOT; + else if (btn->features & QStyleOptionButton::DefaultButton && (state & State_Active)) + stateId = PBS_DEFAULTED; + else + stateId = PBS_NORMAL; + + if (!justFlat) { + + if (d->transitionsEnabled() && (btn->features & QStyleOptionButton::DefaultButton) && + !(state & (State_Sunken | State_On)) && !(state & State_MouseOver) && + (state & State_Enabled) && (state & State_Active)) + { + QWindowsVistaAnimation *anim = qobject_cast<QWindowsVistaAnimation *>(d->animation(styleObject(option))); + + if (!anim) { + QImage startImage = createAnimationBuffer(option, widget); + QImage alternateImage = createAnimationBuffer(option, widget); + + QWindowsVistaPulse *pulse = new QWindowsVistaPulse(styleObject(option)); + + QPainter startPainter(&startImage); + stateId = PBS_DEFAULTED; + 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()); + pulse->setDuration(2000); + d->startAnimation(pulse); + anim = pulse; + } + + if (anim) + anim->paint(painter, option); + else { + QWindowsThemeData theme(widget, painter, themeNumber, partId, stateId, rect); + d->drawBackground(theme); + } + } + else { + QWindowsThemeData theme(widget, painter, themeNumber, partId, stateId, rect); + d->drawBackground(theme); + } + } + + if (btn->features & QStyleOptionButton::HasMenu) { + int mbiw = 0, mbih = 0; + QWindowsThemeData theme(widget, nullptr, QWindowsVistaStylePrivate::ToolBarTheme, + TP_DROPDOWNBUTTON); + if (theme.isValid()) { + const QSizeF size = theme.size() * QStyleHelper::dpiScaled(1, option); + if (!size.isEmpty()) { + mbiw = qRound(size.width()); + mbih = qRound(size.height()); + } + } + 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)); + proxy()->drawPrimitive(PE_IndicatorArrowDown, &newBtn, painter, widget); + } + } + 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<const QStyleOptionSizeGrip *>(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<const QStyleOptionTab *>(option)) + stateId = tab->state & State_Enabled ? TIS_NORMAL : TIS_DISABLED; + break; + + case CE_TabBarTabShape: + if (const auto *tab = qstyleoption_cast<const QStyleOptionTab *>(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<const QStyleOptionProgressBar *>(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 auto *bar = qstyleoption_cast<const QStyleOptionProgressBar *>(option)) { + bool isIndeterminate = (bar->minimum == 0 && bar->maximum == 0); + const bool vertical = !(bar->state & QStyle::State_Horizontal); + const bool inverted = bar->invertedAppearance; + + if (isIndeterminate || (bar->progress > 0 && (bar->progress < bar->maximum) && d->transitionsEnabled())) { + if (!d->animation(styleObject(option))) + d->startAnimation(new QProgressStyleAnimation(d->animationFps, styleObject(option))); + } else { + d->stopAnimation(styleObject(option)); + } + + 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 (auto *progressAnimation = qobject_cast<QProgressStyleAnimation *>(d->animation(styleObject(option)))) { + int glowSize = 120; + int animationWidth = glowSize * 2 + (vertical ? theme.rect.height() : theme.rect.width()); + int animOffset = progressAnimation->startTime().msecsTo(current) / 4; + if (animOffset > animationWidth) + progressAnimation->setStartTime(d->animationTime()); + painter->save(); + painter->setClipRect(theme.rect); + QRect animRect; + QSize pixmapSize(14, 14); + if (vertical) { + animRect = QRect(theme.rect.left(), + inverted ? rect.top() - glowSize + animOffset : + rect.bottom() + glowSize - animOffset, + rect.width(), glowSize); + 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); + pixmapSize.setWidth(animRect.width()); + } + QString name = QString::fromLatin1("qiprogress-%1-%2").arg(pixmapSize.width()).arg(pixmapSize.height()); + QPixmap pixmap; + if (!QPixmapCache::find(name, &pixmap)) { + QImage image(pixmapSize, QImage::Format_ARGB32); + image.fill(Qt::transparent); + QPainter imagePainter(&image); + theme.painter = &imagePainter; + theme.partId = vertical ? PP_FILLVERT : PP_FILL; + theme.rect = QRect(QPoint(0,0), animRect.size()); + QLinearGradient alphaGradient(0, 0, vertical ? 0 : image.width(), + vertical ? image.height() : 0); + alphaGradient.setColorAt(0, QColor(0, 0, 0, 0)); + alphaGradient.setColorAt(0.5, QColor(0, 0, 0, 220)); + alphaGradient.setColorAt(1, QColor(0, 0, 0, 0)); + imagePainter.fillRect(image.rect(), alphaGradient); + imagePainter.setCompositionMode(QPainter::CompositionMode_SourceIn); + d->drawBackground(theme); + imagePainter.end(); + pixmap = QPixmap::fromImage(image); + QPixmapCache::insert(name, pixmap); + } + painter->drawPixmap(animRect, pixmap); + painter->restore(); + } + } else { + qint64 progress = qMax<qint64>(bar->progress, bar->minimum); // workaround for bug in QProgressBar + + if (vertical) { + int maxHeight = option->rect.height(); + int minHeight = 0; + double vc6_workaround = ((progress - qint64(bar->minimum)) / qMax(double(1.0), double(qint64(bar->maximum) - qint64(bar->minimum))) * maxHeight); + int height = isIndeterminate ? maxHeight: qMax(int(vc6_workaround), minHeight); + theme.rect.setHeight(height); + if (!inverted) + theme.rect.moveTop(rect.height() - theme.rect.height()); + } else { + int maxWidth = option->rect.width(); + int minWidth = 0; + double vc6_workaround = ((progress - qint64(bar->minimum)) / qMax(double(1.0), double(qint64(bar->maximum) - qint64(bar->minimum))) * maxWidth); + 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); + } + d->drawBackground(theme); + + if (QProgressStyleAnimation *a = qobject_cast<QProgressStyleAnimation *>(d->animation(styleObject(option)))) { + int glowSize = 140; + int animationWidth = glowSize * 2 + (vertical ? theme.rect.height() : theme.rect.width()); + int animOffset = a->startTime().msecsTo(current) / 4; + theme.partId = vertical ? PP_MOVEOVERLAYVERT : PP_MOVEOVERLAY; + if (animOffset > animationWidth) { + if (bar->progress < bar->maximum) + a->setStartTime(d->animationTime()); + else + d->stopAnimation(styleObject(option)); //we stop the glow motion only after it has + //moved out of view + } + painter->save(); + painter->setClipRect(theme.rect); + if (vertical) { + theme.rect = QRect(theme.rect.left(), + inverted ? rect.top() - glowSize + animOffset : + rect.bottom() + glowSize - animOffset, + rect.width(), glowSize); + } else { + theme.rect = QRect(rect.left() - glowSize + animOffset,rect.top(), glowSize, rect.height()); + theme.rect = QStyle::visualRect(reverse ? Qt::RightToLeft : Qt::LeftToRight, option->rect, theme.rect); + } + d->drawBackground(theme); + painter->restore(); + } + } + } + return; + + case CE_MenuBarItem: + if (const auto *mbi = qstyleoption_cast<const QStyleOptionMenuItem *>(option)) { + if (mbi->menuItemType == QStyleOptionMenuItem::DefaultItem) + break; + + QPalette::ColorRole textRole = disabled ? QPalette::Text : QPalette::ButtonText; + QPixmap pix = mbi->icon.pixmap(proxy()->pixelMetric(PM_SmallIconSize, option, widget), QIcon::Normal); + + int alignment = Qt::AlignCenter | Qt::TextShowMnemonic | Qt::TextDontClip | Qt::TextSingleLine; + if (!proxy()->styleHint(SH_UnderlineShortcut, mbi, widget)) + alignment |= Qt::TextHideMnemonic; + + 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. + 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; + + 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); + } + return; + +#if QT_CONFIG(menu) + case CE_MenuEmptyArea: + if (const auto *menuitem = qstyleoption_cast<const QStyleOptionMenuItem *>(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 auto *menuitem = qstyleoption_cast<const QStyleOptionMenuItem *>(option)) { + // windows always has a check column, regardless whether we have an icon or not + const qreal factor = QWindowsVistaStylePrivate::nativeMetricScaleFactor(widget); + int checkcol = qRound(qreal(25) * factor); + const int gutterWidth = qRound(qreal(3) * factor); + { + 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); + checkcol = qMax(menuitem->maxIconWidth, qRound(gutterWidth + size.width() + margins.left() + margins.right())); + } + QRect rect = option->rect; + + //draw vertical menu line + if (option->direction == Qt::LeftToRight) + checkcol += rect.x(); + 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); + QWindowsThemeData theme2(widget, painter, QWindowsVistaStylePrivate::MenuTheme, + MENU_POPUPGUTTER, stateId, gutterRect); + d->drawBackground(theme2); + + int x, y, w, h; + menuitem->rect.getRect(&x, &y, &w, &h); + int tab = menuitem->reservedShortcutWidth; + bool dis = !(menuitem->state & State_Enabled); + bool checked = menuitem->checkType != QStyleOptionMenuItem::NotCheckable + ? menuitem->checked : false; + bool act = menuitem->state & State_Selected; + + if (menuitem->menuItemType == QStyleOptionMenuItem::Separator) { + int yoff = y-2 + h / 2; + const int separatorSize = qRound(qreal(6) * QWindowsStylePrivate::nativeMetricScaleFactor(widget)); + QPoint p1 = QPoint(x + checkcol, yoff); + QPoint p2 = QPoint(x + w + separatorSize, yoff); + stateId = MBI_HOT; + QRect subRect(p1.x() + (gutterWidth - menuitem->rect.x()), p1.y(), + p2.x() - p1.x(), separatorSize); + subRect = QStyle::visualRect(option->direction, option->rect, 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())); + + if (act) { + stateId = dis ? MBI_DISABLED : MBI_HOT; + QWindowsThemeData theme2(widget, painter, + QWindowsVistaStylePrivate::MenuTheme, + MENU_POPUPITEM, stateId, option->rect); + d->drawBackground(theme2); + } + + if (checked) { + 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); + const QMarginsF margins = themeSize.margins() * QWindowsStylePrivate::nativeMetricScaleFactor(widget); + QRect checkRect(0, 0, qRound(size.width() + margins.left() + margins.right()), + qRound(size.height() + margins.bottom() + margins.top())); + checkRect.moveCenter(vCheckRect.center()); + theme.rect = checkRect; + + d->drawBackground(theme); + + if (menuitem->icon.isNull()) { + checkRect = QRect(QPoint(0, 0), size.toSize()); + checkRect.moveCenter(theme.rect.center()); + theme.rect = checkRect; + + theme.partId = MENU_POPUPCHECK; + bool bullet = menuitem->checkType & QStyleOptionMenuItem::Exclusive; + if (dis) + theme.stateId = bullet ? MC_BULLETDISABLED: MC_CHECKMARKDISABLED; + else + theme.stateId = bullet ? MC_BULLETNORMAL: MC_CHECKMARKNORMAL; + d->drawBackground(theme); + } + } + + if (!menuitem->icon.isNull()) { + QIcon::Mode mode = dis ? QIcon::Disabled : QIcon::Normal; + if (act && !dis) + mode = QIcon::Active; + const auto size = proxy()->pixelMetric(PM_SmallIconSize, option, widget); + const auto dpr = painter->device()->devicePixelRatio(); + const auto pixmap = menuitem->icon.pixmap({size, size}, dpr, mode, + checked ? QIcon::On : QIcon::Off); + QRect pmr(QPoint(0, 0), pixmap.deviceIndependentSize().toSize()); + pmr.moveCenter(vCheckRect.center()); + painter->setPen(menuitem->palette.text().color()); + painter->drawPixmap(pmr.topLeft(), pixmap); + } + + painter->setPen(menuitem->palette.buttonText().color()); + + const QColor textColor = menuitem->palette.text().color(); + if (dis) + painter->setPen(textColor); + + int xm = windowsItemFrame + checkcol + windowsItemHMargin + (gutterWidth - menuitem->rect.x()) - 1; + int xpos = menuitem->rect.x() + xm; + QRect textRect(xpos, y + windowsItemVMargin, w - xm - windowsRightBorder - tab + 1, h - 2 * windowsItemVMargin); + QRect vTextRect = visualRect(option->direction, menuitem->rect, textRect); + QString s = menuitem->text; + if (!s.isEmpty()) { // draw text + painter->save(); + int t = s.indexOf(QLatin1Char('\t')); + int text_flags = Qt::AlignVCenter | Qt::TextShowMnemonic | Qt::TextDontClip | Qt::TextSingleLine; + if (!proxy()->styleHint(SH_UnderlineShortcut, menuitem, widget)) + text_flags |= Qt::TextHideMnemonic; + text_flags |= Qt::AlignLeft; + if (t >= 0) { + QRect vShortcutRect = visualRect(option->direction, menuitem->rect, + QRect(textRect.topRight(), QPoint(menuitem->rect.right(), textRect.bottom()))); + painter->drawText(vShortcutRect, text_flags, s.mid(t + 1)); + s = s.left(t); + } + QFont font = menuitem->font; + if (menuitem->menuItemType == QStyleOptionMenuItem::DefaultItem) + font.setBold(true); + painter->setFont(font); + painter->setPen(textColor); + painter->drawText(vTextRect, text_flags, s.left(t)); + painter->restore(); + } + if (menuitem->menuItemType == QStyleOptionMenuItem::SubMenu) {// draw sub menu arrow + int dim = (h - 2 * windowsItemFrame) / 2; + PrimitiveElement arrow; + arrow = (option->direction == Qt::RightToLeft) ? PE_IndicatorArrowLeft : PE_IndicatorArrowRight; + xpos = x + w - windowsArrowHMargin - windowsItemFrame - dim; + QRect vSubMenuRect = visualRect(option->direction, menuitem->rect, QRect(xpos, y + h / 2 - dim / 2, dim, dim)); + QStyleOptionMenuItem newMI = *menuitem; + newMI.rect = vSubMenuRect; + newMI.state = dis ? State_None : State_Enabled; + proxy()->drawPrimitive(arrow, &newMI, painter, widget); + } + } + return; +#endif // QT_CONFIG(menu) + + case CE_HeaderSection: + if (const QStyleOptionHeader *header = qstyleoption_cast<const QStyleOptionHeader *>(option)) { + partId = HP_HEADERITEM; + if (flags & State_Sunken) + stateId = HIS_PRESSED; + else if (flags & State_MouseOver) + stateId = HIS_HOT; + else + stateId = HIS_NORMAL; + + if (header->sortIndicator != QStyleOptionHeader::None) + stateId += 3; + + QWindowsThemeData theme(widget, painter, + QWindowsVistaStylePrivate::HeaderTheme, + partId, stateId, option->rect); + d->drawBackground(theme); + } + 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 auto *toolbar = qstyleoption_cast<const QStyleOptionToolBar *>(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); + } + return; + +#if QT_CONFIG(dockwidget) + case CE_DockWidgetTitle: + if (const auto *dwOpt = qstyleoption_cast<const QStyleOptionDockWidget *>(option)) { + QRect rect = option->rect; + const QDockWidget *dw = qobject_cast<const QDockWidget *>(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; + + if (verticalTitleBar) { + rect = rect.transposed(); + + painter->translate(rect.left() - 1, rect.top() + rect.width()); + painter->rotate(-90); + painter->translate(-rect.left() + 1, -rect.top()); + } + + QRect r = option->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 && 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) { + 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 | Qt::TextHideMnemonic, 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 | Qt::TextHideMnemonic, 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::TextHideMnemonic, + dwOpt->palette, + dwOpt->state & State_Enabled, titleText, + QPalette::WindowText); + } + } + } + return; +#endif // QT_CONFIG(dockwidget) + +#if QT_CONFIG(rubberband) + case CE_RubberBand: + if (qstyleoption_cast<const QStyleOptionRubberBand *>(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<const QAbstractItemView *>(widget); + bool newStyle = true; + + if (qobject_cast<const QTableView*>(widget)) + newStyle = false; + + QWindowsThemeData theme(widget, painter, themeNumber, partId, stateId, rect); + + if (newStyle && view && (vopt = qstyleoption_cast<const QStyleOptionViewItem *>(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; + if (!theme.isValid()) { + QWindowsStyle::drawControl(element, &adjustedOption, painter, widget); + return; + } + } else { + if (!theme.isValid()) { + QWindowsStyle::drawControl(element, option, painter, widget); + return; + } + } + + 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); + return; +#endif // QT_CONFIG(combobox) + + default: + 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 +{ + QWindowsVistaStylePrivate *d = const_cast<QWindowsVistaStylePrivate*>(d_func()); + + if (!QWindowsVistaStylePrivate::useVista()) { + QWindowsStyle::drawComplexControl(control, option, painter, widget); + return; + } + + State state = option->state; + SubControls sub = option->subControls; + QRect r = option->rect; + + int partId = 0; + int stateId = 0; + + State flags = option->state; + if (widget && widget->testAttribute(Qt::WA_UnderMouse) && widget->isActiveWindow()) + flags |= State_MouseOver; + + 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(); + int oldActiveControls = styleObject->property("_q_stylecontrols").toInt(); + + QRect oldRect = styleObject->property("_q_stylerect").toRect(); + styleObject->setProperty("_q_stylestate", int(option->state)); + 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)); + + if (qstyleoption_cast<const QStyleOptionSlider *>(option)) { + QRect oldSliderPos = styleObject->property("_q_stylesliderpos").toRect(); + QRect currentPos = proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarSlider, widget); + styleObject->setProperty("_q_stylesliderpos", currentPos); + if (oldSliderPos != currentPos) { + doTransition = false; + d->stopAnimation(styleObject); + } + } else if (control == CC_SpinBox) { + //spinboxes have a transition when focus changes + if (!doTransition) + doTransition = (state & State_HasFocus) != (oldState & State_HasFocus); + } + + if (oldRect != option->rect) { + doTransition = false; + d->stopAnimation(styleObject); + } + + if (doTransition) { + QImage startImage = createAnimationBuffer(option, widget); + QPainter startPainter(&startImage); + + QImage endImage = createAnimationBuffer(option, widget); + QPainter endPainter(&endImage); + + QWindowsVistaAnimation *anim = qobject_cast<QWindowsVistaAnimation *>(d->animation(styleObject)); + QWindowsVistaTransition *t = new QWindowsVistaTransition(styleObject); + + // Draw the image that ends the animation by using the current styleoption + QStyleOptionComplex *styleOption = qstyleoption_cast<QStyleOptionComplex*>(clonedAnimationStyleOption(option)); + + styleObject->setProperty("_q_no_animation", true); + + // Draw transition source + if (!anim) { + styleOption->state = QStyle::State(oldState); + styleOption->activeSubControls = QStyle::SubControl(oldActiveControls); + proxy()->drawComplexControl(control, styleOption, &startPainter, widget); + } else { + anim->paint(&startPainter, option); + } + t->setStartImage(startImage); + + // Draw transition target + styleOption->state = option->state; + styleOption->activeSubControls = option->activeSubControls; + proxy()->drawComplexControl(control, styleOption, &endPainter, widget); + + styleObject->setProperty("_q_no_animation", false); + + t->setEndImage(endImage); + t->setStartTime(d->animationTime()); + + if (option->state & State_MouseOver || option->state & State_Sunken) + t->setDuration(150); + else + t->setDuration(500); + + deleteClonedAnimationStyleOption(styleOption); + d->startAnimation(t); + } + if (QWindowsVistaAnimation *anim = qobject_cast<QWindowsVistaAnimation *>(d->animation(styleObject))) { + anim->paint(painter, option); + return; + } + } + } + + switch (control) { + +#if QT_CONFIG(slider) + case CC_Slider: + if (const auto *slider = qstyleoption_cast<const QStyleOptionSlider *>(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<QLine, 32> 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<const QStyleOptionToolButton *>(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<const QStyleOptionTitleBar *>(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<const QStyleOptionSlider *>(option)) + QStyleHelper::drawDial(dial, painter); + break; +#endif // QT_CONFIG(dial) + + case CC_ComboBox: + if (const QStyleOptionComboBox *cmb = qstyleoption_cast<const QStyleOptionComboBox *>(option)) { + if (cmb->editable) { + if (sub & SC_ComboBoxEditField) { + partId = EP_EDITBORDER_NOSCROLL; + if (!(flags & State_Enabled)) + stateId = ETS_DISABLED; + else if (flags & State_MouseOver) + stateId = ETS_HOT; + else if (flags & State_HasFocus) + stateId = ETS_FOCUSED; + else + stateId = ETS_NORMAL; + + 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); + QWindowsThemeData theme(widget, painter, QWindowsVistaStylePrivate::ComboboxTheme); + theme.rect = subRect; + partId = option->direction == Qt::RightToLeft ? CP_DROPDOWNBUTTONLEFT : CP_DROPDOWNBUTTONRIGHT; + + if (!(cmb->state & State_Enabled)) + stateId = CBXS_DISABLED; + else if (cmb->state & State_Sunken || cmb->state & State_On) + stateId = CBXS_PRESSED; + else if (cmb->state & State_MouseOver && option->activeSubControls & SC_ComboBoxArrow) + stateId = CBXS_HOT; + else + stateId = CBXS_NORMAL; + + theme.partId = partId; + theme.stateId = stateId; + d->drawBackground(theme); + } + + } else { + if (sub & SC_ComboBoxFrame) { + QWindowsThemeData theme(widget, painter, QWindowsVistaStylePrivate::ComboboxTheme); + theme.rect = option->rect; + theme.partId = CP_READONLY; + if (!(cmb->state & State_Enabled)) + theme.stateId = CBXS_DISABLED; + else if (cmb->state & State_Sunken || cmb->state & State_On) + theme.stateId = CBXS_PRESSED; + else if (cmb->state & State_MouseOver) + theme.stateId = CBXS_HOT; + else + theme.stateId = CBXS_NORMAL; + d->drawBackground(theme); + } + if (sub & SC_ComboBoxArrow) { + 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)) + theme.stateId = CBXS_DISABLED; + else + theme.stateId = CBXS_NORMAL; + d->drawBackground(theme); + } + if ((sub & SC_ComboBoxEditField) && (flags & State_HasFocus)) { + QStyleOptionFocusRect fropt; + fropt.QStyleOption::operator=(*cmb); + fropt.rect = proxy()->subControlRect(CC_ComboBox, option, SC_ComboBoxEditField, widget); + proxy()->drawPrimitive(PE_FrameFocusRect, &fropt, painter, widget); + } + } + } + break; + + case CC_ScrollBar: + if (const QStyleOptionSlider *scrollbar = qstyleoption_cast<const QStyleOptionSlider *>(option)) { + QWindowsThemeData theme(widget, painter, QWindowsVistaStylePrivate::ScrollBarTheme); + bool maxedOut = (scrollbar->maximum == scrollbar->minimum); + if (maxedOut) + flags &= ~State_Enabled; + + bool isHorz = flags & State_Horizontal; + bool isRTL = option->direction == Qt::RightToLeft; + if (sub & SC_ScrollBarAddLine) { + theme.rect = proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarAddLine, widget); + partId = SBP_ARROWBTN; + if (!(flags & State_Enabled)) + stateId = (isHorz ? (isRTL ? ABS_LEFTDISABLED : ABS_RIGHTDISABLED) : ABS_DOWNDISABLED); + else if (scrollbar->activeSubControls & SC_ScrollBarAddLine && (scrollbar->state & State_Sunken)) + stateId = (isHorz ? (isRTL ? ABS_LEFTPRESSED : ABS_RIGHTPRESSED) : ABS_DOWNPRESSED); + else if (scrollbar->activeSubControls & SC_ScrollBarAddLine && (scrollbar->state & State_MouseOver)) + stateId = (isHorz ? (isRTL ? ABS_LEFTHOT : ABS_RIGHTHOT) : ABS_DOWNHOT); + else if (scrollbar->state & State_MouseOver) + stateId = (isHorz ? (isRTL ? ABS_LEFTHOVER : ABS_RIGHTHOVER) : ABS_DOWNHOVER); + else + stateId = (isHorz ? (isRTL ? ABS_LEFTNORMAL : ABS_RIGHTNORMAL) : ABS_DOWNNORMAL); + theme.partId = partId; + theme.stateId = stateId; + d->drawBackground(theme); + } + if (sub & SC_ScrollBarSubLine) { + theme.rect = proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarSubLine, widget); + partId = SBP_ARROWBTN; + if (!(flags & State_Enabled)) + stateId = (isHorz ? (isRTL ? ABS_RIGHTDISABLED : ABS_LEFTDISABLED) : ABS_UPDISABLED); + else if (scrollbar->activeSubControls & SC_ScrollBarSubLine && (scrollbar->state & State_Sunken)) + stateId = (isHorz ? (isRTL ? ABS_RIGHTPRESSED : ABS_LEFTPRESSED) : ABS_UPPRESSED); + else if (scrollbar->activeSubControls & SC_ScrollBarSubLine && (scrollbar->state & State_MouseOver)) + stateId = (isHorz ? (isRTL ? ABS_RIGHTHOT : ABS_LEFTHOT) : ABS_UPHOT); + else if (scrollbar->state & State_MouseOver) + stateId = (isHorz ? (isRTL ? ABS_RIGHTHOVER : ABS_LEFTHOVER) : ABS_UPHOVER); + else + stateId = (isHorz ? (isRTL ? ABS_RIGHTNORMAL : ABS_LEFTNORMAL) : ABS_UPNORMAL); + theme.partId = partId; + theme.stateId = stateId; + d->drawBackground(theme); + } + if (maxedOut) { + theme.rect = proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarSlider, widget); + theme.rect = theme.rect.united(proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarSubPage, widget)); + theme.rect = theme.rect.united(proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarAddPage, widget)); + partId = flags & State_Horizontal ? SBP_LOWERTRACKHORZ : SBP_LOWERTRACKVERT; + stateId = SCRBS_DISABLED; + theme.partId = partId; + theme.stateId = stateId; + d->drawBackground(theme); + } else { + if (sub & SC_ScrollBarSubPage) { + theme.rect = proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarSubPage, widget); + partId = flags & State_Horizontal ? SBP_UPPERTRACKHORZ : SBP_UPPERTRACKVERT; + if (!(flags & State_Enabled)) + stateId = SCRBS_DISABLED; + else if (scrollbar->activeSubControls & SC_ScrollBarSubPage && (scrollbar->state & State_Sunken)) + stateId = SCRBS_PRESSED; + else if (scrollbar->activeSubControls & SC_ScrollBarSubPage && (scrollbar->state & State_MouseOver)) + stateId = SCRBS_HOT; + else + stateId = SCRBS_NORMAL; + theme.partId = partId; + theme.stateId = stateId; + d->drawBackground(theme); + } + if (sub & SC_ScrollBarAddPage) { + theme.rect = proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarAddPage, widget); + partId = flags & State_Horizontal ? SBP_LOWERTRACKHORZ : SBP_LOWERTRACKVERT; + if (!(flags & State_Enabled)) + stateId = SCRBS_DISABLED; + else if (scrollbar->activeSubControls & SC_ScrollBarAddPage && (scrollbar->state & State_Sunken)) + stateId = SCRBS_PRESSED; + else if (scrollbar->activeSubControls & SC_ScrollBarAddPage && (scrollbar->state & State_MouseOver)) + stateId = SCRBS_HOT; + else + stateId = SCRBS_NORMAL; + theme.partId = partId; + theme.stateId = stateId; + d->drawBackground(theme); + } + if (sub & SC_ScrollBarSlider) { + theme.rect = proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarSlider, widget); + if (!(flags & State_Enabled)) + stateId = SCRBS_DISABLED; + else if (scrollbar->activeSubControls & SC_ScrollBarSlider && (scrollbar->state & State_Sunken)) + stateId = SCRBS_PRESSED; + else if (scrollbar->activeSubControls & SC_ScrollBarSlider && (scrollbar->state & State_MouseOver)) + stateId = SCRBS_HOT; + else if (option->state & State_MouseOver) + stateId = SCRBS_HOVER; + else + stateId = SCRBS_NORMAL; + + // Draw handle + theme.partId = flags & State_Horizontal ? SBP_THUMBBTNHORZ : SBP_THUMBBTNVERT; + theme.stateId = stateId; + d->drawBackground(theme); + } + } + } + break; + +#if QT_CONFIG(spinbox) + case CC_SpinBox: + if (const QStyleOptionSpinBox *sb = qstyleoption_cast<const QStyleOptionSpinBox *>(option)) { + QWindowsThemeData theme(widget, painter, QWindowsVistaStylePrivate::SpinTheme); + if (sb->frame && (sub & SC_SpinBoxFrame)) { + partId = EP_EDITBORDER_NOSCROLL; + if (!(flags & State_Enabled)) + stateId = ETS_DISABLED; + else if (flags & State_MouseOver) + stateId = ETS_HOT; + else if (flags & State_HasFocus) + stateId = ETS_SELECTED; + else + stateId = ETS_NORMAL; + + 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), + // let ftheme.noContent be false, which fixes the spinbox rendering in QQC + ftheme.noContent = (widget != nullptr); + d->drawBackground(ftheme); + } + if (sub & SC_SpinBoxUp) { + theme.rect = proxy()->subControlRect(CC_SpinBox, option, SC_SpinBoxUp, widget).adjusted(0, 0, 0, 1); + partId = SPNP_UP; + if (!(sb->stepEnabled & QAbstractSpinBox::StepUpEnabled) || !(flags & State_Enabled)) + stateId = UPS_DISABLED; + else if (sb->activeSubControls == SC_SpinBoxUp && (sb->state & State_Sunken)) + stateId = UPS_PRESSED; + else if (sb->activeSubControls == SC_SpinBoxUp && (sb->state & State_MouseOver)) + stateId = UPS_HOT; + else + stateId = UPS_NORMAL; + theme.partId = partId; + theme.stateId = stateId; + d->drawBackground(theme); + } + if (sub & SC_SpinBoxDown) { + theme.rect = proxy()->subControlRect(CC_SpinBox, option, SC_SpinBoxDown, widget); + partId = SPNP_DOWN; + if (!(sb->stepEnabled & QAbstractSpinBox::StepDownEnabled) || !(flags & State_Enabled)) + stateId = DNS_DISABLED; + else if (sb->activeSubControls == SC_SpinBoxDown && (sb->state & State_Sunken)) + stateId = DNS_PRESSED; + else if (sb->activeSubControls == SC_SpinBoxDown && (sb->state & State_MouseOver)) + stateId = DNS_HOT; + else + stateId = DNS_NORMAL; + theme.partId = partId; + theme.stateId = stateId; + d->drawBackground(theme); + } + } + break; +#endif // QT_CONFIG(spinbox) + + default: + QWindowsStyle::drawComplexControl(control, option, painter, widget); + break; + } +} + +/*! + \internal + */ +QSize QWindowsVistaStyle::sizeFromContents(ContentsType type, const QStyleOption *option, + const QSize &size, const QWidget *widget) const +{ + if (!QWindowsVistaStylePrivate::useVista()) + return QWindowsStyle::sizeFromContents(type, option, size, widget); + + QSize contentSize(size); + + switch (type) { + 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 QT_CONFIG(menubar) + case CT_MenuBarItem: + if (!contentSize.isEmpty()) + contentSize += QSize(windowsItemHMargin * 5 + 1, 5); + break; +#endif + + 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<const QStyleOptionMenuItem *>(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<const QStyleOptionComplex *>(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; + } + 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<QStyleOptionHeader *>(const_cast<QStyleOption *>(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 contentSize; +} + +/*! + \internal + */ +QRect QWindowsVistaStyle::subElementRect(SubElement element, const QStyleOption *option, const QWidget *widget) const +{ + if (!QWindowsVistaStylePrivate::useVista()) + return QWindowsStyle::subElementRect(element, option, widget); + + QRect rect(option->rect); + + switch (element) { + case SE_PushButtonContents: + if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(option)) { + MARGINS borderSize; + const HTHEME theme = OpenThemeData(widget ? QWindowsVistaStylePrivate::winId(widget) : nullptr, L"Button"); + if (theme) { + int stateId = PBS_NORMAL; + 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; + + 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_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<const QStyleOptionTabWidgetFrame *>(option)) { + rect = QWindowsStyle::subElementRect(element, option, widget); + if (const QTabWidget *tabWidget = qobject_cast<const QTabWidget *>(widget)) { + if (tabWidget->documentMode()) + break; + rect.adjust(0, 0, -2, -2); + } + } + break; + + case SE_TabWidgetTabBar: { + rect = QWindowsStyle::subElementRect(element, option, widget); + const auto *twfOption = qstyleoption_cast<const QStyleOptionTabWidgetFrame *>(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_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 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<const QStyleOptionHeader *>(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 SE_ProgressBarContents: + rect = QCommonStyle::subElementRect(SE_ProgressBarGroove, option, widget); + break; + + case SE_ItemViewItemDecoration: + rect = QWindowsStyle::subElementRect(element, option, widget); + if (qstyleoption_cast<const QStyleOptionViewItem *>(option)) + rect.adjust(-2, 0, 2, 0); + break; + + case SE_ItemViewItemFocusRect: + rect = QWindowsStyle::subElementRect(element, option, widget); + if (const QStyleOptionViewItem *vopt = qstyleoption_cast<const QStyleOptionViewItem *>(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: + rect = QWindowsStyle::subElementRect(element, option, widget); + break; + } + + return rect; +} + +/*! + \internal + */ +QRect QWindowsVistaStyle::subControlRect(ComplexControl control, const QStyleOptionComplex *option, + SubControl subControl, const QWidget *widget) const +{ + if (!QWindowsVistaStylePrivate::useVista()) + return QWindowsStyle::subControlRect(control, option, subControl, widget); + + QRect rect; + + switch (control) { +#if QT_CONFIG(combobox) + case CC_ComboBox: + if (const auto *cb = qstyleoption_cast<const QStyleOptionComboBox *>(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; + const int arrowWidth = qRound(QStyleHelper::dpiScaled(16, option)); + const int arrowButtonWidth = bmarg + arrowWidth; + const int xpos = x + wi - arrowButtonWidth; + + switch (subControl) { + case SC_ComboBoxFrame: + case SC_ComboBoxListBoxPopup: + rect = cb->rect; + break; + + case SC_ComboBoxArrow: { + rect.setRect(xpos, y , arrowButtonWidth, he); + } + break; + + case SC_ComboBoxEditField: { + rect.setRect(x + margin, y + margin, wi - 2 * margin - arrowWidth, he - 2 * margin); + } + break; + + default: + break; + } + } + break; +#endif // QT_CONFIG(combobox) + + case CC_TitleBar: + if (const QStyleOptionTitleBar *tb = qstyleoption_cast<const QStyleOptionTitleBar *>(option)) { + if (!buttonVisible(subControl, tb)) + return rect; + const qreal factor = QWindowsStylePrivate::nativeMetricScaleFactor(widget); + 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)); + + 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; + const int delta = buttonWidth + 2; + int controlTop = option->rect.bottom() - buttonHeight - 2; + + switch (subControl) { + case SC_TitleBarLabel: { + rect = QRect(frameWidth, 0, width - (buttonWidth + frameWidth + 10), height); + if (isToolTitle) { + if (sysmenuHint) { + rect.adjust(0, 0, int(-buttonWidth - 3 * factor), 0); + } + if (minimizeHint || maximizeHint) + rect.adjust(0, 0, int(-buttonWidth - 2 * factor), 0); + } else { + if (sysmenuHint) { + const int leftOffset = int(height - 8 * factor); + rect.adjust(leftOffset, 0, 0, int(4 * factor)); + } + if (minimizeHint) + rect.adjust(0, 0, int(-buttonWidth - 2 * factor), 0); + if (maximizeHint) + rect.adjust(0, 0, int(-buttonWidth - 2 * factor), 0); + if (contextHint) + rect.adjust(0, 0, int(-buttonWidth - 2 * factor), 0); + if (shadeHint) + rect.adjust(0, 0, int(-buttonWidth - 2 * factor), 0); + } + rect.translate(0, int(2 * factor)); + } + 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 = 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; + +#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: + return 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 +{ + return QWindowsStyle::hitTestComplexControl(control, option, pos, widget); +} + +/*! + \internal + */ +int QWindowsVistaStyle::pixelMetric(PixelMetric metric, const QStyleOption *option, const QWidget *widget) const +{ + if (!QWindowsVistaStylePrivate::useVista()) + return QWindowsStyle::pixelMetric(metric, option, widget); + + int ret = QWindowsVistaStylePrivate::fixedPixelMetric(metric); + if (ret != QWindowsStylePrivate::InvalidMetric) + return int(QStyleHelper::dpiScaled(ret, option)); + + 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<const QListView*>(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<const QStyleOptionTab *>(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<const QStyleOptionToolButton *>(option) ? 1 : 0; + break; + + default: + res = QWindowsStyle::pixelMetric(metric, option, widget); + } + + return res; +} + +/*! + \internal + */ +void QWindowsVistaStyle::polish(QWidget *widget) +{ + QWindowsStyle::polish(widget); + if (!QWindowsVistaStylePrivate::useVista()) + return; + + if (false +#if QT_CONFIG(abstractbutton) + || qobject_cast<QAbstractButton*>(widget) +#endif // QT_CONFIG(abstractbutton) + || qobject_cast<QToolButton*>(widget) + || qobject_cast<QTabBar*>(widget) +#if QT_CONFIG(combobox) + || qobject_cast<QComboBox*>(widget) +#endif // QT_CONFIG(combobox) + || qobject_cast<QScrollBar*>(widget) + || qobject_cast<QSlider*>(widget) + || qobject_cast<QHeaderView*>(widget) +#if QT_CONFIG(spinbox) + || qobject_cast<QAbstractSpinBox*>(widget) + || qobject_cast<QSpinBox*>(widget) +#endif // QT_CONFIG(spinbox) + ) { + widget->setAttribute(Qt::WA_Hover); + } + +#if QT_CONFIG(rubberband) + if (qobject_cast<QRubberBand*>(widget)) + widget->setWindowOpacity(0.6); +#endif + +#if QT_CONFIG(lineedit) + if (qobject_cast<QLineEdit*>(widget)) + widget->setAttribute(Qt::WA_Hover); + else +#endif // QT_CONFIG(lineedit) + if (qobject_cast<QGroupBox*>(widget)) + widget->setAttribute(Qt::WA_Hover); +#if QT_CONFIG(commandlinkbutton) + else if (qobject_cast<QCommandLinkButton*>(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::Active, QPalette::ButtonText, QColor(21, 28, 85)); + pal.setColor(QPalette::Active, 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); + pal.setResolveMask(0); + widget->setPalette(pal); + } + } else if (qobject_cast<QMessageBox *> (widget)) { + widget->setAttribute(Qt::WA_StyledBackground); +#if QT_CONFIG(dialogbuttonbox) + QDialogButtonBox *buttonBox = widget->findChild<QDialogButtonBox *>(QLatin1String("qt_msgbox_buttonbox")); + if (buttonBox) + buttonBox->setContentsMargins(0, 9, 0, 0); +#endif + } +#if QT_CONFIG(inputdialog) + else if (qobject_cast<QInputDialog *> (widget)) { + widget->setAttribute(Qt::WA_StyledBackground); +#if QT_CONFIG(dialogbuttonbox) + QDialogButtonBox *buttonBox = widget->findChild<QDialogButtonBox *>(QLatin1String("qt_inputdlg_buttonbox")); + if (buttonBox) + buttonBox->setContentsMargins(0, 9, 0, 0); +#endif + } +#endif // QT_CONFIG(inputdialog) + else if (QTreeView *tree = qobject_cast<QTreeView *> (widget)) { + tree->viewport()->setAttribute(Qt::WA_Hover); + } + else if (QListView *list = qobject_cast<QListView *> (widget)) { + list->viewport()->setAttribute(Qt::WA_Hover); + } +} + +/*! + \internal + */ +void QWindowsVistaStyle::unpolish(QWidget *widget) +{ + Q_D(QWindowsVistaStyle); + +#if QT_CONFIG(rubberband) + if (qobject_cast<QRubberBand*>(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<QAbstractButton*>(widget) + #endif + || qobject_cast<QToolButton*>(widget) + || qobject_cast<QTabBar*>(widget) + #if QT_CONFIG(combobox) + || qobject_cast<QComboBox*>(widget) + #endif // QT_CONFIG(combobox) + || qobject_cast<QScrollBar*>(widget) + || qobject_cast<QSlider*>(widget) + || qobject_cast<QHeaderView*>(widget) + #if QT_CONFIG(spinbox) + || qobject_cast<QAbstractSpinBox*>(widget) + || qobject_cast<QSpinBox*>(widget) + #endif // QT_CONFIG(spinbox) + ) { + widget->setAttribute(Qt::WA_Hover, false); + } + + QWindowsStyle::unpolish(widget); + + d->stopAnimation(widget); + +#if QT_CONFIG(lineedit) + if (qobject_cast<QLineEdit*>(widget)) + widget->setAttribute(Qt::WA_Hover, false); + else { +#endif // QT_CONFIG(lineedit) + if (qobject_cast<QGroupBox*>(widget)) + widget->setAttribute(Qt::WA_Hover, false); + else if (qobject_cast<QMessageBox *> (widget)) { + widget->setAttribute(Qt::WA_StyledBackground, false); +#if QT_CONFIG(dialogbuttonbox) + QDialogButtonBox *buttonBox = widget->findChild<QDialogButtonBox *>(QLatin1String("qt_msgbox_buttonbox")); + if (buttonBox) + buttonBox->setContentsMargins(0, 0, 0, 0); +#endif + } +#if QT_CONFIG(inputdialog) + else if (qobject_cast<QInputDialog *> (widget)) { + widget->setAttribute(Qt::WA_StyledBackground, false); +#if QT_CONFIG(dialogbuttonbox) + QDialogButtonBox *buttonBox = widget->findChild<QDialogButtonBox *>(QLatin1String("qt_inputdlg_buttonbox")); + if (buttonBox) + buttonBox->setContentsMargins(0, 0, 0, 0); +#endif + } +#endif // QT_CONFIG(inputdialog) + else if (QTreeView *tree = qobject_cast<QTreeView *> (widget)) { + tree->viewport()->setAttribute(Qt::WA_Hover, false); + } +#if QT_CONFIG(commandlinkbutton) + else if (qobject_cast<QCommandLinkButton*>(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) + } +} + +/*! + \internal + */ +void QWindowsVistaStyle::polish(QPalette &pal) +{ + Q_D(QWindowsVistaStyle); + + if (QGuiApplication::styleHints()->colorScheme() == Qt::ColorScheme::Dark) { + // System runs in dark mode, but the Vista style cannot use a dark palette. + // Overwrite with the light system palette. + using QWindowsApplication = QNativeInterface::Private::QWindowsApplication; + if (auto nativeWindowsApp = dynamic_cast<QWindowsApplication *>(QGuiApplicationPrivate::platformIntegration())) + nativeWindowsApp->populateLightSystemPalette(pal); + } + + 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)); +} + +/*! + \internal + */ +void QWindowsVistaStyle::polish(QApplication *app) +{ + // Override windows theme palettes to light + if (qApp->styleHints()->colorScheme() == Qt::ColorScheme::Dark) { + static const char* themedWidgets[] = { + "QToolButton", + "QAbstractButton", + "QCheckBox", + "QRadioButton", + "QHeaderView", + "QAbstractItemView", + "QMessageBoxLabel", + "QTabBar", + "QLabel", + "QGroupBox", + "QMenu", + "QMenuBar", + "QTextEdit", + "QTextControl", + "QLineEdit" + }; + for (const auto& themedWidget : std::as_const(themedWidgets)) { + auto defaultResolveMask = QApplication::palette().resolveMask(); + auto widgetResolveMask = QApplication::palette(themedWidget).resolveMask(); + if (widgetResolveMask != defaultResolveMask) + QApplication::setPalette(QApplication::palette(), themedWidget); + } + } + + QWindowsStyle::polish(app); +} + +/*! + \internal + */ +QPixmap QWindowsVistaStyle::standardPixmap(StandardPixmap standardPixmap, const QStyleOption *option, + const QWidget *widget) const +{ + if (!QWindowsVistaStylePrivate::useVista()) { + return QWindowsStyle::standardPixmap(standardPixmap, option, widget); + } + + switch (standardPixmap) { + case SP_TitleBarMaxButton: + case SP_TitleBarCloseButton: + if (qstyleoption_cast<const QStyleOptionDockWidget *>(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 QWindowsStyle::standardPixmap(standardPixmap, option, widget); +} + +/*! +\reimp +*/ +QIcon QWindowsVistaStyle::standardIcon(StandardPixmap standardIcon, + const QStyleOption *option, + const QWidget *widget) const +{ + if (!QWindowsVistaStylePrivate::useVista()) { + return QWindowsStyle::standardIcon(standardIcon, option, widget); + } + + auto *d = const_cast<QWindowsVistaStylePrivate*>(d_func()); + + switch (standardIcon) { + case SP_TitleBarMaxButton: + if (qstyleoption_cast<const QStyleOptionDockWidget *>(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; + + case SP_TitleBarCloseButton: + if (qstyleoption_cast<const QStyleOptionDockWidget *>(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; + + case SP_TitleBarNormalButton: + if (qstyleoption_cast<const QStyleOptionDockWidget *>(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 QWindowsStyle::standardIcon(standardIcon, option, widget); +} + +QT_END_NAMESPACE diff --git a/src/plugins/styles/windowsvista/qwindowsvistastyle_p.h b/src/plugins/styles/modernwindows/qwindowsvistastyle_p.h index dab19a67b8..ff5300beca 100644 --- a/src/plugins/styles/windowsvista/qwindowsvistastyle_p.h +++ b/src/plugins/styles/modernwindows/qwindowsvistastyle_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWidgets module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// 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 QWINDOWSVISTASTYLE_P_H #define QWINDOWSVISTASTYLE_P_H @@ -52,12 +16,12 @@ // #include <QtWidgets/private/qtwidgetsglobal_p.h> -#include "qwindowsxpstyle_p.h" +#include <QtWidgets/private/qwindowsstyle_p.h> QT_BEGIN_NAMESPACE class QWindowsVistaStylePrivate; -class QWindowsVistaStyle : public QWindowsXPStyle +class QWindowsVistaStyle : public QWindowsStyle { Q_OBJECT public: @@ -94,7 +58,9 @@ public: void polish(QWidget *widget) override; void unpolish(QWidget *widget) override; void polish(QPalette &pal) override; - + void polish(QApplication *app) override; +protected: + QWindowsVistaStyle(QWindowsVistaStylePrivate &dd); private: Q_DISABLE_COPY_MOVE(QWindowsVistaStyle) Q_DECLARE_PRIVATE(QWindowsVistaStyle) diff --git a/src/plugins/styles/modernwindows/qwindowsvistastyle_p_p.h b/src/plugins/styles/modernwindows/qwindowsvistastyle_p_p.h new file mode 100644 index 0000000000..053e98b68d --- /dev/null +++ b/src/plugins/styles/modernwindows/qwindowsvistastyle_p_p.h @@ -0,0 +1,176 @@ +// 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 QWINDOWSVISTASTYLE_P_P_H +#define QWINDOWSVISTASTYLE_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 <QtWidgets/private/qtwidgetsglobal_p.h> +#include "qwindowsvistastyle_p.h" +#include "qwindowsthemedata_p.h" +#include <private/qpaintengine_raster_p.h> +#include <qpaintengine.h> +#include <qwidget.h> +#include <qapplication.h> +#include <qpixmapcache.h> +#include <qstyleoption.h> +#include <QtWidgets/private/qwindowsstyle_p_p.h> +#include <qmap.h> + +#if QT_CONFIG(pushbutton) +#include <qpushbutton.h> +#endif +#include <qradiobutton.h> +#if QT_CONFIG(lineedit) +#include <qlineedit.h> +#endif +#include <qgroupbox.h> +#if QT_CONFIG(toolbutton) +#include <qtoolbutton.h> +#endif +#if QT_CONFIG(spinbox) +#include <qspinbox.h> +#endif +#if QT_CONFIG(toolbar) +#include <qtoolbar.h> +#endif +#if QT_CONFIG(combobox) +#include <qcombobox.h> +#endif +#if QT_CONFIG(scrollbar) +#include <qscrollbar.h> +#endif +#if QT_CONFIG(progressbar) +#include <qprogressbar.h> +#endif +#if QT_CONFIG(dockwidget) +#include <qdockwidget.h> +#endif +#if QT_CONFIG(listview) +#include <qlistview.h> +#endif +#if QT_CONFIG(treeview) +#include <qtreeview.h> +#endif +#include <qtextedit.h> +#include <qmessagebox.h> +#if QT_CONFIG(dialogbuttonbox) +#include <qdialogbuttonbox.h> +#endif +#include <qinputdialog.h> +#if QT_CONFIG(tableview) +#include <qtableview.h> +#endif +#include <qdatetime.h> +#if QT_CONFIG(commandlinkbutton) +#include <qcommandlinkbutton.h> +#endif +#include <qlabel.h> +#include <qheaderview.h> +#include <uxtheme.h> + +QT_BEGIN_NAMESPACE + +class QWindowsVistaStylePrivate : public QWindowsStylePrivate +{ + Q_DECLARE_PUBLIC(QWindowsVistaStyle) + +public: + 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 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 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; + +private: + static bool initVistaTreeViewTheming(); + static void cleanupVistaTreeViewTheming(); + + static QBasicAtomicInt ref; + static bool useVistaTheme; + + QHash<ThemeMapKey, ThemeMapData> alphaCache; + HDC bufferDC = nullptr; + HBITMAP bufferBitmap = nullptr; + HBITMAP nullBitmap = nullptr; + uchar *bufferPixels = nullptr; + int bufferW = 0; + int bufferH = 0; + + static HWND m_vistaTreeViewHelper; +}; + +QT_END_NAMESPACE + +#endif // QWINDOWSVISTASTYLE_P_P_H diff --git a/src/plugins/styles/styles.pro b/src/plugins/styles/styles.pro deleted file mode 100644 index 542ad1329a..0000000000 --- a/src/plugins/styles/styles.pro +++ /dev/null @@ -1,8 +0,0 @@ -TEMPLATE = subdirs -QT_FOR_CONFIG += widgets-private - -qtConfig(style-android): SUBDIRS += android - -qtConfig(style-mac): SUBDIRS += mac - -qtConfig(style-windowsvista): SUBDIRS += windowsvista diff --git a/src/plugins/styles/windowsvista/main.cpp b/src/plugins/styles/windowsvista/main.cpp deleted file mode 100644 index 5e7bcf5e6e..0000000000 --- a/src/plugins/styles/windowsvista/main.cpp +++ /dev/null @@ -1,64 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include <QtWidgets/private/qtwidgetsglobal_p.h> -#include <QtWidgets/qstyleplugin.h> -#include "qwindowsvistastyle_p.h" - -QT_BEGIN_NAMESPACE - -class QWindowsVistaStylePlugin : public QStylePlugin -{ - Q_OBJECT - Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QStyleFactoryInterface" FILE "windowsvistastyle.json") -public: - QStyle *create(const QString &key) override; -}; - -QStyle *QWindowsVistaStylePlugin::create(const QString &key) -{ - if (key.compare(QLatin1String("windowsvista"), Qt::CaseInsensitive) == 0) - return new QWindowsVistaStyle(); - - return nullptr; -} - -QT_END_NAMESPACE - -#include "main.moc" diff --git a/src/plugins/styles/windowsvista/qwindowsvistastyle.cpp b/src/plugins/styles/windowsvista/qwindowsvistastyle.cpp deleted file mode 100644 index 906e654e6d..0000000000 --- a/src/plugins/styles/windowsvista/qwindowsvistastyle.cpp +++ /dev/null @@ -1,2480 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWidgets module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qwindowsvistastyle_p.h" -#include "qwindowsvistastyle_p_p.h" -#include <qoperatingsystemversion.h> -#include <qscreen.h> -#include <qwindow.h> -#include <private/qstyleanimation_p.h> -#include <private/qstylehelper_p.h> -#include <qpa/qplatformnativeinterface.h> - -QT_BEGIN_NAMESPACE - -static const int windowsItemFrame = 2; // menu item frame width -static const int windowsItemHMargin = 3; // menu item hor text margin -static const int windowsItemVMargin = 4; // menu item ver text margin -static const int windowsArrowHMargin = 6; // arrow horizontal margin -static const int windowsRightBorder = 15; // right border on windows - -#ifndef TMT_CONTENTMARGINS -# define TMT_CONTENTMARGINS 3602 -#endif -#ifndef TMT_SIZINGMARGINS -# define TMT_SIZINGMARGINS 3601 -#endif -#ifndef LISS_NORMAL -# define LISS_NORMAL 1 -# define LISS_HOT 2 -# define LISS_SELECTED 3 -# define LISS_DISABLED 4 -# define LISS_SELECTEDNOTFOCUS 5 -# define LISS_HOTSELECTED 6 -#endif -#ifndef BP_COMMANDLINK -# define BP_COMMANDLINK 6 -# define BP_COMMANDLINKGLYPH 7 -# define CMDLGS_NORMAL 1 -# define CMDLGS_HOT 2 -# define CMDLGS_PRESSED 3 -# define CMDLGS_DISABLED 4 -#endif - -/* \internal - Checks if we should use Vista style , or if we should - fall back to Windows style. -*/ -bool QWindowsVistaStylePrivate::useVista() -{ - return QWindowsVistaStylePrivate::useXP(); -} - -/* \internal - Checks and returns the style object -*/ -inline QObject *styleObject(const QStyleOption *option) { - return option ? option->styleObject : nullptr; -} - -/* \internal - Checks if we can animate on a style option -*/ -bool canAnimate(const QStyleOption *option) { - return option - && option->styleObject - && !option->styleObject->property("_q_no_animation").toBool(); -} - -static inline QImage createAnimationBuffer(const QStyleOption *option, const QWidget *widget) -{ - const qreal devicePixelRatio = widget - ? widget->devicePixelRatioF() : qApp->devicePixelRatio(); - QImage result(option->rect.size() * devicePixelRatio, QImage::Format_ARGB32_Premultiplied); - result.setDevicePixelRatio(devicePixelRatio); - result.fill(0); - return result; -} - -/* \internal - Used by animations to clone a styleoption and shift its offset -*/ -QStyleOption *clonedAnimationStyleOption(const QStyleOption*option) { - QStyleOption *styleOption = nullptr; - if (const QStyleOptionSlider *slider = qstyleoption_cast<const QStyleOptionSlider*>(option)) - styleOption = new QStyleOptionSlider(*slider); - else if (const QStyleOptionSpinBox *spinbox = qstyleoption_cast<const QStyleOptionSpinBox*>(option)) - styleOption = new QStyleOptionSpinBox(*spinbox); - else if (const QStyleOptionGroupBox *groupBox = qstyleoption_cast<const QStyleOptionGroupBox*>(option)) - styleOption = new QStyleOptionGroupBox(*groupBox); - else if (const QStyleOptionComboBox *combo = qstyleoption_cast<const QStyleOptionComboBox*>(option)) - styleOption = new QStyleOptionComboBox(*combo); - else if (const QStyleOptionButton *button = qstyleoption_cast<const QStyleOptionButton*>(option)) - styleOption = new QStyleOptionButton(*button); - else - styleOption = new QStyleOption(*option); - styleOption->rect = QRect(QPoint(0,0), option->rect.size()); - return styleOption; -} - -/* \internal - Used by animations to delete cloned styleoption -*/ -void deleteClonedAnimationStyleOption(const QStyleOption *option) -{ - if (const QStyleOptionSlider *slider = qstyleoption_cast<const QStyleOptionSlider*>(option)) - delete slider; - else if (const QStyleOptionSpinBox *spinbox = qstyleoption_cast<const QStyleOptionSpinBox*>(option)) - delete spinbox; - else if (const QStyleOptionGroupBox *groupBox = qstyleoption_cast<const QStyleOptionGroupBox*>(option)) - delete groupBox; - else if (const QStyleOptionComboBox *combo = qstyleoption_cast<const QStyleOptionComboBox*>(option)) - delete combo; - else if (const QStyleOptionButton *button = qstyleoption_cast<const QStyleOptionButton*>(option)) - delete button; - else - 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 - - \warning This style is only available on the Windows Vista platform - because it makes use of Windows Vista's style engine. - - \sa QMacStyle, QWindowsXPStyle, QFusionStyle -*/ - -/*! - Constructs a QWindowsVistaStyle object. -*/ -QWindowsVistaStyle::QWindowsVistaStyle() - : QWindowsXPStyle(*new QWindowsVistaStylePrivate) -{ -} - -/*! - Destructor. -*/ -QWindowsVistaStyle::~QWindowsVistaStyle() = default; - -//convert Qt state flags to uxtheme button states -static int buttonStateId(int flags, int partId) -{ - int stateId = 0; - if (partId == BP_RADIOBUTTON || partId == BP_CHECKBOX) { - if (!(flags & QStyle::State_Enabled)) - stateId = RBS_UNCHECKEDDISABLED; - else if (flags & QStyle::State_Sunken) - stateId = RBS_UNCHECKEDPRESSED; - else if (flags & QStyle::State_MouseOver) - stateId = RBS_UNCHECKEDHOT; - else - stateId = RBS_UNCHECKEDNORMAL; - - if (flags & QStyle::State_On) - stateId += RBS_CHECKEDNORMAL-1; - - } else if (partId == BP_PUSHBUTTON) { - if (!(flags & QStyle::State_Enabled)) - stateId = PBS_DISABLED; - else if (flags & (QStyle::State_Sunken | QStyle::State_On)) - stateId = PBS_PRESSED; - else if (flags & QStyle::State_MouseOver) - stateId = PBS_HOT; - else - stateId = PBS_NORMAL; - } else { - Q_ASSERT(1); - } - 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) -{ - bool result = false; - switch (element) { - case QStyle::PE_IndicatorRadioButton: - case QStyle::PE_IndicatorCheckBox: - result = true; - break; - // QTBUG-40634, do not animate when color is set in palette for PE_PanelLineEdit. - case QStyle::PE_FrameLineEdit: - result = !QWindowsXPStylePrivate::isLineEditBaseColorSet(option, widget); - break; - default: - break; - } - return result; -} - -/*! - \internal - - Animations are used for some state transitions on specific widgets. - - Only one running animation can exist for a widget at any specific - time. Animations can be added through - QWindowsVistaStylePrivate::startAnimation(Animation *) and any - existing animation on a widget can be retrieved with - QWindowsVistaStylePrivate::widgetAnimation(Widget *). - - Once an animation has been started, - QWindowsVistaStylePrivate::timerEvent(QTimerEvent *) will - continuously call update() on the widget until it is stopped, - meaning that drawPrimitive will be called many times until the - transition has completed. During this time, the result will be - retrieved by the Animation::paint(...) function and not by the style - itself. - - To determine if a transition should occur, the style needs to know - the previous state of the widget as well as the current one. This is - solved by updating dynamic properties on the widget every time the - function is called. - - Transitions interrupting existing transitions should always be - smooth, so whenever a hover-transition is started on a pulsating - button, it uses the current frame of the pulse-animation as the - starting image for the hover transition. - - */ - -void QWindowsVistaStyle::drawPrimitive(PrimitiveElement element, const QStyleOption *option, - QPainter *painter, const QWidget *widget) const -{ - QWindowsVistaStylePrivate *d = const_cast<QWindowsVistaStylePrivate*>(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); - - 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); - - bool doTransition = oldState && - ((state & State_Sunken) != (oldState & State_Sunken) || - (state & State_On) != (oldState & State_On) || - (state & State_MouseOver) != (oldState & State_MouseOver)); - - if (oldRect != newRect || - (state & State_Enabled) != (oldState & State_Enabled) || - (state & State_Active) != (oldState & State_Active)) - d->stopAnimation(styleObject); - - if (option->state & State_ReadOnly && element == PE_FrameLineEdit) // Do not animate read only line edits - doTransition = false; - - if (doTransition) { - QStyleOption *styleOption = clonedAnimationStyleOption(option); - styleOption->state = QStyle::State(oldState); - - QWindowsVistaAnimation *anim = qobject_cast<QWindowsVistaAnimation *>(d->animation(styleObject)); - QWindowsVistaTransition *t = new QWindowsVistaTransition(styleObject); - - // 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); - - QImage endImage = createAnimationBuffer(option, widget); - QPainter endPainter(&endImage); - - // 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); - - t->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); - - 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; - - 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; - - } 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); - } - - // 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(QTime::currentTime()); - - deleteClonedAnimationStyleOption(styleOption); - d->startAnimation(t); - } - styleObject->setProperty("_q_no_animation", false); - } - - } // End of animation part - } - - QRect rect = option->rect; - - switch (element) { - case PE_IndicatorHeaderArrow: - if (const QStyleOptionHeader *header = qstyleoption_cast<const QStyleOptionHeader *>(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); - d->drawBackground(theme); - } - 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 - } - break; - - case PE_PanelButtonBevel: - case PE_IndicatorCheckBox: - case PE_IndicatorRadioButton: - { - if (QWindowsVistaAnimation *a = - qobject_cast<QWindowsVistaAnimation *>(d->animation(styleObject(option)))){ - a->paint(painter, option); - } else { - QWindowsXPStyle::drawPrimitive(element, option, painter, widget); - } - } - 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); - } - break; - case PE_Frame: { -#ifndef QT_NO_ACCESSIBILITY - if (QStyleHelper::isInstanceOf(option->styleObject, QAccessible::EditableText) - || QStyleHelper::isInstanceOf(option->styleObject, QAccessible::StaticText) || -#else - if ( -#endif - (widget && widget->inherits("QTextEdit"))) { - painter->save(); - int stateId = ETS_NORMAL; - if (!(state & State_Enabled)) - stateId = ETS_DISABLED; - else if (state & State_ReadOnly) - stateId = ETS_READONLY; - else if (state & State_HasFocus) - stateId = ETS_SELECTED; - XPThemeData theme(widget, painter, - QWindowsXPStylePrivate::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); - QRegion clipRegion = option->rect; - QRegion content = option->rect.adjusted(borderSize, borderSize, -borderSize, -borderSize); - clipRegion ^= content; - painter->setClipRegion(clipRegion); - d->drawBackground(theme); - painter->restore(); - } else { - QWindowsXPStyle::drawPrimitive(element, option, painter, widget); - } - } - break; - - case PE_PanelLineEdit: - if (const QStyleOptionFrame *panel = qstyleoption_cast<const QStyleOptionFrame *>(option)) { - bool isEnabled = option->state & State_Enabled; - if (QWindowsXPStylePrivate::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) - stateId = EBS_READONLY; - else if (state & State_MouseOver) - stateId = EBS_HOT; - - XPThemeData theme(nullptr, painter, QWindowsXPStylePrivate::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 ) { - d->drawBackground(theme); - } else { - QBrush fillColor = option->palette.brush(QPalette::Base); - if (!isEnabled) { - PROPERTYORIGIN origin = PO_NOTFOUND; - GetThemePropertyOrigin(theme.handle(), theme.partId, theme.stateId, TMT_FILLCOLOR, &origin); - // Use only if the fill property comes from our part - if ((origin == PO_PART || origin == PO_STATE)) { - COLORREF bgRef; - GetThemeColor(theme.handle(), partId, stateId, TMT_FILLCOLOR, &bgRef); - fillColor = QBrush(qRgb(GetRValue(bgRef), GetGValue(bgRef), GetBValue(bgRef))); - } - } - painter->fillRect(option->rect, fillColor); - } - } - if (panel->lineWidth > 0) - proxy()->drawPrimitive(PE_FrameLineEdit, panel, painter, widget); - return; - } - break; - - case PE_FrameLineEdit: - if (QWindowsVistaAnimation *anim = qobject_cast<QWindowsVistaAnimation *>(d->animation(styleObject(option)))) { - anim->paint(painter, option); - } else { - QPainter *p = painter; - if (QWindowsXPStylePrivate::isItemViewDelegateLineEdit(widget)) { - // we try to check if this lineedit is a delegate on a QAbstractItemView-derived class. - QPen oldPen = p->pen(); - // Inner white border - p->setPen(QPen(option->palette.base().color(), 1)); - p->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); - return; - } - int stateId = ETS_NORMAL; - if (!(state & State_Enabled)) - stateId = ETS_DISABLED; - else if (state & State_ReadOnly) - stateId = ETS_READONLY; - else if (state & State_MouseOver) - stateId = ETS_HOT; - else if (state & State_HasFocus) - stateId = ETS_SELECTED; - XPThemeData theme(widget, painter, - QWindowsXPStylePrivate::EditTheme, - EP_EDITBORDER_NOSCROLL, stateId, option->rect); - theme.noContent = true; - painter->save(); - QRegion clipRegion = option->rect; - clipRegion -= option->rect.adjusted(2, 2, -2, -2); - painter->setClipRegion(clipRegion); - d->drawBackground(theme); - painter->restore(); - } - break; - - 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; - 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); - } - break; - - case PE_PanelTipLabel: { - XPThemeData theme(widget, painter, - QWindowsXPStylePrivate::ToolTipTheme, - TTP_STANDARD, TTSS_NORMAL, option->rect); - d->drawBackground(theme); - break; - } - - 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<const QAbstractItemView *>(widget)) { - newStyle = !qobject_cast<const QTableView*>(view); - selectionBehavior = view->selectionBehavior(); - selectionMode = view->selectionMode(); -#ifndef QT_NO_ACCESSIBILITY - } else if (!widget) { - newStyle = !QStyleHelper::hasAncestor(option->styleObject, QAccessible::MenuItem) ; -#endif - } - - if (newStyle && (vopt = qstyleoption_cast<const QStyleOptionViewItem *>(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()); - - 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()); - - QSize sectionSize = itemRect.size(); - if (vopt->showDecorationSelected) - sectionSize = vopt->rect.size(); - - 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 (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 (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); - } - } - } else { - QWindowsXPStyle::drawPrimitive(element, option, painter, widget); - } - break; - } - case PE_Widget: - { -#if QT_CONFIG(dialogbuttonbox) - const QDialogButtonBox *buttonBox = nullptr; - - if (qobject_cast<const QMessageBox *> (widget)) - buttonBox = widget->findChild<const QDialogButtonBox *>(QLatin1String("qt_msgbox_buttonbox")); -#if QT_CONFIG(inputdialog) - else if (qobject_cast<const QInputDialog *> (widget)) - buttonBox = widget->findChild<const QDialogButtonBox *>(QLatin1String("qt_inputdlg_buttonbox")); -#endif // QT_CONFIG(inputdialog) - - 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); - - //draw bottom panel part - QRect buttonRect = option->rect; - buttonRect.setTop(buttonBox->geometry().top()); - theme.rect = buttonRect; - theme.partId = TDLG_SECONDARYPANEL; - d->drawBackground(theme); - } -#endif - } - break; - default: - QWindowsXPStyle::drawPrimitive(element, option, painter, widget); - break; - } -} - - -/*! - \internal - - see drawPrimitive for comments on the animation support - */ -void QWindowsVistaStyle::drawControl(ControlElement element, const QStyleOption *option, - QPainter *painter, const QWidget *widget) const -{ - QWindowsVistaStylePrivate *d = const_cast<QWindowsVistaStylePrivate*>(d_func()); - - if (!QWindowsVistaStylePrivate::useVista()) { - QWindowsStyle::drawControl(element, option, painter, widget); - return; - } - - bool selected = option->state & State_Selected; - bool pressed = option->state & State_Sunken; - bool disabled = !(option->state & State_Enabled); - - int state = option->state; - int themeNumber = -1; - - QRect rect(option->rect); - State flags = option->state; - int partId = 0; - int stateId = 0; - - if (d->transitionsEnabled() && canAnimate(option)) - { - if (element == CE_PushButtonBevel) { - QRect oldRect; - QRect newRect; - - QObject *styleObject = option->styleObject; - - 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); - - bool wasDefault = false; - bool isDefault = false; - if (const QStyleOptionButton *button = qstyleoption_cast<const QStyleOptionButton *>(option)) { - wasDefault = styleObject->property("_q_isdefault").toBool(); - isDefault = button->features & QStyleOptionButton::DefaultButton; - styleObject->setProperty("_q_isdefault", isDefault); - } - - bool doTransition = ((state & State_Sunken) != (oldState & State_Sunken) || - (state & State_On) != (oldState & State_On) || - (state & State_MouseOver) != (oldState & State_MouseOver)); - - if (oldRect != newRect || (wasDefault && !isDefault)) { - doTransition = false; - d->stopAnimation(styleObject); - } - - if (doTransition) { - styleObject->setProperty("_q_no_animation", true); - - QWindowsVistaTransition *t = new QWindowsVistaTransition(styleObject); - QWindowsVistaAnimation *anim = qobject_cast<QWindowsVistaAnimation *>(d->animation(styleObject)); - QStyleOption *styleOption = clonedAnimationStyleOption(option); - styleOption->state = QStyle::State(oldState); - - QImage startImage = createAnimationBuffer(option, widget); - QPainter startPainter(&startImage); - - // Use current state of existing animation if already one is running - if (!anim) { - proxy()->drawControl(element, styleOption, &startPainter, widget); - } else { - anim->paint(&startPainter, styleOption); - d->stopAnimation(styleObject); - } - - t->setStartImage(startImage); - QImage endImage = createAnimationBuffer(option, widget); - QPainter endPainter(&endImage); - styleOption->state = option->state; - proxy()->drawControl(element, styleOption, &endPainter, widget); - t->setEndImage(endImage); - - - DWORD duration = 0; - const HTHEME theme = OpenThemeData(nullptr, L"Button"); - - int fromState = buttonStateId(oldState, BP_PUSHBUTTON); - int toState = buttonStateId(option->state, BP_PUSHBUTTON); - if (GetThemeTransitionDuration(theme, BP_PUSHBUTTON, fromState, toState, TMT_TRANSITIONDURATIONS, &duration) == S_OK) - t->setDuration(int(duration)); - else - t->setDuration(0); - t->setStartTime(QTime::currentTime()); - styleObject->setProperty("_q_no_animation", false); - - deleteClonedAnimationStyleOption(styleOption); - d->startAnimation(t); - } - - QWindowsVistaAnimation *anim = qobject_cast<QWindowsVistaAnimation *>(d->animation(styleObject)); - if (anim) { - anim->paint(painter, option); - return; - } - - } - } - switch (element) { - case CE_PushButtonBevel: - if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(option)) - { - themeNumber = QWindowsXPStylePrivate::ButtonTheme; - partId = BP_PUSHBUTTON; - if (btn->features & QStyleOptionButton::CommandLinkButton) - partId = BP_COMMANDLINK; - bool justFlat = (btn->features & QStyleOptionButton::Flat) && !(flags & (State_On|State_Sunken)); - if (!(flags & State_Enabled) && !(btn->features & QStyleOptionButton::Flat)) - stateId = PBS_DISABLED; - else if (justFlat) - ; - else if (flags & (State_Sunken | State_On)) - stateId = PBS_PRESSED; - else if (flags & State_MouseOver) - stateId = PBS_HOT; - else if (btn->features & QStyleOptionButton::DefaultButton && (state & State_Active)) - stateId = PBS_DEFAULTED; - else - stateId = PBS_NORMAL; - - if (!justFlat) { - - if (d->transitionsEnabled() && (btn->features & QStyleOptionButton::DefaultButton) && - !(state & (State_Sunken | State_On)) && !(state & State_MouseOver) && - (state & State_Enabled) && (state & State_Active)) - { - QWindowsVistaAnimation *anim = qobject_cast<QWindowsVistaAnimation *>(d->animation(styleObject(option))); - - if (!anim) { - QImage startImage = createAnimationBuffer(option, widget); - QImage alternateImage = createAnimationBuffer(option, widget); - - QWindowsVistaPulse *pulse = new QWindowsVistaPulse(styleObject(option)); - - QPainter startPainter(&startImage); - stateId = PBS_DEFAULTED; - XPThemeData 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(QTime::currentTime()); - pulse->setDuration(2000); - d->startAnimation(pulse); - anim = pulse; - } - - if (anim) - anim->paint(painter, option); - else { - XPThemeData theme(widget, painter, themeNumber, partId, stateId, rect); - d->drawBackground(theme); - } - } - else { - XPThemeData 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); - if (theme.isValid()) { - const QSizeF size = theme.size() * QStyleHelper::dpiScaled(1, option); - if (!size.isEmpty()) { - mbiw = qRound(size.width()); - mbih = qRound(size.height()); - } - } - 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)); - proxy()->drawPrimitive(PE_IndicatorArrowDown, &newBtn, painter, widget); - } - return; - } - break; - - case CE_ProgressBarContents: - if (const QStyleOptionProgressBar *bar - = qstyleoption_cast<const QStyleOptionProgressBar *>(option)) { - bool isIndeterminate = (bar->minimum == 0 && bar->maximum == 0); - const bool vertical = !(bar->state & QStyle::State_Horizontal); - const bool inverted = bar->invertedAppearance; - - if (isIndeterminate || (bar->progress > 0 && (bar->progress < bar->maximum) && d->transitionsEnabled())) { - if (!d->animation(styleObject(option))) - d->startAnimation(new QProgressStyleAnimation(d->animationFps, styleObject(option))); - } else { - d->stopAnimation(styleObject(option)); - } - - XPThemeData theme(widget, painter, - QWindowsXPStylePrivate::ProgressTheme, - vertical ? PP_FILLVERT : PP_FILL); - theme.rect = option->rect; - bool reverse = (bar->direction == Qt::LeftToRight && inverted) || (bar->direction == Qt::RightToLeft && !inverted); - QTime current = QTime::currentTime(); - - if (isIndeterminate) { - if (QProgressStyleAnimation *a = qobject_cast<QProgressStyleAnimation *>(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; - if (animOffset > animationWidth) - a->setStartTime(QTime::currentTime()); - painter->save(); - painter->setClipRect(theme.rect); - QRect animRect; - QSize pixmapSize(14, 14); - if (vertical) { - animRect = QRect(theme.rect.left(), - inverted ? rect.top() - glowSize + animOffset : - rect.bottom() + glowSize - animOffset, - rect.width(), glowSize); - 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); - pixmapSize.setWidth(animRect.width()); - } - QString name = QString::fromLatin1("qiprogress-%1-%2").arg(pixmapSize.width()).arg(pixmapSize.height()); - QPixmap pixmap; - if (!QPixmapCache::find(name, &pixmap)) { - QImage image(pixmapSize, QImage::Format_ARGB32); - image.fill(Qt::transparent); - QPainter imagePainter(&image); - theme.painter = &imagePainter; - theme.partId = vertical ? PP_FILLVERT : PP_FILL; - theme.rect = QRect(QPoint(0,0), animRect.size()); - QLinearGradient alphaGradient(0, 0, vertical ? 0 : image.width(), - vertical ? image.height() : 0); - alphaGradient.setColorAt(0, QColor(0, 0, 0, 0)); - alphaGradient.setColorAt(0.5, QColor(0, 0, 0, 220)); - alphaGradient.setColorAt(1, QColor(0, 0, 0, 0)); - imagePainter.fillRect(image.rect(), alphaGradient); - imagePainter.setCompositionMode(QPainter::CompositionMode_SourceIn); - d->drawBackground(theme); - imagePainter.end(); - pixmap = QPixmap::fromImage(image); - QPixmapCache::insert(name, pixmap); - } - painter->drawPixmap(animRect, pixmap); - painter->restore(); - } - } - else { - qint64 progress = qMax<qint64>(bar->progress, bar->minimum); // workaround for bug in QProgressBar - - if (vertical) { - int maxHeight = option->rect.height(); - int minHeight = 0; - double vc6_workaround = ((progress - qint64(bar->minimum)) / qMax(double(1.0), double(qint64(bar->maximum) - qint64(bar->minimum))) * maxHeight); - int height = isIndeterminate ? maxHeight: qMax(int(vc6_workaround), minHeight); - theme.rect.setHeight(height); - if (!inverted) - theme.rect.moveTop(rect.height() - theme.rect.height()); - } else { - int maxWidth = option->rect.width(); - int minWidth = 0; - double vc6_workaround = ((progress - qint64(bar->minimum)) / qMax(double(1.0), double(qint64(bar->maximum) - qint64(bar->minimum))) * maxWidth); - 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); - } - d->drawBackground(theme); - - if (QProgressStyleAnimation *a = qobject_cast<QProgressStyleAnimation *>(d->animation(styleObject(option)))) { - int glowSize = 140; - int animationWidth = glowSize * 2 + (vertical ? theme.rect.height() : theme.rect.width()); - int animOffset = a->startTime().msecsTo(current) / 4; - theme.partId = vertical ? PP_MOVEOVERLAYVERT : PP_MOVEOVERLAY; - if (animOffset > animationWidth) { - if (bar->progress < bar->maximum) - a->setStartTime(QTime::currentTime()); - else - d->stopAnimation(styleObject(option)); //we stop the glow motion only after it has - //moved out of view - } - painter->save(); - painter->setClipRect(theme.rect); - if (vertical) { - theme.rect = QRect(theme.rect.left(), - inverted ? rect.top() - glowSize + animOffset : - rect.bottom() + glowSize - animOffset, - rect.width(), glowSize); - } else { - theme.rect = QRect(rect.left() - glowSize + animOffset,rect.top(), glowSize, rect.height()); - theme.rect = QStyle::visualRect(reverse ? Qt::RightToLeft : Qt::LeftToRight, option->rect, theme.rect); - } - d->drawBackground(theme); - painter->restore(); - } - } - } - break; - - case CE_MenuBarItem: - { - - if (const QStyleOptionMenuItem *mbi = qstyleoption_cast<const QStyleOptionMenuItem *>(option)) - { - if (mbi->menuItemType == QStyleOptionMenuItem::DefaultItem) - break; - - QPalette::ColorRole textRole = disabled ? QPalette::Text : QPalette::ButtonText; - QPixmap pix = mbi->icon.pixmap(proxy()->pixelMetric(PM_SmallIconSize, option, widget), QIcon::Normal); - - int alignment = Qt::AlignCenter | Qt::TextShowMnemonic | Qt::TextDontClip | Qt::TextSingleLine; - if (!proxy()->styleHint(SH_UnderlineShortcut, mbi, widget)) - alignment |= Qt::TextHideMnemonic; - - 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)); - 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); - - 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; -#if QT_CONFIG(menu) - case CE_MenuItem: - if (const QStyleOptionMenuItem *menuitem = qstyleoption_cast<const QStyleOptionMenuItem *>(option)) { - // windows always has a check column, regardless whether we have an icon or not - const qreal factor = QWindowsXPStylePrivate::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; - themeSize.partId = MENU_POPUPCHECK; - themeSize.stateId = 0; - const QSizeF size = themeSize.size() * QWindowsStylePrivate::nativeMetricScaleFactor(widget); - const QMarginsF margins = themeSize.margins() * QWindowsStylePrivate::nativeMetricScaleFactor(widget); - checkcol = qMax(menuitem->maxIconWidth, qRound(gutterWidth + size.width() + margins.left() + margins.right())); - } - QRect rect = option->rect; - - //draw vertical menu line - if (option->direction == Qt::LeftToRight) - checkcol += rect.x(); - 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); - d->drawBackground(theme2); - - int x, y, w, h; - menuitem->rect.getRect(&x, &y, &w, &h); - int tab = menuitem->reservedShortcutWidth; - bool dis = !(menuitem->state & State_Enabled); - bool checked = menuitem->checkType != QStyleOptionMenuItem::NotCheckable - ? menuitem->checked : false; - bool act = menuitem->state & State_Selected; - - if (menuitem->menuItemType == QStyleOptionMenuItem::Separator) { - int yoff = y-2 + h / 2; - const int separatorSize = qRound(qreal(6) * QWindowsStylePrivate::nativeMetricScaleFactor(widget)); - QPoint p1 = QPoint(x + checkcol, yoff); - QPoint p2 = QPoint(x + w + separatorSize, yoff); - stateId = MBI_HOT; - 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); - 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())); - - if (act) { - stateId = dis ? MBI_DISABLED : MBI_HOT; - XPThemeData theme2(widget, painter, - QWindowsXPStylePrivate::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; - themeSize.partId = MENU_POPUPCHECK; - themeSize.stateId = 0; - const QSizeF size = themeSize.size() * QWindowsStylePrivate::nativeMetricScaleFactor(widget); - const QMarginsF margins = themeSize.margins() * QWindowsStylePrivate::nativeMetricScaleFactor(widget); - QRect checkRect(0, 0, qRound(size.width() + margins.left() + margins.right()), - qRound(size.height() + margins.bottom() + margins.top())); - checkRect.moveCenter(vCheckRect.center()); - theme.rect = checkRect; - - d->drawBackground(theme); - - if (menuitem->icon.isNull()) { - checkRect = QRect(QPoint(0, 0), size.toSize()); - checkRect.moveCenter(theme.rect.center()); - theme.rect = checkRect; - - theme.partId = MENU_POPUPCHECK; - bool bullet = menuitem->checkType & QStyleOptionMenuItem::Exclusive; - if (dis) - theme.stateId = bullet ? MC_BULLETDISABLED: MC_CHECKMARKDISABLED; - else - theme.stateId = bullet ? MC_BULLETNORMAL: MC_CHECKMARKNORMAL; - d->drawBackground(theme); - } - } - - if (!menuitem->icon.isNull()) { - QIcon::Mode mode = dis ? QIcon::Disabled : QIcon::Normal; - if (act && !dis) - mode = QIcon::Active; - QPixmap pixmap; - if (checked) - pixmap = menuitem->icon.pixmap(proxy()->pixelMetric(PM_SmallIconSize, option, widget), mode, QIcon::On); - else - pixmap = menuitem->icon.pixmap(proxy()->pixelMetric(PM_SmallIconSize, option, widget), mode); - const int pixw = pixmap.width() / pixmap.devicePixelRatio(); - const int pixh = pixmap.height() / pixmap.devicePixelRatio(); - QRect pmr(0, 0, pixw, pixh); - pmr.moveCenter(vCheckRect.center()); - painter->setPen(menuitem->palette.text().color()); - painter->drawPixmap(pmr.topLeft(), pixmap); - } - - painter->setPen(menuitem->palette.buttonText().color()); - - const QColor textColor = menuitem->palette.text().color(); - if (dis) - painter->setPen(textColor); - - int xm = windowsItemFrame + checkcol + windowsItemHMargin + (gutterWidth - menuitem->rect.x()) - 1; - int xpos = menuitem->rect.x() + xm; - QRect textRect(xpos, y + windowsItemVMargin, w - xm - windowsRightBorder - tab + 1, h - 2 * windowsItemVMargin); - QRect vTextRect = visualRect(option->direction, menuitem->rect, textRect); - QString s = menuitem->text; - if (!s.isEmpty()) { // draw text - painter->save(); - int t = s.indexOf(QLatin1Char('\t')); - int text_flags = Qt::AlignVCenter | Qt::TextShowMnemonic | Qt::TextDontClip | Qt::TextSingleLine; - if (!proxy()->styleHint(SH_UnderlineShortcut, menuitem, widget)) - text_flags |= Qt::TextHideMnemonic; - text_flags |= Qt::AlignLeft; - if (t >= 0) { - QRect vShortcutRect = visualRect(option->direction, menuitem->rect, - QRect(textRect.topRight(), QPoint(menuitem->rect.right(), textRect.bottom()))); - painter->drawText(vShortcutRect, text_flags, s.mid(t + 1)); - s = s.left(t); - } - QFont font = menuitem->font; - if (menuitem->menuItemType == QStyleOptionMenuItem::DefaultItem) - font.setBold(true); - painter->setFont(font); - painter->setPen(textColor); - painter->drawText(vTextRect, text_flags, s.left(t)); - painter->restore(); - } - if (menuitem->menuItemType == QStyleOptionMenuItem::SubMenu) {// draw sub menu arrow - int dim = (h - 2 * windowsItemFrame) / 2; - PrimitiveElement arrow; - arrow = (option->direction == Qt::RightToLeft) ? PE_IndicatorArrowLeft : PE_IndicatorArrowRight; - xpos = x + w - windowsArrowHMargin - windowsItemFrame - dim; - QRect vSubMenuRect = visualRect(option->direction, menuitem->rect, QRect(xpos, y + h / 2 - dim / 2, dim, dim)); - QStyleOptionMenuItem newMI = *menuitem; - newMI.rect = vSubMenuRect; - newMI.state = dis ? State_None : State_Enabled; - proxy()->drawPrimitive(arrow, &newMI, painter, widget); - } - } - break; -#endif // QT_CONFIG(menu) - case CE_HeaderSection: - if (const QStyleOptionHeader *header = qstyleoption_cast<const QStyleOptionHeader *>(option)) { - partId = HP_HEADERITEM; - if (flags & State_Sunken) - stateId = HIS_PRESSED; - else if (flags & State_MouseOver) - stateId = HIS_HOT; - else - stateId = HIS_NORMAL; - - 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); - d->drawBackground(theme); - } - break; - case CE_ToolBar: - if (const QStyleOptionToolBar *toolbar = qstyleoption_cast<const QStyleOptionToolBar *>(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; -#if QT_CONFIG(dockwidget) - case CE_DockWidgetTitle: - if (const QStyleOptionDockWidget *dwOpt = qstyleoption_cast<const QStyleOptionDockWidget *>(option)) { - const QDockWidget *dockWidget = qobject_cast<const QDockWidget *>(widget); - QRect rect = option->rect; - if (dockWidget && dockWidget->isFloating()) { - QWindowsXPStyle::drawControl(element, option, painter, widget); - break; //otherwise fall through - } - - const bool verticalTitleBar = dwOpt->verticalTitleBar; - - if (verticalTitleBar) { - rect = rect.transposed(); - - painter->translate(rect.left() - 1, rect.top() + rect.width()); - painter->rotate(-90); - 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<const QDockWidget *>(widget); - bool isFloating = dw && dw->isFloating(); - - QRect r = option->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 && 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 (!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; -#endif // QT_CONFIG(dockwidget) -#if QT_CONFIG(itemviews) - case CE_ItemViewItem: - { - const QStyleOptionViewItem *vopt; - - const QAbstractItemView *view = qobject_cast<const QAbstractItemView *>(widget); - bool newStyle = true; - - if (qobject_cast<const QTableView*>(widget)) - newStyle = false; - - if (newStyle && view && (vopt = qstyleoption_cast<const QStyleOptionViewItem *>(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) - && !(vopt->state & State_KeyboardFocusChange)) - adjustedOption.state &= ~State_HasFocus; - QWindowsXPStyle::drawControl(element, &adjustedOption, painter, widget); - } else { - QWindowsXPStyle::drawControl(element, option, painter, widget); - } - break; - } -#endif // QT_CONFIG(itemviews) -#if QT_CONFIG(combobox) - case CE_ComboBoxLabel: - QCommonStyle::drawControl(element, option, painter, widget); - break; -#endif // QT_CONFIG(combobox) - default: - QWindowsXPStyle::drawControl(element, option, painter, widget); - break; - } -} - -/*! - \internal - - see drawPrimitive for comments on the animation support - - */ -void QWindowsVistaStyle::drawComplexControl(ComplexControl control, const QStyleOptionComplex *option, - QPainter *painter, const QWidget *widget) const -{ - QWindowsVistaStylePrivate *d = const_cast<QWindowsVistaStylePrivate*>(d_func()); - if (!QWindowsVistaStylePrivate::useVista()) { - QWindowsStyle::drawComplexControl(control, option, painter, widget); - return; - } - - State state = option->state; - SubControls sub = option->subControls; - QRect r = option->rect; - - int partId = 0; - int stateId = 0; - - State flags = option->state; - if (widget && widget->testAttribute(Qt::WA_UnderMouse) && widget->isActiveWindow()) - flags |= State_MouseOver; - - 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(); - int oldActiveControls = styleObject->property("_q_stylecontrols").toInt(); - - QRect oldRect = styleObject->property("_q_stylerect").toRect(); - styleObject->setProperty("_q_stylestate", int(option->state)); - 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)); - - if (qstyleoption_cast<const QStyleOptionSlider *>(option)) { - QRect oldSliderPos = styleObject->property("_q_stylesliderpos").toRect(); - QRect currentPos = proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarSlider, widget); - styleObject->setProperty("_q_stylesliderpos", currentPos); - if (oldSliderPos != currentPos) { - doTransition = false; - d->stopAnimation(styleObject); - } - } else if (control == CC_SpinBox) { - //spinboxes have a transition when focus changes - if (!doTransition) - doTransition = (state & State_HasFocus) != (oldState & State_HasFocus); - } - - if (oldRect != option->rect) { - doTransition = false; - d->stopAnimation(styleObject); - } - - if (doTransition) { - QImage startImage = createAnimationBuffer(option, widget); - QPainter startPainter(&startImage); - - QImage endImage = createAnimationBuffer(option, widget); - QPainter endPainter(&endImage); - - QWindowsVistaAnimation *anim = qobject_cast<QWindowsVistaAnimation *>(d->animation(styleObject)); - QWindowsVistaTransition *t = new QWindowsVistaTransition(styleObject); - - // Draw the image that ends the animation by using the current styleoption - QStyleOptionComplex *styleOption = qstyleoption_cast<QStyleOptionComplex*>(clonedAnimationStyleOption(option)); - - styleObject->setProperty("_q_no_animation", true); - - // Draw transition source - if (!anim) { - styleOption->state = QStyle::State(oldState); - styleOption->activeSubControls = QStyle::SubControl(oldActiveControls); - proxy()->drawComplexControl(control, styleOption, &startPainter, widget); - } else { - anim->paint(&startPainter, option); - } - t->setStartImage(startImage); - - // Draw transition target - styleOption->state = option->state; - styleOption->activeSubControls = option->activeSubControls; - proxy()->drawComplexControl(control, styleOption, &endPainter, widget); - - styleObject->setProperty("_q_no_animation", false); - - t->setEndImage(endImage); - t->setStartTime(QTime::currentTime()); - - if (option->state & State_MouseOver || option->state & State_Sunken) - t->setDuration(150); - else - t->setDuration(500); - - deleteClonedAnimationStyleOption(styleOption); - d->startAnimation(t); - } - if (QWindowsVistaAnimation *anim = qobject_cast<QWindowsVistaAnimation *>(d->animation(styleObject))) { - anim->paint(painter, option); - return; - } - } - } - - switch (control) { - case CC_ComboBox: - if (const QStyleOptionComboBox *cmb = qstyleoption_cast<const QStyleOptionComboBox *>(option)) - { - if (cmb->editable) { - if (sub & SC_ComboBoxEditField) { - partId = EP_EDITBORDER_NOSCROLL; - if (!(flags & State_Enabled)) - stateId = ETS_DISABLED; - else if (flags & State_MouseOver) - stateId = ETS_HOT; - else if (flags & State_HasFocus) - stateId = ETS_FOCUSED; - else - stateId = ETS_NORMAL; - - XPThemeData theme(widget, painter, - QWindowsXPStylePrivate::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); - theme.rect = subRect; - partId = option->direction == Qt::RightToLeft ? CP_DROPDOWNBUTTONLEFT : CP_DROPDOWNBUTTONRIGHT; - - if (!(cmb->state & State_Enabled)) - stateId = CBXS_DISABLED; - else if (cmb->state & State_Sunken || cmb->state & State_On) - stateId = CBXS_PRESSED; - else if (cmb->state & State_MouseOver && option->activeSubControls & SC_ComboBoxArrow) - stateId = CBXS_HOT; - else - stateId = CBXS_NORMAL; - - theme.partId = partId; - theme.stateId = stateId; - d->drawBackground(theme); - } - - } else { - if (sub & SC_ComboBoxFrame) { - XPThemeData theme(widget, painter, QWindowsXPStylePrivate::ComboboxTheme); - theme.rect = option->rect; - theme.partId = CP_READONLY; - if (!(cmb->state & State_Enabled)) - theme.stateId = CBXS_DISABLED; - else if (cmb->state & State_Sunken || cmb->state & State_On) - theme.stateId = CBXS_PRESSED; - else if (cmb->state & State_MouseOver) - theme.stateId = CBXS_HOT; - else - theme.stateId = CBXS_NORMAL; - d->drawBackground(theme); - } - if (sub & SC_ComboBoxArrow) { - XPThemeData theme(widget, painter, QWindowsXPStylePrivate::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)) - theme.stateId = CBXS_DISABLED; - else - theme.stateId = CBXS_NORMAL; - d->drawBackground(theme); - } - if ((sub & SC_ComboBoxEditField) && (flags & State_HasFocus)) { - QStyleOptionFocusRect fropt; - fropt.QStyleOption::operator=(*cmb); - fropt.rect = proxy()->subControlRect(CC_ComboBox, option, SC_ComboBoxEditField, widget); - proxy()->drawPrimitive(PE_FrameFocusRect, &fropt, painter, widget); - } - } - } - break; - case CC_ScrollBar: - if (const QStyleOptionSlider *scrollbar = qstyleoption_cast<const QStyleOptionSlider *>(option)) - { - XPThemeData theme(widget, painter, QWindowsXPStylePrivate::ScrollBarTheme); - bool maxedOut = (scrollbar->maximum == scrollbar->minimum); - if (maxedOut) - flags &= ~State_Enabled; - - bool isHorz = flags & State_Horizontal; - bool isRTL = option->direction == Qt::RightToLeft; - if (sub & SC_ScrollBarAddLine) { - theme.rect = proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarAddLine, widget); - partId = SBP_ARROWBTN; - if (!(flags & State_Enabled)) - stateId = (isHorz ? (isRTL ? ABS_LEFTDISABLED : ABS_RIGHTDISABLED) : ABS_DOWNDISABLED); - else if (scrollbar->activeSubControls & SC_ScrollBarAddLine && (scrollbar->state & State_Sunken)) - stateId = (isHorz ? (isRTL ? ABS_LEFTPRESSED : ABS_RIGHTPRESSED) : ABS_DOWNPRESSED); - else if (scrollbar->activeSubControls & SC_ScrollBarAddLine && (scrollbar->state & State_MouseOver)) - stateId = (isHorz ? (isRTL ? ABS_LEFTHOT : ABS_RIGHTHOT) : ABS_DOWNHOT); - else if (scrollbar->state & State_MouseOver) - stateId = (isHorz ? (isRTL ? ABS_LEFTHOVER : ABS_RIGHTHOVER) : ABS_DOWNHOVER); - else - stateId = (isHorz ? (isRTL ? ABS_LEFTNORMAL : ABS_RIGHTNORMAL) : ABS_DOWNNORMAL); - theme.partId = partId; - theme.stateId = stateId; - d->drawBackground(theme); - } - if (sub & SC_ScrollBarSubLine) { - theme.rect = proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarSubLine, widget); - partId = SBP_ARROWBTN; - if (!(flags & State_Enabled)) - stateId = (isHorz ? (isRTL ? ABS_RIGHTDISABLED : ABS_LEFTDISABLED) : ABS_UPDISABLED); - else if (scrollbar->activeSubControls & SC_ScrollBarSubLine && (scrollbar->state & State_Sunken)) - stateId = (isHorz ? (isRTL ? ABS_RIGHTPRESSED : ABS_LEFTPRESSED) : ABS_UPPRESSED); - else if (scrollbar->activeSubControls & SC_ScrollBarSubLine && (scrollbar->state & State_MouseOver)) - stateId = (isHorz ? (isRTL ? ABS_RIGHTHOT : ABS_LEFTHOT) : ABS_UPHOT); - else if (scrollbar->state & State_MouseOver) - stateId = (isHorz ? (isRTL ? ABS_RIGHTHOVER : ABS_LEFTHOVER) : ABS_UPHOVER); - else - stateId = (isHorz ? (isRTL ? ABS_RIGHTNORMAL : ABS_LEFTNORMAL) : ABS_UPNORMAL); - theme.partId = partId; - theme.stateId = stateId; - d->drawBackground(theme); - } - if (maxedOut) { - theme.rect = proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarSlider, widget); - theme.rect = theme.rect.united(proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarSubPage, widget)); - theme.rect = theme.rect.united(proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarAddPage, widget)); - partId = flags & State_Horizontal ? SBP_LOWERTRACKHORZ : SBP_LOWERTRACKVERT; - stateId = SCRBS_DISABLED; - theme.partId = partId; - theme.stateId = stateId; - d->drawBackground(theme); - } else { - if (sub & SC_ScrollBarSubPage) { - theme.rect = proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarSubPage, widget); - partId = flags & State_Horizontal ? SBP_UPPERTRACKHORZ : SBP_UPPERTRACKVERT; - if (!(flags & State_Enabled)) - stateId = SCRBS_DISABLED; - else if (scrollbar->activeSubControls & SC_ScrollBarSubPage && (scrollbar->state & State_Sunken)) - stateId = SCRBS_PRESSED; - else if (scrollbar->activeSubControls & SC_ScrollBarSubPage && (scrollbar->state & State_MouseOver)) - stateId = SCRBS_HOT; - else - stateId = SCRBS_NORMAL; - theme.partId = partId; - theme.stateId = stateId; - d->drawBackground(theme); - } - if (sub & SC_ScrollBarAddPage) { - theme.rect = proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarAddPage, widget); - partId = flags & State_Horizontal ? SBP_LOWERTRACKHORZ : SBP_LOWERTRACKVERT; - if (!(flags & State_Enabled)) - stateId = SCRBS_DISABLED; - else if (scrollbar->activeSubControls & SC_ScrollBarAddPage && (scrollbar->state & State_Sunken)) - stateId = SCRBS_PRESSED; - else if (scrollbar->activeSubControls & SC_ScrollBarAddPage && (scrollbar->state & State_MouseOver)) - stateId = SCRBS_HOT; - else - stateId = SCRBS_NORMAL; - theme.partId = partId; - theme.stateId = stateId; - d->drawBackground(theme); - } - if (sub & SC_ScrollBarSlider) { - theme.rect = proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarSlider, widget); - if (!(flags & State_Enabled)) - stateId = SCRBS_DISABLED; - else if (scrollbar->activeSubControls & SC_ScrollBarSlider && (scrollbar->state & State_Sunken)) - stateId = SCRBS_PRESSED; - else if (scrollbar->activeSubControls & SC_ScrollBarSlider && (scrollbar->state & State_MouseOver)) - stateId = SCRBS_HOT; - else if (option->state & State_MouseOver) - stateId = SCRBS_HOVER; - else - stateId = SCRBS_NORMAL; - - // Draw handle - theme.partId = flags & State_Horizontal ? SBP_THUMBBTNHORZ : SBP_THUMBBTNVERT; - theme.stateId = stateId; - d->drawBackground(theme); - - if (QOperatingSystemVersion::current() < QOperatingSystemVersion::Windows8) { - const QRect gripperBounds = QWindowsXPStylePrivate::scrollBarGripperBounds(flags, widget, &theme); - // Draw gripper if there is enough space - if (!gripperBounds.isEmpty() && flags & State_Enabled) { - painter->save(); - XPThemeData grippBackground = theme; - grippBackground.partId = flags & State_Horizontal ? SBP_LOWERTRACKHORZ : SBP_LOWERTRACKVERT; - theme.rect = gripperBounds; - painter->setClipRegion(d->region(theme));// Only change inside the region of the gripper - d->drawBackground(grippBackground);// The gutter is the grippers background - d->drawBackground(theme); // Transparent gripper ontop of background - painter->restore(); - } - } - } - } - } - break; -#if QT_CONFIG(spinbox) - case CC_SpinBox: - if (const QStyleOptionSpinBox *sb = qstyleoption_cast<const QStyleOptionSpinBox *>(option)) - { - XPThemeData theme(widget, painter, QWindowsXPStylePrivate::SpinTheme); - if (sb->frame && (sub & SC_SpinBoxFrame)) { - partId = EP_EDITBORDER_NOSCROLL; - if (!(flags & State_Enabled)) - stateId = ETS_DISABLED; - else if (flags & State_MouseOver) - stateId = ETS_HOT; - else if (flags & State_HasFocus) - stateId = ETS_SELECTED; - else - stateId = ETS_NORMAL; - - XPThemeData ftheme(widget, painter, - QWindowsXPStylePrivate::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), - // let ftheme.noContent be false, which fixes the spinbox rendering in QQC - ftheme.noContent = (widget != nullptr); - d->drawBackground(ftheme); - } - if (sub & SC_SpinBoxUp) { - theme.rect = proxy()->subControlRect(CC_SpinBox, option, SC_SpinBoxUp, widget).adjusted(0, 0, 0, 1); - partId = SPNP_UP; - if (!(sb->stepEnabled & QAbstractSpinBox::StepUpEnabled) || !(flags & State_Enabled)) - stateId = UPS_DISABLED; - else if (sb->activeSubControls == SC_SpinBoxUp && (sb->state & State_Sunken)) - stateId = UPS_PRESSED; - else if (sb->activeSubControls == SC_SpinBoxUp && (sb->state & State_MouseOver)) - stateId = UPS_HOT; - else - stateId = UPS_NORMAL; - theme.partId = partId; - theme.stateId = stateId; - d->drawBackground(theme); - } - if (sub & SC_SpinBoxDown) { - theme.rect = proxy()->subControlRect(CC_SpinBox, option, SC_SpinBoxDown, widget); - partId = SPNP_DOWN; - if (!(sb->stepEnabled & QAbstractSpinBox::StepDownEnabled) || !(flags & State_Enabled)) - stateId = DNS_DISABLED; - else if (sb->activeSubControls == SC_SpinBoxDown && (sb->state & State_Sunken)) - stateId = DNS_PRESSED; - else if (sb->activeSubControls == SC_SpinBoxDown && (sb->state & State_MouseOver)) - stateId = DNS_HOT; - else - stateId = DNS_NORMAL; - theme.partId = partId; - theme.stateId = stateId; - d->drawBackground(theme); - } - } - break; -#endif // QT_CONFIG(spinbox) - default: - QWindowsXPStyle::drawComplexControl(control, option, painter, widget); - break; - } -} - -/*! - \internal - */ -QSize QWindowsVistaStyle::sizeFromContents(ContentsType type, const QStyleOption *option, - const QSize &size, const QWidget *widget) const -{ - if (!QWindowsVistaStylePrivate::useVista()) - return QWindowsStyle::sizeFromContents(type, option, size, widget); - - QSize sz(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()); - } - - if (const QStyleOptionMenuItem *menuitem = qstyleoption_cast<const QStyleOptionMenuItem *>(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; -#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); - } - return sz; - 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<QStyleOptionHeader *>(const_cast<QStyleOption *>(option))) { - QStyleOptionHeader::SortIndicator sortInd = hdr->sortIndicator; - hdr->sortIndicator = QStyleOptionHeader::None; - sz = QWindowsXPStyle::sizeFromContents(type, hdr, size, widget); - hdr->sortIndicator = sortInd; - return sz; - } - break; - } - default: - break; - } - return QWindowsXPStyle::sizeFromContents(type, option, size, widget); -} - -/*! - \internal - */ -QRect QWindowsVistaStyle::subElementRect(SubElement element, const QStyleOption *option, const QWidget *widget) const -{ - if (!QWindowsVistaStylePrivate::useVista()) - return QWindowsStyle::subElementRect(element, option, widget); - - QRect rect = QWindowsXPStyle::subElementRect(element, option, widget); - switch (element) { - - case SE_PushButtonContents: - if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(option)) { - MARGINS borderSize; - const HTHEME theme = OpenThemeData(widget ? QWindowsVistaStylePrivate::winId(widget) : nullptr, L"Button"); - if (theme) { - int stateId = PBS_NORMAL; - 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; - - 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_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<const QStyleOptionHeader *>(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 SE_ProgressBarContents: - rect = QCommonStyle::subElementRect(SE_ProgressBarGroove, option, widget); - break; - case SE_ItemViewItemDecoration: - if (qstyleoption_cast<const QStyleOptionViewItem *>(option)) - rect.adjust(-2, 0, 2, 0); - break; - case SE_ItemViewItemFocusRect: - if (const QStyleOptionViewItem *vopt = qstyleoption_cast<const QStyleOptionViewItem *>(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; - 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<QWindowsVistaStylePrivate*>(d_func()); - int ret = 0; - switch (hint) { - case SH_MessageBox_CenterButtons: - ret = false; - break; - case SH_ToolTip_Mask: - if (option) { - if (QStyleHintReturnMask *mask = qstyleoption_cast<QStyleHintReturnMask*>(returnData)) { - ret = true; - XPThemeData themeData(widget, nullptr, - QWindowsXPStylePrivate::ToolTipTheme, - TTP_STANDARD, TTSS_NORMAL, option->rect); - mask->region = d->region(themeData); - } - } - 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; - default: - ret = QWindowsXPStyle::styleHint(hint, option, widget, returnData); - break; - } - return ret; -} - - -/*! - \internal - */ -QRect QWindowsVistaStyle::subControlRect(ComplexControl control, const QStyleOptionComplex *option, - SubControl subControl, const QWidget *widget) const -{ - if (!QWindowsVistaStylePrivate::useVista()) - return QWindowsStyle::subControlRect(control, option, subControl, widget); - - QRect rect = QWindowsXPStyle::subControlRect(control, option, subControl, widget); - switch (control) { -#if QT_CONFIG(combobox) - case CC_ComboBox: - if (const QStyleOptionComboBox *cb = qstyleoption_cast<const QStyleOptionComboBox *>(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; - const int arrowWidth = qRound(QStyleHelper::dpiScaled(16, option)); - const int arrowButtonWidth = bmarg + arrowWidth; - const int xpos = x + wi - arrowButtonWidth; - - switch (subControl) { - case SC_ComboBoxFrame: - rect = cb->rect; - break; - case SC_ComboBoxArrow: - rect.setRect(xpos, y , arrowButtonWidth, he); - break; - 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<const QStyleOptionTitleBar *>(option)) { - if (!buttonVisible(subControl, tb)) - return rect; - const qreal factor = QWindowsStylePrivate::nativeMetricScaleFactor(widget); - const bool isToolTitle = false; - const int height = tb->rect.height(); - const int width = tb->rect.width(); - const int buttonWidth = - 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; - 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; - - switch (subControl) { - case SC_TitleBarLabel: - rect = QRect(frameWidth, 0, width - (buttonWidth + frameWidth + 10), height); - if (isToolTitle) { - if (sysmenuHint) { - rect.adjust(0, 0, int(-buttonWidth - 3 * factor), 0); - } - if (minimizeHint || maximizeHint) - rect.adjust(0, 0, int(-buttonWidth - 2 * factor), 0); - } else { - if (sysmenuHint) { - const int leftOffset = int(height - 8 * factor); - rect.adjust(leftOffset, 0, 0, int(4 * factor)); - } - if (minimizeHint) - rect.adjust(0, 0, int(-buttonWidth - 2 * factor), 0); - if (maximizeHint) - rect.adjust(0, 0, int(-buttonWidth - 2 * factor), 0); - if (contextHint) - rect.adjust(0, 0, int(-buttonWidth - 2 * factor), 0); - if (shadeHint) - 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); - } - break; - default: - break; - } - } - break; - default: - break; - } - return 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) -{ - 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; -} - -/*! - \internal - */ -int QWindowsVistaStyle::pixelMetric(PixelMetric metric, const QStyleOption *option, const QWidget *widget) const -{ - if (!QWindowsVistaStylePrivate::useVista()) - return QWindowsStyle::pixelMetric(metric, option, widget); - - int ret = QWindowsVistaStylePrivate::fixedPixelMetric(metric); - if (ret != QWindowsStylePrivate::InvalidMetric) - return int(QStyleHelper::dpiScaled(ret, option)); - - return QWindowsXPStyle::pixelMetric(metric, option, widget); -} - -/*! - \internal - */ -void QWindowsVistaStyle::polish(QWidget *widget) -{ - QWindowsXPStyle::polish(widget); -#if QT_CONFIG(lineedit) - if (qobject_cast<QLineEdit*>(widget)) - widget->setAttribute(Qt::WA_Hover); - else -#endif // QT_CONFIG(lineedit) - if (qobject_cast<QGroupBox*>(widget)) - widget->setAttribute(Qt::WA_Hover); -#if QT_CONFIG(commandlinkbutton) - else if (qobject_cast<QCommandLinkButton*>(widget)) { - QFont buttonFont = widget->font(); - buttonFont.setFamily(QLatin1String("Segoe UI")); - widget->setFont(buttonFont); - } -#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<QMessageBox *> (widget)) { - widget->setAttribute(Qt::WA_StyledBackground); -#if QT_CONFIG(dialogbuttonbox) - QDialogButtonBox *buttonBox = widget->findChild<QDialogButtonBox *>(QLatin1String("qt_msgbox_buttonbox")); - if (buttonBox) - buttonBox->setContentsMargins(0, 9, 0, 0); -#endif - } -#if QT_CONFIG(inputdialog) - else if (qobject_cast<QInputDialog *> (widget)) { - widget->setAttribute(Qt::WA_StyledBackground); -#if QT_CONFIG(dialogbuttonbox) - QDialogButtonBox *buttonBox = widget->findChild<QDialogButtonBox *>(QLatin1String("qt_inputdlg_buttonbox")); - if (buttonBox) - buttonBox->setContentsMargins(0, 9, 0, 0); -#endif - } -#endif // QT_CONFIG(inputdialog) - else if (QTreeView *tree = qobject_cast<QTreeView *> (widget)) { - tree->viewport()->setAttribute(Qt::WA_Hover); - } - else if (QListView *list = qobject_cast<QListView *> (widget)) { - list->viewport()->setAttribute(Qt::WA_Hover); - } -} - -/*! - \internal - */ -void QWindowsVistaStyle::unpolish(QWidget *widget) -{ - QWindowsXPStyle::unpolish(widget); - - QWindowsVistaStylePrivate *d = d_func(); - - d->stopAnimation(widget); - -#if QT_CONFIG(lineedit) - if (qobject_cast<QLineEdit*>(widget)) - widget->setAttribute(Qt::WA_Hover, false); - else -#endif // QT_CONFIG(lineedit) - if (qobject_cast<QGroupBox*>(widget)) - widget->setAttribute(Qt::WA_Hover, false); - else if (qobject_cast<QMessageBox *> (widget)) { - widget->setAttribute(Qt::WA_StyledBackground, false); -#if QT_CONFIG(dialogbuttonbox) - QDialogButtonBox *buttonBox = widget->findChild<QDialogButtonBox *>(QLatin1String("qt_msgbox_buttonbox")); - if (buttonBox) - buttonBox->setContentsMargins(0, 0, 0, 0); -#endif - } -#if QT_CONFIG(inputdialog) - else if (qobject_cast<QInputDialog *> (widget)) { - widget->setAttribute(Qt::WA_StyledBackground, false); -#if QT_CONFIG(dialogbuttonbox) - QDialogButtonBox *buttonBox = widget->findChild<QDialogButtonBox *>(QLatin1String("qt_inputdlg_buttonbox")); - if (buttonBox) - buttonBox->setContentsMargins(0, 0, 0, 0); -#endif - } -#endif // QT_CONFIG(inputdialog) - else if (QTreeView *tree = qobject_cast<QTreeView *> (widget)) { - tree->viewport()->setAttribute(Qt::WA_Hover, false); - } -#if QT_CONFIG(commandlinkbutton) - else if (qobject_cast<QCommandLinkButton*>(widget)) { - QFont font = QApplication::font("QCommandLinkButton"); - QFont widgetFont = widget->font(); - widgetFont.setFamily(font.family()); //Only family set by polish - widget->setFont(widgetFont); - } -#endif // QT_CONFIG(commandlinkbutton) -} - -/*! - \internal - */ -void QWindowsVistaStyle::polish(QPalette &pal) -{ - QWindowsStyle::polish(pal); - pal.setBrush(QPalette::AlternateBase, pal.base().color().darker(104)); -} - -/*! - \internal - */ -QPixmap QWindowsVistaStyle::standardPixmap(StandardPixmap standardPixmap, const QStyleOption *option, - 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; - } - return false; -} - -/*! -\reimp -*/ -QIcon QWindowsVistaStyle::standardIcon(StandardPixmap standardIcon, - const QStyleOption *option, - const QWidget *widget) const -{ - if (!QWindowsVistaStylePrivate::useVista()) { - return QWindowsStyle::standardIcon(standardIcon, option, widget); - } - - QWindowsVistaStylePrivate *d = const_cast<QWindowsVistaStylePrivate *>(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); - - 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); -} - -QT_END_NAMESPACE diff --git a/src/plugins/styles/windowsvista/qwindowsvistastyle_p_p.h b/src/plugins/styles/windowsvista/qwindowsvistastyle_p_p.h deleted file mode 100644 index 4fd4740ffe..0000000000 --- a/src/plugins/styles/windowsvista/qwindowsvistastyle_p_p.h +++ /dev/null @@ -1,202 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWidgets module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QWINDOWSVISTASTYLE_P_P_H -#define QWINDOWSVISTASTYLE_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 <QtWidgets/private/qtwidgetsglobal_p.h> -#include "qwindowsvistastyle_p.h" -#include "qwindowsxpstyle_p_p.h" -#include <private/qstyleanimation_p.h> -#include <private/qpaintengine_raster_p.h> -#include <qpaintengine.h> -#include <qwidget.h> -#include <qapplication.h> -#include <qpixmapcache.h> -#include <qstyleoption.h> -#if QT_CONFIG(pushbutton) -#include <qpushbutton.h> -#endif -#include <qradiobutton.h> -#if QT_CONFIG(lineedit) -#include <qlineedit.h> -#endif -#include <qgroupbox.h> -#if QT_CONFIG(toolbutton) -#include <qtoolbutton.h> -#endif -#if QT_CONFIG(spinbox) -#include <qspinbox.h> -#endif -#if QT_CONFIG(toolbar) -#include <qtoolbar.h> -#endif -#if QT_CONFIG(combobox) -#include <qcombobox.h> -#endif -#if QT_CONFIG(scrollbar) -#include <qscrollbar.h> -#endif -#if QT_CONFIG(progressbar) -#include <qprogressbar.h> -#endif -#if QT_CONFIG(dockwidget) -#include <qdockwidget.h> -#endif -#if QT_CONFIG(listview) -#include <qlistview.h> -#endif -#if QT_CONFIG(treeview) -#include <qtreeview.h> -#endif -#include <qtextedit.h> -#include <qmessagebox.h> -#if QT_CONFIG(dialogbuttonbox) -#include <qdialogbuttonbox.h> -#endif -#include <qinputdialog.h> -#if QT_CONFIG(tableview) -#include <qtableview.h> -#endif -#include <qdatetime.h> -#if QT_CONFIG(commandlinkbutton) -#include <qcommandlinkbutton.h> -#endif - -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 -{ - 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) {} -}; - - -class QWindowsVistaStylePrivate : public QWindowsXPStylePrivate -{ - Q_DECLARE_PUBLIC(QWindowsVistaStyle) - -public: - static int fixedPixelMetric(QStyle::PixelMetric pm); - static inline bool useVista(); - bool transitionsEnabled() const; -}; - -QT_END_NAMESPACE - -#endif // QWINDOWSVISTASTYLE_P_P_H diff --git a/src/plugins/styles/windowsvista/qwindowsxpstyle.cpp b/src/plugins/styles/windowsvista/qwindowsxpstyle.cpp deleted file mode 100644 index a324de0fc8..0000000000 --- a/src/plugins/styles/windowsvista/qwindowsxpstyle.cpp +++ /dev/null @@ -1,4165 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWidgets module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#include "qwindowsxpstyle_p.h" -#include "qwindowsxpstyle_p_p.h" - -#include <private/qobject_p.h> -#include <private/qpaintengine_raster_p.h> -#include <private/qapplication_p.h> -#include <private/qstylehelper_p.h> -#include <private/qwidget_p.h> -#include <qpainter.h> -#include <qpaintengine.h> -#include <qwidget.h> -#include <qbackingstore.h> -#include <qapplication.h> -#include <qpixmapcache.h> -#include <private/qapplication_p.h> -#include <qpa/qplatformintegration.h> - -#if QT_CONFIG(toolbutton) -#include <qtoolbutton.h> -#endif -#if QT_CONFIG(tabbar) -#include <qtabbar.h> -#endif -#if QT_CONFIG(combobox) -#include <qcombobox.h> -#endif -#if QT_CONFIG(scrollbar) -#include <qscrollbar.h> -#endif -#include <qheaderview.h> -#if QT_CONFIG(spinbox) -#include <qspinbox.h> -#endif -#if QT_CONFIG(listview) -#include <qlistview.h> -#endif -#if QT_CONFIG(stackedwidget) -#include <qstackedwidget.h> -#endif -#if QT_CONFIG(pushbutton) -#include <qpushbutton.h> -#endif -#if QT_CONFIG(toolbar) -#include <qtoolbar.h> -#endif -#include <qlabel.h> -#include <qvarlengtharray.h> -#include <qdebug.h> - -#include <algorithm> - -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<HDC>(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; -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 (particulary 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 = QPlatformInterface::Private::QWindowsApplication; - - HWND result = nullptr; - if (auto nativeWindowsApp = dynamic_cast<QWindowsApplication *>(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 refere 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(); - // Exlude 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<QAbstractSpinBox*>(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 QStyleOptionProgressBar *pb = qstyleoption_cast<const QStyleOptionProgressBar *>(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<QWidget *>(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<HWND>(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<void **>(&bufferPixels), nullptr, 0); - GdiFlush(); - nullBitmap = static_cast<HBITMAP>(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<RGNDATA*>(buf); - if (GetRegionData(dest, numBytes, rd) == 0) { - delete [] buf; - return QRegion(); - } - - RECT *r = reinterpret_cast<RECT*>(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<const DWORD *>(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 occures. -*/ -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<DWORD *>(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 determin 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<DWORD *>(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<const QWidget *>(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 (horizonal 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(); - 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<void *>(&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(w, h, 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<void *>(&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<QAbstractButton*>(widget) -#endif - || qobject_cast<QToolButton*>(widget) - || qobject_cast<QTabBar*>(widget) -#if QT_CONFIG(combobox) - || qobject_cast<QComboBox*>(widget) -#endif // QT_CONFIG(combobox) - || qobject_cast<QScrollBar*>(widget) - || qobject_cast<QSlider*>(widget) - || qobject_cast<QHeaderView*>(widget) -#if QT_CONFIG(spinbox) - || qobject_cast<QAbstractSpinBox*>(widget) - || qobject_cast<QSpinBox*>(widget) -#endif // QT_CONFIG(spinbox) - ) { - widget->setAttribute(Qt::WA_Hover); - } - -#if QT_CONFIG(rubberband) - if (qobject_cast<QRubberBand*>(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<QRubberBand*>(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<QAbstractButton*>(widget) -#endif - || qobject_cast<QToolButton*>(widget) - || qobject_cast<QTabBar*>(widget) -#if QT_CONFIG(combobox) - || qobject_cast<QComboBox*>(widget) -#endif // QT_CONFIG(combobox) - || qobject_cast<QScrollBar*>(widget) - || qobject_cast<QSlider*>(widget) - || qobject_cast<QHeaderView*>(widget) -#if QT_CONFIG(spinbox) - || qobject_cast<QAbstractSpinBox*>(widget) - || qobject_cast<QSpinBox*>(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<const QStyleOptionTabWidgetFrame *>(option)) - { - rect = QWindowsStyle::subElementRect(sr, option, widget); - if (sr == SE_TabWidgetTabContents) { - if (const QTabWidget *tabWidget = qobject_cast<const QTabWidget *>(widget)) { - if (tabWidget->documentMode()) - break; - } - - rect.adjust(0, 0, -2, -2); - } - } - break; - case SE_TabWidgetTabBar: { - rect = QWindowsStyle::subElementRect(sr, option, widget); - const QStyleOptionTabWidgetFrame *twfOption = - qstyleoption_cast<const QStyleOptionTabWidgetFrame *>(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 QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(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 -{ - QWindowsXPStylePrivate *d = const_cast<QWindowsXPStylePrivate*>(d_func()); - - if (!QWindowsXPStylePrivate::useXP()) { - QWindowsStyle::drawPrimitive(pe, option, p, widget); - return; - } - - 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 QStyleOptionTabBarBase *tbb - = qstyleoption_cast<const QStyleOptionTabBarBase *>(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_FrameLineEdit: { - // we try to check if this lineedit is a delegate on a QAbstractItemView-derived class. - if (QWindowsXPStylePrivate::isItemViewDelegateLineEdit(widget)) { - QPen oldPen = p->pen(); - // Inner white border - p->setPen(QPen(option->palette.base().color(), 1)); - p->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); - return; - } - if (qstyleoption_cast<const QStyleOptionFrame *>(option)) { - themeNumber = QWindowsXPStylePrivate::EditTheme; - partId = EP_EDITTEXT; - noContent = true; - if (!(flags & State_Enabled)) - stateId = ETS_DISABLED; - else - stateId = ETS_NORMAL; - } - break; - } - - case PE_PanelLineEdit: - if (const QStyleOptionFrame *panel = qstyleoption_cast<const QStyleOptionFrame *>(option)) { - themeNumber = QWindowsXPStylePrivate::EditTheme; - partId = EP_EDITTEXT; - noBorder = true; - bool isEnabled = flags & State_Enabled; - - stateId = isEnabled ? ETS_NORMAL : ETS_DISABLED; - - if (QWindowsXPStylePrivate::isLineEditBaseColorSet(option, widget)) { - p->fillRect(panel->rect, panel->palette.brush(QPalette::Base)); - } else { - XPThemeData theme(nullptr, p, themeNumber, partId, stateId, rect); - if (!theme.isValid()) { - QWindowsStyle::drawPrimitive(pe, option, p, widget); - return; - } - int bgType; - GetThemeEnumValue(theme.handle(), partId, stateId, TMT_BGTYPE, &bgType); - if( bgType == BT_IMAGEFILE ) { - theme.mirrorHorizontally = hMirrored; - theme.mirrorVertically = vMirrored; - theme.noBorder = noBorder; - theme.noContent = noContent; - theme.rotate = rotate; - d->drawBackground(theme); - } else { - QBrush fillColor = option->palette.brush(QPalette::Base); - - if (!isEnabled) { - PROPERTYORIGIN origin = PO_NOTFOUND; - GetThemePropertyOrigin(theme.handle(), theme.partId, theme.stateId, TMT_FILLCOLOR, &origin); - // Use only if the fill property comes from our part - if ((origin == PO_PART || origin == PO_STATE)) { - COLORREF bgRef; - GetThemeColor(theme.handle(), partId, stateId, TMT_FILLCOLOR, &bgRef); - fillColor = QBrush(qRgb(GetRValue(bgRef), GetGValue(bgRef), GetBValue(bgRef))); - } - } - p->fillRect(option->rect, fillColor); - } - } - - if (panel->lineWidth > 0) - proxy()->drawPrimitive(PE_FrameLineEdit, panel, p, widget); - return; - } - break; - - case PE_FrameTabWidget: - if (const QStyleOptionTabWidgetFrame *tab = qstyleoption_cast<const QStyleOptionTabWidgetFrame *>(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 QStyleOptionFrame *frm = qstyleoption_cast<const QStyleOptionFrame *>(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 QStyleOptionHeader *header = qstyleoption_cast<const QStyleOptionHeader *>(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 QStyleOptionFrame *frame = qstyleoption_cast<const QStyleOptionFrame *>(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_IndicatorProgressChunk: - { - Qt::Orientation orient = Qt::Horizontal; - bool inverted = false; - if (const QStyleOptionProgressBar *pb = qstyleoption_cast<const QStyleOptionProgressBar *>(option)) { - orient = pb->state & QStyle::State_Horizontal ? Qt::Horizontal : Qt::Vertical; - inverted = pb->invertedAppearance; - } - if (orient & Qt::Horizontal) { - partId = PP_CHUNK; - rect = QRect(option->rect.x(), option->rect.y(), option->rect.width(), option->rect.height() ); - if (inverted && option->direction == Qt::LeftToRight) - hMirrored = true; - } else { - partId = PP_CHUNKVERT; - rect = QRect(option->rect.x(), option->rect.y(), option->rect.width(), option->rect.height()); - } - themeNumber = QWindowsXPStylePrivate::ProgressTheme; - stateId = 1; - } - break; - - case PE_FrameWindow: - if (const QStyleOptionFrame *frm = qstyleoption_cast<const QStyleOptionFrame *>(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_IndicatorBranch: - { - static const int decoration_size = 9; - 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; - QBrush brush(option->palette.dark().color(), Qt::Dense4Pattern); - if (option->state & State_Item) { - if (option->direction == Qt::RightToLeft) - p->fillRect(option->rect.left(), mid_v, bef_h - option->rect.left(), 1, brush); - else - p->fillRect(aft_h, mid_v, option->rect.right() - aft_h + 1, 1, brush); - } - if (option->state & State_Sibling) - p->fillRect(mid_h, aft_v, 1, option->rect.bottom() - aft_v + 1, brush); - if (option->state & (State_Open | State_Children | State_Item | State_Sibling)) - p->fillRect(mid_h, option->rect.y(), 1, bef_v - option->rect.y(), brush); - if (option->state & State_Children) { - int delta = decoration_size / 2; - bef_h -= delta; - bef_v -= delta; - aft_h += delta; - aft_v += delta; - XPThemeData theme(nullptr, p, QWindowsXPStylePrivate::XpTreeViewTheme); - theme.rect = QRect(bef_h, bef_v, decoration_size, decoration_size); - theme.partId = TVP_GLYPH; - theme.stateId = flags & QStyle::State_Open ? GLPS_OPENED : GLPS_CLOSED; - d->drawBackground(theme); - } - } - return; - - 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 -{ - QWindowsXPStylePrivate *d = const_cast<QWindowsXPStylePrivate*>(d_func()); - if (!QWindowsXPStylePrivate::useXP()) { - QWindowsStyle::drawControl(element, option, p, widget); - return; - } - - QRect rect(option->rect); - State flags = option->state; - - 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 QStyleOptionSizeGrip *sg = qstyleoption_cast<const QStyleOptionSizeGrip *>(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_HeaderSection: - themeNumber = QWindowsXPStylePrivate::HeaderTheme; - partId = HP_HEADERITEM; - if (flags & State_Sunken) - stateId = HIS_PRESSED; - else if (flags & State_MouseOver) - stateId = HIS_HOT; - else - stateId = HIS_NORMAL; - break; - - case CE_Splitter: - p->eraseRect(option->rect); - return; - - case CE_PushButtonBevel: - if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(option)) - { - themeNumber = QWindowsXPStylePrivate::ButtonTheme; - partId = BP_PUSHBUTTON; - bool justFlat = ((btn->features & QStyleOptionButton::Flat) && !(flags & (State_On|State_Sunken))) - || ((btn->features & QStyleOptionButton::CommandLinkButton) - && !(flags & State_MouseOver) - && !(btn->features & QStyleOptionButton::DefaultButton)); - if (!(flags & State_Enabled) && !(btn->features & QStyleOptionButton::Flat)) - stateId = PBS_DISABLED; - else if (justFlat) - ; - else if (flags & (State_Sunken | State_On)) - stateId = PBS_PRESSED; - else if (flags & State_MouseOver) - stateId = PBS_HOT; - else if (btn->features & QStyleOptionButton::DefaultButton) - stateId = PBS_DEFAULTED; - else - stateId = PBS_NORMAL; - - if (!justFlat) { - XPThemeData theme(widget, p, themeNumber, partId, stateId, rect); - d->drawBackground(theme); - } - - if (btn->features & QStyleOptionButton::HasMenu) { - int mbiw = 0, mbih = 0; - XPThemeData theme(widget, nullptr, - QWindowsXPStylePrivate::ToolBarTheme, - TP_SPLITBUTTONDROPDOWN); - if (theme.isValid()) { - const QSize size = (theme.size() * QWindowsStylePrivate::nativeMetricScaleFactor(widget)).toSize(); - mbiw = size.width(); - mbih = size.height(); - } - - QRect ir = btn->rect; - QStyleOptionButton newBtn = *btn; - newBtn.rect = QRect(ir.right() - mbiw - 1, 1 + (ir.height()/2) - (mbih/2), mbiw, mbih); - proxy()->drawPrimitive(PE_IndicatorArrowDown, &newBtn, p, widget); - } - return; - } - break; - case CE_TabBarTab: - if (const QStyleOptionTab *tab = qstyleoption_cast<const QStyleOptionTab *>(option)) - { - stateId = tab->state & State_Enabled ? TIS_NORMAL : TIS_DISABLED; - } - break; - - case CE_TabBarTabShape: - if (const QStyleOptionTab *tab = qstyleoption_cast<const QStyleOptionTab *>(option)) - { - themeNumber = QWindowsXPStylePrivate::TabTheme; - bool isDisabled = !(tab->state & State_Enabled); - bool hasFocus = tab->state & State_HasFocus; - bool isHot = tab->state & State_MouseOver; - bool selected = tab->state & State_Selected; - bool lastTab = tab->position == QStyleOptionTab::End; - bool firstTab = tab->position == QStyleOptionTab::Beginning; - bool onlyOne = tab->position == QStyleOptionTab::OnlyOneTab; - bool leftAligned = proxy()->styleHint(SH_TabBar_Alignment, tab, widget) == Qt::AlignLeft; - bool centerAligned = proxy()->styleHint(SH_TabBar_Alignment, tab, widget) == Qt::AlignCenter; - int borderThickness = proxy()->pixelMetric(PM_DefaultFrameWidth, option, widget); - 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; - } - bool begin = firstTab || onlyOne; - 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 QStyleOptionMenuItem *menuitem = qstyleoption_cast<const QStyleOptionMenuItem *>(option)) - { - int tab = menuitem->reservedShortcutWidth; - bool dis = !(menuitem->state & State_Enabled); - bool act = menuitem->state & State_Selected; - bool checkable = menuitem->menuHasCheckableItems; - bool checked = checkable ? menuitem->checked : false; - - // windows always has a check column, regardless whether we have an icon or not - 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); - const int pixw = pixmap.width() / pixmap.devicePixelRatio(); - const int pixh = pixmap.height() / pixmap.devicePixelRatio(); - QRect iconRect(0, 0, pixw, pixh); - 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 QStyleOptionMenuItem *mbi = qstyleoption_cast<const QStyleOptionMenuItem *>(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 QStyleOptionDockWidget *dwOpt = qstyleoption_cast<const QStyleOptionDockWidget *>(option)) - { - int buttonMargin = 4; - int mw = proxy()->pixelMetric(QStyle::PM_DockWidgetTitleMargin, dwOpt, widget); - int fw = proxy()->pixelMetric(PM_DockWidgetFrameWidth, dwOpt, widget); - bool isFloating = widget && widget->isWindow(); - 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<const QStyleOptionRubberBand *>(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->isTopLevel())? 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). -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 -{ - QWindowsXPStylePrivate *d = const_cast<QWindowsXPStylePrivate*>(d_func()); - - if (!QWindowsXPStylePrivate::useXP()) { - QWindowsStyle::drawComplexControl(cc, option, p, widget); - return; - } - - State flags = option->state; - SubControls sub = option->subControls; - QRect r = option->rect; - - int partId = 0; - int stateId = 0; - if (widget && widget->testAttribute(Qt::WA_UnderMouse) && widget->isActiveWindow()) - flags |= State_MouseOver; - - switch (cc) { -#if QT_CONFIG(spinbox) - case CC_SpinBox: - if (const QStyleOptionSpinBox *sb = qstyleoption_cast<const QStyleOptionSpinBox *>(option)) - { - XPThemeData theme(widget, p, QWindowsXPStylePrivate::SpinTheme); - - if (sb->frame && (sub & SC_SpinBoxFrame)) { - partId = EP_EDITTEXT; - if (!(flags & State_Enabled)) - stateId = ETS_DISABLED; - else if (flags & State_HasFocus) - stateId = ETS_FOCUSED; - else - stateId = ETS_NORMAL; - - XPThemeData ftheme(widget, p, QWindowsXPStylePrivate::EditTheme, - partId, stateId, r); - ftheme.noContent = true; - d->drawBackground(ftheme); - } - if (sub & SC_SpinBoxUp) { - theme.rect = proxy()->subControlRect(CC_SpinBox, option, SC_SpinBoxUp, widget); - partId = SPNP_UP; - if (!(sb->stepEnabled & QAbstractSpinBox::StepUpEnabled) || !(flags & State_Enabled)) - stateId = UPS_DISABLED; - else if (sb->activeSubControls == SC_SpinBoxUp && (sb->state & State_Sunken)) - stateId = UPS_PRESSED; - else if (sb->activeSubControls == SC_SpinBoxUp && (sb->state & State_MouseOver)) - stateId = UPS_HOT; - else - stateId = UPS_NORMAL; - theme.partId = partId; - theme.stateId = stateId; - d->drawBackground(theme); - } - if (sub & SC_SpinBoxDown) { - theme.rect = proxy()->subControlRect(CC_SpinBox, option, SC_SpinBoxDown, widget); - partId = SPNP_DOWN; - if (!(sb->stepEnabled & QAbstractSpinBox::StepDownEnabled) || !(flags & State_Enabled)) - stateId = DNS_DISABLED; - else if (sb->activeSubControls == SC_SpinBoxDown && (sb->state & State_Sunken)) - stateId = DNS_PRESSED; - else if (sb->activeSubControls == SC_SpinBoxDown && (sb->state & State_MouseOver)) - stateId = DNS_HOT; - else - stateId = DNS_NORMAL; - theme.partId = partId; - theme.stateId = stateId; - d->drawBackground(theme); - } - } - break; -#endif // QT_CONFIG(spinbox) -#if QT_CONFIG(combobox) - case CC_ComboBox: - if (const QStyleOptionComboBox *cmb = qstyleoption_cast<const QStyleOptionComboBox *>(option)) - { - if (sub & SC_ComboBoxEditField) { - if (cmb->frame) { - partId = EP_EDITTEXT; - if (!(flags & State_Enabled)) - stateId = ETS_DISABLED; - else if (flags & State_HasFocus) - stateId = ETS_FOCUSED; - else - stateId = ETS_NORMAL; - XPThemeData theme(widget, p, QWindowsXPStylePrivate::EditTheme, partId, stateId, r); - d->drawBackground(theme); - } else { - QBrush editBrush = cmb->palette.brush(QPalette::Base); - p->fillRect(option->rect, editBrush); - } - if (!cmb->editable) { - QRect re = proxy()->subControlRect(CC_ComboBox, option, SC_ComboBoxEditField, widget); - if (option->state & State_HasFocus) { - p->fillRect(re, option->palette.highlight()); - p->setPen(option->palette.highlightedText().color()); - p->setBackground(option->palette.highlight()); - } else { - p->fillRect(re, option->palette.base()); - p->setPen(option->palette.text().color()); - p->setBackground(option->palette.base()); - } - } - } - - if (sub & SC_ComboBoxArrow) { - XPThemeData theme(widget, p, QWindowsXPStylePrivate::ComboboxTheme); - theme.rect = proxy()->subControlRect(CC_ComboBox, option, SC_ComboBoxArrow, widget); - partId = CP_DROPDOWNBUTTON; - if (!(flags & State_Enabled)) - stateId = CBXS_DISABLED; - else if (cmb->activeSubControls == SC_ComboBoxArrow && (cmb->state & State_Sunken)) - stateId = CBXS_PRESSED; - else if (cmb->activeSubControls == SC_ComboBoxArrow && (cmb->state & State_MouseOver)) - stateId = CBXS_HOT; - else - stateId = CBXS_NORMAL; - theme.partId = partId; - theme.stateId = stateId; - d->drawBackground(theme); - } - } - break; -#endif // QT_CONFIG(combobox) - case CC_ScrollBar: - if (const QStyleOptionSlider *scrollbar = qstyleoption_cast<const QStyleOptionSlider *>(option)) - { - XPThemeData theme(widget, p, QWindowsXPStylePrivate::ScrollBarTheme); - bool maxedOut = (scrollbar->maximum == scrollbar->minimum); - if (maxedOut) - flags &= ~State_Enabled; - - bool isHorz = flags & State_Horizontal; - bool isRTL = option->direction == Qt::RightToLeft; - if (sub & SC_ScrollBarAddLine) { - theme.rect = proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarAddLine, widget); - partId = SBP_ARROWBTN; - if (!(flags & State_Enabled)) - stateId = (isHorz ? (isRTL ? ABS_LEFTDISABLED : ABS_RIGHTDISABLED) : ABS_DOWNDISABLED); - else if (scrollbar->activeSubControls & SC_ScrollBarAddLine && (scrollbar->state & State_Sunken)) - stateId = (isHorz ? (isRTL ? ABS_LEFTPRESSED : ABS_RIGHTPRESSED) : ABS_DOWNPRESSED); - else if (scrollbar->activeSubControls & SC_ScrollBarAddLine && (scrollbar->state & State_MouseOver)) - stateId = (isHorz ? (isRTL ? ABS_LEFTHOT : ABS_RIGHTHOT) : ABS_DOWNHOT); - else - stateId = (isHorz ? (isRTL ? ABS_LEFTNORMAL : ABS_RIGHTNORMAL) : ABS_DOWNNORMAL); - theme.partId = partId; - theme.stateId = stateId; - d->drawBackground(theme); - } - if (sub & SC_ScrollBarSubLine) { - theme.rect = proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarSubLine, widget); - partId = SBP_ARROWBTN; - if (!(flags & State_Enabled)) - stateId = (isHorz ? (isRTL ? ABS_RIGHTDISABLED : ABS_LEFTDISABLED) : ABS_UPDISABLED); - else if (scrollbar->activeSubControls & SC_ScrollBarSubLine && (scrollbar->state & State_Sunken)) - stateId = (isHorz ? (isRTL ? ABS_RIGHTPRESSED : ABS_LEFTPRESSED) : ABS_UPPRESSED); - else if (scrollbar->activeSubControls & SC_ScrollBarSubLine && (scrollbar->state & State_MouseOver)) - stateId = (isHorz ? (isRTL ? ABS_RIGHTHOT : ABS_LEFTHOT) : ABS_UPHOT); - else - stateId = (isHorz ? (isRTL ? ABS_RIGHTNORMAL : ABS_LEFTNORMAL) : ABS_UPNORMAL); - theme.partId = partId; - theme.stateId = stateId; - d->drawBackground(theme); - } - if (maxedOut) { - theme.rect = proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarSlider, widget); - theme.rect = theme.rect.united(proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarSubPage, widget)); - theme.rect = theme.rect.united(proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarAddPage, widget)); - partId = scrollbar->orientation == Qt::Horizontal ? SBP_LOWERTRACKHORZ : SBP_LOWERTRACKVERT; - stateId = SCRBS_DISABLED; - theme.partId = partId; - theme.stateId = stateId; - d->drawBackground(theme); - } else { - if (sub & SC_ScrollBarSubPage) { - theme.rect = proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarSubPage, widget); - partId = flags & State_Horizontal ? SBP_UPPERTRACKHORZ : SBP_UPPERTRACKVERT; - if (!(flags & State_Enabled)) - stateId = SCRBS_DISABLED; - else if (scrollbar->activeSubControls & SC_ScrollBarSubPage && (scrollbar->state & State_Sunken)) - stateId = SCRBS_PRESSED; - else if (scrollbar->activeSubControls & SC_ScrollBarSubPage && (scrollbar->state & State_MouseOver)) - stateId = SCRBS_HOT; - else - stateId = SCRBS_NORMAL; - theme.partId = partId; - theme.stateId = stateId; - d->drawBackground(theme); - } - if (sub & SC_ScrollBarAddPage) { - theme.rect = proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarAddPage, widget); - partId = flags & State_Horizontal ? SBP_LOWERTRACKHORZ : SBP_LOWERTRACKVERT; - if (!(flags & State_Enabled)) - stateId = SCRBS_DISABLED; - else if (scrollbar->activeSubControls & SC_ScrollBarAddPage && (scrollbar->state & State_Sunken)) - stateId = SCRBS_PRESSED; - else if (scrollbar->activeSubControls & SC_ScrollBarAddPage && (scrollbar->state & State_MouseOver)) - stateId = SCRBS_HOT; - else - stateId = SCRBS_NORMAL; - theme.partId = partId; - theme.stateId = stateId; - d->drawBackground(theme); - } - if (sub & SC_ScrollBarSlider) { - theme.rect = proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarSlider, widget); - if (!(flags & State_Enabled)) - stateId = SCRBS_DISABLED; - else if (scrollbar->activeSubControls & SC_ScrollBarSlider && (scrollbar->state & State_Sunken)) - stateId = SCRBS_PRESSED; - else if (scrollbar->activeSubControls & SC_ScrollBarSlider && (scrollbar->state & State_MouseOver)) - stateId = SCRBS_HOT; - else - stateId = SCRBS_NORMAL; - - // Draw handle - theme.partId = flags & State_Horizontal ? SBP_THUMBBTNHORZ : SBP_THUMBBTNVERT; - theme.stateId = stateId; - d->drawBackground(theme); - - const QRect gripperBounds = QWindowsXPStylePrivate::scrollBarGripperBounds(flags, widget, &theme); - // Draw gripper if there is enough space - if (!gripperBounds.isEmpty()) { - p->save(); - theme.rect = gripperBounds; - p->setClipRegion(d->region(theme));// Only change inside the region of the gripper - d->drawBackground(theme); // Transparent gripper ontop of background - p->restore(); - } - } - } - } - break; - -#if QT_CONFIG(slider) - case CC_Slider: - if (const QStyleOptionSlider *slider = qstyleoption_cast<const QStyleOptionSlider *>(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<QLine, 32> 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 QStyleOptionToolButton *toolbutton - = qstyleoption_cast<const QStyleOptionToolButton *>(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 QStyleOptionTitleBar *tb = qstyleoption_cast<const QStyleOptionTitleBar *>(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 QStyleOptionSlider *dial = qstyleoption_cast<const QStyleOptionSlider *>(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<const QListView*>(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 QStyleOptionTab *tab = qstyleoption_cast<const QStyleOptionTab *>(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<const QStyleOptionToolButton *>(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 QStyleOptionTitleBar *tb = qstyleoption_cast<const QStyleOptionTitleBar *>(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 QStyleOptionComboBox *cmb = qstyleoption_cast<const QStyleOptionComboBox *>(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_SpinBox: - { - //Spinbox adds frame twice - sz = QWindowsStyle::sizeFromContents(ct, option, contentsSize, widget); - int border = proxy()->pixelMetric(PM_SpinBoxFrameWidth, option, widget); - sz -= QSize(2*border, 2*border); - } - 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 QStyleOptionMenuItem *menuitem = qstyleoption_cast<const QStyleOptionMenuItem *>(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 QStyleOptionComplex *styleOpt = qstyleoption_cast<const QStyleOptionComplex *>(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 -{ - QWindowsXPStylePrivate *d = const_cast<QWindowsXPStylePrivate*>(d_func()); - if (!QWindowsXPStylePrivate::useXP()) - return QWindowsStyle::styleHint(hint, option, widget, returnData); - - int res = 0; - switch (hint) { - - case SH_EtchDisabledText: - res = (qobject_cast<const QLabel*>(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 && widget->isEnabled())) - res = d->groupBoxTextColor; - else - res = d->groupBoxTextColorDisabled; - break; - - case SH_Table_GridLineColor: - res = 0xC0C0C0; - break; - - case SH_WindowFrame_Mask: - { - res = 1; - QStyleHintReturnMask *mask = qstyleoption_cast<QStyleHintReturnMask *>(returnData); - const QStyleOptionTitleBar *titlebar = qstyleoption_cast<const QStyleOptionTitleBar *>(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<const QStyleOptionRubberBand *>(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<const QStyleOptionDockWidget *>(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); - } - - QWindowsXPStylePrivate *d = const_cast<QWindowsXPStylePrivate*>(d_func()); - switch(standardIcon) { - case SP_TitleBarMaxButton: - if (qstyleoption_cast<const QStyleOptionDockWidget *>(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<const QStyleOptionDockWidget *>(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<const QStyleOptionDockWidget *>(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 <tmschema.h> -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<PropPair> 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 determin 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 bd4946cf03..0000000000 --- a/src/plugins/styles/windowsvista/qwindowsxpstyle_p.h +++ /dev/null @@ -1,105 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWidgets module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#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 <QtWidgets/private/qtwidgetsglobal_p.h> -#include <QtWidgets/private/qwindowsstyle_p.h> - -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 bfba950523..0000000000 --- a/src/plugins/styles/windowsvista/qwindowsxpstyle_p_p.h +++ /dev/null @@ -1,345 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWidgets module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#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 <QtWidgets/private/qtwidgetsglobal_p.h> -#include "qwindowsxpstyle_p.h" -#include <QtWidgets/private/qwindowsstyle_p_p.h> -#include <qmap.h> -#include <qt_windows.h> - -#include <uxtheme.h> -#include <vssym32.h> - -#include <limits.h> - -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<ThemeMapKey, ThemeMapData> 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 diff --git a/src/plugins/styles/windowsvista/windowsvista.pro b/src/plugins/styles/windowsvista/windowsvista.pro deleted file mode 100644 index 483914c13d..0000000000 --- a/src/plugins/styles/windowsvista/windowsvista.pro +++ /dev/null @@ -1,20 +0,0 @@ -TARGET = qwindowsvistastyle - -QT += widgets-private - -SOURCES += main.cpp - -HEADERS += qwindowsvistastyle_p.h qwindowsvistastyle_p_p.h -SOURCES += qwindowsvistastyle.cpp - -HEADERS += qwindowsxpstyle_p.h qwindowsxpstyle_p_p.h -SOURCES += qwindowsxpstyle.cpp - -QMAKE_USE_PRIVATE += user32 gdi32 -LIBS_PRIVATE *= -luxtheme - -DISTFILES += windowsvistastyle.json - -PLUGIN_TYPE = styles -PLUGIN_CLASS_NAME = QWindowsVistaStylePlugin -load(qt_plugin) diff --git a/src/plugins/styles/windowsvista/windowsvistastyle.json b/src/plugins/styles/windowsvista/windowsvistastyle.json deleted file mode 100644 index 771aa0c600..0000000000 --- a/src/plugins/styles/windowsvista/windowsvistastyle.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "Keys": [ "windowsvista" ] -} |