summaryrefslogtreecommitdiffstats
path: root/src/plugins
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins')
-rw-r--r--src/plugins/plugins.pro1
-rw-r--r--src/plugins/styles/android/android.pro16
-rw-r--r--src/plugins/styles/android/androidstyle.json3
-rw-r--r--src/plugins/styles/android/main.cpp64
-rw-r--r--src/plugins/styles/android/qandroidstyle.cpp1805
-rw-r--r--src/plugins/styles/android/qandroidstyle_p.h391
-rw-r--r--src/plugins/styles/mac/mac.pro19
-rw-r--r--src/plugins/styles/mac/macstyle.json3
-rw-r--r--src/plugins/styles/mac/main.mm65
-rw-r--r--src/plugins/styles/mac/qmacstyle.qdoc207
-rw-r--r--src/plugins/styles/mac/qmacstyle_mac.mm7041
-rw-r--r--src/plugins/styles/mac/qmacstyle_mac_p.h128
-rw-r--r--src/plugins/styles/mac/qmacstyle_mac_p_p.h257
-rw-r--r--src/plugins/styles/styles.pro8
-rw-r--r--src/plugins/styles/windowsvista/main.cpp64
-rw-r--r--src/plugins/styles/windowsvista/qwindowsvistastyle.cpp2487
-rw-r--r--src/plugins/styles/windowsvista/qwindowsvistastyle_p.h106
-rw-r--r--src/plugins/styles/windowsvista/qwindowsvistastyle_p_p.h180
-rw-r--r--src/plugins/styles/windowsvista/qwindowsxpstyle.cpp4223
-rw-r--r--src/plugins/styles/windowsvista/qwindowsxpstyle_p.h104
-rw-r--r--src/plugins/styles/windowsvista/qwindowsxpstyle_p_p.h341
-rw-r--r--src/plugins/styles/windowsvista/windowsvista.pro22
-rw-r--r--src/plugins/styles/windowsvista/windowsvistastyle.json3
23 files changed, 17538 insertions, 0 deletions
diff --git a/src/plugins/plugins.pro b/src/plugins/plugins.pro
index 620d9cb8c9..710a6c94c1 100644
--- a/src/plugins/plugins.pro
+++ b/src/plugins/plugins.pro
@@ -8,6 +8,7 @@ qtHaveModule(gui) {
qtConfig(imageformatplugin): SUBDIRS *= imageformats
!android:qtConfig(library): SUBDIRS *= generic
}
+qtHaveModule(widgets): SUBDIRS += styles
!winrt:!wince:qtHaveModule(printsupport): \
SUBDIRS += printsupport
diff --git a/src/plugins/styles/android/android.pro b/src/plugins/styles/android/android.pro
new file mode 100644
index 0000000000..4ca35d8046
--- /dev/null
+++ b/src/plugins/styles/android/android.pro
@@ -0,0 +1,16 @@
+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/androidstyle.json b/src/plugins/styles/android/androidstyle.json
new file mode 100644
index 0000000000..6843bd3301
--- /dev/null
+++ b/src/plugins/styles/android/androidstyle.json
@@ -0,0 +1,3 @@
+{
+ "Keys": [ "android" ]
+}
diff --git a/src/plugins/styles/android/main.cpp b/src/plugins/styles/android/main.cpp
new file mode 100644
index 0000000000..2121538b0a
--- /dev/null
+++ b/src/plugins/styles/android/main.cpp
@@ -0,0 +1,64 @@
+/****************************************************************************
+**
+** 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/qstyleplugin.h>
+#include "qandroidstyle_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QAndroidStylePlugin : public QStylePlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QStyleFactoryInterface" FILE "androidstyle.json")
+public:
+ QStyle *create(const QString &key);
+};
+
+QStyle *QAndroidStylePlugin::create(const QString &key)
+{
+ if (key.compare(QLatin1String("android"), Qt::CaseInsensitive) == 0)
+ return new QAndroidStyle();
+
+ return 0;
+}
+
+QT_END_NAMESPACE
+
+#include "main.moc"
+
diff --git a/src/plugins/styles/android/qandroidstyle.cpp b/src/plugins/styles/android/qandroidstyle.cpp
new file mode 100644
index 0000000000..ed8c342668
--- /dev/null
+++ b/src/plugins/styles/android/qandroidstyle.cpp
@@ -0,0 +1,1805 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+#include "qandroidstyle_p.h"
+
+#include <QFile>
+#include <QFont>
+#include <QApplication>
+#include <qdrawutil.h>
+#include <QPixmapCache>
+#include <QFileInfo>
+#include <QStyleOption>
+#include <QPainter>
+#include <QJsonDocument>
+#include <QJsonObject>
+#include <QDebug>
+
+#include <QGuiApplication>
+#include <qpa/qplatformnativeinterface.h>
+#include <qpa/qplatformtheme.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace {
+ const quint32 NO_COLOR = 1;
+ const quint32 TRANSPARENT_COLOR = 0;
+}
+
+QAndroidStyle::QAndroidStyle()
+ : QFusionStyle()
+{
+ QPixmapCache::clear();
+ checkBoxControl = NULL;
+ QPlatformNativeInterface *nativeInterface = QGuiApplication::platformNativeInterface();
+ QPalette *standardPalette = reinterpret_cast<QPalette *>(nativeInterface->nativeResourceForIntegration("AndroidStandardPalette"));
+ if (standardPalette)
+ m_standardPalette = *standardPalette;
+
+ QHash<QByteArray, QFont> *qwidgetsFonts = reinterpret_cast<QHash<QByteArray, QFont> *>(nativeInterface->nativeResourceForIntegration("AndroidQWidgetFonts"));
+ if (qwidgetsFonts) {
+ for (auto it = qwidgetsFonts->constBegin(); it != qwidgetsFonts->constEnd(); ++it)
+ QApplication::setFont(it.value(), it.key());
+ qwidgetsFonts->clear(); // free the memory
+ }
+
+ QJsonObject *object = reinterpret_cast<QJsonObject *>(nativeInterface->nativeResourceForIntegration("AndroidStyleData"));
+ if (!object)
+ return;
+
+ for (QJsonObject::const_iterator objectIterator = object->constBegin();
+ objectIterator != object->constEnd();
+ ++objectIterator) {
+ QString key = objectIterator.key();
+ QJsonValue value = objectIterator.value();
+ if (Q_UNLIKELY(!value.isObject())) {
+ qWarning("Style.json structure is unrecognized.");
+ continue;
+ }
+
+ QJsonObject item = value.toObject();
+ QAndroidStyle::ItemType itemType = qtControl(key);
+ if (QC_UnknownType == itemType)
+ continue;
+
+ switch (itemType) {
+ case QC_Checkbox:
+ checkBoxControl = new AndroidCompoundButtonControl(item.toVariantMap(), itemType);
+ m_androidControlsHash[int(itemType)] = checkBoxControl;
+ break;
+ case QC_RadioButton:
+ m_androidControlsHash[int(itemType)] = new AndroidCompoundButtonControl(item.toVariantMap(),
+ itemType);
+ break;
+
+ case QC_ProgressBar:
+ m_androidControlsHash[int(itemType)] = new AndroidProgressBarControl(item.toVariantMap(),
+ itemType);
+ break;
+
+ case QC_Slider:
+ m_androidControlsHash[int(itemType)] = new AndroidSeekBarControl(item.toVariantMap(),
+ itemType);
+ break;
+
+ case QC_Combobox:
+ m_androidControlsHash[int(itemType)] = new AndroidSpinnerControl(item.toVariantMap(),
+ itemType);
+ break;
+
+ default:
+ m_androidControlsHash[int(itemType)] = new AndroidControl(item.toVariantMap(),
+ itemType);
+ break;
+ }
+ }
+ *object = QJsonObject(); // free memory
+}
+
+QAndroidStyle::~QAndroidStyle()
+{
+ qDeleteAll(m_androidControlsHash);
+}
+
+QAndroidStyle::ItemType QAndroidStyle::qtControl(const QString &android)
+{
+ if (android == QLatin1String("buttonStyle"))
+ return QC_Button;
+ if (android == QLatin1String("editTextStyle"))
+ return QC_EditText;
+ if (android == QLatin1String("radioButtonStyle"))
+ return QC_RadioButton;
+ if (android == QLatin1String("checkboxStyle"))
+ return QC_Checkbox;
+ if (android == QLatin1String("textViewStyle"))
+ return QC_View;
+ if (android == QLatin1String("buttonStyleToggle"))
+ return QC_Switch;
+ if (android == QLatin1String("spinnerStyle"))
+ return QC_Combobox;
+ if (android == QLatin1String("progressBarStyleHorizontal"))
+ return QC_ProgressBar;
+ if (android == QLatin1String("seekBarStyle"))
+ return QC_Slider;
+
+ return QC_UnknownType;
+}
+
+QAndroidStyle::ItemType QAndroidStyle::qtControl(QStyle::ComplexControl control)
+{
+ switch (control) {
+ case CC_ComboBox:
+ return QC_Combobox;
+ case CC_Slider:
+ return QC_Slider;
+ default:
+ return QC_UnknownType;
+ }
+}
+
+QAndroidStyle::ItemType QAndroidStyle::qtControl(QStyle::ContentsType contentsType)
+{
+ switch (contentsType) {
+ case CT_PushButton:
+ return QC_Button;
+ case CT_CheckBox:
+ return QC_Checkbox;
+ case CT_RadioButton:
+ return QC_RadioButton;
+ case CT_ComboBox:
+ return QC_Combobox;
+ case CT_ProgressBar:
+ return QC_ProgressBar;
+ case CT_Slider:
+ return QC_Slider;
+ case CT_ScrollBar:
+ return QC_Slider;
+ case CT_TabWidget:
+ return QC_Tab;
+ case CT_TabBarTab:
+ return QC_TabButton;
+ case CT_LineEdit:
+ return QC_EditText;
+ case CT_GroupBox:
+ return QC_GroupBox;
+ default:
+ return QC_UnknownType;
+ }
+}
+
+QAndroidStyle::ItemType QAndroidStyle::qtControl(QStyle::ControlElement controlElement)
+{
+ switch (controlElement) {
+ case CE_PushButton:
+ case CE_PushButtonBevel:
+ case CE_PushButtonLabel:
+ return QC_Button;
+
+ case CE_CheckBox:
+ case CE_CheckBoxLabel:
+ return QC_Checkbox;
+
+ case CE_RadioButton:
+ case CE_RadioButtonLabel:
+ return QC_RadioButton;
+
+ case CE_TabBarTab:
+ case CE_TabBarTabShape:
+ case CE_TabBarTabLabel:
+ return QC_Tab;
+
+ case CE_ProgressBar:
+ case CE_ProgressBarGroove:
+ case CE_ProgressBarContents:
+ case CE_ProgressBarLabel:
+ return QC_ProgressBar;
+
+ case CE_ComboBoxLabel:
+ return QC_Combobox;
+
+ case CE_ShapedFrame:
+ return QC_View;
+
+ default:
+ return QC_UnknownType;
+ }
+}
+
+QAndroidStyle::ItemType QAndroidStyle::qtControl(QStyle::PrimitiveElement primitiveElement)
+{
+ switch (primitiveElement) {
+ case QStyle::PE_PanelLineEdit:
+ case QStyle::PE_FrameLineEdit:
+ return QC_EditText;
+
+ case QStyle::PE_IndicatorViewItemCheck:
+ case QStyle::PE_IndicatorCheckBox:
+ return QC_Checkbox;
+
+ case QStyle::PE_FrameWindow:
+ case QStyle::PE_Widget:
+ case QStyle::PE_Frame:
+ case QStyle::PE_FrameFocusRect:
+ return QC_View;
+ default:
+ return QC_UnknownType;
+ }
+}
+
+QAndroidStyle::ItemType QAndroidStyle::qtControl(QStyle::SubElement subElement)
+{
+ switch (subElement) {
+ case QStyle::SE_LineEditContents:
+ return QC_EditText;
+
+ case QStyle::SE_PushButtonContents:
+ case QStyle::SE_PushButtonFocusRect:
+ return QC_Button;
+
+ case SE_RadioButtonContents:
+ return QC_RadioButton;
+
+ case SE_CheckBoxContents:
+ return QC_Checkbox;
+
+ default:
+ return QC_UnknownType;
+ }
+}
+
+void QAndroidStyle::drawPrimitive(PrimitiveElement pe,
+ const QStyleOption *opt,
+ QPainter *p,
+ const QWidget *w) const
+{
+ const ItemType itemType = qtControl(pe);
+ AndroidControlsHash::const_iterator it = itemType != QC_UnknownType
+ ? m_androidControlsHash.find(itemType)
+ : m_androidControlsHash.end();
+ if (it != m_androidControlsHash.end()) {
+ if (itemType != QC_EditText) {
+ it.value()->drawControl(opt, p, w);
+ } else {
+ QStyleOption copy(*opt);
+ copy.state &= ~QStyle::State_Sunken;
+ it.value()->drawControl(&copy, p, w);
+ }
+ } else if (pe == PE_FrameGroupBox) {
+ if (const QStyleOptionFrame *frame = qstyleoption_cast<const QStyleOptionFrame *>(opt)) {
+ if (frame->features & QStyleOptionFrame::Flat) {
+ QRect fr = frame->rect;
+ QPoint p1(fr.x(), fr.y() + 1);
+ QPoint p2(fr.x() + fr.width(), p1.y());
+ qDrawShadeLine(p, p1, p2, frame->palette, true,
+ frame->lineWidth, frame->midLineWidth);
+ } else {
+ qDrawShadeRect(p, frame->rect.x(), frame->rect.y(), frame->rect.width(),
+ frame->rect.height(), frame->palette, true,
+ frame->lineWidth, frame->midLineWidth);
+ }
+ }
+ } else {
+ QFusionStyle::drawPrimitive(pe, opt, p, w);
+ }
+}
+
+
+void QAndroidStyle::drawControl(QStyle::ControlElement element,
+ const QStyleOption *opt,
+ QPainter *p,
+ const QWidget *w) const
+{
+ const ItemType itemType = qtControl(element);
+ AndroidControlsHash::const_iterator it = itemType != QC_UnknownType
+ ? m_androidControlsHash.find(itemType)
+ : m_androidControlsHash.end();
+ if (it != m_androidControlsHash.end()) {
+ AndroidControl *androidControl = it.value();
+ androidControl->drawControl(opt, p, w);
+
+ switch (itemType) {
+ case QC_Button:
+ if (const QStyleOptionButton *buttonOption =
+ qstyleoption_cast<const QStyleOptionButton *>(opt)) {
+ QMargins padding = androidControl->padding();
+ QStyleOptionButton copy(*buttonOption);
+ copy.rect.adjust(padding.left(), padding.top(), -padding.right(), -padding.bottom());
+ QFusionStyle::drawControl(CE_PushButtonLabel, &copy, p, w);
+ }
+ break;
+ case QC_Checkbox:
+ case QC_RadioButton:
+ if (const QStyleOptionButton *btn =
+ qstyleoption_cast<const QStyleOptionButton *>(opt)) {
+ const bool isRadio = (element == CE_RadioButton);
+ QStyleOptionButton subopt(*btn);
+ subopt.rect = subElementRect(isRadio ? SE_RadioButtonContents
+ : SE_CheckBoxContents, btn, w);
+ QFusionStyle::drawControl(isRadio ? CE_RadioButtonLabel : CE_CheckBoxLabel, &subopt, p, w);
+ }
+ break;
+ case QC_Combobox:
+ if (const QStyleOptionComboBox *comboboxOption =
+ qstyleoption_cast<const QStyleOptionComboBox *>(opt)) {
+ QMargins padding = androidControl->padding();
+ QStyleOptionComboBox copy (*comboboxOption);
+ copy.rect.adjust(padding.left(), padding.top(), -padding.right(), -padding.bottom());
+ QFusionStyle::drawControl(CE_ComboBoxLabel, comboboxOption, p, w);
+ }
+ break;
+ default:
+ QFusionStyle::drawControl(element, opt, p, w);
+ break;
+ }
+ } else {
+ QFusionStyle::drawControl(element, opt, p, w);
+ }
+}
+
+QRect QAndroidStyle::subElementRect(SubElement subElement,
+ const QStyleOption *option,
+ const QWidget *widget) const
+{
+ const ItemType itemType = qtControl(subElement);
+ AndroidControlsHash::const_iterator it = itemType != QC_UnknownType
+ ? m_androidControlsHash.find(itemType)
+ : m_androidControlsHash.end();
+ if (it != m_androidControlsHash.end())
+ return it.value()->subElementRect(subElement, option, widget);
+ return QFusionStyle::subElementRect(subElement, option, widget);
+}
+
+void QAndroidStyle::drawComplexControl(ComplexControl cc,
+ const QStyleOptionComplex *opt,
+ QPainter *p,
+ const QWidget *widget) const
+{
+ const ItemType itemType = qtControl(cc);
+ AndroidControlsHash::const_iterator it = itemType != QC_UnknownType
+ ? m_androidControlsHash.find(itemType)
+ : m_androidControlsHash.end();
+ if (it != m_androidControlsHash.end()) {
+ it.value()->drawControl(opt, p, widget);
+ return;
+ }
+ if (cc == CC_GroupBox) {
+ if (const QStyleOptionGroupBox *groupBox = qstyleoption_cast<const QStyleOptionGroupBox *>(opt)) {
+ // Draw frame
+ QRect textRect = subControlRect(CC_GroupBox, opt, SC_GroupBoxLabel, widget);
+ QRect checkBoxRect;
+ if (groupBox->subControls & SC_GroupBoxCheckBox)
+ checkBoxRect = subControlRect(CC_GroupBox, opt, SC_GroupBoxCheckBox, widget);
+ if (groupBox->subControls & QStyle::SC_GroupBoxFrame) {
+ QStyleOptionFrame frame;
+ frame.QStyleOption::operator=(*groupBox);
+ frame.features = groupBox->features;
+ frame.lineWidth = groupBox->lineWidth;
+ frame.midLineWidth = groupBox->midLineWidth;
+ frame.rect = subControlRect(CC_GroupBox, opt, SC_GroupBoxFrame, widget);
+ p->save();
+ QRegion region(groupBox->rect);
+ if (!groupBox->text.isEmpty()) {
+ bool ltr = groupBox->direction == Qt::LeftToRight;
+ QRect finalRect;
+ if (groupBox->subControls & QStyle::SC_GroupBoxCheckBox) {
+ finalRect = checkBoxRect.united(textRect);
+ finalRect.adjust(ltr ? -4 : 0, 0, ltr ? 0 : 4, 0);
+ } else {
+ finalRect = textRect;
+ }
+ region -= finalRect;
+ }
+ p->setClipRegion(region);
+ drawPrimitive(PE_FrameGroupBox, &frame, p, widget);
+ p->restore();
+ }
+
+ // Draw title
+ if ((groupBox->subControls & QStyle::SC_GroupBoxLabel) && !groupBox->text.isEmpty()) {
+ QColor textColor = groupBox->textColor;
+ if (textColor.isValid())
+ p->setPen(textColor);
+ int alignment = int(groupBox->textAlignment);
+ if (!styleHint(QStyle::SH_UnderlineShortcut, opt, widget))
+ alignment |= Qt::TextHideMnemonic;
+
+ drawItemText(p, textRect, Qt::TextShowMnemonic | Qt::AlignHCenter | alignment,
+ groupBox->palette, groupBox->state & State_Enabled, groupBox->text,
+ textColor.isValid() ? QPalette::NoRole : QPalette::WindowText);
+
+ if (groupBox->state & State_HasFocus) {
+ QStyleOptionFocusRect fropt;
+ fropt.QStyleOption::operator=(*groupBox);
+ fropt.rect = textRect;
+ drawPrimitive(PE_FrameFocusRect, &fropt, p, widget);
+ }
+ }
+
+ // Draw checkbox
+ if (groupBox->subControls & SC_GroupBoxCheckBox) {
+ QStyleOptionButton box;
+ box.QStyleOption::operator=(*groupBox);
+ box.rect = checkBoxRect;
+ checkBoxControl->drawControl(&box, p, widget);
+ }
+ }
+ return;
+ }
+ QFusionStyle::drawComplexControl(cc, opt, p, widget);
+}
+
+QStyle::SubControl QAndroidStyle::hitTestComplexControl(ComplexControl cc,
+ const QStyleOptionComplex *opt,
+ const QPoint &pt,
+ const QWidget *widget) const
+{
+ const ItemType itemType = qtControl(cc);
+ AndroidControlsHash::const_iterator it = itemType != QC_UnknownType
+ ? m_androidControlsHash.find(itemType)
+ : m_androidControlsHash.end();
+ if (it != m_androidControlsHash.end()) {
+ switch (cc) {
+ case CC_Slider:
+ if (const QStyleOptionSlider *slider = qstyleoption_cast<const QStyleOptionSlider *>(opt)) {
+ QRect r = it.value()->subControlRect(slider, SC_SliderHandle, widget);
+ if (r.isValid() && r.contains(pt)) {
+ return SC_SliderHandle;
+ } else {
+ r = it.value()->subControlRect(slider, SC_SliderGroove, widget);
+ if (r.isValid() && r.contains(pt))
+ return SC_SliderGroove;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ return QFusionStyle::hitTestComplexControl(cc, opt, pt, widget);
+}
+
+QRect QAndroidStyle::subControlRect(ComplexControl cc,
+ const QStyleOptionComplex *opt,
+ SubControl sc,
+ const QWidget *widget) const
+{
+ const ItemType itemType = qtControl(cc);
+ AndroidControlsHash::const_iterator it = itemType != QC_UnknownType
+ ? m_androidControlsHash.find(itemType)
+ : m_androidControlsHash.end();
+ if (it != m_androidControlsHash.end())
+ return it.value()->subControlRect(opt, sc, widget);
+ QRect rect = opt->rect;
+ switch (cc) {
+ case CC_GroupBox: {
+ if (const QStyleOptionGroupBox *groupBox = qstyleoption_cast<const QStyleOptionGroupBox *>(opt)) {
+ QSize textSize = opt->fontMetrics.boundingRect(groupBox->text).size() + QSize(2, 2);
+ QSize checkBoxSize = checkBoxControl->size(opt);
+ int indicatorWidth = checkBoxSize.width();
+ int indicatorHeight = checkBoxSize.height();
+ QRect checkBoxRect;
+ if (opt->subControls & QStyle::SC_GroupBoxCheckBox) {
+ checkBoxRect.setWidth(indicatorWidth);
+ checkBoxRect.setHeight(indicatorHeight);
+ }
+ checkBoxRect.moveLeft(1);
+ QRect textRect = checkBoxRect;
+ textRect.setSize(textSize);
+ if (opt->subControls & QStyle::SC_GroupBoxCheckBox)
+ textRect.translate(indicatorWidth + 5, (indicatorHeight - textSize.height()) / 2);
+ if (sc == SC_GroupBoxFrame) {
+ rect = opt->rect.adjusted(0, 0, 0, 0);
+ rect.translate(0, textRect.height() / 2);
+ rect.setHeight(rect.height() - textRect.height() / 2);
+ } else if (sc == SC_GroupBoxContents) {
+ QRect frameRect = opt->rect.adjusted(0, 0, 0, -groupBox->lineWidth);
+ int margin = 3;
+ int leftMarginExtension = 0;
+ int topMargin = qMax(pixelMetric(PM_ExclusiveIndicatorHeight), opt->fontMetrics.height()) + groupBox->lineWidth;
+ frameRect.adjust(leftMarginExtension + margin, margin + topMargin, -margin, -margin - groupBox->lineWidth);
+ frameRect.translate(0, textRect.height() / 2);
+ rect = frameRect;
+ rect.setHeight(rect.height() - textRect.height() / 2);
+ } else if (sc == SC_GroupBoxCheckBox) {
+ rect = checkBoxRect;
+ } else if (sc == SC_GroupBoxLabel) {
+ rect = textRect;
+ }
+ return visualRect(opt->direction, opt->rect, rect);
+ }
+
+ return rect;
+ }
+
+ default:
+ break;
+ }
+
+
+ return QFusionStyle::subControlRect(cc, opt, sc, widget);
+}
+
+int QAndroidStyle::pixelMetric(PixelMetric metric, const QStyleOption *option,
+ const QWidget *widget) const
+{
+ switch (metric) {
+ case PM_ButtonMargin:
+ case PM_FocusFrameVMargin:
+ case PM_FocusFrameHMargin:
+ case PM_ComboBoxFrameWidth:
+ case PM_SpinBoxFrameWidth:
+ case PM_ScrollBarExtent:
+ return 0;
+ case PM_IndicatorWidth:
+ return checkBoxControl->size(option).width();
+ case PM_IndicatorHeight:
+ return checkBoxControl->size(option).height();
+ default:
+ return QFusionStyle::pixelMetric(metric, option, widget);
+ }
+
+}
+
+QSize QAndroidStyle::sizeFromContents(ContentsType ct,
+ const QStyleOption *opt,
+ const QSize &contentsSize,
+ const QWidget *w) const
+{
+ QSize sz = QFusionStyle::sizeFromContents(ct, opt, contentsSize, w);
+ if (ct == CT_HeaderSection) {
+ if (const QStyleOptionHeader *hdr = qstyleoption_cast<const QStyleOptionHeader *>(opt)) {
+ bool nullIcon = hdr->icon.isNull();
+ int margin = pixelMetric(QStyle::PM_HeaderMargin, hdr, w);
+ int iconSize = nullIcon ? 0 : checkBoxControl->size(opt).width();
+ QSize txt;
+/*
+ * These next 4 lines are a bad hack to fix a bug in case a QStyleSheet is applied at QApplication level.
+ * In that case, even if the stylesheet does not refer to headers, the header font is changed to application
+ * font, which is wrong. Even worst, hdr->fontMetrics(...) does not report the proper size.
+ */
+ if (qApp->styleSheet().isEmpty())
+ txt = hdr->fontMetrics.size(0, hdr->text);
+ else
+ txt = qApp->fontMetrics().size(0, hdr->text);
+
+ sz.setHeight(margin + qMax(iconSize, txt.height()) + margin);
+ sz.setWidth((nullIcon ? 0 : margin) + iconSize
+ + (hdr->text.isNull() ? 0 : margin) + txt.width() + margin);
+ if (hdr->sortIndicator != QStyleOptionHeader::None) {
+ int margin = pixelMetric(QStyle::PM_HeaderMargin, hdr, w);
+ if (hdr->orientation == Qt::Horizontal)
+ sz.rwidth() += sz.height() + margin;
+ else
+ sz.rheight() += sz.width() + margin;
+ }
+ return sz;
+ }
+ }
+ const ItemType itemType = qtControl(ct);
+ AndroidControlsHash::const_iterator it = itemType != QC_UnknownType
+ ? m_androidControlsHash.find(itemType)
+ : m_androidControlsHash.end();
+ if (it != m_androidControlsHash.end())
+ return it.value()->sizeFromContents(opt, sz, w);
+ if (ct == CT_GroupBox) {
+ if (const QStyleOptionGroupBox *groupBox = qstyleoption_cast<const QStyleOptionGroupBox *>(opt)) {
+ QSize textSize = opt->fontMetrics.boundingRect(groupBox->text).size() + QSize(2, 2);
+ QSize checkBoxSize = checkBoxControl->size(opt);
+ int indicatorWidth = checkBoxSize.width();
+ int indicatorHeight = checkBoxSize.height();
+ QRect checkBoxRect;
+ if (groupBox->subControls & QStyle::SC_GroupBoxCheckBox) {
+ checkBoxRect.setWidth(indicatorWidth);
+ checkBoxRect.setHeight(indicatorHeight);
+ }
+ checkBoxRect.moveLeft(1);
+ QRect textRect = checkBoxRect;
+ textRect.setSize(textSize);
+ if (groupBox->subControls & QStyle::SC_GroupBoxCheckBox)
+ textRect.translate(indicatorWidth + 5, (indicatorHeight - textSize.height()) / 2);
+ QRect u = textRect.united(checkBoxRect);
+ sz = QSize(sz.width(), sz.height() + u.height());
+ }
+ }
+ return sz;
+}
+
+QPixmap QAndroidStyle::standardPixmap(StandardPixmap standardPixmap,
+ const QStyleOption *opt,
+ const QWidget *widget) const
+{
+ return QFusionStyle::standardPixmap(standardPixmap, opt, widget);
+}
+
+QPixmap QAndroidStyle::generatedIconPixmap(QIcon::Mode iconMode,
+ const QPixmap &pixmap,
+ const QStyleOption *opt) const
+{
+ return QFusionStyle::generatedIconPixmap(iconMode, pixmap, opt);
+}
+
+int QAndroidStyle::styleHint(QStyle::StyleHint hint, const QStyleOption *option, const QWidget *widget, QStyleHintReturn *returnData) const
+{
+ switch (hint) {
+ case SH_Slider_AbsoluteSetButtons:
+ return Qt::LeftButton;
+
+ case SH_Slider_PageSetButtons:
+ return 0;
+
+ case SH_RequestSoftwareInputPanel:
+ return RSIP_OnMouseClick;
+
+ default:
+ return QFusionStyle::styleHint(hint, option, widget, returnData);
+ }
+}
+
+QPalette QAndroidStyle::standardPalette() const
+{
+ return m_standardPalette;
+}
+
+void QAndroidStyle::polish(QWidget *widget)
+{
+ widget->setAttribute(Qt::WA_StyledBackground, true);
+}
+
+void QAndroidStyle::unpolish(QWidget *widget)
+{
+ widget->setAttribute(Qt::WA_StyledBackground, false);
+}
+
+QAndroidStyle::AndroidDrawable::AndroidDrawable(const QVariantMap &drawable,
+ QAndroidStyle::ItemType itemType)
+{
+ initPadding(drawable);
+ m_itemType = itemType;
+}
+
+QAndroidStyle::AndroidDrawable::~AndroidDrawable()
+{
+}
+
+void QAndroidStyle::AndroidDrawable::initPadding(const QVariantMap &drawable)
+{
+ QVariantMap::const_iterator it = drawable.find(QLatin1String("padding"));
+ if (it != drawable.end())
+ m_padding = extractMargins(it.value().toMap());
+}
+
+const QMargins &QAndroidStyle::AndroidDrawable::padding() const
+{
+ return m_padding;
+}
+
+QSize QAndroidStyle::AndroidDrawable::size() const
+{
+ if (type() == Image || type() == NinePatch)
+ return static_cast<const QAndroidStyle::AndroidImageDrawable *>(this)->size();
+
+ return QSize();
+}
+
+QAndroidStyle::AndroidDrawable * QAndroidStyle::AndroidDrawable::fromMap(const QVariantMap &drawable,
+ ItemType itemType)
+{
+ const QString type = drawable.value(QLatin1String("type")).toString();
+ if (type == QLatin1String("image"))
+ return new QAndroidStyle::AndroidImageDrawable(drawable, itemType);
+ if (type == QLatin1String("9patch"))
+ return new QAndroidStyle::Android9PatchDrawable(drawable, itemType);
+ if (type == QLatin1String("stateslist"))
+ return new QAndroidStyle::AndroidStateDrawable(drawable, itemType);
+ if (type == QLatin1String("layer"))
+ return new QAndroidStyle::AndroidLayerDrawable(drawable, itemType);
+ if (type == QLatin1String("gradient"))
+ return new QAndroidStyle::AndroidGradientDrawable(drawable, itemType);
+ if (type == QLatin1String("clipDrawable"))
+ return new QAndroidStyle::AndroidClipDrawable(drawable, itemType);
+ if (type == QLatin1String("color"))
+ return new QAndroidStyle::AndroidColorDrawable(drawable, itemType);
+ return 0;
+}
+
+QMargins QAndroidStyle::AndroidDrawable::extractMargins(const QVariantMap &value)
+{
+ QMargins m;
+ m.setLeft(value.value(QLatin1String("left")).toInt());
+ m.setRight(value.value(QLatin1String("right")).toInt());
+ m.setTop(value.value(QLatin1String("top")).toInt());
+ m.setBottom(value.value(QLatin1String("bottom")).toInt());
+ return m;
+}
+
+void QAndroidStyle::AndroidDrawable::setPaddingLeftToSizeWidth()
+{
+ QSize sz = size();
+ if (m_padding.isNull() && !sz.isNull())
+ m_padding.setLeft(sz.width());
+}
+
+
+QAndroidStyle::AndroidImageDrawable::AndroidImageDrawable(const QVariantMap &drawable,
+ QAndroidStyle::ItemType itemType)
+ : AndroidDrawable(drawable, itemType)
+{
+ m_filePath = drawable.value(QLatin1String("path")).toString();
+ m_size.setHeight(drawable.value(QLatin1String("height")).toInt());
+ m_size.setWidth(drawable.value(QLatin1String("width")).toInt());
+}
+
+QAndroidStyle::AndroidDrawableType QAndroidStyle::AndroidImageDrawable::type() const
+{
+ return QAndroidStyle::Image;
+}
+
+void QAndroidStyle::AndroidImageDrawable::draw(QPainter *painter, const QStyleOption *opt) const
+{
+ if (m_hashKey.isEmpty())
+ m_hashKey = QFileInfo(m_filePath).fileName();
+
+ QPixmap pm;
+ if (!QPixmapCache::find(m_hashKey, &pm)) {
+ pm.load(m_filePath);
+ QPixmapCache::insert(m_hashKey, pm);
+ }
+
+ painter->drawPixmap(opt->rect.x(), opt->rect.y() + (opt->rect.height() - pm.height()) / 2, pm);
+}
+
+QSize QAndroidStyle::AndroidImageDrawable::size() const
+{
+ return m_size;
+}
+
+QAndroidStyle::AndroidColorDrawable::AndroidColorDrawable(const QVariantMap &drawable,
+ ItemType itemType)
+ : AndroidDrawable(drawable, itemType)
+{
+ m_color.setRgba(QRgb(drawable.value(QLatin1String("color")).toInt()));
+}
+
+QAndroidStyle::AndroidDrawableType QAndroidStyle::AndroidColorDrawable::type() const
+{
+ return QAndroidStyle::Color;
+}
+
+void QAndroidStyle::AndroidColorDrawable::draw(QPainter *painter, const QStyleOption *opt) const
+{
+ painter->fillRect(opt->rect, m_color);
+}
+
+QAndroidStyle::Android9PatchDrawable::Android9PatchDrawable(const QVariantMap &drawable,
+ QAndroidStyle::ItemType itemType)
+ : AndroidImageDrawable(drawable.value(QLatin1String("drawable")).toMap(), itemType)
+{
+ initPadding(drawable);
+ QVariantMap chunk = drawable.value(QLatin1String("chunkInfo")).toMap();
+ extractIntArray(chunk.value(QLatin1String("xdivs")).toList(), m_chunkData.xDivs);
+ extractIntArray(chunk.value(QLatin1String("ydivs")).toList(), m_chunkData.yDivs);
+ extractIntArray(chunk.value(QLatin1String("colors")).toList(), m_chunkData.colors);
+}
+
+QAndroidStyle::AndroidDrawableType QAndroidStyle::Android9PatchDrawable::type() const
+{
+ return QAndroidStyle::NinePatch;
+}
+
+int QAndroidStyle::Android9PatchDrawable::calculateStretch(int boundsLimit,
+ int startingPoint,
+ int srcSpace,
+ int numStrechyPixelsRemaining,
+ int numFixedPixelsRemaining)
+{
+ int spaceRemaining = boundsLimit - startingPoint;
+ int stretchySpaceRemaining = spaceRemaining - numFixedPixelsRemaining;
+ return (float(srcSpace) * stretchySpaceRemaining / numStrechyPixelsRemaining + .5);
+}
+
+void QAndroidStyle::Android9PatchDrawable::extractIntArray(const QVariantList &values,
+ QVector<int> & array)
+{
+ for (const QVariant &value : values)
+ array << value.toInt();
+}
+
+
+void QAndroidStyle::Android9PatchDrawable::draw(QPainter *painter, const QStyleOption *opt) const
+{
+ if (m_hashKey.isEmpty())
+ m_hashKey = QFileInfo(m_filePath).fileName();
+
+ QPixmap pixmap;
+ if (!QPixmapCache::find(m_hashKey, &pixmap)) {
+ pixmap.load(m_filePath);
+ QPixmapCache::insert(m_hashKey, pixmap);
+ }
+
+ const QRect &bounds = opt->rect;
+
+ // shamelessly stolen from Android's sources (NinepatchImpl.cpp) and adapted for Qt
+ const int pixmapWidth = pixmap.width();
+ const int pixmapHeight = pixmap.height();
+
+ if (bounds.isNull() || !pixmapWidth || !pixmapHeight)
+ return;
+
+ QPainter::RenderHints savedHints = painter->renderHints();
+
+ // The patchs doesn't need smooth transform !
+ painter->setRenderHints(QPainter::SmoothPixmapTransform, false);
+
+ QRectF dst;
+ QRectF src;
+
+ const qint32 x0 = m_chunkData.xDivs[0];
+ const qint32 y0 = m_chunkData.yDivs[0];
+ const quint8 numXDivs = m_chunkData.xDivs.size();
+ const quint8 numYDivs = m_chunkData.yDivs.size();
+ int i;
+ int j;
+ int colorIndex = 0;
+ quint32 color;
+ bool xIsStretchable;
+ const bool initialXIsStretchable = (x0 == 0);
+ bool yIsStretchable = (y0 == 0);
+ const int bitmapWidth = pixmap.width();
+ const int bitmapHeight = pixmap.height();
+
+ int *dstRights = static_cast<int *>(alloca((numXDivs + 1) * sizeof(int)));
+ bool dstRightsHaveBeenCached = false;
+
+ int numStretchyXPixelsRemaining = 0;
+ for (i = 0; i < numXDivs; i += 2)
+ numStretchyXPixelsRemaining += m_chunkData.xDivs[i + 1] - m_chunkData.xDivs[i];
+
+ int numFixedXPixelsRemaining = bitmapWidth - numStretchyXPixelsRemaining;
+ int numStretchyYPixelsRemaining = 0;
+ for (i = 0; i < numYDivs; i += 2)
+ numStretchyYPixelsRemaining += m_chunkData.yDivs[i + 1] - m_chunkData.yDivs[i];
+
+ int numFixedYPixelsRemaining = bitmapHeight - numStretchyYPixelsRemaining;
+ src.setTop(0);
+ dst.setTop(bounds.top());
+ // The first row always starts with the top being at y=0 and the bottom
+ // being either yDivs[1] (if yDivs[0]=0) of yDivs[0]. In the former case
+ // the first row is stretchable along the Y axis, otherwise it is fixed.
+ // The last row always ends with the bottom being bitmap.height and the top
+ // being either yDivs[numYDivs-2] (if yDivs[numYDivs-1]=bitmap.height) or
+ // yDivs[numYDivs-1]. In the former case the last row is stretchable along
+ // the Y axis, otherwise it is fixed.
+ //
+ // The first and last columns are similarly treated with respect to the X
+ // axis.
+ //
+ // The above is to help explain some of the special casing that goes on the
+ // code below.
+
+ // The initial yDiv and whether the first row is considered stretchable or
+ // not depends on whether yDiv[0] was zero or not.
+ for (j = yIsStretchable ? 1 : 0;
+ j <= numYDivs && src.top() < bitmapHeight;
+ j++, yIsStretchable = !yIsStretchable) {
+ src.setLeft(0);
+ dst.setLeft(bounds.left());
+ if (j == numYDivs) {
+ src.setBottom(bitmapHeight);
+ dst.setBottom(bounds.bottom());
+ } else {
+ src.setBottom(m_chunkData.yDivs[j]);
+ const int srcYSize = src.bottom() - src.top();
+ if (yIsStretchable) {
+ dst.setBottom(dst.top() + calculateStretch(bounds.bottom(), dst.top(),
+ srcYSize,
+ numStretchyYPixelsRemaining,
+ numFixedYPixelsRemaining));
+ numStretchyYPixelsRemaining -= srcYSize;
+ } else {
+ dst.setBottom(dst.top() + srcYSize);
+ numFixedYPixelsRemaining -= srcYSize;
+ }
+ }
+
+ xIsStretchable = initialXIsStretchable;
+ // The initial xDiv and whether the first column is considered
+ // stretchable or not depends on whether xDiv[0] was zero or not.
+ for (i = xIsStretchable ? 1 : 0;
+ i <= numXDivs && src.left() < bitmapWidth;
+ i++, xIsStretchable = !xIsStretchable) {
+ color = m_chunkData.colors[colorIndex++];
+ if (color != TRANSPARENT_COLOR)
+ color = NO_COLOR;
+ if (i == numXDivs) {
+ src.setRight(bitmapWidth);
+ dst.setRight(bounds.right());
+ } else {
+ src.setRight(m_chunkData.xDivs[i]);
+ if (dstRightsHaveBeenCached) {
+ dst.setRight(dstRights[i]);
+ } else {
+ const int srcXSize = src.right() - src.left();
+ if (xIsStretchable) {
+ dst.setRight(dst.left() + calculateStretch(bounds.right(), dst.left(),
+ srcXSize,
+ numStretchyXPixelsRemaining,
+ numFixedXPixelsRemaining));
+ numStretchyXPixelsRemaining -= srcXSize;
+ } else {
+ dst.setRight(dst.left() + srcXSize);
+ numFixedXPixelsRemaining -= srcXSize;
+ }
+ dstRights[i] = dst.right();
+ }
+ }
+ // If this horizontal patch is too small to be displayed, leave
+ // the destination left edge where it is and go on to the next patch
+ // in the source.
+ if (src.left() >= src.right()) {
+ src.setLeft(src.right());
+ continue;
+ }
+ // Make sure that we actually have room to draw any bits
+ if (dst.right() <= dst.left() || dst.bottom() <= dst.top()) {
+ goto nextDiv;
+ }
+ // If this patch is transparent, skip and don't draw.
+ if (color == TRANSPARENT_COLOR)
+ goto nextDiv;
+ if (color != NO_COLOR)
+ painter->fillRect(dst, QRgb(color));
+ else
+ painter->drawPixmap(dst, pixmap, src);
+nextDiv:
+ src.setLeft(src.right());
+ dst.setLeft(dst.right());
+ }
+ src.setTop(src.bottom());
+ dst.setTop(dst.bottom());
+ dstRightsHaveBeenCached = true;
+ }
+ painter->setRenderHints(savedHints);
+}
+
+QAndroidStyle::AndroidGradientDrawable::AndroidGradientDrawable(const QVariantMap &drawable,
+ QAndroidStyle::ItemType itemType)
+ : AndroidDrawable(drawable, itemType), m_orientation(TOP_BOTTOM)
+{
+ m_radius = drawable.value(QLatin1String("radius")).toInt();
+ if (m_radius < 0)
+ m_radius = 0;
+
+ QVariantList colors = drawable.value(QLatin1String("colors")).toList();
+ QVariantList positions = drawable.value(QLatin1String("positions")).toList();
+ int min = colors.size() < positions.size() ? colors.size() : positions.size();
+ for (int i = 0; i < min; i++)
+ m_gradient.setColorAt(positions.at(i).toDouble(), QRgb(colors.at(i).toInt()));
+
+ QByteArray orientation = drawable.value(QLatin1String("orientation")).toByteArray();
+ if (orientation == "TOP_BOTTOM") // draw the gradient from the top to the bottom
+ m_orientation = TOP_BOTTOM;
+ else if (orientation == "TR_BL") // draw the gradient from the top-right to the bottom-left
+ m_orientation = TR_BL;
+ else if (orientation == "RIGHT_LEFT") // draw the gradient from the right to the left
+ m_orientation = RIGHT_LEFT;
+ else if (orientation == "BR_TL") // draw the gradient from the bottom-right to the top-left
+ m_orientation = BR_TL;
+ else if (orientation == "BOTTOM_TOP") // draw the gradient from the bottom to the top
+ m_orientation = BOTTOM_TOP;
+ else if (orientation == "BL_TR") // draw the gradient from the bottom-left to the top-right
+ m_orientation = BL_TR;
+ else if (orientation == "LEFT_RIGHT") // draw the gradient from the left to the right
+ m_orientation = LEFT_RIGHT;
+ else if (orientation == "TL_BR") // draw the gradient from the top-left to the bottom-right
+ m_orientation = TL_BR;
+ else
+ qWarning("AndroidGradientDrawable: unknown orientation");
+}
+
+QAndroidStyle::AndroidDrawableType QAndroidStyle::AndroidGradientDrawable::type() const
+{
+ return QAndroidStyle::Gradient;
+}
+
+void QAndroidStyle::AndroidGradientDrawable::draw(QPainter *painter, const QStyleOption *opt) const
+{
+ const int width = opt->rect.width();
+ const int height = opt->rect.height();
+ switch (m_orientation) {
+ case TOP_BOTTOM:
+ // draw the gradient from the top to the bottom
+ m_gradient.setStart(width / 2, 0);
+ m_gradient.setFinalStop(width / 2, height);
+ break;
+ case TR_BL:
+ // draw the gradient from the top-right to the bottom-left
+ m_gradient.setStart(width, 0);
+ m_gradient.setFinalStop(0, height);
+ break;
+ case RIGHT_LEFT:
+ // draw the gradient from the right to the left
+ m_gradient.setStart(width, height / 2);
+ m_gradient.setFinalStop(0, height / 2);
+ break;
+ case BR_TL:
+ // draw the gradient from the bottom-right to the top-left
+ m_gradient.setStart(width, height);
+ m_gradient.setFinalStop(0, 0);
+ break;
+ case BOTTOM_TOP:
+ // draw the gradient from the bottom to the top
+ m_gradient.setStart(width / 2, height);
+ m_gradient.setFinalStop(width / 2, 0);
+ break;
+ case BL_TR:
+ // draw the gradient from the bottom-left to the top-right
+ m_gradient.setStart(0, height);
+ m_gradient.setFinalStop(width, 0);
+ break;
+ case LEFT_RIGHT:
+ // draw the gradient from the left to the right
+ m_gradient.setStart(0, height / 2);
+ m_gradient.setFinalStop(width, height / 2);
+ break;
+ case TL_BR:
+ // draw the gradient from the top-left to the bottom-right
+ m_gradient.setStart(0, 0);
+ m_gradient.setFinalStop(width, height);
+ break;
+ }
+
+ const QBrush &oldBrush = painter->brush();
+ const QPen oldPen = painter->pen();
+ painter->setPen(Qt::NoPen);
+ painter->setBrush(m_gradient);
+ painter->drawRoundedRect(opt->rect, m_radius, m_radius);
+ painter->setBrush(oldBrush);
+ painter->setPen(oldPen);
+}
+
+QSize QAndroidStyle::AndroidGradientDrawable::size() const
+{
+ return QSize(m_radius * 2, m_radius * 2);
+}
+
+QAndroidStyle::AndroidClipDrawable::AndroidClipDrawable(const QVariantMap &drawable,
+ QAndroidStyle::ItemType itemType)
+ : AndroidDrawable(drawable, itemType)
+{
+ m_drawable = fromMap(drawable.value(QLatin1String("drawable")).toMap(), itemType);
+ m_factor = 0;
+ m_orientation = Qt::Horizontal;
+}
+
+QAndroidStyle::AndroidClipDrawable::~AndroidClipDrawable()
+{
+ delete m_drawable;
+}
+
+QAndroidStyle::AndroidDrawableType QAndroidStyle::AndroidClipDrawable::type() const
+{
+ return QAndroidStyle::Clip;
+}
+
+void QAndroidStyle::AndroidClipDrawable::setFactor(double factor, Qt::Orientation orientation)
+{
+ m_factor = factor;
+ m_orientation = orientation;
+}
+
+void QAndroidStyle::AndroidClipDrawable::draw(QPainter *painter, const QStyleOption *opt) const
+{
+ QStyleOption copy(*opt);
+ if (m_orientation == Qt::Horizontal)
+ copy.rect.setWidth(copy.rect.width() * m_factor);
+ else
+ copy.rect.setHeight(copy.rect.height() * m_factor);
+
+ m_drawable->draw(painter, &copy);
+}
+
+QAndroidStyle::AndroidStateDrawable::AndroidStateDrawable(const QVariantMap &drawable,
+ QAndroidStyle::ItemType itemType)
+ : AndroidDrawable(drawable, itemType)
+{
+ const QVariantList states = drawable.value(QLatin1String("stateslist")).toList();
+ for (const QVariant &stateVariant : states) {
+ QVariantMap state = stateVariant.toMap();
+ const int s = extractState(state.value(QLatin1String("states")).toMap());
+ if (-1 == s)
+ continue;
+ const AndroidDrawable *ad = fromMap(state.value(QLatin1String("drawable")).toMap(), itemType);
+ if (!ad)
+ continue;
+ StateType item;
+ item.first = s;
+ item.second = ad;
+ m_states<<item;
+ }
+}
+
+QAndroidStyle::AndroidStateDrawable::~AndroidStateDrawable()
+{
+ for (const StateType &type : qAsConst(m_states))
+ delete type.second;
+}
+
+QAndroidStyle::AndroidDrawableType QAndroidStyle::AndroidStateDrawable::type() const
+{
+ return QAndroidStyle::State;
+}
+
+void QAndroidStyle::AndroidStateDrawable::draw(QPainter *painter, const QStyleOption *opt) const
+{
+ const AndroidDrawable *drawable = bestAndroidStateMatch(opt);
+ if (drawable)
+ drawable->draw(painter, opt);
+}
+QSize QAndroidStyle::AndroidStateDrawable::sizeImage(const QStyleOption *opt) const
+{
+ QSize s;
+ const AndroidDrawable *drawable = bestAndroidStateMatch(opt);
+ if (drawable)
+ s = drawable->size();
+ return s;
+}
+
+const QAndroidStyle::AndroidDrawable * QAndroidStyle::AndroidStateDrawable::bestAndroidStateMatch(const QStyleOption *opt) const
+{
+ const AndroidDrawable *bestMatch = 0;
+ if (!opt) {
+ if (m_states.size())
+ return m_states[0].second;
+ return bestMatch;
+ }
+
+ uint bestCost = 0xffff;
+ for (const StateType & state : m_states) {
+ if (int(opt->state) == state.first)
+ return state.second;
+ uint cost = 1;
+
+ int difference = int(opt->state^state.first);
+
+ if (difference & QStyle::State_Active)
+ cost <<= 1;
+
+ if (difference & QStyle::State_Enabled)
+ cost <<= 1;
+
+ if (difference & QStyle::State_Raised)
+ cost <<= 1;
+
+ if (difference & QStyle::State_Sunken)
+ cost <<= 1;
+
+ if (difference & QStyle::State_Off)
+ cost <<= 1;
+
+ if (difference & QStyle::State_On)
+ cost <<= 1;
+
+ if (difference & QStyle::State_HasFocus)
+ cost <<= 1;
+
+ if (difference & QStyle::State_Selected)
+ cost <<= 1;
+
+ if (cost < bestCost) {
+ bestCost = cost;
+ bestMatch = state.second;
+ }
+ }
+ return bestMatch;
+}
+
+int QAndroidStyle::AndroidStateDrawable::extractState(const QVariantMap &value)
+{
+ 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");
+ if (key == QLatin1String("enabled")) {
+ state.setFlag(QStyle::State_Enabled, val);
+ continue;
+ }
+
+ if (key == QLatin1String("window_focused")) {
+ state.setFlag(QStyle::State_Active, val);
+ continue;
+ }
+
+ if (key == QLatin1String("focused")) {
+ state.setFlag(QStyle::State_HasFocus, val);
+ continue;
+ }
+
+ if (key == QLatin1String("checked")) {
+ state |= val ? QStyle::State_On : QStyle::State_Off;
+ continue;
+ }
+
+ if (key == QLatin1String("pressed")) {
+ state |= val ? QStyle::State_Sunken : QStyle::State_Raised;
+ continue;
+ }
+
+ if (key == QLatin1String("selected")) {
+ state.setFlag(QStyle::State_Selected, val);
+ continue;
+ }
+
+ if (key == QLatin1String("active")) {
+ state.setFlag(QStyle::State_Active, val);
+ continue;
+ }
+
+ if (key == QLatin1String("multiline"))
+ return 0;
+
+ if (key == QLatin1String("background") && val)
+ return -1;
+ }
+ return static_cast<int>(state);
+}
+
+void QAndroidStyle::AndroidStateDrawable::setPaddingLeftToSizeWidth()
+{
+ for (const StateType &type : qAsConst(m_states))
+ const_cast<AndroidDrawable *>(type.second)->setPaddingLeftToSizeWidth();
+}
+
+QAndroidStyle::AndroidLayerDrawable::AndroidLayerDrawable(const QVariantMap &drawable,
+ QAndroidStyle::ItemType itemType)
+ : AndroidDrawable(drawable, itemType)
+{
+ m_id = 0;
+ m_factor = 1;
+ m_orientation = Qt::Horizontal;
+ const QVariantList layers = drawable.value(QLatin1String("layers")).toList();
+ for (const QVariant &layer : layers) {
+ QVariantMap layerMap = layer.toMap();
+ AndroidDrawable *ad = fromMap(layerMap, itemType);
+ if (ad) {
+ LayerType l;
+ l.second = ad;
+ l.first = layerMap.value(QLatin1String("id")).toInt();
+ m_layers << l;
+ }
+ }
+}
+
+QAndroidStyle::AndroidLayerDrawable::~AndroidLayerDrawable()
+{
+ for (const LayerType &layer : qAsConst(m_layers))
+ delete layer.second;
+}
+
+QAndroidStyle::AndroidDrawableType QAndroidStyle::AndroidLayerDrawable::type() const
+{
+ return QAndroidStyle::Layer;
+}
+
+void QAndroidStyle::AndroidLayerDrawable::setFactor(int id, double factor, Qt::Orientation orientation)
+{
+ m_id = id;
+ m_factor = factor;
+ m_orientation = orientation;
+}
+
+void QAndroidStyle::AndroidLayerDrawable::draw(QPainter *painter, const QStyleOption *opt) const
+{
+ for (const LayerType &layer : m_layers) {
+ if (layer.first == m_id) {
+ QStyleOption copy(*opt);
+ if (m_orientation == Qt::Horizontal)
+ copy.rect.setWidth(copy.rect.width() * m_factor);
+ else
+ copy.rect.setHeight(copy.rect.height() * m_factor);
+ layer.second->draw(painter, &copy);
+ } else {
+ layer.second->draw(painter, opt);
+ }
+ }
+}
+
+QAndroidStyle::AndroidDrawable *QAndroidStyle::AndroidLayerDrawable::layer(int id) const
+{
+ for (const LayerType &layer : m_layers)
+ if (layer.first == id)
+ return layer.second;
+ return 0;
+}
+
+QSize QAndroidStyle::AndroidLayerDrawable::size() const
+{
+ QSize sz;
+ for (const LayerType &layer : m_layers)
+ sz = sz.expandedTo(layer.second->size());
+ return sz;
+}
+
+QAndroidStyle::AndroidControl::AndroidControl(const QVariantMap &control,
+ QAndroidStyle::ItemType itemType)
+{
+ QVariantMap::const_iterator it = control.find(QLatin1String("View_background"));
+ if (it != control.end())
+ m_background = AndroidDrawable::fromMap(it.value().toMap(), itemType);
+ else
+ m_background = 0;
+
+ it = control.find(QLatin1String("View_minWidth"));
+ if (it != control.end())
+ m_minSize.setWidth(it.value().toInt());
+
+ it = control.find(QLatin1String("View_minHeight"));
+ if (it != control.end())
+ m_minSize.setHeight(it.value().toInt());
+
+ it = control.find(QLatin1String("View_maxWidth"));
+ if (it != control.end())
+ m_maxSize.setWidth(it.value().toInt());
+
+ it = control.find(QLatin1String("View_maxHeight"));
+ if (it != control.end())
+ m_maxSize.setHeight(it.value().toInt());
+}
+
+QAndroidStyle::AndroidControl::~AndroidControl()
+{
+ delete m_background;
+}
+
+void QAndroidStyle::AndroidControl::drawControl(const QStyleOption *opt, QPainter *p, const QWidget * /* w */)
+{
+ if (m_background) {
+ m_background->draw(p, opt);
+ } else {
+ if (const QStyleOptionFrame *frame = qstyleoption_cast<const QStyleOptionFrame *>(opt)) {
+ if ((frame->state & State_Sunken) || (frame->state & State_Raised)) {
+ qDrawShadePanel(p, frame->rect, frame->palette, frame->state & State_Sunken,
+ frame->lineWidth);
+ } else {
+ qDrawPlainRect(p, frame->rect, frame->palette.foreground().color(), frame->lineWidth);
+ }
+ } else {
+ if (const QStyleOptionFocusRect *fropt = qstyleoption_cast<const QStyleOptionFocusRect *>(opt)) {
+ QColor bg = fropt->backgroundColor;
+ QPen oldPen = p->pen();
+ if (bg.isValid()) {
+ int h, s, v;
+ bg.getHsv(&h, &s, &v);
+ if (v >= 128)
+ p->setPen(Qt::black);
+ else
+ p->setPen(Qt::white);
+ } else {
+ p->setPen(opt->palette.foreground().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));
+ }
+ }
+ }
+}
+
+QRect QAndroidStyle::AndroidControl::subElementRect(QStyle::SubElement /* subElement */,
+ const QStyleOption *option,
+ const QWidget * /* widget */) const
+{
+ if (const AndroidDrawable *drawable = backgroundDrawable()) {
+ if (drawable->type() == State)
+ drawable = static_cast<const AndroidStateDrawable *>(backgroundDrawable())->bestAndroidStateMatch(option);
+
+ const QMargins &padding = drawable->padding();
+
+ QRect r = option->rect.adjusted(padding.left(), padding.top(),
+ -padding.right(), -padding.bottom());
+
+ if (r.width() < m_minSize.width())
+ r.setWidth(m_minSize.width());
+
+ if (r.height() < m_minSize.height())
+ r.setHeight(m_minSize.height());
+
+ return visualRect(option->direction, option->rect, r);
+ }
+ return option->rect;
+}
+
+QRect QAndroidStyle::AndroidControl::subControlRect(const QStyleOptionComplex *option,
+ QStyle::SubControl /*sc*/,
+ const QWidget *widget) const
+{
+ return subElementRect(QStyle::SE_CustomBase, option, widget);
+}
+
+QSize QAndroidStyle::AndroidControl::sizeFromContents(const QStyleOption *opt,
+ const QSize &contentsSize,
+ const QWidget * /* w */) const
+{
+ QSize sz;
+ if (const AndroidDrawable *drawable = backgroundDrawable()) {
+
+ if (drawable->type() == State)
+ drawable = static_cast<const AndroidStateDrawable*>(backgroundDrawable())->bestAndroidStateMatch(opt);
+ const QMargins &padding = drawable->padding();
+ sz.setWidth(padding.left() + padding.right());
+ sz.setHeight(padding.top() + padding.bottom());
+ if (sz.isEmpty())
+ sz = drawable->size();
+ }
+ sz += contentsSize;
+ if (contentsSize.height() < opt->fontMetrics.height())
+ sz.setHeight(sz.height() + (opt->fontMetrics.height() - contentsSize.height()));
+ if (sz.height() < m_minSize.height())
+ sz.setHeight(m_minSize.height());
+ if (sz.width() < m_minSize.width())
+ sz.setWidth(m_minSize.width());
+ return sz;
+}
+
+QMargins QAndroidStyle::AndroidControl::padding()
+{
+ if (const AndroidDrawable *drawable = m_background) {
+ if (drawable->type() == State)
+ drawable = static_cast<const AndroidStateDrawable *>(m_background)->bestAndroidStateMatch(0);
+ return drawable->padding();
+ }
+ return QMargins();
+}
+
+QSize QAndroidStyle::AndroidControl::size(const QStyleOption *option)
+{
+ if (const AndroidDrawable *drawable = backgroundDrawable()) {
+ if (drawable->type() == State)
+ drawable = static_cast<const AndroidStateDrawable *>(backgroundDrawable())->bestAndroidStateMatch(option);
+ return drawable->size();
+ }
+ return QSize();
+}
+
+const QAndroidStyle::AndroidDrawable *QAndroidStyle::AndroidControl::backgroundDrawable() const
+{
+ return m_background;
+}
+
+QAndroidStyle::AndroidCompoundButtonControl::AndroidCompoundButtonControl(const QVariantMap &control,
+ ItemType itemType)
+ : AndroidControl(control, itemType)
+{
+ QVariantMap::const_iterator it = control.find(QLatin1String("CompoundButton_button"));
+ if (it != control.end()) {
+ m_button = AndroidDrawable::fromMap(it.value().toMap(), itemType);
+ const_cast<AndroidDrawable *>(m_button)->setPaddingLeftToSizeWidth();
+ } else {
+ m_button = 0;
+ }
+}
+
+QAndroidStyle::AndroidCompoundButtonControl::~AndroidCompoundButtonControl()
+{
+ delete m_button;
+}
+
+void QAndroidStyle::AndroidCompoundButtonControl::drawControl(const QStyleOption *opt,
+ QPainter *p,
+ const QWidget *w)
+{
+ AndroidControl::drawControl(opt, p, w);
+ if (m_button)
+ m_button->draw(p, opt);
+}
+
+QMargins QAndroidStyle::AndroidCompoundButtonControl::padding()
+{
+ if (m_button)
+ return m_button->padding();
+ return AndroidControl::padding();
+}
+
+QSize QAndroidStyle::AndroidCompoundButtonControl::size(const QStyleOption *option)
+{
+ if (m_button) {
+ if (m_button->type() == State)
+ return static_cast<const AndroidStateDrawable *>(m_button)->bestAndroidStateMatch(option)->size();
+ return m_button->size();
+ }
+ return AndroidControl::size(option);
+}
+
+const QAndroidStyle::AndroidDrawable * QAndroidStyle::AndroidCompoundButtonControl::backgroundDrawable() const
+{
+ return m_background ? m_background : m_button;
+}
+
+QAndroidStyle::AndroidProgressBarControl::AndroidProgressBarControl(const QVariantMap &control,
+ ItemType itemType)
+ : AndroidControl(control, itemType)
+{
+ QVariantMap::const_iterator it = control.find(QLatin1String("ProgressBar_indeterminateDrawable"));
+ if (it != control.end())
+ m_indeterminateDrawable = AndroidDrawable::fromMap(it.value().toMap(), itemType);
+ else
+ m_indeterminateDrawable = 0;
+
+ it = control.find(QLatin1String("ProgressBar_progressDrawable"));
+ if (it != control.end())
+ m_progressDrawable = AndroidDrawable::fromMap(it.value().toMap(), itemType);
+ else
+ m_progressDrawable = 0;
+
+ it = control.find(QLatin1String("ProgressBar_progress_id"));
+ if (it != control.end())
+ m_progressId = it.value().toInt();
+
+ it = control.find(QLatin1String("ProgressBar_secondaryProgress_id"));
+ if (it != control.end())
+ m_secondaryProgress_id = it.value().toInt();
+
+ it = control.find(QLatin1String("ProgressBar_minWidth"));
+ if (it != control.end())
+ m_minSize.setWidth(it.value().toInt());
+
+ it = control.find(QLatin1String("ProgressBar_minHeight"));
+ if (it != control.end())
+ m_minSize.setHeight(it.value().toInt());
+
+ it = control.find(QLatin1String("ProgressBar_maxWidth"));
+ if (it != control.end())
+ m_maxSize.setWidth(it.value().toInt());
+
+ it = control.find(QLatin1String("ProgressBar_maxHeight"));
+ if (it != control.end())
+ m_maxSize.setHeight(it.value().toInt());
+}
+
+QAndroidStyle::AndroidProgressBarControl::~AndroidProgressBarControl()
+{
+ delete m_progressDrawable;
+ delete m_indeterminateDrawable;
+}
+
+void QAndroidStyle::AndroidProgressBarControl::drawControl(const QStyleOption *option, QPainter *p, const QWidget * /* w */)
+{
+ if (!m_progressDrawable)
+ return;
+
+ if (const QStyleOptionProgressBar *pb = qstyleoption_cast<const QStyleOptionProgressBar *>(option)) {
+ if (m_progressDrawable->type() == QAndroidStyle::Layer) {
+ const double fraction = double(qint64(pb->progress) - pb->minimum) / (qint64(pb->maximum) - pb->minimum);
+ QAndroidStyle::AndroidDrawable *clipDrawable = static_cast<QAndroidStyle::AndroidLayerDrawable *>(m_progressDrawable)->layer(m_progressId);
+ if (clipDrawable->type() == QAndroidStyle::Clip)
+ static_cast<AndroidClipDrawable *>(clipDrawable)->setFactor(fraction, pb->orientation);
+ else
+ static_cast<AndroidLayerDrawable *>(m_progressDrawable)->setFactor(m_progressId, fraction, pb->orientation);
+ }
+ m_progressDrawable->draw(p, option);
+ }
+}
+
+QRect QAndroidStyle::AndroidProgressBarControl::subElementRect(QStyle::SubElement subElement,
+ const QStyleOption *option,
+ const QWidget *widget) const
+{
+ if (const QStyleOptionProgressBar *progressBarOption =
+ qstyleoption_cast<const QStyleOptionProgressBar *>(option)) {
+ const bool horizontal = progressBarOption->orientation == Qt::Vertical;
+ if (!m_background)
+ return option->rect;
+
+ QMargins padding = m_background->padding();
+ QRect p(padding.left(), padding.top(), padding.right() - padding.left(), padding.bottom() - padding.top());
+ padding = m_indeterminateDrawable->padding();
+ p |= QRect(padding.left(), padding.top(), padding.right() - padding.left(), padding.bottom() - padding.top());
+ padding = m_progressDrawable->padding();
+ p |= QRect(padding.left(), padding.top(), padding.right() - padding.left(), padding.bottom() - padding.top());
+ QRect r = option->rect.adjusted(p.left(), p.top(), -p.right(), -p.bottom());
+
+ if (horizontal) {
+ if (r.height()<m_minSize.height())
+ r.setHeight(m_minSize.height());
+
+ if (r.height()>m_maxSize.height())
+ r.setHeight(m_maxSize.height());
+ } else {
+ if (r.width()<m_minSize.width())
+ r.setWidth(m_minSize.width());
+
+ if (r.width()>m_maxSize.width())
+ r.setWidth(m_maxSize.width());
+ }
+ return visualRect(option->direction, option->rect, r);
+ }
+ return AndroidControl::subElementRect(subElement, option, widget);
+}
+
+QSize QAndroidStyle::AndroidProgressBarControl::sizeFromContents(const QStyleOption *opt,
+ const QSize &contentsSize,
+ const QWidget * /* w */) const
+{
+ QSize sz(contentsSize);
+ if (sz.height() < m_minSize.height())
+ sz.setHeight(m_minSize.height());
+ if (sz.width() < m_minSize.width())
+ sz.setWidth(m_minSize.width());
+
+ if (const QStyleOptionProgressBar *progressBarOption =
+ qstyleoption_cast<const QStyleOptionProgressBar *>(opt)) {
+ if (progressBarOption->orientation == Qt::Vertical) {
+ if (sz.height() > m_maxSize.height())
+ sz.setHeight(m_maxSize.height());
+ } else {
+ if (sz.width() > m_maxSize.width())
+ sz.setWidth(m_maxSize.width());
+ }
+ }
+ return contentsSize;
+}
+
+QAndroidStyle::AndroidSeekBarControl::AndroidSeekBarControl(const QVariantMap &control,
+ ItemType itemType)
+ : AndroidProgressBarControl(control, itemType)
+{
+ QVariantMap::const_iterator it = control.find(QLatin1String("SeekBar_thumb"));
+ if (it != control.end())
+ m_seekBarThumb = AndroidDrawable::fromMap(it.value().toMap(), itemType);
+ else
+ m_seekBarThumb = 0;
+}
+
+QAndroidStyle::AndroidSeekBarControl::~AndroidSeekBarControl()
+{
+ delete m_seekBarThumb;
+}
+
+void QAndroidStyle::AndroidSeekBarControl::drawControl(const QStyleOption *option,
+ QPainter *p,
+ const QWidget * /* w */)
+{
+ if (!m_seekBarThumb || !m_progressDrawable)
+ return;
+
+ if (const QStyleOptionSlider *styleOption =
+ qstyleoption_cast<const QStyleOptionSlider *>(option)) {
+ double factor = double(styleOption->sliderPosition - styleOption->minimum)
+ / double(styleOption->maximum - styleOption->minimum);
+
+ // Android does not have a vertical slider. To support the vertical orientation, we rotate
+ // the painter and pretend that we are horizontal.
+ if (styleOption->orientation == Qt::Vertical)
+ factor = 1 - factor;
+
+ if (m_progressDrawable->type() == QAndroidStyle::Layer) {
+ QAndroidStyle::AndroidDrawable *clipDrawable = static_cast<QAndroidStyle::AndroidLayerDrawable *>(m_progressDrawable)->layer(m_progressId);
+ if (clipDrawable->type() == QAndroidStyle::Clip)
+ static_cast<QAndroidStyle::AndroidClipDrawable *>(clipDrawable)->setFactor(factor, Qt::Horizontal);
+ else
+ static_cast<QAndroidStyle::AndroidLayerDrawable *>(m_progressDrawable)->setFactor(m_progressId, factor, Qt::Horizontal);
+ }
+ const AndroidDrawable *drawable = m_seekBarThumb;
+ if (drawable->type() == State)
+ drawable = static_cast<const QAndroidStyle::AndroidStateDrawable *>(m_seekBarThumb)->bestAndroidStateMatch(option);
+ QStyleOption copy(*option);
+
+ p->save();
+
+ if (styleOption->orientation == Qt::Vertical) {
+ // rotate the painter, and transform the rectangle to match
+ p->rotate(90);
+ copy.rect = QRect(copy.rect.y(), copy.rect.x() - copy.rect.width(), copy.rect.height(), copy.rect.width());
+ }
+
+ copy.rect.setHeight(m_progressDrawable->size().height());
+ copy.rect.setWidth(copy.rect.width() - drawable->size().width());
+ const int yTranslate = abs(drawable->size().height() - copy.rect.height()) / 2;
+ copy.rect.translate(drawable->size().width() / 2, yTranslate);
+ m_progressDrawable->draw(p, &copy);
+ int pos = copy.rect.width() * factor - drawable->size().width() / 2;
+ copy.rect.translate(pos, -yTranslate);
+ copy.rect.setSize(drawable->size());
+ m_seekBarThumb->draw(p, &copy);
+
+ p->restore();
+ }
+}
+
+QSize QAndroidStyle::AndroidSeekBarControl::sizeFromContents(const QStyleOption *opt,
+ const QSize &contentsSize,
+ const QWidget *w) const
+{
+ QSize sz = AndroidProgressBarControl::sizeFromContents(opt, contentsSize, w);
+ if (!m_seekBarThumb)
+ return sz;
+ const AndroidDrawable *drawable = m_seekBarThumb;
+ if (drawable->type() == State)
+ drawable = static_cast<const QAndroidStyle::AndroidStateDrawable *>(m_seekBarThumb)->bestAndroidStateMatch(opt);
+ return sz.expandedTo(drawable->size());
+}
+
+QRect QAndroidStyle::AndroidSeekBarControl::subControlRect(const QStyleOptionComplex *option,
+ SubControl sc,
+ const QWidget * /* widget */) const
+{
+ const QStyleOptionSlider *styleOption =
+ qstyleoption_cast<const QStyleOptionSlider *>(option);
+
+ if (m_seekBarThumb && sc == SC_SliderHandle && styleOption) {
+ const AndroidDrawable *drawable = m_seekBarThumb;
+ if (drawable->type() == State)
+ drawable = static_cast<const QAndroidStyle::AndroidStateDrawable *>(m_seekBarThumb)->bestAndroidStateMatch(option);
+
+ QRect r(option->rect);
+ double factor = double(styleOption->sliderPosition - styleOption->minimum)
+ / (styleOption->maximum - styleOption->minimum);
+ if (styleOption->orientation == Qt::Vertical) {
+ int pos = option->rect.height() * (1 - factor) - double(drawable->size().height() / 2);
+ r.setY(r.y() + pos);
+ } else {
+ int pos = option->rect.width() * factor - double(drawable->size().width() / 2);
+ r.setX(r.x() + pos);
+ }
+ r.setSize(drawable->size());
+ return r;
+ }
+ return option->rect;
+}
+
+QAndroidStyle::AndroidSpinnerControl::AndroidSpinnerControl(const QVariantMap &control,
+ QAndroidStyle::ItemType itemType)
+ : AndroidControl(control, itemType)
+{}
+
+QRect QAndroidStyle::AndroidSpinnerControl::subControlRect(const QStyleOptionComplex *option,
+ SubControl sc,
+ const QWidget *widget) const
+{
+ if (sc == QStyle::SC_ComboBoxListBoxPopup)
+ return option->rect;
+ if (sc == QStyle::SC_ComboBoxArrow) {
+ const QRect editField = subControlRect(option, QStyle::SC_ComboBoxEditField, widget);
+ return QRect(editField.topRight(), QSize(option->rect.width() - editField.width(), option->rect.height()));
+ }
+ return AndroidControl::subControlRect(option, sc, widget);
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/styles/android/qandroidstyle_p.h b/src/plugins/styles/android/qandroidstyle_p.h
new file mode 100644
index 0000000000..3faa08afb9
--- /dev/null
+++ b/src/plugins/styles/android/qandroidstyle_p.h
@@ -0,0 +1,391 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+#ifndef QANDROIDSTYLE_P_H
+#define QANDROIDSTYLE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of qstylefactory.cpp. This header may change from version to version
+// without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtWidgets/private/qtwidgetsglobal_p.h>
+#include <QtWidgets/private/qfusionstyle_p.h>
+#include <QtCore/QList>
+#include <QtCore/QMargins>
+#include <QtCore/QHash>
+#include <QtCore/QVariantMap>
+
+QT_BEGIN_NAMESPACE
+
+class Q_WIDGETS_EXPORT QAndroidStyle : public QFusionStyle
+{
+ Q_OBJECT
+
+public:
+ enum ItemType
+ {
+ QC_UnknownType = -1,
+ QC_View,
+ QC_GroupBox,
+ QC_Button,
+ QC_Checkbox,
+ QC_RadioButton,
+ QC_Slider,
+ QC_Switch,
+ QC_EditText,
+ QC_Combobox,
+ QC_BusyIndicator,
+ QC_ProgressBar,
+ QC_Tab,
+ QC_TabButton,
+ QC_RatingIndicator,
+ QC_SearchBox,
+ QC_CustomControl=0xf00,
+ QC_ControlMask=0xfff
+ };
+
+ struct Android9PatchChunk
+ {
+ QVector<int> xDivs;
+ QVector<int> yDivs;
+ QVector<int> colors;
+ };
+
+ struct AndroidItemStateInfo
+ {
+ AndroidItemStateInfo():state(0){}
+ int state;
+ QByteArray filePath;
+ QByteArray hashKey;
+ Android9PatchChunk chunkData;
+ QSize size;
+ QMargins padding;
+ };
+
+ enum AndroidDrawableType
+ {
+ Color,
+ Image,
+ Clip,
+ NinePatch,
+ Gradient,
+ State,
+ Layer
+ };
+
+ class AndroidDrawable
+ {
+ public:
+ AndroidDrawable(const QVariantMap &drawable, ItemType itemType);
+ virtual ~AndroidDrawable();
+ virtual void initPadding(const QVariantMap &drawable);
+ virtual AndroidDrawableType type() const = 0;
+ virtual void draw(QPainter *painter,const QStyleOption *opt) const = 0;
+ const QMargins &padding() const;
+ virtual QSize size() const;
+ static AndroidDrawable *fromMap(const QVariantMap &drawable, ItemType itemType);
+ static QMargins extractMargins(const QVariantMap &value);
+ virtual void setPaddingLeftToSizeWidth();
+ protected:
+ ItemType m_itemType;
+ QMargins m_padding;
+ };
+
+ class AndroidColorDrawable: public AndroidDrawable
+ {
+ public:
+ AndroidColorDrawable(const QVariantMap &drawable, ItemType itemType);
+ virtual AndroidDrawableType type() const;
+ virtual void draw(QPainter *painter,const QStyleOption *opt) const;
+
+ protected:
+ QColor m_color;
+ };
+
+ class AndroidImageDrawable: public AndroidDrawable
+ {
+ public:
+ AndroidImageDrawable(const QVariantMap &drawable, ItemType itemType);
+ virtual AndroidDrawableType type() const;
+ virtual void draw(QPainter *painter,const QStyleOption *opt) const;
+ virtual QSize size() const;
+
+ protected:
+ QString m_filePath;
+ mutable QString m_hashKey;
+ QSize m_size;
+ };
+
+ class Android9PatchDrawable: public AndroidImageDrawable
+ {
+ public:
+ Android9PatchDrawable(const QVariantMap &drawable, ItemType itemType);
+ virtual AndroidDrawableType type() const;
+ virtual void draw(QPainter *painter, const QStyleOption *opt) const;
+ private:
+ static int calculateStretch(int boundsLimit, int startingPoint,
+ int srcSpace, int numStrechyPixelsRemaining,
+ int numFixedPixelsRemaining);
+ void extractIntArray(const QVariantList &values, QVector<int> &array);
+ private:
+ Android9PatchChunk m_chunkData;
+ };
+
+ class AndroidGradientDrawable: public AndroidDrawable
+ {
+ public:
+ enum GradientOrientation
+ {
+ TOP_BOTTOM,
+ TR_BL,
+ RIGHT_LEFT,
+ BR_TL,
+ BOTTOM_TOP,
+ BL_TR,
+ LEFT_RIGHT,
+ TL_BR
+ };
+
+ public:
+ AndroidGradientDrawable(const QVariantMap &drawable, ItemType itemType);
+ virtual AndroidDrawableType type() const;
+ virtual void draw(QPainter *painter, const QStyleOption *opt) const;
+ QSize size() const;
+ private:
+ mutable QLinearGradient m_gradient;
+ GradientOrientation m_orientation;
+ int m_radius;
+ };
+
+ class AndroidClipDrawable: public AndroidDrawable
+ {
+ public:
+ AndroidClipDrawable(const QVariantMap &drawable, ItemType itemType);
+ ~AndroidClipDrawable();
+ virtual AndroidDrawableType type() const;
+ virtual void setFactor(double factor, Qt::Orientation orientation);
+ virtual void draw(QPainter *painter, const QStyleOption *opt) const;
+
+ private:
+ double m_factor;
+ Qt::Orientation m_orientation;
+ const AndroidDrawable *m_drawable;
+ };
+
+ class AndroidStateDrawable: public AndroidDrawable
+ {
+ public:
+ AndroidStateDrawable(const QVariantMap &drawable, ItemType itemType);
+ ~AndroidStateDrawable();
+ virtual AndroidDrawableType type() const;
+ virtual void draw(QPainter *painter, const QStyleOption *opt) const;
+ inline const AndroidDrawable *bestAndroidStateMatch(const QStyleOption *opt) const;
+ static int extractState(const QVariantMap &value);
+ virtual void setPaddingLeftToSizeWidth();
+ QSize sizeImage(const QStyleOption *opt) const;
+ private:
+ typedef QPair<int, const AndroidDrawable *> StateType;
+ QList<StateType> m_states;
+ };
+
+ class AndroidLayerDrawable: public AndroidDrawable
+ {
+ public:
+ AndroidLayerDrawable(const QVariantMap &drawable, QAndroidStyle::ItemType itemType);
+ ~AndroidLayerDrawable();
+ virtual AndroidDrawableType type() const;
+ virtual void setFactor(int id, double factor, Qt::Orientation orientation);
+ virtual void draw(QPainter *painter, const QStyleOption *opt) const;
+ AndroidDrawable *layer(int id) const;
+ QSize size() const;
+ private:
+ typedef QPair<int, AndroidDrawable *> LayerType;
+ QList<LayerType> m_layers;
+ int m_id;
+ double m_factor;
+ Qt::Orientation m_orientation;
+ };
+
+ class AndroidControl
+ {
+ public:
+ AndroidControl(const QVariantMap &control, ItemType itemType);
+ virtual ~AndroidControl();
+ virtual void drawControl(const QStyleOption *opt, QPainter *p, const QWidget *w);
+ virtual QRect subElementRect(SubElement subElement,
+ const QStyleOption *option,
+ const QWidget *widget = 0) const;
+ virtual QRect subControlRect(const QStyleOptionComplex *option,
+ SubControl sc,
+ const QWidget *widget = 0) const;
+ virtual QSize sizeFromContents(const QStyleOption *opt,
+ const QSize &contentsSize,
+ const QWidget *w) const;
+ virtual QMargins padding();
+ virtual QSize size(const QStyleOption *option);
+ protected:
+ virtual const AndroidDrawable * backgroundDrawable() const;
+ const AndroidDrawable *m_background;
+ QSize m_minSize;
+ QSize m_maxSize;
+ };
+
+ class AndroidCompoundButtonControl : public AndroidControl
+ {
+ public:
+ AndroidCompoundButtonControl(const QVariantMap &control, ItemType itemType);
+ virtual ~AndroidCompoundButtonControl();
+ virtual void drawControl(const QStyleOption *opt, QPainter *p, const QWidget *w);
+ virtual QMargins padding();
+ virtual QSize size(const QStyleOption *option);
+ protected:
+ virtual const AndroidDrawable * backgroundDrawable() const;
+ const AndroidDrawable *m_button;
+ };
+
+ class AndroidProgressBarControl : public AndroidControl
+ {
+ public:
+ AndroidProgressBarControl(const QVariantMap &control, ItemType itemType);
+ virtual ~AndroidProgressBarControl();
+ virtual void drawControl(const QStyleOption *option, QPainter *p, const QWidget *w);
+ virtual QRect subElementRect(SubElement subElement,
+ const QStyleOption *option,
+ const QWidget *widget = 0) const;
+
+ QSize sizeFromContents(const QStyleOption *opt,
+ const QSize &contentsSize,
+ const QWidget *w) const;
+ protected:
+ AndroidDrawable *m_progressDrawable;
+ AndroidDrawable *m_indeterminateDrawable;
+ int m_secondaryProgress_id;
+ int m_progressId;
+ };
+
+ class AndroidSeekBarControl : public AndroidProgressBarControl
+ {
+ public:
+ AndroidSeekBarControl(const QVariantMap &control, ItemType itemType);
+ virtual ~AndroidSeekBarControl();
+ virtual void drawControl(const QStyleOption *option, QPainter *p, const QWidget *w);
+ QSize sizeFromContents(const QStyleOption *opt,
+ const QSize &contentsSize, const QWidget *w) const;
+ QRect subControlRect(const QStyleOptionComplex *option, SubControl sc,
+ const QWidget *widget = 0) const;
+ private:
+ AndroidDrawable *m_seekBarThumb;
+ };
+
+ class AndroidSpinnerControl : public AndroidControl
+ {
+ public:
+ AndroidSpinnerControl(const QVariantMap &control, ItemType itemType);
+ virtual ~AndroidSpinnerControl(){}
+ virtual QRect subControlRect(const QStyleOptionComplex *option,
+ SubControl sc,
+ const QWidget *widget = 0) const;
+ };
+
+ typedef QList<AndroidItemStateInfo *> AndroidItemStateInfoList;
+
+public:
+ QAndroidStyle();
+ ~QAndroidStyle();
+
+ virtual void drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, QPainter *p,
+ const QWidget *w = 0) const;
+
+ virtual void drawControl(QStyle::ControlElement element, const QStyleOption *opt, QPainter *p,
+ const QWidget *w = 0) const;
+
+ virtual QRect subElementRect(SubElement subElement, const QStyleOption *option,
+ const QWidget *widget = 0) const;
+ virtual void drawComplexControl(ComplexControl cc, const QStyleOptionComplex *opt, QPainter *p,
+ const QWidget *widget = 0) const;
+ virtual SubControl hitTestComplexControl(ComplexControl cc, const QStyleOptionComplex *opt,
+ const QPoint &pt, const QWidget *widget = 0) const;
+ virtual QRect subControlRect(ComplexControl cc, const QStyleOptionComplex *opt,
+ SubControl sc, const QWidget *widget = 0) const;
+
+ virtual int pixelMetric(PixelMetric metric, const QStyleOption *option = 0,
+ const QWidget *widget = 0) const;
+
+ virtual QSize sizeFromContents(ContentsType ct, const QStyleOption *opt,
+ const QSize &contentsSize, const QWidget *w = 0) const;
+
+ virtual QPixmap standardPixmap(StandardPixmap standardPixmap, const QStyleOption *opt = 0,
+ const QWidget *widget = 0) 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;
+
+ virtual QPalette standardPalette() const;
+ void polish(QWidget *widget);
+ void unpolish(QWidget *widget);
+
+private:
+ Q_DISABLE_COPY(QAndroidStyle)
+ static ItemType qtControl(QStyle::ComplexControl control);
+ static ItemType qtControl(QStyle::ContentsType contentsType);
+ static ItemType qtControl(QStyle::ControlElement controlElement);
+ static ItemType qtControl(QStyle::PrimitiveElement primitiveElement);
+ static ItemType qtControl(QStyle::SubElement subElement);
+ static ItemType qtControl(const QString &android);
+
+private:
+ typedef QHash<int, AndroidControl *> AndroidControlsHash;
+ AndroidControlsHash m_androidControlsHash;
+ QPalette m_standardPalette;
+ AndroidCompoundButtonControl *checkBoxControl;
+};
+
+QT_END_NAMESPACE
+
+#endif // QANDROIDSTYLE_P_H
diff --git a/src/plugins/styles/mac/mac.pro b/src/plugins/styles/mac/mac.pro
new file mode 100644
index 0000000000..bf9660eed3
--- /dev/null
+++ b/src/plugins/styles/mac/mac.pro
@@ -0,0 +1,19 @@
+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 -framework Carbon
+
+DISTFILES += macstyle.json
+
+PLUGIN_TYPE = styles
+PLUGIN_CLASS_NAME = QMacStylePlugin
+load(qt_plugin)
diff --git a/src/plugins/styles/mac/macstyle.json b/src/plugins/styles/mac/macstyle.json
new file mode 100644
index 0000000000..5897815eec
--- /dev/null
+++ b/src/plugins/styles/mac/macstyle.json
@@ -0,0 +1,3 @@
+{
+ "Keys": [ "macintosh" ]
+}
diff --git a/src/plugins/styles/mac/main.mm b/src/plugins/styles/mac/main.mm
new file mode 100644
index 0000000000..ae31bb95fb
--- /dev/null
+++ b/src/plugins/styles/mac/main.mm
@@ -0,0 +1,65 @@
+/****************************************************************************
+**
+** 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/qstyleplugin.h>
+#include "qmacstyle_mac_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QMacStylePlugin : public QStylePlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QStyleFactoryInterface" FILE "macstyle.json")
+public:
+ QStyle *create(const QString &key);
+};
+
+QStyle *QMacStylePlugin::create(const QString &key)
+{
+ QMacAutoReleasePool pool;
+ if (key.compare(QLatin1String("macintosh"), Qt::CaseInsensitive) == 0)
+ return new QMacStyle();
+
+ return 0;
+}
+
+QT_END_NAMESPACE
+
+#include "main.moc"
+
diff --git a/src/plugins/styles/mac/qmacstyle.qdoc b/src/plugins/styles/mac/qmacstyle.qdoc
new file mode 100644
index 0000000000..fcbc813844
--- /dev/null
+++ b/src/plugins/styles/mac/qmacstyle.qdoc
@@ -0,0 +1,207 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** 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 Free Documentation License Usage
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of
+** this file. Please review the following information to ensure
+** the GNU Free Documentation License version 1.3 requirements
+** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+/*!
+ \class QMacStyle
+ \brief The QMacStyle class provides a \macos style using the Apple Appearance Manager.
+
+ \ingroup appearance
+ \inmodule QtWidgets
+ \internal
+
+ This class is implemented as a wrapper to the HITheme
+ APIs, allowing applications to be styled according to the current
+ theme in use on \macos. This is done by having primitives
+ in QStyle implemented in terms of what \macos would normally theme.
+
+ \warning This style is only available on \macos because it relies on the
+ HITheme APIs.
+
+ There are additional issues that should be taken
+ into consideration to make an application compatible with the
+ \l{Apple Human Interface Guidelines}{Apple Human Interface Guidelines}. Some of these issues are outlined
+ below.
+
+ \list
+
+ \li Layout - The restrictions on window layout are such that some
+ aspects of layout that are style-dependent cannot be achieved
+ using QLayout. Changes are being considered (and feedback would be
+ appreciated) to make layouts QStyle-able. Some of the restrictions
+ involve horizontal and vertical widget alignment and widget size
+ (covered below).
+
+ \li Widget size - \macos allows widgets to have specific fixed sizes. Qt
+ does not fully implement this behavior so as to maintain cross-platform
+ compatibility. As a result some widgets sizes may be inappropriate (and
+ subsequently not rendered correctly by the HITheme APIs).The
+ QWidget::sizeHint() will return the appropriate size for many
+ managed widgets (widgets enumerated in \l QStyle::ContentsType).
+
+ \li Effects - QMacStyle uses HITheme for performing most of the drawing, but
+ also uses emulation in a few cases where HITheme does not provide the
+ required functionality (for example, tab bars on Panther, the toolbar
+ separator, etc). We tried to make the emulation as close to the original as
+ possible. Please report any issues you see in effects or non-standard
+ widgets.
+
+ \endlist
+
+ There are other issues that need to be considered in the feel of
+ your application (including the general color scheme to match the
+ Aqua colors). The Guidelines mentioned above will remain current
+ with new advances and design suggestions for \macos.
+
+ Note that the functions provided by QMacStyle are
+ reimplementations of QStyle functions; see QStyle for their
+ documentation.
+
+ \image qmacstyle.png
+ \sa QWindowsVistaStyle, QWindowsStyle, QFusionStyle
+*/
+
+
+/*!
+ \enum QMacStyle::WidgetSizePolicy
+
+ \value SizeSmall
+ \value SizeLarge
+ \value SizeMini
+ \value SizeDefault
+*/
+
+/*! \fn QMacStyle::QMacStyle()
+ Constructs a QMacStyle object.
+*/
+
+/*! \fn QMacStyle::~QMacStyle()
+ Destructs a QMacStyle object.
+*/
+
+/*! \fn void QMacStyle::polish(QPalette &pal)
+ \reimp
+*/
+
+/*! \fn void QMacStyle::polish(QApplication *)
+ \reimp
+*/
+
+/*! \fn void QMacStyle::unpolish(QApplication *)
+ \reimp
+*/
+
+/*! \fn void QMacStyle::polish(QWidget* w)
+ \reimp
+*/
+
+/*! \fn void QMacStyle::unpolish(QWidget* w)
+ \reimp
+*/
+
+/*! \fn int QMacStyle::pixelMetric(PixelMetric metric, const QStyleOption *opt, const QWidget *widget) const
+ \reimp
+*/
+
+/*! \fn QPalette QMacStyle::standardPalette() const
+ \reimp
+*/
+
+/*! \fn int QMacStyle::styleHint(StyleHint sh, const QStyleOption *opt, const QWidget *w, QStyleHintReturn *hret) const
+ \reimp
+*/
+
+/*! \fn QPixmap QMacStyle::generatedIconPixmap(QIcon::Mode iconMode, const QPixmap &pixmap, const QStyleOption *opt) const
+ \reimp
+*/
+
+/*! \fn QPixmap QMacStyle::standardPixmap(StandardPixmap standardPixmap, const QStyleOption *opt, const QWidget *widget) const
+ \reimp
+*/
+
+/*! \fn void QMacStyle::setWidgetSizePolicy(const QWidget *widget, WidgetSizePolicy policy)
+
+ \obsolete
+
+ Call QWidget::setAttribute() with Qt::WA_MacMiniSize, Qt::WA_MacSmallSize,
+ or Qt::WA_MacNormalSize instead.
+*/
+
+/*! \fn QMacStyle::WidgetSizePolicy QMacStyle::widgetSizePolicy(const QWidget *widget, const QStyleOption *opt = 0)
+ \obsolete
+
+ Call QWidget::testAttribute() with Qt::WA_MacMiniSize, Qt::WA_MacSmallSize,
+ or Qt::WA_MacNormalSize instead.
+*/
+
+/*! \fn void QMacStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, QPainter *p, const QWidget *w) const
+
+ \reimp
+*/
+
+/*! \fn void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter *p, const QWidget *w) const
+
+ \reimp
+*/
+
+/*! \fn QRect QMacStyle::subElementRect(SubElement sr, const QStyleOption *opt, const QWidget *widget) const
+
+ \reimp
+*/
+
+/*! \fn void QMacStyle::drawComplexControl(ComplexControl cc, const QStyleOptionComplex *opt, QPainter *p, const QWidget *widget) const
+ \reimp
+*/
+
+/*! \fn QStyle::SubControl QMacStyle::hitTestComplexControl(ComplexControl cc, const QStyleOptionComplex *opt, const QPoint &pt, const QWidget *widget) const
+ \reimp
+*/
+
+/*! \fn QRect QMacStyle::subControlRect(ComplexControl cc, const QStyleOptionComplex *opt, SubControl sc, const QWidget *widget) const
+ \reimp
+*/
+
+/*! \fn QSize QMacStyle::sizeFromContents(ContentsType ct, const QStyleOption *opt, const QSize &csz, const QWidget *widget) const
+ \reimp
+*/
+
+/*! \fn void QMacStyle::drawItemText(QPainter *p, const QRect &r, int flags, const QPalette &pal, bool enabled, const QString &text, QPalette::ColorRole textRole) const
+ \reimp
+*/
+
+/*! \fn bool QMacStyle::event(QEvent *e)
+ \reimp
+*/
+
+/*! \fn QIcon QMacStyle::standardIcon(StandardPixmap standardIcon, const QStyleOption *opt, const QWidget *widget) const
+ \reimp
+*/
+
+/*! \fn int QMacStyle::layoutSpacing(QSizePolicy::ControlType control1, QSizePolicy::ControlType control2, Qt::Orientation orientation, const QStyleOption *option, const QWidget *widget) const
+ \reimp
+*/
+
diff --git a/src/plugins/styles/mac/qmacstyle_mac.mm b/src/plugins/styles/mac/qmacstyle_mac.mm
new file mode 100644
index 0000000000..47193f2a84
--- /dev/null
+++ b/src/plugins/styles/mac/qmacstyle_mac.mm
@@ -0,0 +1,7041 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+/*
+ Note: The qdoc comments for QMacStyle are contained in
+ .../doc/src/qstyles.qdoc.
+*/
+
+#include <AppKit/AppKit.h>
+
+#include "qmacstyle_mac_p.h"
+#include "qmacstyle_mac_p_p.h"
+
+#define QMAC_QAQUASTYLE_SIZE_CONSTRAIN
+//#define DEBUG_SIZE_CONSTRAINT
+
+#include <private/qcore_mac_p.h>
+#include <private/qcombobox_p.h>
+#include <private/qtabbar_p.h>
+#include <private/qpainter_p.h>
+#include <qapplication.h>
+#include <qbitmap.h>
+#include <qcombobox.h>
+#if QT_CONFIG(dialogbuttonbox)
+#include <qdialogbuttonbox.h>
+#endif
+#include <qdockwidget.h>
+#include <qevent.h>
+#include <qfocusframe.h>
+#include <qformlayout.h>
+#include <qgroupbox.h>
+#include <qhash.h>
+#include <qheaderview.h>
+#include <qlineedit.h>
+#include <qmainwindow.h>
+#include <qmdisubwindow.h>
+#include <qmenubar.h>
+#include <qpaintdevice.h>
+#include <qpainter.h>
+#include <qpixmapcache.h>
+#include <qpointer.h>
+#include <qprogressbar.h>
+#if QT_CONFIG(pushbutton)
+#include <qpushbutton.h>
+#endif
+#include <qradiobutton.h>
+#include <qrubberband.h>
+#include <qscrollbar.h>
+#include <qsizegrip.h>
+#include <qstyleoption.h>
+#include <qtoolbar.h>
+#include <qtoolbutton.h>
+#include <qtreeview.h>
+#include <qtableview.h>
+#include <qoperatingsystemversion.h>
+#include <qwizard.h>
+#include <qdebug.h>
+#include <qlibrary.h>
+#include <qdatetimeedit.h>
+#include <qmath.h>
+#include <QtWidgets/qgraphicsproxywidget.h>
+#include <QtWidgets/qgraphicsview.h>
+#include <QtCore/qvariant.h>
+#include <private/qstylehelper_p.h>
+#include <private/qstyleanimation_p.h>
+#include <qpa/qplatformfontdatabase.h>
+#include <qpa/qplatformtheme.h>
+#include <QtGui/private/qcoregraphics_p.h>
+
+QT_USE_NAMESPACE
+
+static QWindow *qt_getWindow(const QWidget *widget)
+{
+ return widget ? widget->window()->windowHandle() : 0;
+}
+
+@interface QT_MANGLE_NAMESPACE(NotificationReceiver) : NSObject {
+QMacStylePrivate *mPrivate;
+}
+- (id)initWithPrivate:(QMacStylePrivate *)priv;
+- (void)scrollBarStyleDidChange:(NSNotification *)notification;
+@end
+
+QT_NAMESPACE_ALIAS_OBJC_CLASS(NotificationReceiver);
+
+@implementation NotificationReceiver
+- (id)initWithPrivate:(QMacStylePrivate *)priv
+{
+ self = [super init];
+ mPrivate = priv;
+ return self;
+}
+
+- (void)scrollBarStyleDidChange:(NSNotification *)notification
+{
+ Q_UNUSED(notification);
+
+ // purge destroyed scroll bars:
+ QMacStylePrivate::scrollBars.removeAll(QPointer<QObject>());
+
+ QEvent event(QEvent::StyleChange);
+ for (const auto &o : QMacStylePrivate::scrollBars)
+ QCoreApplication::sendEvent(o, &event);
+}
+@end
+
+QT_BEGIN_NAMESPACE
+
+// The following constants are used for adjusting the size
+// of push buttons so that they are drawn inside their bounds.
+const int QMacStylePrivate::PushButtonLeftOffset = 6;
+const int QMacStylePrivate::PushButtonTopOffset = 4;
+const int QMacStylePrivate::PushButtonRightOffset = 12;
+const int QMacStylePrivate::PushButtonBottomOffset = 12;
+const int QMacStylePrivate::MiniButtonH = 26;
+const int QMacStylePrivate::SmallButtonH = 30;
+const int QMacStylePrivate::BevelButtonW = 50;
+const int QMacStylePrivate::BevelButtonH = 22;
+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
+
+static QLinearGradient titlebarGradientActive()
+{
+ static QLinearGradient gradient;
+ if (gradient == QLinearGradient()) {
+ 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;
+}
+
+static QLinearGradient titlebarGradientInactive()
+{
+ static QLinearGradient gradient;
+ if (gradient == QLinearGradient()) {
+ gradient.setColorAt(0, QColor(250, 250, 250));
+ gradient.setColorAt(1, QColor(225, 225, 225));
+ }
+ return gradient;
+}
+
+static const QColor titlebarSeparatorLineActive(111, 111, 111);
+static const QColor titlebarSeparatorLineInactive(131, 131, 131);
+
+// Gradient colors used for the dock widget title bar and
+// non-unifed tool bar bacground.
+static const QColor mainWindowGradientBegin(240, 240, 240);
+static const QColor mainWindowGradientEnd(200, 200, 200);
+
+static const int DisclosureOffset = 4;
+
+// Tab bar colors
+// active: window is active
+// selected: tab is selected
+// hovered: tab is hovered
+static const QColor tabBarTabBackgroundActive(190, 190, 190);
+static const QColor tabBarTabBackgroundActiveHovered(178, 178, 178);
+static const QColor tabBarTabBackgroundActiveSelected(211, 211, 211);
+static const QColor tabBarTabBackground(227, 227, 227);
+static const QColor tabBarTabBackgroundSelected(246, 246, 246);
+static const QColor tabBarTabLineActive(160, 160, 160);
+static const QColor tabBarTabLineActiveHovered(150, 150, 150);
+static const QColor tabBarTabLine(210, 210, 210);
+static const QColor tabBarTabLineSelected(189, 189, 189);
+static const QColor tabBarCloseButtonBackgroundHovered(162, 162, 162);
+static const QColor tabBarCloseButtonBackgroundPressed(153, 153, 153);
+static const QColor tabBarCloseButtonBackgroundSelectedHovered(192, 192, 192);
+static const QColor tabBarCloseButtonBackgroundSelectedPressed(181, 181, 181);
+static const QColor tabBarCloseButtonCross(100, 100, 100);
+static const QColor tabBarCloseButtonCrossSelected(115, 115, 115);
+
+static const int closeButtonSize = 14;
+static const qreal closeButtonCornerRadius = 2.0;
+
+// Resolve these at run-time, since the functions was moved in Leopard.
+typedef HIRect * (*PtrHIShapeGetBounds)(HIShapeRef, HIRect *);
+static PtrHIShapeGetBounds ptrHIShapeGetBounds = 0;
+
+#ifndef QT_NO_TABBAR
+static bool isVerticalTabs(const QTabBar::Shape shape) {
+ return (shape == QTabBar::RoundedEast
+ || shape == QTabBar::TriangularEast
+ || shape == QTabBar::RoundedWest
+ || shape == QTabBar::TriangularWest);
+}
+#endif
+
+static bool isInMacUnifiedToolbarArea(QWindow *window, int windowY)
+{
+ QPlatformNativeInterface *nativeInterface = QGuiApplication::platformNativeInterface();
+ QPlatformNativeInterface::NativeResourceForIntegrationFunction function =
+ nativeInterface->nativeResourceFunctionForIntegration("testContentBorderPosition");
+ if (!function)
+ return false; // Not Cocoa platform plugin.
+
+ typedef bool (*TestContentBorderPositionFunction)(QWindow *, int);
+ return (reinterpret_cast<TestContentBorderPositionFunction>(function))(window, windowY);
+}
+
+
+void drawTabCloseButton(QPainter *p, bool hover, bool selected, bool pressed)
+{
+ p->setRenderHints(QPainter::Antialiasing);
+ QRect rect(0, 0, closeButtonSize, closeButtonSize);
+ const int width = rect.width();
+ const int height = rect.height();
+
+ if (hover) {
+ // draw background circle
+ QColor background;
+ if (selected) {
+ background = pressed ? tabBarCloseButtonBackgroundSelectedPressed : tabBarCloseButtonBackgroundSelectedHovered;
+ } else {
+ background = pressed ? tabBarCloseButtonBackgroundPressed : tabBarCloseButtonBackgroundHovered;
+ }
+ p->setPen(Qt::transparent);
+ p->setBrush(background);
+ p->drawRoundedRect(rect, closeButtonCornerRadius, closeButtonCornerRadius);
+ }
+
+ // draw cross
+ const int margin = 3;
+ QPen crossPen;
+ crossPen.setColor(selected ? tabBarCloseButtonCrossSelected : tabBarCloseButtonCross);
+ crossPen.setWidthF(1.1);
+ crossPen.setCapStyle(Qt::FlatCap);
+ p->setPen(crossPen);
+ p->drawLine(margin, margin, width - margin, height - margin);
+ p->drawLine(margin, height - margin, width - margin, margin);
+}
+
+#ifndef QT_NO_TABBAR
+QRect rotateTabPainter(QPainter *p, QTabBar::Shape shape, QRect tabRect)
+{
+ if (isVerticalTabs(shape)) {
+ int newX, newY, newRot;
+ if (shape == QTabBar::RoundedEast
+ || shape == QTabBar::TriangularEast) {
+ newX = tabRect.width();
+ newY = tabRect.y();
+ newRot = 90;
+ } else {
+ newX = 0;
+ newY = tabRect.y() + tabRect.height();
+ newRot = -90;
+ }
+ tabRect.setRect(0, 0, tabRect.height(), tabRect.width());
+ QMatrix m;
+ m.translate(newX, newY);
+ m.rotate(newRot);
+ p->setMatrix(m, true);
+ }
+ return tabRect;
+}
+
+void drawTabShape(QPainter *p, const QStyleOptionTab *tabOpt, bool isUnified, int tabOverlap)
+{
+ QRect rect = tabOpt->rect;
+
+ switch (tabOpt->shape) {
+ case QTabBar::RoundedNorth:
+ case QTabBar::TriangularNorth:
+ case QTabBar::RoundedSouth:
+ case QTabBar::TriangularSouth:
+ rect.adjust(-tabOverlap, 0, 0, 0);
+ break;
+ case QTabBar::RoundedEast:
+ case QTabBar::TriangularEast:
+ case QTabBar::RoundedWest:
+ case QTabBar::TriangularWest:
+ rect.adjust(0, -tabOverlap, 0, 0);
+ break;
+ default:
+ break;
+ }
+
+ p->translate(rect.x(), rect.y());
+ rect.moveLeft(0);
+ rect.moveTop(0);
+ const QRect tabRect = rotateTabPainter(p, tabOpt->shape, rect);
+
+ const int width = tabRect.width();
+ const int height = tabRect.height();
+ const bool active = (tabOpt->state & QStyle::State_Active);
+ const bool selected = (tabOpt->state & QStyle::State_Selected);
+
+ const QRect bodyRect(1, 1, width - 2, height - 2);
+ const QRect topLineRect(1, 0, width - 2, 1);
+ const QRect bottomLineRect(1, height - 1, width - 2, 1);
+ if (selected) {
+ // fill body
+ if (tabOpt->documentMode && isUnified) {
+ p->save();
+ p->setCompositionMode(QPainter::CompositionMode_Source);
+ p->fillRect(tabRect, QColor(Qt::transparent));
+ p->restore();
+ } else if (active) {
+ p->fillRect(bodyRect, tabBarTabBackgroundActiveSelected);
+ // top line
+ p->fillRect(topLineRect, tabBarTabLineSelected);
+ } else {
+ p->fillRect(bodyRect, tabBarTabBackgroundSelected);
+ }
+ } else {
+ // when the mouse is over non selected tabs they get a new color
+ const bool hover = (tabOpt->state & QStyle::State_MouseOver);
+ if (hover) {
+ // fill body
+ p->fillRect(bodyRect, tabBarTabBackgroundActiveHovered);
+ // bottom line
+ p->fillRect(bottomLineRect, tabBarTabLineActiveHovered);
+ }
+ }
+
+ // separator lines between tabs
+ const QRect leftLineRect(0, 1, 1, height - 2);
+ const QRect rightLineRect(width - 1, 1, 1, height - 2);
+ const QColor separatorLineColor = active ? tabBarTabLineActive : tabBarTabLine;
+ p->fillRect(leftLineRect, separatorLineColor);
+ p->fillRect(rightLineRect, separatorLineColor);
+}
+
+void drawTabBase(QPainter *p, const QStyleOptionTabBarBase *tbb, const QWidget *w)
+{
+ QRect r = tbb->rect;
+ if (isVerticalTabs(tbb->shape)) {
+ r.setWidth(w->width());
+ } else {
+ r.setHeight(w->height());
+ }
+ const QRect tabRect = rotateTabPainter(p, tbb->shape, r);
+ const int width = tabRect.width();
+ const int height = tabRect.height();
+ const bool active = (tbb->state & QStyle::State_Active);
+
+ // fill body
+ const QRect bodyRect(0, 1, width, height - 1);
+ const QColor bodyColor = active ? tabBarTabBackgroundActive : tabBarTabBackground;
+ p->fillRect(bodyRect, bodyColor);
+
+ // top line
+ const QRect topLineRect(0, 0, width, 1);
+ const QColor topLineColor = active ? tabBarTabLineActive : tabBarTabLine;
+ p->fillRect(topLineRect, topLineColor);
+
+ // bottom line
+ const QRect bottomLineRect(0, height - 1, width, 1);
+ const QColor bottomLineColor = active ? tabBarTabLineActive : tabBarTabLine;
+ p->fillRect(bottomLineRect, bottomLineColor);
+}
+#endif
+
+static int getControlSize(const QStyleOption *option, const QWidget *widget)
+{
+ switch (QStyleHelper::widgetSizePolicy(widget, option)) {
+ case QStyleHelper::SizeSmall:
+ return QAquaSizeSmall;
+ case QStyleHelper::SizeMini:
+ return QAquaSizeMini;
+ default:
+ break;
+ }
+ return QAquaSizeLarge;
+}
+
+
+#ifndef QT_NO_TREEVIEW
+static inline bool isTreeView(const QWidget *widget)
+{
+ return (widget && widget->parentWidget() &&
+ (qobject_cast<const QTreeView *>(widget->parentWidget())
+ ));
+}
+#endif
+
+#ifndef QT_NO_TABBAR
+static inline ThemeTabDirection getTabDirection(QTabBar::Shape shape)
+{
+ ThemeTabDirection ttd;
+ switch (shape) {
+ case QTabBar::RoundedSouth:
+ case QTabBar::TriangularSouth:
+ ttd = kThemeTabSouth;
+ break;
+ default: // Added to remove the warning, since all values are taken care of, really!
+ case QTabBar::RoundedNorth:
+ case QTabBar::TriangularNorth:
+ ttd = kThemeTabNorth;
+ break;
+ case QTabBar::RoundedWest:
+ case QTabBar::TriangularWest:
+ ttd = kThemeTabWest;
+ break;
+ case QTabBar::RoundedEast:
+ case QTabBar::TriangularEast:
+ ttd = kThemeTabEast;
+ break;
+ }
+ return ttd;
+}
+#endif
+
+static QString qt_mac_removeMnemonics(const QString &original)
+{
+ QString returnText(original.size(), 0);
+ int finalDest = 0;
+ int currPos = 0;
+ int l = original.length();
+ while (l) {
+ if (original.at(currPos) == QLatin1Char('&')
+ && (l == 1 || original.at(currPos + 1) != QLatin1Char('&'))) {
+ ++currPos;
+ --l;
+ if (l == 0)
+ break;
+ } else if (original.at(currPos) == QLatin1Char('(') && l >= 4 &&
+ original.at(currPos + 1) == QLatin1Char('&') &&
+ original.at(currPos + 2) != QLatin1Char('&') &&
+ original.at(currPos + 3) == QLatin1Char(')')) {
+ /* remove mnemonics its format is "\s*(&X)" */
+ int n = 0;
+ while (finalDest > n && returnText.at(finalDest - n - 1).isSpace())
+ ++n;
+ finalDest -= n;
+ currPos += 4;
+ l -= 4;
+ continue;
+ }
+ returnText[finalDest] = original.at(currPos);
+ ++currPos;
+ ++finalDest;
+ --l;
+ }
+ returnText.truncate(finalDest);
+ return returnText;
+}
+
+OSStatus qt_mac_shape2QRegionHelper(int inMessage, HIShapeRef, const CGRect *inRect, void *inRefcon)
+{
+ QRegion *region = static_cast<QRegion *>(inRefcon);
+ if (!region)
+ return paramErr;
+
+ switch (inMessage) {
+ case kHIShapeEnumerateRect:
+ *region += QRect(inRect->origin.x, inRect->origin.y,
+ inRect->size.width, inRect->size.height);
+ break;
+ case kHIShapeEnumerateInit:
+ // Assume the region is already setup correctly
+ case kHIShapeEnumerateTerminate:
+ default:
+ break;
+ }
+ return noErr;
+}
+
+/*!
+ \internal
+ Create's a mutable shape, it's the caller's responsibility to release.
+ WARNING: this function clamps the coordinates to SHRT_MIN/MAX on 10.4 and below.
+*/
+HIMutableShapeRef qt_mac_toHIMutableShape(const QRegion &region)
+{
+ HIMutableShapeRef shape = HIShapeCreateMutable();
+ if (region.rectCount() < 2 ) {
+ QRect qtRect = region.boundingRect();
+ CGRect cgRect = CGRectMake(qtRect.x(), qtRect.y(), qtRect.width(), qtRect.height());
+ HIShapeUnionWithRect(shape, &cgRect);
+ } else {
+ for (const QRect &qtRect : region) {
+ CGRect cgRect = CGRectMake(qtRect.x(), qtRect.y(), qtRect.width(), qtRect.height());
+ HIShapeUnionWithRect(shape, &cgRect);
+ }
+ }
+ return shape;
+}
+
+QRegion qt_mac_fromHIShapeRef(HIShapeRef shape)
+{
+ QRegion returnRegion;
+ //returnRegion.detach();
+ HIShapeEnumerate(shape, kHIShapeParseFromTopLeft, qt_mac_shape2QRegionHelper, &returnRegion);
+ return returnRegion;
+}
+
+bool qt_macWindowIsTextured(const QWidget *window)
+{
+ if (QWindow *w = window->windowHandle())
+ if (w->handle())
+ if (NSWindow *nswindow = static_cast<NSWindow*>(QGuiApplication::platformNativeInterface()->nativeResourceForWindow(QByteArrayLiteral("NSWindow"), w)))
+ return ([nswindow styleMask] & NSTexturedBackgroundWindowMask) ? true : false;
+ return false;
+}
+
+static bool qt_macWindowMainWindow(const QWidget *window)
+{
+ if (QWindow *w = window->windowHandle()) {
+ if (w->handle()) {
+ if (NSWindow *nswindow = static_cast<NSWindow*>(QGuiApplication::platformNativeInterface()->nativeResourceForWindow(QByteArrayLiteral("nswindow"), w))) {
+ return [nswindow isMainWindow];
+ }
+ }
+ }
+ return false;
+}
+
+/*****************************************************************************
+ QMacCGStyle globals
+ *****************************************************************************/
+const int qt_mac_hitheme_version = 0; //the HITheme version we speak
+const int macItemFrame = 2; // menu item frame width
+const int macItemHMargin = 3; // menu item hor text margin
+const int macRightBorder = 12; // right border on mac
+const ThemeWindowType QtWinType = kThemeDocumentWindow; // Window type we use for QTitleBar.
+QPixmap *qt_mac_backgroundPattern = 0; // stores the standard widget background.
+
+/*****************************************************************************
+ QMacCGStyle utility functions
+ *****************************************************************************/
+static inline int qt_mac_hitheme_tab_version()
+{
+ return 1;
+}
+
+static inline HIRect qt_hirectForQRect(const QRect &convertRect, const QRect &rect = QRect())
+{
+ return CGRectMake(convertRect.x() + rect.x(), convertRect.y() + rect.y(),
+ convertRect.width() - rect.width(), convertRect.height() - rect.height());
+}
+
+static inline const QRect qt_qrectForHIRect(const HIRect &hirect)
+{
+ return QRect(QPoint(int(hirect.origin.x), int(hirect.origin.y)),
+ QSize(int(hirect.size.width), int(hirect.size.height)));
+}
+
+inline bool qt_mac_is_metal(const QWidget *w)
+{
+ for (; w; w = w->parentWidget()) {
+ if (w->testAttribute(Qt::WA_MacBrushedMetal))
+ return true;
+ if (w->isWindow() && w->testAttribute(Qt::WA_WState_Created)) { // If not created will fall through to the opaque check and be fine anyway.
+ return qt_macWindowIsTextured(w);
+ }
+#ifndef QT_NO_ACCESSIBILITY
+ if (w->d_func()->isOpaque)
+ break;
+#endif
+ }
+ return false;
+}
+
+static int qt_mac_aqua_get_metric(ThemeMetric met)
+{
+ SInt32 ret;
+ GetThemeMetric(met, &ret);
+ return ret;
+}
+
+static QSize qt_aqua_get_known_size(QStyle::ContentsType ct, const QWidget *widg, QSize szHint,
+ QAquaWidgetSize sz)
+{
+ QSize ret(-1, -1);
+ if (sz != QAquaSizeSmall && sz != QAquaSizeLarge && sz != QAquaSizeMini) {
+ qDebug("Not sure how to return this...");
+ return ret;
+ }
+ if ((widg && widg->testAttribute(Qt::WA_SetFont)) || !QApplication::desktopSettingsAware()) {
+ // If you're using a custom font and it's bigger than the default font,
+ // then no constraints for you. If you are smaller, we can try to help you out
+ QFont font = qt_app_fonts_hash()->value(widg->metaObject()->className(), QFont());
+ if (widg->font().pointSize() > font.pointSize())
+ return ret;
+ }
+
+ if (ct == QStyle::CT_CustomBase && widg) {
+#if QT_CONFIG(pushbutton)
+ if (qobject_cast<const QPushButton *>(widg))
+ ct = QStyle::CT_PushButton;
+#endif
+ else if (qobject_cast<const QRadioButton *>(widg))
+ ct = QStyle::CT_RadioButton;
+#if QT_CONFIG(checkbox)
+ else if (qobject_cast<const QCheckBox *>(widg))
+ ct = QStyle::CT_CheckBox;
+#endif
+#ifndef QT_NO_COMBOBOX
+ else if (qobject_cast<const QComboBox *>(widg))
+ ct = QStyle::CT_ComboBox;
+#endif
+#ifndef QT_NO_TOOLBUTTON
+ else if (qobject_cast<const QToolButton *>(widg))
+ ct = QStyle::CT_ToolButton;
+#endif
+ else if (qobject_cast<const QSlider *>(widg))
+ ct = QStyle::CT_Slider;
+#ifndef QT_NO_PROGRESSBAR
+ else if (qobject_cast<const QProgressBar *>(widg))
+ ct = QStyle::CT_ProgressBar;
+#endif
+#ifndef QT_NO_LINEEDIT
+ else if (qobject_cast<const QLineEdit *>(widg))
+ ct = QStyle::CT_LineEdit;
+#endif
+ else if (qobject_cast<const QHeaderView *>(widg))
+ ct = QStyle::CT_HeaderSection;
+#ifndef QT_NO_MENUBAR
+ else if (qobject_cast<const QMenuBar *>(widg))
+ ct = QStyle::CT_MenuBar;
+#endif
+#ifndef QT_NO_SIZEGRIP
+ else if (qobject_cast<const QSizeGrip *>(widg))
+ ct = QStyle::CT_SizeGrip;
+#endif
+ else
+ return ret;
+ }
+
+ switch (ct) {
+#if QT_CONFIG(pushbutton)
+ case QStyle::CT_PushButton: {
+ const QPushButton *psh = qobject_cast<const QPushButton *>(widg);
+ // If this comparison is false, then the widget was not a push button.
+ // This is bad and there's very little we can do since we were requested to find a
+ // sensible size for a widget that pretends to be a QPushButton but is not.
+ if(psh) {
+ QString buttonText = qt_mac_removeMnemonics(psh->text());
+ if (buttonText.contains(QLatin1Char('\n')))
+ ret = QSize(-1, -1);
+ else if (sz == QAquaSizeLarge)
+ ret = QSize(-1, qt_mac_aqua_get_metric(kThemeMetricPushButtonHeight));
+ else if (sz == QAquaSizeSmall)
+ ret = QSize(-1, qt_mac_aqua_get_metric(kThemeMetricSmallPushButtonHeight));
+ else if (sz == QAquaSizeMini)
+ ret = QSize(-1, qt_mac_aqua_get_metric(kThemeMetricMiniPushButtonHeight));
+
+ if (!psh->icon().isNull()){
+ // If the button got an icon, and the icon is larger than the
+ // button, we can't decide on a default size
+ ret.setWidth(-1);
+ if (ret.height() < psh->iconSize().height())
+ ret.setHeight(-1);
+ }
+ else if (buttonText == QLatin1String("OK") || buttonText == QLatin1String("Cancel")){
+ // Aqua Style guidelines restrict the size of OK and Cancel buttons to 68 pixels.
+ // However, this doesn't work for German, therefore only do it for English,
+ // I suppose it would be better to do some sort of lookups for languages
+ // that like to have really long words.
+ ret.setWidth(77 - 8);
+ }
+ } else {
+ // The only sensible thing to do is to return whatever the style suggests...
+ if (sz == QAquaSizeLarge)
+ ret = QSize(-1, qt_mac_aqua_get_metric(kThemeMetricPushButtonHeight));
+ else if (sz == QAquaSizeSmall)
+ ret = QSize(-1, qt_mac_aqua_get_metric(kThemeMetricSmallPushButtonHeight));
+ else if (sz == QAquaSizeMini)
+ ret = QSize(-1, qt_mac_aqua_get_metric(kThemeMetricMiniPushButtonHeight));
+ else
+ // Since there's no default size we return the large size...
+ ret = QSize(-1, qt_mac_aqua_get_metric(kThemeMetricPushButtonHeight));
+ }
+#endif
+#if 0 //Not sure we are applying the rules correctly for RadioButtons/CheckBoxes --Sam
+ } else if (ct == QStyle::CT_RadioButton) {
+ QRadioButton *rdo = static_cast<QRadioButton *>(widg);
+ // Exception for case where multiline radio button text requires no size constrainment
+ if (rdo->text().find('\n') != -1)
+ return ret;
+ if (sz == QAquaSizeLarge)
+ ret = QSize(-1, qt_mac_aqua_get_metric(kThemeMetricRadioButtonHeight));
+ else if (sz == QAquaSizeSmall)
+ ret = QSize(-1, qt_mac_aqua_get_metric(kThemeMetricSmallRadioButtonHeight));
+ else if (sz == QAquaSizeMini)
+ ret = QSize(-1, qt_mac_aqua_get_metric(kThemeMetricMiniRadioButtonHeight));
+ } else if (ct == QStyle::CT_CheckBox) {
+ if (sz == QAquaSizeLarge)
+ ret = QSize(-1, qt_mac_aqua_get_metric(kThemeMetricCheckBoxHeight));
+ else if (sz == QAquaSizeSmall)
+ ret = QSize(-1, qt_mac_aqua_get_metric(kThemeMetricSmallCheckBoxHeight));
+ else if (sz == QAquaSizeMini)
+ ret = QSize(-1, qt_mac_aqua_get_metric(kThemeMetricMiniCheckBoxHeight));
+#endif
+ break;
+ }
+ case QStyle::CT_SizeGrip:
+ if (sz == QAquaSizeLarge || sz == QAquaSizeSmall) {
+ HIRect r;
+ HIPoint p = { 0, 0 };
+ HIThemeGrowBoxDrawInfo gbi;
+ gbi.version = 0;
+ gbi.state = kThemeStateActive;
+ gbi.kind = kHIThemeGrowBoxKindNormal;
+ gbi.direction = QApplication::isRightToLeft() ? kThemeGrowLeft | kThemeGrowDown
+ : kThemeGrowRight | kThemeGrowDown;
+ gbi.size = sz == QAquaSizeSmall ? kHIThemeGrowBoxSizeSmall : kHIThemeGrowBoxSizeNormal;
+ if (HIThemeGetGrowBoxBounds(&p, &gbi, &r) == noErr) {
+ int width = 0;
+#ifndef QT_NO_MDIAREA
+ if (widg && qobject_cast<QMdiSubWindow *>(widg->parentWidget()))
+ width = r.size.width;
+#endif
+ ret = QSize(width, r.size.height);
+ }
+ }
+ break;
+ case QStyle::CT_ComboBox:
+ switch (sz) {
+ case QAquaSizeLarge:
+ ret = QSize(-1, qt_mac_aqua_get_metric(kThemeMetricPopupButtonHeight));
+ break;
+ case QAquaSizeSmall:
+ ret = QSize(-1, qt_mac_aqua_get_metric(kThemeMetricSmallPopupButtonHeight));
+ break;
+ case QAquaSizeMini:
+ ret = QSize(-1, qt_mac_aqua_get_metric(kThemeMetricMiniPopupButtonHeight));
+ break;
+ default:
+ break;
+ }
+ break;
+ case QStyle::CT_ToolButton:
+ if (sz == QAquaSizeSmall) {
+ int width = 0, height = 0;
+ if (szHint == QSize(-1, -1)) { //just 'guess'..
+#ifndef QT_NO_TOOLBUTTON
+ const QToolButton *bt = qobject_cast<const QToolButton *>(widg);
+ // If this conversion fails then the widget was not what it claimed to be.
+ if(bt) {
+ if (!bt->icon().isNull()) {
+ QSize iconSize = bt->iconSize();
+ QSize pmSize = bt->icon().actualSize(QSize(32, 32), QIcon::Normal);
+ width = qMax(width, qMax(iconSize.width(), pmSize.width()));
+ height = qMax(height, qMax(iconSize.height(), pmSize.height()));
+ }
+ if (!bt->text().isNull() && bt->toolButtonStyle() != Qt::ToolButtonIconOnly) {
+ int text_width = bt->fontMetrics().width(bt->text()),
+ text_height = bt->fontMetrics().height();
+ if (bt->toolButtonStyle() == Qt::ToolButtonTextUnderIcon) {
+ width = qMax(width, text_width);
+ height += text_height;
+ } else {
+ width += text_width;
+ width = qMax(height, text_height);
+ }
+ }
+ } else
+#endif
+ {
+ // Let's return the size hint...
+ width = szHint.width();
+ height = szHint.height();
+ }
+ } else {
+ width = szHint.width();
+ height = szHint.height();
+ }
+ width = qMax(20, width + 5); //border
+ height = qMax(20, height + 5); //border
+ ret = QSize(width, height);
+ }
+ break;
+ case QStyle::CT_Slider: {
+ int w = -1;
+ const QSlider *sld = qobject_cast<const QSlider *>(widg);
+ // If this conversion fails then the widget was not what it claimed to be.
+ if(sld) {
+ if (sz == QAquaSizeLarge) {
+ if (sld->orientation() == Qt::Horizontal) {
+ w = qt_mac_aqua_get_metric(kThemeMetricHSliderHeight);
+ if (sld->tickPosition() != QSlider::NoTicks)
+ w += qt_mac_aqua_get_metric(kThemeMetricHSliderTickHeight);
+ } else {
+ w = qt_mac_aqua_get_metric(kThemeMetricVSliderWidth);
+ if (sld->tickPosition() != QSlider::NoTicks)
+ w += qt_mac_aqua_get_metric(kThemeMetricVSliderTickWidth);
+ }
+ } else if (sz == QAquaSizeSmall) {
+ if (sld->orientation() == Qt::Horizontal) {
+ w = qt_mac_aqua_get_metric(kThemeMetricSmallHSliderHeight);
+ if (sld->tickPosition() != QSlider::NoTicks)
+ w += qt_mac_aqua_get_metric(kThemeMetricSmallHSliderTickHeight);
+ } else {
+ w = qt_mac_aqua_get_metric(kThemeMetricSmallVSliderWidth);
+ if (sld->tickPosition() != QSlider::NoTicks)
+ w += qt_mac_aqua_get_metric(kThemeMetricSmallVSliderTickWidth);
+ }
+ } else if (sz == QAquaSizeMini) {
+ if (sld->orientation() == Qt::Horizontal) {
+ w = qt_mac_aqua_get_metric(kThemeMetricMiniHSliderHeight);
+ if (sld->tickPosition() != QSlider::NoTicks)
+ w += qt_mac_aqua_get_metric(kThemeMetricMiniHSliderTickHeight);
+ } else {
+ w = qt_mac_aqua_get_metric(kThemeMetricMiniVSliderWidth);
+ if (sld->tickPosition() != QSlider::NoTicks)
+ w += qt_mac_aqua_get_metric(kThemeMetricMiniVSliderTickWidth);
+ }
+ }
+ } else {
+ // This is tricky, we were requested to find a size for a slider which is not
+ // a slider. We don't know if this is vertical or horizontal or if we need to
+ // have tick marks or not.
+ // For this case we will return an horizontal slider without tick marks.
+ w = qt_mac_aqua_get_metric(kThemeMetricHSliderHeight);
+ w += qt_mac_aqua_get_metric(kThemeMetricHSliderTickHeight);
+ }
+ if (sld->orientation() == Qt::Horizontal)
+ ret.setHeight(w);
+ else
+ ret.setWidth(w);
+ break;
+ }
+#ifndef QT_NO_PROGRESSBAR
+ case QStyle::CT_ProgressBar: {
+ int finalValue = -1;
+ Qt::Orientation orient = Qt::Horizontal;
+ if (const QProgressBar *pb = qobject_cast<const QProgressBar *>(widg))
+ orient = pb->orientation();
+
+ if (sz == QAquaSizeLarge)
+ finalValue = qt_mac_aqua_get_metric(kThemeMetricLargeProgressBarThickness)
+ + qt_mac_aqua_get_metric(kThemeMetricProgressBarShadowOutset);
+ else
+ finalValue = qt_mac_aqua_get_metric(kThemeMetricNormalProgressBarThickness)
+ + qt_mac_aqua_get_metric(kThemeMetricSmallProgressBarShadowOutset);
+ if (orient == Qt::Horizontal)
+ ret.setHeight(finalValue);
+ else
+ ret.setWidth(finalValue);
+ break;
+ }
+#endif
+#ifndef QT_NO_COMBOBOX
+ case QStyle::CT_LineEdit:
+ if (!widg || !qobject_cast<QComboBox *>(widg->parentWidget())) {
+ //should I take into account the font dimentions of the lineedit? -Sam
+ if (sz == QAquaSizeLarge)
+ ret = QSize(-1, 21);
+ else
+ ret = QSize(-1, 19);
+ }
+ break;
+#endif
+ case QStyle::CT_HeaderSection:
+#ifndef QT_NO_TREEVIEW
+ if (isTreeView(widg))
+ ret = QSize(-1, qt_mac_aqua_get_metric(kThemeMetricListHeaderHeight));
+#endif
+ break;
+ case QStyle::CT_MenuBar:
+ if (sz == QAquaSizeLarge) {
+ ret = QSize(-1, [[NSApp mainMenu] menuBarHeight]);
+ // In the qt_mac_set_native_menubar(false) case,
+ // we come it here with a zero-height main menu,
+ // preventing the in-window menu from displaying.
+ // Use 22 pixels for the height, by observation.
+ if (ret.height() <= 0)
+ ret.setHeight(22);
+ }
+ break;
+ default:
+ break;
+ }
+ return ret;
+}
+
+
+#if defined(QMAC_QAQUASTYLE_SIZE_CONSTRAIN) || defined(DEBUG_SIZE_CONSTRAINT)
+static QAquaWidgetSize qt_aqua_guess_size(const QWidget *widg, QSize large, QSize small, QSize mini)
+{
+ Q_UNUSED(widg);
+
+ if (large == QSize(-1, -1)) {
+ if (small != QSize(-1, -1))
+ return QAquaSizeSmall;
+ if (mini != QSize(-1, -1))
+ return QAquaSizeMini;
+ return QAquaSizeUnknown;
+ } else if (small == QSize(-1, -1)) {
+ if (mini != QSize(-1, -1))
+ return QAquaSizeMini;
+ return QAquaSizeLarge;
+ } else if (mini == QSize(-1, -1)) {
+ return QAquaSizeLarge;
+ }
+
+#ifndef QT_NO_MAINWINDOW
+ if (qEnvironmentVariableIsSet("QWIDGET_ALL_SMALL")) {
+ //if (small.width() != -1 || small.height() != -1)
+ return QAquaSizeSmall;
+ } else if (qEnvironmentVariableIsSet("QWIDGET_ALL_MINI")) {
+ return QAquaSizeMini;
+ }
+#endif
+
+#if 0
+ /* Figure out which size we're closer to, I just hacked this in, I haven't
+ tested it as it would probably look pretty strange to have some widgets
+ big and some widgets small in the same window?? -Sam */
+ int large_delta=0;
+ if (large.width() != -1) {
+ int delta = large.width() - widg->width();
+ large_delta += delta * delta;
+ }
+ if (large.height() != -1) {
+ int delta = large.height() - widg->height();
+ large_delta += delta * delta;
+ }
+ int small_delta=0;
+ if (small.width() != -1) {
+ int delta = small.width() - widg->width();
+ small_delta += delta * delta;
+ }
+ if (small.height() != -1) {
+ int delta = small.height() - widg->height();
+ small_delta += delta * delta;
+ }
+ int mini_delta=0;
+ if (mini.width() != -1) {
+ int delta = mini.width() - widg->width();
+ mini_delta += delta * delta;
+ }
+ if (mini.height() != -1) {
+ int delta = mini.height() - widg->height();
+ mini_delta += delta * delta;
+ }
+ if (mini_delta < small_delta && mini_delta < large_delta)
+ return QAquaSizeMini;
+ else if (small_delta < large_delta)
+ return QAquaSizeSmall;
+#endif
+ return QAquaSizeLarge;
+}
+#endif
+
+void QMacStylePrivate::drawFocusRing(QPainter *p, const QRect &targetRect, int hMargin, int vMargin, qreal radius) const
+{
+ const qreal pixelRatio = p->device()->devicePixelRatioF();
+ static const QString keyFormat = QLatin1String("$qt_focusring%1-%2-%3-%4");
+ const QString &key = keyFormat.arg(hMargin).arg(vMargin).arg(radius).arg(pixelRatio);
+ QPixmap focusRingPixmap;
+ const qreal size = radius * 2 + 5;
+
+ if (!QPixmapCache::find(key, focusRingPixmap)) {
+ focusRingPixmap = QPixmap((QSize(size, size) + 2 * QSize(hMargin, vMargin)) * pixelRatio);
+ focusRingPixmap.fill(Qt::transparent);
+ focusRingPixmap.setDevicePixelRatio(pixelRatio);
+ {
+ const CGFloat focusRingWidth = radius > 0 ? 3.5 : 6;
+ QMacAutoReleasePool pool;
+ QMacCGContext ctx(&focusRingPixmap);
+ CGContextBeginTransparencyLayer(ctx, NULL);
+ CGContextSetAlpha(ctx, 0.5); // As applied to the stroke color below
+
+ [NSGraphicsContext saveGraphicsState];
+ [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithCGContext:ctx
+ flipped:NO]];
+ CGRect focusRingRect = CGRectMake(hMargin, vMargin, size, size);
+ NSBezierPath *focusRingPath;
+ if (radius > 0) {
+ const CGFloat roundedRectInset = -1.5;
+ focusRingPath = [NSBezierPath bezierPathWithRoundedRect:CGRectInset(focusRingRect, roundedRectInset, roundedRectInset)
+ xRadius:radius
+ yRadius:radius];
+ } else {
+ const CGFloat outerClipInset = -focusRingWidth / 2;
+ NSBezierPath *focusRingClipPath = [NSBezierPath bezierPathWithRect:CGRectInset(focusRingRect, outerClipInset, outerClipInset)];
+ const CGFloat innerClipInset = 1;
+ NSBezierPath *focusRingInnerClipPath = [NSBezierPath bezierPathWithRect:CGRectInset(focusRingRect, innerClipInset, innerClipInset)];
+ [focusRingClipPath appendBezierPath:focusRingInnerClipPath.bezierPathByReversingPath];
+ [focusRingClipPath setClip];
+ focusRingPath = [NSBezierPath bezierPathWithRect:focusRingRect];
+ focusRingPath.lineJoinStyle = NSRoundLineJoinStyle;
+ }
+
+ focusRingPath.lineWidth = focusRingWidth;
+ [[NSColor keyboardFocusIndicatorColor] setStroke];
+ [focusRingPath stroke];
+
+ CGContextEndTransparencyLayer(ctx);
+ [NSGraphicsContext restoreGraphicsState];
+ }
+ QPixmapCache::insert(key, focusRingPixmap);
+ }
+
+ // Add 2 for the actual ring tickness going inwards
+ const qreal hCornerSize = 2 + hMargin + radius;
+ const qreal vCornerSize = 2 + vMargin + radius;
+ const qreal shCornerSize = hCornerSize * pixelRatio;
+ const qreal svCornerSize = vCornerSize * pixelRatio;
+ // top-left corner
+ p->drawPixmap(QPointF(targetRect.left(), targetRect.top()), focusRingPixmap,
+ QRectF(0, 0, shCornerSize, svCornerSize));
+ // top-right corner
+ p->drawPixmap(QPointF(targetRect.right() - hCornerSize + 1, targetRect.top()), focusRingPixmap,
+ QRectF(focusRingPixmap.width() - shCornerSize, 0, shCornerSize, svCornerSize));
+ // bottom-left corner
+ p->drawPixmap(QPointF(targetRect.left(), targetRect.bottom() - vCornerSize + 1), focusRingPixmap,
+ QRectF(0, focusRingPixmap.height() - svCornerSize, shCornerSize, svCornerSize));
+ // bottom-right corner
+ p->drawPixmap(QPointF(targetRect.right() - hCornerSize + 1, targetRect.bottom() - vCornerSize + 1), focusRingPixmap,
+ QRect(focusRingPixmap.width() - shCornerSize, focusRingPixmap.height() - svCornerSize, shCornerSize, svCornerSize));
+ // top edge
+ p->drawPixmap(QRectF(targetRect.left() + hCornerSize, targetRect.top(), targetRect.width() - 2 * hCornerSize, vCornerSize), focusRingPixmap,
+ QRect(shCornerSize, 0, focusRingPixmap.width() - 2 * shCornerSize, svCornerSize));
+ // bottom edge
+ p->drawPixmap(QRectF(targetRect.left() + hCornerSize, targetRect.bottom() - vCornerSize + 1, targetRect.width() - 2 * hCornerSize, vCornerSize), focusRingPixmap,
+ QRect(shCornerSize, focusRingPixmap.height() - svCornerSize, focusRingPixmap.width() - 2 * shCornerSize, svCornerSize));
+ // left edge
+ p->drawPixmap(QRectF(targetRect.left(), targetRect.top() + vCornerSize, hCornerSize, targetRect.height() - 2 * vCornerSize), focusRingPixmap,
+ QRect(0, svCornerSize, shCornerSize, focusRingPixmap.width() - 2 * svCornerSize));
+ // right edge
+ p->drawPixmap(QRectF(targetRect.right() - hCornerSize + 1, targetRect.top() + vCornerSize, hCornerSize, targetRect.height() - 2 * vCornerSize), focusRingPixmap,
+ QRect(focusRingPixmap.width() - shCornerSize, svCornerSize, shCornerSize, focusRingPixmap.width() - 2 * svCornerSize));
+}
+
+#ifndef QT_NO_TABBAR
+void QMacStylePrivate::tabLayout(const QStyleOptionTab *opt, const QWidget *widget, QRect *textRect) const
+{
+ Q_ASSERT(textRect);
+ QRect tr = opt->rect;
+ const bool verticalTabs = opt->shape == QTabBar::RoundedEast
+ || opt->shape == QTabBar::RoundedWest
+ || opt->shape == QTabBar::TriangularEast
+ || opt->shape == QTabBar::TriangularWest;
+ if (verticalTabs)
+ tr.setRect(0, 0, tr.height(), tr.width()); // 0, 0 as we will have a translate transform
+
+ int verticalShift = proxyStyle->pixelMetric(QStyle::PM_TabBarTabShiftVertical, opt, widget);
+ int horizontalShift = proxyStyle->pixelMetric(QStyle::PM_TabBarTabShiftHorizontal, opt, widget);
+ const int hpadding = 4;
+ const int vpadding = proxyStyle->pixelMetric(QStyle::PM_TabBarTabVSpace, opt, widget) / 2;
+ if (opt->shape == QTabBar::RoundedSouth || opt->shape == QTabBar::TriangularSouth)
+ verticalShift = -verticalShift;
+ tr.adjust(hpadding, verticalShift - vpadding, horizontalShift - hpadding, vpadding);
+
+ // left widget
+ if (!opt->leftButtonSize.isEmpty()) {
+ const int buttonSize = verticalTabs ? opt->leftButtonSize.height() : opt->leftButtonSize.width();
+ tr.setLeft(tr.left() + 4 + buttonSize);
+ // make text aligned to center
+ if (opt->rightButtonSize.isEmpty())
+ tr.setRight(tr.right() - 4 - buttonSize);
+ }
+ // right widget
+ if (!opt->rightButtonSize.isEmpty()) {
+ const int buttonSize = verticalTabs ? opt->rightButtonSize.height() : opt->rightButtonSize.width();
+ tr.setRight(tr.right() - 4 - buttonSize);
+ // make text aligned to center
+ if (opt->leftButtonSize.isEmpty())
+ tr.setLeft(tr.left() + 4 + buttonSize);
+ }
+
+ if (!verticalTabs)
+ tr = proxyStyle->visualRect(opt->direction, opt->rect, tr);
+
+ *textRect = tr;
+}
+#endif //QT_NO_TABBAR
+
+QAquaWidgetSize QMacStylePrivate::effectiveAquaSizeConstrain(const QStyleOption *option,
+ const QWidget *widg,
+ QStyle::ContentsType ct,
+ QSize szHint, QSize *insz) const
+{
+ QAquaWidgetSize sz = aquaSizeConstrain(option, widg, ct, szHint, insz);
+ if (sz == QAquaSizeUnknown)
+ return QAquaSizeLarge;
+ return sz;
+}
+
+QAquaWidgetSize QMacStylePrivate::aquaSizeConstrain(const QStyleOption *option, const QWidget *widg,
+ QStyle::ContentsType ct, QSize szHint, QSize *insz) const
+{
+#if defined(QMAC_QAQUASTYLE_SIZE_CONSTRAIN) || defined(DEBUG_SIZE_CONSTRAINT)
+ if (option) {
+ if (option->state & QStyle::State_Small)
+ return QAquaSizeSmall;
+ if (option->state & QStyle::State_Mini)
+ return QAquaSizeMini;
+ }
+
+ if (!widg) {
+ if (insz)
+ *insz = QSize();
+ if (qEnvironmentVariableIsSet("QWIDGET_ALL_SMALL"))
+ return QAquaSizeSmall;
+ if (qEnvironmentVariableIsSet("QWIDGET_ALL_MINI"))
+ return QAquaSizeMini;
+ return QAquaSizeUnknown;
+ }
+
+ QSize large = qt_aqua_get_known_size(ct, widg, szHint, QAquaSizeLarge),
+ small = qt_aqua_get_known_size(ct, widg, szHint, QAquaSizeSmall),
+ mini = qt_aqua_get_known_size(ct, widg, szHint, QAquaSizeMini);
+ bool guess_size = false;
+ QAquaWidgetSize ret = QAquaSizeUnknown;
+ QStyleHelper::WidgetSizePolicy wsp = QStyleHelper::widgetSizePolicy(widg);
+ if (wsp == QStyleHelper::SizeDefault)
+ guess_size = true;
+ else if (wsp == QStyleHelper::SizeMini)
+ ret = QAquaSizeMini;
+ else if (wsp == QStyleHelper::SizeSmall)
+ ret = QAquaSizeSmall;
+ else if (wsp == QStyleHelper::SizeLarge)
+ ret = QAquaSizeLarge;
+ if (guess_size)
+ ret = qt_aqua_guess_size(widg, large, small, mini);
+
+ QSize *sz = 0;
+ if (ret == QAquaSizeSmall)
+ sz = &small;
+ else if (ret == QAquaSizeLarge)
+ sz = &large;
+ else if (ret == QAquaSizeMini)
+ sz = &mini;
+ if (insz)
+ *insz = sz ? *sz : QSize(-1, -1);
+#ifdef DEBUG_SIZE_CONSTRAINT
+ if (sz) {
+ const char *size_desc = "Unknown";
+ if (sz == &small)
+ size_desc = "Small";
+ else if (sz == &large)
+ size_desc = "Large";
+ else if (sz == &mini)
+ size_desc = "Mini";
+ qDebug("%s - %s: %s taken (%d, %d) [%d, %d]",
+ widg ? widg->objectName().toLatin1().constData() : "*Unknown*",
+ widg ? widg->metaObject()->className() : "*Unknown*", size_desc, widg->width(), widg->height(),
+ sz->width(), sz->height());
+ }
+#endif
+ return ret;
+#else
+ if (insz)
+ *insz = QSize();
+ Q_UNUSED(widg);
+ Q_UNUSED(ct);
+ Q_UNUSED(szHint);
+ return QAquaSizeUnknown;
+#endif
+}
+
+/**
+ Returns the free space awailable for contents inside the
+ button (and not the size of the contents itself)
+*/
+HIRect QMacStylePrivate::pushButtonContentBounds(const QStyleOptionButton *btn,
+ const HIThemeButtonDrawInfo *bdi) const
+{
+ HIRect outerBounds = qt_hirectForQRect(btn->rect);
+ // Adjust the bounds to correct for
+ // carbon not calculating the content bounds fully correct
+ if (bdi->kind == kThemePushButton || bdi->kind == kThemePushButtonSmall){
+ outerBounds.origin.y += QMacStylePrivate::PushButtonTopOffset;
+ outerBounds.size.height -= QMacStylePrivate::PushButtonBottomOffset;
+ } else if (bdi->kind == kThemePushButtonMini) {
+ outerBounds.origin.y += QMacStylePrivate::PushButtonTopOffset;
+ }
+
+ HIRect contentBounds;
+ HIThemeGetButtonContentBounds(&outerBounds, bdi, &contentBounds);
+ return contentBounds;
+}
+
+/**
+ Calculates the size of the button contents.
+ This includes both the text and the icon.
+*/
+QSize QMacStylePrivate::pushButtonSizeFromContents(const QStyleOptionButton *btn) const
+{
+ Q_Q(const QMacStyle);
+ QSize csz(0, 0);
+ QSize iconSize = btn->icon.isNull() ? QSize(0, 0)
+ : (btn->iconSize + QSize(QMacStylePrivate::PushButtonContentPadding, 0));
+ QRect textRect = btn->text.isEmpty() ? QRect(0, 0, 1, 1)
+ : btn->fontMetrics.boundingRect(QRect(), Qt::AlignCenter, btn->text);
+ csz.setWidth(iconSize.width() + textRect.width()
+ + ((btn->features & QStyleOptionButton::HasMenu)
+ ? q->proxy()->pixelMetric(QStyle::PM_MenuButtonIndicator, btn, 0) : 0));
+ csz.setHeight(qMax(iconSize.height(), textRect.height()));
+ return csz;
+}
+
+/**
+ Checks if the actual contents of btn fits inside the free content bounds of
+ 'buttonKindToCheck'. Meant as a helper function for 'initHIThemePushButton'
+ for determining which button kind to use for drawing.
+*/
+bool QMacStylePrivate::contentFitsInPushButton(const QStyleOptionButton *btn,
+ HIThemeButtonDrawInfo *bdi,
+ ThemeButtonKind buttonKindToCheck) const
+{
+ ThemeButtonKind tmp = bdi->kind;
+ bdi->kind = buttonKindToCheck;
+ QSize contentSize = pushButtonSizeFromContents(btn);
+ QRect freeContentRect = qt_qrectForHIRect(pushButtonContentBounds(btn, bdi));
+ bdi->kind = tmp;
+ return freeContentRect.contains(QRect(freeContentRect.x(), freeContentRect.y(),
+ contentSize.width(), contentSize.height()));
+}
+
+/**
+ Creates a HIThemeButtonDrawInfo structure that specifies the correct button
+ kind and other details to use for drawing the given push button. Which
+ button kind depends on the size of the button, the size of the contents,
+ explicit user style settings, etc.
+*/
+void QMacStylePrivate::initHIThemePushButton(const QStyleOptionButton *btn,
+ const QWidget *widget,
+ const ThemeDrawState tds,
+ HIThemeButtonDrawInfo *bdi) const
+{
+ ThemeDrawState tdsModified = tds;
+ if (btn->state & QStyle::State_On)
+ tdsModified = kThemeStatePressed;
+ bdi->version = qt_mac_hitheme_version;
+ bdi->state = tdsModified;
+ bdi->value = kThemeButtonOff;
+
+ if (tds == kThemeStateInactive)
+ bdi->state = kThemeStateActive;
+ if (btn->state & QStyle::State_HasFocus)
+ bdi->adornment = kThemeAdornmentFocus;
+ else
+ bdi->adornment = kThemeAdornmentNone;
+
+
+ if (btn->features & (QStyleOptionButton::Flat)) {
+ bdi->kind = kThemeBevelButton;
+ } else {
+ switch (aquaSizeConstrain(btn, widget)) {
+ case QAquaSizeSmall:
+ bdi->kind = kThemePushButtonSmall;
+ break;
+ case QAquaSizeMini:
+ bdi->kind = kThemePushButtonMini;
+ break;
+ case QAquaSizeLarge:
+ // ... We should honor if the user is explicit about using the
+ // large button. But right now Qt will specify the large button
+ // as default rather than QAquaSizeUnknown.
+ // So we treat it like QAquaSizeUnknown
+ // to get the dynamic choosing of button kind.
+ case QAquaSizeUnknown:
+ // Choose the button kind that closest match the button rect, but at the
+ // same time displays the button contents without clipping.
+ bdi->kind = kThemeBevelButton;
+ if (btn->rect.width() >= QMacStylePrivate::BevelButtonW && btn->rect.height() >= QMacStylePrivate::BevelButtonH){
+ if (widget && widget->testAttribute(Qt::WA_MacVariableSize)) {
+ if (btn->rect.height() <= QMacStylePrivate::MiniButtonH){
+ if (contentFitsInPushButton(btn, bdi, kThemePushButtonMini))
+ bdi->kind = kThemePushButtonMini;
+ } else if (btn->rect.height() <= QMacStylePrivate::SmallButtonH){
+ if (contentFitsInPushButton(btn, bdi, kThemePushButtonSmall))
+ bdi->kind = kThemePushButtonSmall;
+ } else if (contentFitsInPushButton(btn, bdi, kThemePushButton)) {
+ bdi->kind = kThemePushButton;
+ }
+ } else {
+ bdi->kind = kThemePushButton;
+ }
+ }
+ }
+ }
+}
+
+#if QT_CONFIG(pushbutton)
+bool qt_mac_buttonIsRenderedFlat(const QPushButton *pushButton, const QStyleOptionButton *option)
+{
+ QMacStyle *macStyle = qobject_cast<QMacStyle *>(pushButton->style());
+ if (!macStyle)
+ return false;
+ HIThemeButtonDrawInfo bdi;
+ macStyle->d_func()->initHIThemePushButton(option, pushButton, kThemeStateActive, &bdi);
+ return bdi.kind == kThemeBevelButton;
+}
+#endif
+
+/**
+ Creates a HIThemeButtonDrawInfo structure that specifies the correct button
+ kind and other details to use for drawing the given combobox. Which button
+ kind depends on the size of the combo, wether or not it is editable,
+ explicit user style settings, etc.
+*/
+void QMacStylePrivate::initComboboxBdi(const QStyleOptionComboBox *combo, HIThemeButtonDrawInfo *bdi,
+ const QWidget *widget, const ThemeDrawState &tds) const
+{
+ bdi->version = qt_mac_hitheme_version;
+ bdi->adornment = kThemeAdornmentArrowLeftArrow;
+ bdi->value = kThemeButtonOff;
+ if (combo->state & QStyle::State_HasFocus)
+ bdi->adornment = kThemeAdornmentFocus;
+ if (combo->activeSubControls & QStyle::SC_ComboBoxArrow)
+ bdi->state = kThemeStatePressed;
+ else
+ bdi->state = tds;
+
+ QAquaWidgetSize aSize = aquaSizeConstrain(combo, widget);
+ switch (aSize) {
+ case QAquaSizeMini:
+ bdi->kind = combo->editable ? ThemeButtonKind(kThemeComboBoxMini)
+ : ThemeButtonKind(kThemePopupButtonMini);
+ break;
+ case QAquaSizeSmall:
+ bdi->kind = combo->editable ? ThemeButtonKind(kThemeComboBoxSmall)
+ : ThemeButtonKind(kThemePopupButtonSmall);
+ break;
+ case QAquaSizeUnknown:
+ case QAquaSizeLarge:
+ // Unless the user explicitly specified large buttons, determine the
+ // kind by looking at the combox size.
+ // ... specifying small and mini-buttons it not a current feature of
+ // Qt (e.g. QWidget::getAttribute(WA_ButtonSize)). But when it is, add
+ // an extra check here before using the mini and small buttons.
+ int h = combo->rect.size().height();
+ if (combo->editable){
+#ifndef QT_NO_DATETIMEEDIT
+ if (qobject_cast<const QDateTimeEdit *>(widget)) {
+ // Except when, you know, we get a QDateTimeEdit with calendarPopup
+ // enabled. And then things get weird, basically because it's a
+ // transvestite spinbox with editable combobox tendencies. Meaning
+ // that it wants to look a combobox, except that it isn't one, so it
+ // doesn't get all those extra free margins around. (Don't know whose
+ // idea those margins were, but now it looks like we're stuck with
+ // them forever). So anyway, the height threshold should be smaller
+ // in this case, or the style gets confused when it needs to render
+ // or return any subcontrol size of the poor thing.
+ if (h < 9)
+ bdi->kind = kThemeComboBoxMini;
+ else if (h < 22)
+ bdi->kind = kThemeComboBoxSmall;
+ else
+ bdi->kind = kThemeComboBox;
+ } else
+#endif
+ {
+ if (h < 21)
+ bdi->kind = kThemeComboBoxMini;
+ else if (h < 26)
+ bdi->kind = kThemeComboBoxSmall;
+ else
+ bdi->kind = kThemeComboBox;
+ }
+ } else {
+ // Even if we specify that we want the kThemePopupButton, Carbon
+ // will use the kThemePopupButtonSmall if the size matches. So we
+ // do the same size check explicit to have the size of the inner
+ // text field be correct. Therefore, do this even if the user specifies
+ // the use of LargeButtons explicit.
+ if (h < 21)
+ bdi->kind = kThemePopupButtonMini;
+ else if (h < 26)
+ bdi->kind = kThemePopupButtonSmall;
+ else
+ bdi->kind = kThemePopupButton;
+ }
+ break;
+ }
+}
+
+/**
+ Carbon draws comboboxes (and other views) outside the rect given as argument. Use this function to obtain
+ the corresponding inner rect for drawing the same combobox so that it stays inside the given outerBounds.
+*/
+HIRect QMacStylePrivate::comboboxInnerBounds(const HIRect &outerBounds, int buttonKind)
+{
+ HIRect innerBounds = outerBounds;
+ // Carbon draw parts of the view outside the rect.
+ // So make the rect a bit smaller to compensate
+ // (I wish HIThemeGetButtonBackgroundBounds worked)
+ switch (buttonKind){
+ case kThemePopupButton:
+ innerBounds.origin.x += 2;
+ innerBounds.origin.y += 2;
+ innerBounds.size.width -= 5;
+ innerBounds.size.height -= 6;
+ break;
+ case kThemePopupButtonSmall:
+ innerBounds.origin.x += 3;
+ innerBounds.origin.y += 3;
+ innerBounds.size.width -= 6;
+ innerBounds.size.height -= 7;
+ break;
+ case kThemePopupButtonMini:
+ innerBounds.origin.x += 2;
+ innerBounds.origin.y += 2;
+ innerBounds.size.width -= 5;
+ innerBounds.size.height -= 6;
+ break;
+ case kThemeComboBox:
+ innerBounds.origin.x += 3;
+ innerBounds.origin.y += 2;
+ innerBounds.size.width -= 6;
+ innerBounds.size.height -= 8;
+ break;
+ case kThemeComboBoxSmall:
+ innerBounds.origin.x += 3;
+ innerBounds.origin.y += 3;
+ innerBounds.size.width -= 7;
+ innerBounds.size.height -= 8;
+ break;
+ case kThemeComboBoxMini:
+ innerBounds.origin.x += 3;
+ innerBounds.origin.y += 3;
+ innerBounds.size.width -= 4;
+ innerBounds.size.height -= 8;
+ break;
+ default:
+ break;
+ }
+ return innerBounds;
+}
+
+/**
+ Inside a combobox Qt places a line edit widget. The size of this widget should depend on the kind
+ of combobox we choose to draw. This function calculates and returns this size.
+*/
+QRect QMacStylePrivate::comboboxEditBounds(const QRect &outerBounds, const HIThemeButtonDrawInfo &bdi)
+{
+ QRect ret = outerBounds;
+ switch (bdi.kind){
+ case kThemeComboBox:
+ ret.adjust(5, 5, -22, -5);
+ break;
+ case kThemeComboBoxSmall:
+ ret.adjust(4, 5, -18, 0);
+ ret.setHeight(16);
+ break;
+ case kThemeComboBoxMini:
+ ret.adjust(4, 5, -16, 0);
+ ret.setHeight(13);
+ break;
+ case kThemePopupButton:
+ ret.adjust(10, 2, -23, -4);
+ break;
+ case kThemePopupButtonSmall:
+ ret.adjust(9, 3, -20, -3);
+ break;
+ case kThemePopupButtonMini:
+ ret.adjust(8, 3, -19, 0);
+ ret.setHeight(13);
+ break;
+ }
+ return ret;
+}
+
+/**
+ Carbon comboboxes don't scale (sight). If the size of the combo suggest a scaled version,
+ create it manually by drawing a small Carbon combo onto a pixmap (use pixmap cache), chop
+ it up, and copy it back onto the widget. Othervise, draw the combobox supplied by Carbon directly.
+*/
+void QMacStylePrivate::drawCombobox(const HIRect &outerBounds, const HIThemeButtonDrawInfo &bdi, QPainter *p)
+{
+ if (!(bdi.kind == kThemeComboBox && outerBounds.size.height > 28)){
+ // We have an unscaled combobox, or popup-button; use Carbon directly.
+ HIRect innerBounds = QMacStylePrivate::comboboxInnerBounds(outerBounds, bdi.kind);
+ HIThemeDrawButton(&innerBounds, &bdi, QMacCGContext(p), kHIThemeOrientationNormal, 0);
+ } else {
+ QPixmap buffer;
+ QString key = QString(QLatin1String("$qt_cbox%1-%2")).arg(int(bdi.state)).arg(int(bdi.adornment));
+ if (!QPixmapCache::find(key, buffer)) {
+ HIRect innerBoundsSmallCombo = {{3, 3}, {29, 25}};
+ buffer = QPixmap(35, 28);
+ buffer.fill(Qt::transparent);
+ QPainter buffPainter(&buffer);
+ HIThemeDrawButton(&innerBoundsSmallCombo, &bdi, QMacCGContext(&buffPainter), kHIThemeOrientationNormal, 0);
+ buffPainter.end();
+ QPixmapCache::insert(key, buffer);
+ }
+
+ const int bwidth = 20;
+ const int fwidth = 10;
+ const int fheight = 10;
+ int w = qRound(outerBounds.size.width);
+ int h = qRound(outerBounds.size.height);
+ int bstart = w - bwidth;
+ int blower = fheight + 1;
+ int flower = h - fheight;
+ int sheight = flower - fheight;
+ int center = qRound(outerBounds.size.height + outerBounds.origin.y) / 2;
+
+ // Draw upper and lower gap
+ p->drawPixmap(fwidth, 0, bstart - fwidth, fheight, buffer, fwidth, 0, 1, fheight);
+ p->drawPixmap(fwidth, flower, bstart - fwidth, fheight, buffer, fwidth, buffer.height() - fheight, 1, fheight);
+ // Draw left and right gap. Right gap is drawn top and bottom separatly
+ p->drawPixmap(0, fheight, fwidth, sheight, buffer, 0, fheight, fwidth, 1);
+ p->drawPixmap(bstart, fheight, bwidth, center - fheight, buffer, buffer.width() - bwidth, fheight - 1, bwidth, 1);
+ p->drawPixmap(bstart, center, bwidth, sheight / 2, buffer, buffer.width() - bwidth, fheight + 6, bwidth, 1);
+ // Draw arrow
+ p->drawPixmap(bstart, center - 4, bwidth - 3, 6, buffer, buffer.width() - bwidth, fheight, bwidth - 3, 6);
+ // Draw corners
+ p->drawPixmap(0, 0, fwidth, fheight, buffer, 0, 0, fwidth, fheight);
+ p->drawPixmap(bstart, 0, bwidth, fheight, buffer, buffer.width() - bwidth, 0, bwidth, fheight);
+ p->drawPixmap(0, flower, fwidth, fheight, buffer, 0, buffer.height() - fheight, fwidth, fheight);
+ p->drawPixmap(bstart, h - blower, bwidth, blower, buffer, buffer.width() - bwidth, buffer.height() - blower, bwidth, blower);
+ }
+}
+
+/**
+ Carbon tableheaders don't scale (sight). So create it manually by drawing a small Carbon header
+ onto a pixmap (use pixmap cache), chop it up, and copy it back onto the widget.
+*/
+void QMacStylePrivate::drawTableHeader(const HIRect &outerBounds,
+ bool drawTopBorder, bool drawLeftBorder, const HIThemeButtonDrawInfo &bdi, QPainter *p)
+{
+ static SInt32 headerHeight = 0;
+ static OSStatus err = GetThemeMetric(kThemeMetricListHeaderHeight, &headerHeight);
+ Q_UNUSED(err);
+
+ QPixmap buffer;
+ QString key = QString(QLatin1String("$qt_tableh%1-%2-%3")).arg(int(bdi.state)).arg(int(bdi.adornment)).arg(int(bdi.value));
+ if (!QPixmapCache::find(key, buffer)) {
+ HIRect headerNormalRect = {{0., 0.}, {16., CGFloat(headerHeight)}};
+ buffer = QPixmap(headerNormalRect.size.width, headerNormalRect.size.height);
+ buffer.fill(Qt::transparent);
+ QPainter buffPainter(&buffer);
+ HIThemeDrawButton(&headerNormalRect, &bdi, QMacCGContext(&buffPainter), kHIThemeOrientationNormal, 0);
+ buffPainter.end();
+ QPixmapCache::insert(key, buffer);
+ }
+ const int buttonw = qRound(outerBounds.size.width);
+ const int buttonh = qRound(outerBounds.size.height);
+ const int framew = 1;
+ const int frameh_n = 4;
+ const int frameh_s = 3;
+ const int transh = buffer.height() - frameh_n - frameh_s;
+ int center = buttonh - frameh_s - int(transh / 2.0f) + 1; // Align bottom;
+
+ int skipTopBorder = 0;
+ if (!drawTopBorder)
+ skipTopBorder = 1;
+
+ p->translate(outerBounds.origin.x, outerBounds.origin.y);
+
+ p->drawPixmap(QRect(QRect(0, -skipTopBorder, buttonw - framew , frameh_n)), buffer, QRect(framew, 0, 1, frameh_n));
+ p->drawPixmap(QRect(0, buttonh - frameh_s, buttonw - framew, frameh_s), buffer, QRect(framew, buffer.height() - frameh_s, 1, frameh_s));
+ // Draw upper and lower center blocks
+ p->drawPixmap(QRect(0, frameh_n - skipTopBorder, buttonw - framew, center - frameh_n + skipTopBorder), buffer, QRect(framew, frameh_n, 1, 1));
+ p->drawPixmap(QRect(0, center, buttonw - framew, buttonh - center - frameh_s), buffer, QRect(framew, buffer.height() - frameh_s, 1, 1));
+ // Draw right center block borders
+ p->drawPixmap(QRect(buttonw - framew, frameh_n - skipTopBorder, framew, center - frameh_n), buffer, QRect(buffer.width() - framew, frameh_n, framew, 1));
+ p->drawPixmap(QRect(buttonw - framew, center, framew, buttonh - center - 1), buffer, QRect(buffer.width() - framew, buffer.height() - frameh_s, framew, 1));
+ // Draw right corners
+ p->drawPixmap(QRect(buttonw - framew, -skipTopBorder, framew, frameh_n), buffer, QRect(buffer.width() - framew, 0, framew, frameh_n));
+ p->drawPixmap(QRect(buttonw - framew, buttonh - frameh_s, framew, frameh_s), buffer, QRect(buffer.width() - framew, buffer.height() - frameh_s, framew, frameh_s));
+ // Draw center transition block
+ p->drawPixmap(QRect(0, center - qRound(transh / 2.0f), buttonw - framew, buffer.height() - frameh_n - frameh_s), buffer, QRect(framew, frameh_n + 1, 1, transh));
+ // Draw right center transition block border
+ p->drawPixmap(QRect(buttonw - framew, center - qRound(transh / 2.0f), framew, buffer.height() - frameh_n - frameh_s), buffer, QRect(buffer.width() - framew, frameh_n + 1, framew, transh));
+ if (drawLeftBorder){
+ // Draw left center block borders
+ p->drawPixmap(QRect(0, frameh_n - skipTopBorder, framew, center - frameh_n + skipTopBorder), buffer, QRect(0, frameh_n, framew, 1));
+ p->drawPixmap(QRect(0, center, framew, buttonh - center - 1), buffer, QRect(0, buffer.height() - frameh_s, framew, 1));
+ // Draw left corners
+ p->drawPixmap(QRect(0, -skipTopBorder, framew, frameh_n), buffer, QRect(0, 0, framew, frameh_n));
+ p->drawPixmap(QRect(0, buttonh - frameh_s, framew, frameh_s), buffer, QRect(0, buffer.height() - frameh_s, framew, frameh_s));
+ // Draw left center transition block border
+ p->drawPixmap(QRect(0, center - qRound(transh / 2.0f), framew, buffer.height() - frameh_n - frameh_s), buffer, QRect(0, frameh_n + 1, framew, transh));
+ }
+
+ p->translate(-outerBounds.origin.x, -outerBounds.origin.y);
+}
+
+/*
+ Returns cutoff sizes for scroll bars.
+ thumbIndicatorCutoff is the smallest size where the thumb indicator is drawn.
+ scrollButtonsCutoff is the smallest size where the up/down buttons is drawn.
+*/
+enum ScrollBarCutoffType { thumbIndicatorCutoff = 0, scrollButtonsCutoff = 1 };
+static int scrollButtonsCutoffSize(ScrollBarCutoffType cutoffType, QStyleHelper::WidgetSizePolicy widgetSize)
+{
+ // Mini scroll bars do not exist as of version 10.4.
+ if (widgetSize == QStyleHelper::SizeMini)
+ return 0;
+
+ const int sizeIndex = (widgetSize == QStyleHelper::SizeSmall) ? 1 : 0;
+ static const int sizeTable[2][2] = { { 61, 56 }, { 49, 44 } };
+ return sizeTable[sizeIndex][cutoffType];
+}
+
+void QMacStylePrivate::getSliderInfo(QStyle::ComplexControl cc, const QStyleOptionSlider *slider,
+ HIThemeTrackDrawInfo *tdi, const QWidget *needToRemoveMe) const
+{
+ memset(tdi, 0, sizeof(HIThemeTrackDrawInfo)); // We don't get it all for some reason or another...
+ tdi->version = qt_mac_hitheme_version;
+ tdi->reserved = 0;
+ tdi->filler1 = 0;
+ bool isScrollbar = (cc == QStyle::CC_ScrollBar);
+ switch (aquaSizeConstrain(slider, needToRemoveMe)) {
+ case QAquaSizeUnknown:
+ case QAquaSizeLarge:
+ if (isScrollbar)
+ tdi->kind = kThemeMediumScrollBar;
+ else
+ tdi->kind = kThemeMediumSlider;
+ break;
+ case QAquaSizeMini:
+ if (isScrollbar)
+ tdi->kind = kThemeSmallScrollBar; // should be kThemeMiniScrollBar, but not implemented
+ else
+ tdi->kind = kThemeMiniSlider;
+ break;
+ case QAquaSizeSmall:
+ if (isScrollbar)
+ tdi->kind = kThemeSmallScrollBar;
+ else
+ tdi->kind = kThemeSmallSlider;
+ break;
+ }
+
+ bool usePlainKnob = slider->tickPosition == QSlider::NoTicks
+ || slider->tickPosition == QSlider::TicksBothSides;
+
+ tdi->bounds = qt_hirectForQRect(slider->rect);
+ if (isScrollbar) {
+ tdi->min = slider->minimum;
+ tdi->max = slider->maximum;
+ tdi->value = slider->sliderPosition;
+ } else {
+ // Fix min and max positions. HITheme seems confused when it comes to rendering
+ // a slider at those positions. We give it a hand by extending and offsetting
+ // the slider range accordingly. See also comment for CC_Slider in drawComplexControl()
+ tdi->min = 0;
+ if (slider->orientation == Qt::Horizontal)
+ tdi->max = 10 * slider->rect.width();
+ else
+ tdi->max = 10 * slider->rect.height();
+
+ int range = slider->maximum - slider->minimum;
+ if (range == 0) {
+ tdi->value = 0;
+ } else if (usePlainKnob || slider->orientation == Qt::Horizontal) {
+ int endsCorrection = usePlainKnob ? 25 : 10;
+ tdi->value = (tdi->max + 2 * endsCorrection) * (slider->sliderPosition - slider->minimum) / range - endsCorrection;
+ } else {
+ tdi->value = (tdi->max + 30) * (slider->sliderPosition - slider->minimum) / range - 20;
+ }
+ }
+ tdi->attributes = kThemeTrackShowThumb;
+ if (slider->upsideDown)
+ tdi->attributes |= kThemeTrackRightToLeft;
+ if (slider->orientation == Qt::Horizontal) {
+ tdi->attributes |= kThemeTrackHorizontal;
+ if (isScrollbar && slider->direction == Qt::RightToLeft) {
+ if (!slider->upsideDown)
+ tdi->attributes |= kThemeTrackRightToLeft;
+ else
+ tdi->attributes &= ~kThemeTrackRightToLeft;
+ }
+ }
+
+ // Tiger broke reverse scroll bars so put them back and "fake it"
+ if (isScrollbar && (tdi->attributes & kThemeTrackRightToLeft)) {
+ tdi->attributes &= ~kThemeTrackRightToLeft;
+ tdi->value = tdi->max - tdi->value;
+ }
+
+ tdi->enableState = (slider->state & QStyle::State_Enabled) ? kThemeTrackActive
+ : kThemeTrackDisabled;
+ if (!isScrollbar) {
+ if (slider->state & QStyle::QStyle::State_HasFocus)
+ tdi->attributes |= kThemeTrackHasFocus;
+ if (usePlainKnob)
+ tdi->trackInfo.slider.thumbDir = kThemeThumbPlain;
+ else if (slider->tickPosition == QSlider::TicksAbove)
+ tdi->trackInfo.slider.thumbDir = kThemeThumbUpward;
+ else
+ tdi->trackInfo.slider.thumbDir = kThemeThumbDownward;
+ } else {
+ tdi->trackInfo.scrollbar.viewsize = slider->pageStep;
+ }
+}
+
+void QMacStylePrivate::setAutoDefaultButton(QObject *button) const
+{
+ if (autoDefaultButton != button) {
+ if (QStyleAnimation *anim = animation(autoDefaultButton)) {
+ anim->updateTarget();
+ stopAnimation(autoDefaultButton);
+ }
+ autoDefaultButton = button;
+ }
+ if (autoDefaultButton && !animation(autoDefaultButton))
+ startAnimation(new QStyleAnimation(autoDefaultButton));
+}
+
+QMacStylePrivate::QMacStylePrivate()
+ : mouseDown(false), backingStoreNSView(nil)
+{
+ defaultButtonStart = CFAbsoluteTimeGetCurrent();
+ memset(&buttonState, 0, sizeof(ButtonState));
+
+ if (ptrHIShapeGetBounds == 0) {
+ QLibrary library(QLatin1String("/System/Library/Frameworks/Carbon.framework/Carbon"));
+ library.setLoadHints(QLibrary::ExportExternalSymbolsHint);
+ ptrHIShapeGetBounds = reinterpret_cast<PtrHIShapeGetBounds>(library.resolve("HIShapeGetBounds"));
+ }
+
+}
+
+QMacStylePrivate::~QMacStylePrivate()
+{
+ QMacAutoReleasePool pool;
+ Q_FOREACH (NSView *b, cocoaControls)
+ [b release];
+}
+
+ThemeDrawState QMacStylePrivate::getDrawState(QStyle::State flags)
+{
+ ThemeDrawState tds = kThemeStateActive;
+ if (flags & QStyle::State_Sunken) {
+ tds = kThemeStatePressed;
+ } else if (flags & QStyle::State_Active) {
+ if (!(flags & QStyle::State_Enabled))
+ tds = kThemeStateUnavailable;
+ } else {
+ if (flags & QStyle::State_Enabled)
+ tds = kThemeStateInactive;
+ else
+ tds = kThemeStateUnavailableInactive;
+ }
+ return tds;
+}
+
+static QCocoaWidget cocoaWidgetFromHIThemeButtonKind(ThemeButtonKind kind)
+{
+ QCocoaWidget w;
+
+ switch (kind) {
+ case kThemePopupButton:
+ case kThemePopupButtonSmall:
+ case kThemePopupButtonMini:
+ w.first = QCocoaPopupButton;
+ break;
+ case kThemeComboBox:
+ w.first = QCocoaComboBox;
+ break;
+ case kThemeArrowButton:
+ w.first = QCocoaArrowButton;
+ break;
+ case kThemeCheckBox:
+ case kThemeCheckBoxSmall:
+ case kThemeCheckBoxMini:
+ w.first = QCocoaCheckBox;
+ break;
+ case kThemeRadioButton:
+ case kThemeRadioButtonSmall:
+ case kThemeRadioButtonMini:
+ w.first = QCocoaRadioButton;
+ break;
+ case kThemePushButton:
+ case kThemePushButtonSmall:
+ case kThemePushButtonMini:
+ w.first = QCocoaPushButton;
+ break;
+ default:
+ break;
+ }
+
+ switch (kind) {
+ case kThemePushButtonSmall:
+ case kThemePopupButtonSmall:
+ case kThemeCheckBoxSmall:
+ case kThemeRadioButtonSmall:
+ w.second = QAquaSizeSmall;
+ break;
+ case kThemePushButtonMini:
+ case kThemePopupButtonMini:
+ case kThemeCheckBoxMini:
+ case kThemeRadioButtonMini:
+ w.second = QAquaSizeMini;
+ break;
+ default:
+ w.second = QAquaSizeLarge;
+ break;
+ }
+
+ return w;
+}
+
+NSView *QMacStylePrivate::cocoaControl(QCocoaWidget widget) const
+{
+ NSView *bv = cocoaControls[widget];
+ if (!bv) {
+
+ if (widget.first == QCocoaPopupButton
+ || widget.first == QCocoaPullDownButton)
+ bv = [[NSPopUpButton alloc] init];
+ else if (widget.first == QCocoaComboBox)
+ bv = [[NSComboBox alloc] init];
+ else if (widget.first == QCocoaHorizontalSlider)
+ bv = [[NSSlider alloc] init];
+ else if (widget.first == QCocoaVerticalSlider)
+ // Cocoa sets the orientation from the view's frame
+ // at construction time, and it cannot be changed later.
+ bv = [[NSSlider alloc] initWithFrame:NSMakeRect(0, 0, 10, 100)];
+ else
+ bv = [[NSButton alloc] init];
+
+ switch (widget.first) {
+ case QCocoaArrowButton: {
+ NSButton *bc = (NSButton *)bv;
+ bc.buttonType = NSOnOffButton;
+ bc.bezelStyle = NSDisclosureBezelStyle;
+ break;
+ }
+ case QCocoaCheckBox: {
+ NSButton *bc = (NSButton *)bv;
+ bc.buttonType = NSSwitchButton;
+ break;
+ }
+ case QCocoaRadioButton: {
+ NSButton *bc = (NSButton *)bv;
+ bc.buttonType = NSRadioButton;
+ break;
+ }
+ case QCocoaPushButton: {
+ NSButton *bc = (NSButton *)bv;
+ bc.buttonType = NSMomentaryLightButton;
+ bc.bezelStyle = NSRoundedBezelStyle;
+ break;
+ }
+ case QCocoaPullDownButton: {
+ NSPopUpButton *bc = (NSPopUpButton *)bv;
+ bc.pullsDown = YES;
+ break;
+ }
+ default:
+ break;
+ }
+
+ if ([bv isKindOfClass:[NSButton class]]) {
+ NSButton *bc = (NSButton *)bv;
+ bc.title = @"";
+ }
+
+ if ([bv isKindOfClass:[NSControl class]]) {
+ NSCell *bcell = [(NSControl *)bv cell];
+ switch (widget.second) {
+ case QAquaSizeSmall:
+ bcell.controlSize = NSSmallControlSize;
+ break;
+ case QAquaSizeMini:
+ bcell.controlSize = NSMiniControlSize;
+ break;
+ default:
+ break;
+ }
+ }
+
+ const_cast<QMacStylePrivate *>(this)->cocoaControls.insert(widget, bv);
+ }
+
+ return bv;
+}
+
+void QMacStylePrivate::drawNSViewInRect(QCocoaWidget widget, NSView *view, const QRect &qtRect, QPainter *p, bool isQWidget, QCocoaDrawRectBlock drawRectBlock) const
+{
+ QPoint offset;
+ if (widget == QCocoaWidget(QCocoaRadioButton, QAquaSizeLarge))
+ offset.setY(2);
+ else if (widget == QCocoaWidget(QCocoaRadioButton, QAquaSizeSmall))
+ offset = QPoint(-1, 2);
+ else if (widget == QCocoaWidget(QCocoaRadioButton, QAquaSizeMini))
+ offset.setY(2);
+ else if (widget == QCocoaWidget(QCocoaPopupButton, QAquaSizeSmall)
+ || widget == QCocoaWidget(QCocoaCheckBox, QAquaSizeLarge))
+ offset.setY(1);
+ else if (widget == QCocoaWidget(QCocoaCheckBox, QAquaSizeSmall))
+ offset.setX(-1);
+ else if (widget == QCocoaWidget(QCocoaCheckBox, QAquaSizeMini))
+ offset = QPoint(7, 5);
+ else if (widget == QCocoaWidget(QCocoaPopupButton, QAquaSizeMini))
+ offset = QPoint(2, -1);
+ else if (widget == QCocoaWidget(QCocoaPullDownButton, QAquaSizeLarge))
+ offset = isQWidget ? QPoint(3, -1) : QPoint(-1, -3);
+ else if (widget == QCocoaWidget(QCocoaPullDownButton, QAquaSizeSmall))
+ offset = QPoint(2, 1);
+ else if (widget == QCocoaWidget(QCocoaPullDownButton, QAquaSizeMini))
+ offset = QPoint(5, 0);
+ else if (widget == QCocoaWidget(QCocoaComboBox, QAquaSizeLarge))
+ offset = QPoint(3, 0);
+
+ QMacCGContext ctx(p);
+ CGContextSaveGState(ctx);
+ CGContextTranslateCTM(ctx, offset.x(), offset.y());
+
+ [NSGraphicsContext saveGraphicsState];
+ [NSGraphicsContext setCurrentContext:[NSGraphicsContext
+ graphicsContextWithGraphicsPort:ctx flipped:YES]];
+
+ NSRect rect = NSMakeRect(qtRect.x() + 1, qtRect.y(), qtRect.width(), qtRect.height());
+
+ [backingStoreNSView addSubview:view];
+ view.frame = rect;
+ if (drawRectBlock)
+ drawRectBlock(rect, (CGContextRef)ctx);
+ else
+ [view drawRect:rect];
+ [view removeFromSuperviewWithoutNeedingDisplay];
+
+ [NSGraphicsContext restoreGraphicsState];
+ CGContextRestoreGState(ctx);
+}
+
+void QMacStylePrivate::resolveCurrentNSView(QWindow *window)
+{
+ backingStoreNSView = window ? (NSView *)window->winId() : nil;
+}
+
+void QMacStylePrivate::drawColorlessButton(const HIRect &macRect, HIThemeButtonDrawInfo *bdi,
+ QPainter *p, const QStyleOption *opt) const
+{
+ int xoff = 0,
+ yoff = 0,
+ extraWidth = 0,
+ extraHeight = 0,
+ finalyoff = 0;
+
+ const bool combo = opt->type == QStyleOption::SO_ComboBox;
+ const bool editableCombo = bdi->kind == kThemeComboBox
+ || bdi->kind == kThemeComboBoxSmall
+ || bdi->kind == kThemeComboBoxMini;
+ const bool button = opt->type == QStyleOption::SO_Button;
+ const bool viewItem = opt->type == QStyleOption::SO_ViewItem;
+ const bool pressed = bdi->state == kThemeStatePressed;
+
+ if (button && pressed) {
+ if (bdi->kind == kThemePushButton) {
+ extraHeight = 2;
+ } else if (bdi->kind == kThemePushButtonSmall) {
+ xoff = 1;
+ extraWidth = 2;
+ extraHeight = 5;
+ }
+ }
+
+ int devicePixelRatio = p->device()->devicePixelRatioF();
+ int width = devicePixelRatio * (int(macRect.size.width) + extraWidth);
+ int height = devicePixelRatio * (int(macRect.size.height) + extraHeight);
+
+ if (width <= 0 || height <= 0)
+ return; // nothing to draw
+
+ QString key = QLatin1String("$qt_mac_style_ctb_") + QString::number(bdi->kind) + QLatin1Char('_')
+ + QString::number(bdi->value) + QLatin1Char('_')
+ + (button ? QString::number(bdi->state) + QLatin1Char('_') : QString())
+ + QLatin1Char('_') + QString::number(width) + QLatin1Char('_') + QString::number(height);
+ QPixmap pm;
+ if (!QPixmapCache::find(key, pm)) {
+ QPixmap activePixmap(width, height);
+ activePixmap.setDevicePixelRatio(devicePixelRatio);
+ activePixmap.fill(Qt::transparent);
+ {
+ if (combo){
+ // Carbon combos don't scale. Therefore we draw it
+ // ourselves, if a scaled version is needed.
+ QPainter tmpPainter(&activePixmap);
+ QMacStylePrivate::drawCombobox(macRect, *bdi, &tmpPainter);
+ } else {
+ QMacCGContext cg(&activePixmap);
+ HIRect newRect = CGRectMake(xoff, yoff, macRect.size.width, macRect.size.height);
+ if (button && pressed)
+ bdi->state = kThemeStateActive;
+ else if (viewItem)
+ bdi->state = kThemeStateInactive;
+ HIThemeDrawButton(&newRect, bdi, cg, kHIThemeOrientationNormal, 0);
+ }
+ }
+
+ if (!combo && !button && bdi->value == kThemeButtonOff) {
+ pm = activePixmap;
+ } else if ((combo && !editableCombo) || button) {
+ QCocoaWidget cw = cocoaWidgetFromHIThemeButtonKind(bdi->kind);
+ NSButton *bc = (NSButton *)cocoaControl(cw);
+ [bc highlight:pressed];
+ bc.enabled = bdi->state != kThemeStateUnavailable && bdi->state != kThemeStateUnavailableInactive;
+ bc.allowsMixedState = YES;
+ bc.state = bdi->value == kThemeButtonOn ? NSOnState :
+ bdi->value == kThemeButtonMixed ? NSMixedState : NSOffState;
+ // The view frame may differ from what we pass to HITheme
+ QRect rect = opt->rect;
+ if (bdi->kind == kThemePopupButtonMini)
+ rect.adjust(0, 0, -5, 0);
+ drawNSViewInRect(cw, bc, rect, p);
+ return;
+ } else if (editableCombo || viewItem) {
+ QImage image = activePixmap.toImage();
+
+ for (int y = 0; y < height; ++y) {
+ QRgb *scanLine = reinterpret_cast<QRgb *>(image.scanLine(y));
+
+ for (int x = 0; x < width; ++x) {
+ QRgb &pixel = scanLine[x];
+ int gray = qRed(pixel); // We know the image is grayscale
+ int alpha = qAlpha(pixel);
+
+ if (gray == 128 && alpha == 128) {
+ pixel = qRgba(255, 255, 255, 255);
+ } else if (alpha == 0) {
+ pixel = 0;
+ } else {
+ bool belowThreshold = (alpha * gray) / 255 + 255 - alpha < 128;
+ gray = belowThreshold ? 0 : 2 * gray - 255;
+ alpha = belowThreshold ? 0 : 2 * alpha - 255;
+ pixel = qRgba(gray, gray, gray, alpha);
+ }
+ }
+ }
+ pm = QPixmap::fromImage(image);
+ } else {
+ QImage activeImage = activePixmap.toImage();
+ QImage colorlessImage;
+ {
+ QPixmap colorlessPixmap(width, height);
+ colorlessPixmap.setDevicePixelRatio(devicePixelRatio);
+ colorlessPixmap.fill(Qt::transparent);
+
+ QMacCGContext cg(&colorlessPixmap);
+ HIRect newRect = CGRectMake(xoff, yoff, macRect.size.width, macRect.size.height);
+ int oldValue = bdi->value;
+ bdi->value = kThemeButtonOff;
+ HIThemeDrawButton(&newRect, bdi, cg, kHIThemeOrientationNormal, 0);
+ bdi->value = oldValue;
+ colorlessImage = colorlessPixmap.toImage();
+ }
+
+ for (int y = 0; y < height; ++y) {
+ QRgb *colorlessScanLine = reinterpret_cast<QRgb *>(colorlessImage.scanLine(y));
+ const QRgb *activeScanLine = reinterpret_cast<const QRgb *>(activeImage.scanLine(y));
+
+ for (int x = 0; x < width; ++x) {
+ QRgb &colorlessPixel = colorlessScanLine[x];
+ QRgb activePixel = activeScanLine[x];
+
+ if (activePixel != colorlessPixel) {
+ int max = qMax(qMax(qRed(activePixel), qGreen(activePixel)),
+ qBlue(activePixel));
+ QRgb newPixel = qRgba(max, max, max, qAlpha(activePixel));
+ if (qGray(newPixel) < qGray(colorlessPixel)
+ || qAlpha(newPixel) > qAlpha(colorlessPixel))
+ colorlessPixel = newPixel;
+ }
+ }
+ }
+ pm = QPixmap::fromImage(colorlessImage);
+ }
+ QPixmapCache::insert(key, pm);
+ }
+ p->drawPixmap(int(macRect.origin.x) - xoff, int(macRect.origin.y) + finalyoff, width / devicePixelRatio, height / devicePixelRatio , pm);
+}
+
+QMacStyle::QMacStyle()
+ : QCommonStyle(*new QMacStylePrivate)
+{
+ Q_D(QMacStyle);
+ QMacAutoReleasePool pool;
+
+ d->receiver = [[NotificationReceiver alloc] initWithPrivate:d];
+ NotificationReceiver *receiver = static_cast<NotificationReceiver *>(d->receiver);
+
+ [[NSNotificationCenter defaultCenter] addObserver:receiver
+ selector:@selector(scrollBarStyleDidChange:)
+ name:NSPreferredScrollerStyleDidChangeNotification
+ object:nil];
+
+ d->nsscroller = [[NSScroller alloc] init];
+ d->indicatorBranchButtonCell = nil;
+}
+
+QMacStyle::~QMacStyle()
+{
+ Q_D(QMacStyle);
+ QMacAutoReleasePool pool;
+
+ [reinterpret_cast<NSScroller*>(d->nsscroller) release];
+
+ NotificationReceiver *receiver = static_cast<NotificationReceiver *>(d->receiver);
+ [[NSNotificationCenter defaultCenter] removeObserver:receiver];
+ [receiver release];
+
+ delete qt_mac_backgroundPattern;
+ qt_mac_backgroundPattern = 0;
+}
+
+/*! \internal
+ Generates the standard widget background pattern.
+*/
+QPixmap QMacStylePrivate::generateBackgroundPattern() const
+{
+ QMacAutoReleasePool pool;
+ QPixmap px(4, 4);
+ QMacCGContext cg(&px);
+ HIThemeSetFill(kThemeBrushDialogBackgroundActive, 0, cg, kHIThemeOrientationNormal);
+ const CGRect cgRect = CGRectMake(0, 0, px.width(), px.height());
+ CGContextFillRect(cg, cgRect);
+ return px;
+}
+
+/*! \internal
+ Fills the given \a rect with the pattern stored in \a brush. As an optimization,
+ HIThemeSetFill us used directly if we are filling with the standard background.
+*/
+void qt_mac_fill_background(QPainter *painter, const QRegion &rgn, const QBrush &brush)
+{
+#if 0
+ QPoint dummy;
+ const QPaintDevice *target = painter->device();
+ const QPaintDevice *redirected = QPainter::redirected(target, &dummy);
+ //const bool usePainter = redirected && redirected != target;
+
+ if (!usePainter && qt_mac_backgroundPattern
+ && qt_mac_backgroundPattern->cacheKey() == brush.texture().cacheKey()) {
+
+ painter->setClipRegion(rgn);
+
+ QMacCGContext cg(target);
+ CGContextSaveGState(cg);
+ HIThemeSetFill(kThemeBrushDialogBackgroundActive, 0, cg, kHIThemeOrientationInverted);
+
+ for (const QRect &rect : rgn) {
+ // Anchor the pattern to the top so it stays put when the window is resized.
+ CGContextSetPatternPhase(cg, CGSizeMake(rect.width(), rect.height()));
+ CGRect mac_rect = CGRectMake(rect.x(), rect.y(), rect.width(), rect.height());
+ CGContextFillRect(cg, mac_rect);
+ }
+
+ CGContextRestoreGState(cg);
+ } else {
+#endif
+ const QRect rect(rgn.boundingRect());
+ painter->setClipRegion(rgn);
+ painter->drawTiledPixmap(rect, brush.texture(), rect.topLeft());
+// }
+}
+
+void QMacStyle::polish(QPalette &pal)
+{
+ Q_D(QMacStyle);
+ if (!qt_mac_backgroundPattern) {
+ if (!qApp)
+ return;
+ qt_mac_backgroundPattern = new QPixmap(d->generateBackgroundPattern());
+ }
+
+
+ QCFString theme;
+ const OSErr err = CopyThemeIdentifier(&theme);
+ if (err == noErr && CFStringCompare(theme, kThemeAppearanceAquaGraphite, 0) == kCFCompareEqualTo) {
+ pal.setBrush(QPalette::All, QPalette::AlternateBase, QColor(240, 240, 240));
+ } else {
+ pal.setBrush(QPalette::All, QPalette::AlternateBase, QColor(237, 243, 254));
+ }
+}
+
+void QMacStyle::polish(QApplication *)
+{
+}
+
+void QMacStyle::unpolish(QApplication *)
+{
+}
+
+void QMacStyle::polish(QWidget* w)
+{
+ if (qt_mac_is_metal(w) && !w->testAttribute(Qt::WA_SetPalette)) {
+ // Set a clear brush so that the metal shines through.
+ QPalette pal = w->palette();
+ QBrush background(Qt::transparent);
+ pal.setBrush(QPalette::All, QPalette::Window, background);
+ pal.setBrush(QPalette::All, QPalette::Button, background);
+ w->setPalette(pal);
+ w->setAttribute(Qt::WA_SetPalette, false);
+ }
+
+#ifndef QT_NO_MENU
+ if (qobject_cast<QMenu*>(w)
+#ifndef QT_NO_COMBOBOX
+ || qobject_cast<QComboBoxPrivateContainer *>(w)
+#endif
+ ) {
+ w->setWindowOpacity(0.985);
+ if (!w->testAttribute(Qt::WA_SetPalette)) {
+ QPixmap px(64, 64);
+ px.fill(Qt::white);
+ HIThemeMenuDrawInfo mtinfo;
+ mtinfo.version = qt_mac_hitheme_version;
+ mtinfo.menuType = kThemeMenuTypePopUp;
+ // HIRect rect = CGRectMake(0, 0, px.width(), px.height());
+ // ###
+ //HIThemeDrawMenuBackground(&rect, &mtinfo, QMacCGContext(&px)),
+ // kHIThemeOrientationNormal);
+ QPalette pal = w->palette();
+ QBrush background(px);
+ pal.setBrush(QPalette::All, QPalette::Window, background);
+ pal.setBrush(QPalette::All, QPalette::Button, background);
+ w->setPalette(pal);
+ w->setAttribute(Qt::WA_SetPalette, false);
+ }
+ }
+#endif
+
+#ifndef QT_NO_TABBAR
+ if (QTabBar *tb = qobject_cast<QTabBar*>(w)) {
+ if (tb->documentMode()) {
+ w->setAttribute(Qt::WA_Hover);
+ w->setFont(qt_app_fonts_hash()->value("QSmallFont", QFont()));
+ QPalette p = w->palette();
+ p.setColor(QPalette::WindowText, QColor(17, 17, 17));
+ w->setPalette(p);
+ }
+ }
+#endif
+
+ QCommonStyle::polish(w);
+
+ if (QRubberBand *rubber = qobject_cast<QRubberBand*>(w)) {
+ rubber->setWindowOpacity(0.25);
+ rubber->setAttribute(Qt::WA_PaintOnScreen, false);
+ rubber->setAttribute(Qt::WA_NoSystemBackground, false);
+ }
+
+ if (qobject_cast<QScrollBar*>(w)) {
+ w->setAttribute(Qt::WA_OpaquePaintEvent, false);
+ w->setAttribute(Qt::WA_Hover, true);
+ w->setMouseTracking(true);
+ }
+}
+
+void QMacStyle::unpolish(QWidget* w)
+{
+ if ((
+#ifndef QT_NO_MENU
+ qobject_cast<QMenu*>(w) ||
+#endif
+ qt_mac_is_metal(w)
+ ) && !w->testAttribute(Qt::WA_SetPalette)) {
+ QPalette pal = qApp->palette(w);
+ w->setPalette(pal);
+ w->setAttribute(Qt::WA_SetPalette, false);
+ w->setWindowOpacity(1.0);
+ }
+
+#ifndef QT_NO_COMBOBOX
+ if (QComboBox *combo = qobject_cast<QComboBox *>(w)) {
+ if (!combo->isEditable()) {
+ if (QWidget *widget = combo->findChild<QComboBoxPrivateContainer *>())
+ widget->setWindowOpacity(1.0);
+ }
+ }
+#endif
+
+ if (QRubberBand *rubber = qobject_cast<QRubberBand*>(w)) {
+ rubber->setWindowOpacity(1.0);
+ rubber->setAttribute(Qt::WA_PaintOnScreen, true);
+ rubber->setAttribute(Qt::WA_NoSystemBackground, true);
+ }
+
+ if (QFocusFrame *frame = qobject_cast<QFocusFrame *>(w))
+ frame->setAttribute(Qt::WA_NoSystemBackground, true);
+
+ QCommonStyle::unpolish(w);
+
+ if (qobject_cast<QScrollBar*>(w)) {
+ w->setAttribute(Qt::WA_OpaquePaintEvent, true);
+ w->setAttribute(Qt::WA_Hover, false);
+ w->setMouseTracking(false);
+ }
+}
+
+int QMacStyle::pixelMetric(PixelMetric metric, const QStyleOption *opt, const QWidget *widget) const
+{
+ Q_D(const QMacStyle);
+ int controlSize = getControlSize(opt, widget);
+ SInt32 ret = 0;
+
+ switch (metric) {
+ case PM_TabCloseIndicatorWidth:
+ case PM_TabCloseIndicatorHeight:
+ ret = closeButtonSize;
+ break;
+ case PM_ToolBarIconSize:
+ ret = proxy()->pixelMetric(PM_LargeIconSize);
+ break;
+ case PM_FocusFrameVMargin:
+ case PM_FocusFrameHMargin:
+ GetThemeMetric(kThemeMetricFocusRectOutset, &ret);
+ 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;
+ break;
+
+ case PM_MenuBarVMargin:
+ ret = 0;
+ break;
+
+ case PM_MenuBarPanelWidth:
+ ret = 0;
+ break;
+
+ case QStyle::PM_MenuDesktopFrameWidth:
+ ret = 5;
+ break;
+
+ case PM_CheckBoxLabelSpacing:
+ case PM_RadioButtonLabelSpacing:
+ ret = 2;
+ break;
+ case PM_MenuScrollerHeight:
+#if 0
+ SInt16 ash, asw;
+ GetThemeMenuItemExtra(kThemeMenuItemScrollUpArrow, &ash, &asw);
+ ret = ash;
+#else
+ ret = 15; // I hate having magic numbers in here...
+#endif
+ break;
+ case PM_DefaultFrameWidth:
+#ifndef QT_NO_MAINWINDOW
+ if (widget && (widget->isWindow() || !widget->parentWidget()
+ || (qobject_cast<const QMainWindow*>(widget->parentWidget())
+ && static_cast<QMainWindow *>(widget->parentWidget())->centralWidget() == widget))
+ && qobject_cast<const QAbstractScrollArea *>(widget))
+ ret = 0;
+ else
+#endif
+ // The combo box popup has no frame.
+ if (qstyleoption_cast<const QStyleOptionComboBox *>(opt) != 0)
+ ret = 0;
+ else
+ ret = 1;
+ break;
+ case PM_MaximumDragDistance:
+ ret = -1;
+ break;
+ case PM_ScrollBarSliderMin:
+ ret = 24;
+ break;
+ case PM_SpinBoxFrameWidth:
+ GetThemeMetric(kThemeMetricEditTextFrameOutset, &ret);
+ switch (d->aquaSizeConstrain(opt, widget)) {
+ default:
+ ret += 2;
+ break;
+ case QAquaSizeMini:
+ ret += 1;
+ break;
+ }
+ break;
+ case PM_ButtonShiftHorizontal:
+ case PM_ButtonShiftVertical:
+ ret = 0;
+ break;
+ case PM_SliderLength:
+ ret = 17;
+ break;
+ // Returns the number of pixels to use for the business part of the
+ // slider (i.e., the non-tickmark portion). The remaining space is shared
+ // equally between the tickmark regions.
+ case PM_SliderControlThickness:
+ if (const QStyleOptionSlider *sl = qstyleoption_cast<const QStyleOptionSlider *>(opt)) {
+ int space = (sl->orientation == Qt::Horizontal) ? sl->rect.height() : sl->rect.width();
+ int ticks = sl->tickPosition;
+ int n = 0;
+ if (ticks & QSlider::TicksAbove)
+ ++n;
+ if (ticks & QSlider::TicksBelow)
+ ++n;
+ if (!n) {
+ ret = space;
+ break;
+ }
+
+ int thick = 6; // Magic constant to get 5 + 16 + 5
+ if (ticks != QSlider::TicksBothSides && ticks != QSlider::NoTicks)
+ thick += proxy()->pixelMetric(PM_SliderLength, sl, widget) / 4;
+
+ space -= thick;
+ if (space > 0)
+ thick += (space * 2) / (n + 2);
+ ret = thick;
+ } else {
+ ret = 0;
+ }
+ break;
+ case PM_SmallIconSize:
+ ret = int(QStyleHelper::dpiScaled(16.));
+ break;
+
+ case PM_LargeIconSize:
+ ret = int(QStyleHelper::dpiScaled(32.));
+ break;
+
+ case PM_IconViewIconSize:
+ ret = proxy()->pixelMetric(PM_LargeIconSize, opt, widget);
+ break;
+
+ case PM_ButtonDefaultIndicator:
+ ret = 0;
+ break;
+ case PM_TitleBarHeight:
+ // Always use NSTitledWindowMask since we never need any other type of window here
+ ret = int([NSWindow frameRectForContentRect:NSZeroRect
+ styleMask:NSTitledWindowMask].size.height);
+ break;
+ case QStyle::PM_TabBarTabHSpace:
+ switch (d->aquaSizeConstrain(opt, widget)) {
+ case QAquaSizeLarge:
+ ret = QCommonStyle::pixelMetric(metric, opt, widget);
+ break;
+ case QAquaSizeSmall:
+ ret = 20;
+ break;
+ case QAquaSizeMini:
+ ret = 16;
+ break;
+ case QAquaSizeUnknown:
+ const QStyleOptionTab *tb = qstyleoption_cast<const QStyleOptionTab *>(opt);
+ if (tb && tb->documentMode)
+ ret = 30;
+ else
+ ret = QCommonStyle::pixelMetric(metric, opt, widget);
+ break;
+ }
+ break;
+ case PM_TabBarTabVSpace:
+ ret = 4;
+ break;
+ case PM_TabBarTabShiftHorizontal:
+ case PM_TabBarTabShiftVertical:
+ ret = 0;
+ break;
+ case PM_TabBarBaseHeight:
+ ret = 21;
+ break;
+ case PM_TabBarTabOverlap:
+ ret = 1;
+ break;
+ case PM_TabBarBaseOverlap:
+ switch (d->aquaSizeConstrain(opt, widget)) {
+ case QAquaSizeUnknown:
+ case QAquaSizeLarge:
+ ret = 11;
+ break;
+ case QAquaSizeSmall:
+ ret = 8;
+ break;
+ case QAquaSizeMini:
+ ret = 7;
+ break;
+ }
+ break;
+ case PM_ScrollBarExtent: {
+ const QAquaWidgetSize size = d->effectiveAquaSizeConstrain(opt, widget);
+ ret = static_cast<SInt32>([NSScroller
+ scrollerWidthForControlSize:static_cast<NSControlSize>(size)
+ scrollerStyle:[NSScroller preferredScrollerStyle]]);
+ break; }
+ case PM_IndicatorHeight: {
+ switch (d->aquaSizeConstrain(opt, widget)) {
+ case QAquaSizeUnknown:
+ case QAquaSizeLarge:
+ GetThemeMetric(kThemeMetricCheckBoxHeight, &ret);
+ break;
+ case QAquaSizeMini:
+ GetThemeMetric(kThemeMetricMiniCheckBoxHeight, &ret);
+ break;
+ case QAquaSizeSmall:
+ GetThemeMetric(kThemeMetricSmallCheckBoxHeight, &ret);
+ break;
+ }
+ break; }
+ case PM_IndicatorWidth: {
+ switch (d->aquaSizeConstrain(opt, widget)) {
+ case QAquaSizeUnknown:
+ case QAquaSizeLarge:
+ GetThemeMetric(kThemeMetricCheckBoxWidth, &ret);
+ break;
+ case QAquaSizeMini:
+ GetThemeMetric(kThemeMetricMiniCheckBoxWidth, &ret);
+ break;
+ case QAquaSizeSmall:
+ GetThemeMetric(kThemeMetricSmallCheckBoxWidth, &ret);
+ break;
+ }
+ ++ret;
+ break; }
+ case PM_ExclusiveIndicatorHeight: {
+ switch (d->aquaSizeConstrain(opt, widget)) {
+ case QAquaSizeUnknown:
+ case QAquaSizeLarge:
+ GetThemeMetric(kThemeMetricRadioButtonHeight, &ret);
+ break;
+ case QAquaSizeMini:
+ GetThemeMetric(kThemeMetricMiniRadioButtonHeight, &ret);
+ break;
+ case QAquaSizeSmall:
+ GetThemeMetric(kThemeMetricSmallRadioButtonHeight, &ret);
+ break;
+ }
+ break; }
+ case PM_ExclusiveIndicatorWidth: {
+ switch (d->aquaSizeConstrain(opt, widget)) {
+ case QAquaSizeUnknown:
+ case QAquaSizeLarge:
+ GetThemeMetric(kThemeMetricRadioButtonWidth, &ret);
+ break;
+ case QAquaSizeMini:
+ GetThemeMetric(kThemeMetricMiniRadioButtonWidth, &ret);
+ break;
+ case QAquaSizeSmall:
+ GetThemeMetric(kThemeMetricSmallRadioButtonWidth, &ret);
+ break;
+ }
+ ++ret;
+ break; }
+ case PM_MenuVMargin:
+ ret = 4;
+ break;
+ case PM_MenuPanelWidth:
+ ret = 0;
+ break;
+ case PM_ToolTipLabelFrameWidth:
+ ret = 0;
+ break;
+ case PM_SizeGripSize: {
+ QAquaWidgetSize aSize;
+ if (widget && widget->window()->windowType() == Qt::Tool)
+ aSize = QAquaSizeSmall;
+ else
+ aSize = QAquaSizeLarge;
+ const QSize size = qt_aqua_get_known_size(CT_SizeGrip, widget, QSize(), aSize);
+ ret = size.width();
+ break; }
+ case PM_MdiSubWindowFrameWidth:
+ ret = 1;
+ break;
+ case PM_DockWidgetFrameWidth:
+ ret = 0;
+ break;
+ case PM_DockWidgetTitleMargin:
+ ret = 0;
+ break;
+ case PM_DockWidgetSeparatorExtent:
+ ret = 1;
+ break;
+ case PM_ToolBarHandleExtent:
+ ret = 11;
+ break;
+ case PM_ToolBarItemMargin:
+ ret = 0;
+ break;
+ case PM_ToolBarItemSpacing:
+ ret = 4;
+ break;
+ case PM_SplitterWidth:
+ ret = qMax(7, QApplication::globalStrut().width());
+ break;
+ case PM_LayoutLeftMargin:
+ case PM_LayoutTopMargin:
+ case PM_LayoutRightMargin:
+ case PM_LayoutBottomMargin:
+ {
+ bool isWindow = false;
+ if (opt) {
+ isWindow = (opt->state & State_Window);
+ } else if (widget) {
+ isWindow = widget->isWindow();
+ }
+
+ if (isWindow) {
+ bool isMetal = widget && widget->testAttribute(Qt::WA_MacBrushedMetal);
+ if (isMetal) {
+ if (metric == PM_LayoutTopMargin) {
+ return_SIZE(9 /* AHIG */, 6 /* guess */, 6 /* guess */);
+ } else if (metric == PM_LayoutBottomMargin) {
+ return_SIZE(18 /* AHIG */, 15 /* guess */, 13 /* guess */);
+ } else {
+ return_SIZE(14 /* AHIG */, 11 /* guess */, 9 /* guess */);
+ }
+ } else {
+ /*
+ AHIG would have (20, 8, 10) here but that makes
+ no sense. It would also have 14 for the top margin
+ but this contradicts both Builder and most
+ applications.
+ */
+ return_SIZE(20, 10, 10); // AHIG
+ }
+ } else {
+ // hack to detect QTabWidget
+ if (widget && widget->parentWidget()
+ && widget->parentWidget()->sizePolicy().controlType() == QSizePolicy::TabWidget) {
+ if (metric == PM_LayoutTopMargin) {
+ /*
+ Builder would have 14 (= 20 - 6) instead of 12,
+ but that makes the tab look disproportionate.
+ */
+ return_SIZE(12, 6, 6); // guess
+ } else {
+ return_SIZE(20 /* Builder */, 8 /* guess */, 8 /* guess */);
+ }
+ } else {
+ /*
+ Child margins are highly inconsistent in AHIG and Builder.
+ */
+ return_SIZE(12, 8, 6); // guess
+ }
+ }
+ }
+ case PM_LayoutHorizontalSpacing:
+ case PM_LayoutVerticalSpacing:
+ return -1;
+ case PM_MenuHMargin:
+ ret = 0;
+ break;
+ case PM_ToolBarExtensionExtent:
+ ret = 21;
+ break;
+ case PM_ToolBarFrameWidth:
+ ret = 1;
+ break;
+ case PM_ScrollView_ScrollBarOverlap:
+ ret = [NSScroller preferredScrollerStyle] == NSScrollerStyleOverlay ?
+ pixelMetric(PM_ScrollBarExtent, opt, widget) : 0;
+ break;
+ default:
+ ret = QCommonStyle::pixelMetric(metric, opt, widget);
+ break;
+ }
+ return ret;
+}
+
+QPalette QMacStyle::standardPalette() const
+{
+ QPalette pal = QCommonStyle::standardPalette();
+ pal.setColor(QPalette::Disabled, QPalette::Dark, QColor(191, 191, 191));
+ pal.setColor(QPalette::Active, QPalette::Dark, QColor(191, 191, 191));
+ pal.setColor(QPalette::Inactive, QPalette::Dark, QColor(191, 191, 191));
+ return pal;
+}
+
+int QMacStyle::styleHint(StyleHint sh, const QStyleOption *opt, const QWidget *w,
+ QStyleHintReturn *hret) const
+{
+ QMacAutoReleasePool pool;
+
+ SInt32 ret = 0;
+ switch (sh) {
+ case SH_Slider_SnapToValue:
+ case SH_PrintDialog_RightAlignButtons:
+ case SH_FontDialog_SelectAssociatedText:
+ case SH_MenuBar_MouseTracking:
+ case SH_Menu_MouseTracking:
+ case SH_ComboBox_ListMouseTracking:
+ case SH_MainWindow_SpaceBelowMenuBar:
+ case SH_ItemView_ChangeHighlightOnFocus:
+ ret = 1;
+ break;
+ case SH_ToolBox_SelectedPageTitleBold:
+ ret = 0;
+ break;
+ case SH_DialogButtonBox_ButtonsHaveIcons:
+ ret = 0;
+ break;
+ case SH_Menu_SelectionWrap:
+ ret = false;
+ break;
+ case SH_Menu_KeyboardSearch:
+ ret = true;
+ break;
+ case SH_Menu_SpaceActivatesItem:
+ ret = true;
+ break;
+ case SH_Slider_AbsoluteSetButtons:
+ ret = Qt::LeftButton|Qt::MidButton;
+ break;
+ case SH_Slider_PageSetButtons:
+ ret = 0;
+ break;
+ case SH_ScrollBar_ContextMenu:
+ ret = false;
+ break;
+ case SH_TitleBar_AutoRaise:
+ ret = true;
+ break;
+ case SH_Menu_AllowActiveAndDisabled:
+ ret = false;
+ break;
+ case SH_Menu_SubMenuPopupDelay:
+ ret = 100;
+ break;
+ case SH_Menu_SubMenuUniDirection:
+ ret = true;
+ break;
+ case SH_Menu_SubMenuSloppySelectOtherActions:
+ ret = false;
+ break;
+ case SH_Menu_SubMenuResetWhenReenteringParent:
+ ret = true;
+ break;
+ case SH_Menu_SubMenuDontStartSloppyOnLeave:
+ ret = true;
+ break;
+
+ case SH_ScrollBar_LeftClickAbsolutePosition: {
+ NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
+ bool result = [defaults boolForKey:@"AppleScrollerPagingBehavior"];
+ if(QApplication::keyboardModifiers() & Qt::AltModifier)
+ ret = !result;
+ else
+ ret = result;
+ break; }
+ case SH_TabBar_PreferNoArrows:
+ ret = true;
+ break;
+ /*
+ case SH_DialogButtons_DefaultButton:
+ ret = QDialogButtons::Reject;
+ break;
+ */
+ case SH_GroupBox_TextLabelVerticalAlignment:
+ ret = Qt::AlignTop;
+ break;
+ case SH_ScrollView_FrameOnlyAroundContents:
+ ret = QCommonStyle::styleHint(sh, opt, w, hret);
+ break;
+ case SH_Menu_FillScreenWithScroll:
+ ret = false;
+ break;
+ case SH_Menu_Scrollable:
+ ret = true;
+ break;
+ case SH_RichText_FullWidthSelection:
+ ret = true;
+ break;
+ case SH_BlinkCursorWhenTextSelected:
+ ret = false;
+ break;
+ case SH_ScrollBar_StopMouseOverSlider:
+ ret = true;
+ break;
+ case SH_ListViewExpand_SelectMouseType:
+ ret = QEvent::MouseButtonRelease;
+ break;
+ case SH_TabBar_SelectMouseType:
+#ifndef QT_NO_TABBAR
+ if (const QStyleOptionTabBarBase *opt2 = qstyleoption_cast<const QStyleOptionTabBarBase *>(opt)) {
+ ret = opt2->documentMode ? QEvent::MouseButtonPress : QEvent::MouseButtonRelease;
+ } else
+#endif
+ {
+ ret = QEvent::MouseButtonRelease;
+ }
+ break;
+ case SH_ComboBox_Popup:
+ if (const QStyleOptionComboBox *cmb = qstyleoption_cast<const QStyleOptionComboBox *>(opt))
+ ret = !cmb->editable;
+ else
+ ret = 0;
+ break;
+ case SH_Workspace_FillSpaceOnMaximize:
+ ret = true;
+ break;
+ case SH_Widget_ShareActivation:
+ ret = true;
+ break;
+ case SH_Header_ArrowAlignment:
+ ret = Qt::AlignRight;
+ break;
+ case SH_TabBar_Alignment: {
+#ifndef QT_NO_TABWIDGET
+ if (const QTabWidget *tab = qobject_cast<const QTabWidget*>(w)) {
+ if (tab->documentMode()) {
+ ret = Qt::AlignLeft;
+ break;
+ }
+ }
+#endif
+#ifndef QT_NO_TABBAR
+ if (const QTabBar *tab = qobject_cast<const QTabBar*>(w)) {
+ if (tab->documentMode()) {
+ ret = Qt::AlignLeft;
+ break;
+ }
+ }
+#endif
+ ret = Qt::AlignCenter;
+ } break;
+ case SH_UnderlineShortcut:
+ ret = false;
+ break;
+ case SH_ToolTipLabel_Opacity:
+ ret = 242; // About 95%
+ break;
+ case SH_Button_FocusPolicy:
+ ret = Qt::TabFocus;
+ break;
+ case SH_EtchDisabledText:
+ ret = false;
+ break;
+ case SH_FocusFrame_Mask: {
+ ret = true;
+ if(QStyleHintReturnMask *mask = qstyleoption_cast<QStyleHintReturnMask*>(hret)) {
+ const uchar fillR = 192, fillG = 191, fillB = 190;
+ QImage img;
+
+ QSize pixmapSize = opt->rect.size();
+ if (!pixmapSize.isEmpty()) {
+ QPixmap pix(pixmapSize);
+ pix.fill(QColor(fillR, fillG, fillB));
+ QPainter pix_paint(&pix);
+ proxy()->drawControl(CE_FocusFrame, opt, &pix_paint, w);
+ pix_paint.end();
+ img = pix.toImage();
+ }
+
+ const QRgb *sptr = (QRgb*)img.bits(), *srow;
+ const int sbpl = img.bytesPerLine();
+ const int w = sbpl/4, h = img.height();
+
+ QImage img_mask(img.width(), img.height(), QImage::Format_ARGB32);
+ QRgb *dptr = (QRgb*)img_mask.bits(), *drow;
+ const int dbpl = img_mask.bytesPerLine();
+
+ for (int y = 0; y < h; ++y) {
+ srow = sptr+((y*sbpl)/4);
+ drow = dptr+((y*dbpl)/4);
+ for (int x = 0; x < w; ++x) {
+ const int redDiff = qRed(*srow) - fillR;
+ const int greenDiff = qGreen(*srow) - fillG;
+ const int blueDiff = qBlue(*srow) - fillB;
+ const int diff = (redDiff * redDiff) + (greenDiff * greenDiff) + (blueDiff * blueDiff);
+ (*drow++) = (diff < 10) ? 0xffffffff : 0xff000000;
+ ++srow;
+ }
+ }
+ QBitmap qmask = QBitmap::fromImage(img_mask);
+ mask->region = QRegion(qmask);
+ }
+ break; }
+ case SH_TitleBar_NoBorder:
+ ret = 1;
+ break;
+ case SH_RubberBand_Mask:
+ ret = 0;
+ break;
+ case SH_ComboBox_LayoutDirection:
+ ret = Qt::LeftToRight;
+ break;
+ case SH_ItemView_EllipsisLocation:
+ ret = Qt::AlignHCenter;
+ break;
+ case SH_ItemView_ShowDecorationSelected:
+ ret = true;
+ break;
+ case SH_TitleBar_ModifyNotification:
+ ret = false;
+ break;
+ case SH_ScrollBar_RollBetweenButtons:
+ ret = true;
+ break;
+ case SH_WindowFrame_Mask:
+ ret = 1;
+ if (QStyleHintReturnMask *mask = qstyleoption_cast<QStyleHintReturnMask *>(hret)) {
+ mask->region = opt->rect;
+ mask->region -= QRect(opt->rect.left(), opt->rect.top(), 5, 1);
+ mask->region -= QRect(opt->rect.left(), opt->rect.top() + 1, 3, 1);
+ mask->region -= QRect(opt->rect.left(), opt->rect.top() + 2, 2, 1);
+ mask->region -= QRect(opt->rect.left(), opt->rect.top() + 3, 1, 2);
+
+ mask->region -= QRect(opt->rect.right() - 4, opt->rect.top(), 5, 1);
+ mask->region -= QRect(opt->rect.right() - 2, opt->rect.top() + 1, 3, 1);
+ mask->region -= QRect(opt->rect.right() - 1, opt->rect.top() + 2, 2, 1);
+ mask->region -= QRect(opt->rect.right() , opt->rect.top() + 3, 1, 2);
+ }
+ break;
+ case SH_TabBar_ElideMode:
+ ret = Qt::ElideRight;
+ break;
+#if QT_CONFIG(dialogbuttonbox)
+ case SH_DialogButtonLayout:
+ ret = QDialogButtonBox::MacLayout;
+ break;
+#endif
+ case SH_FormLayoutWrapPolicy:
+ ret = QFormLayout::DontWrapRows;
+ break;
+ case SH_FormLayoutFieldGrowthPolicy:
+ ret = QFormLayout::FieldsStayAtSizeHint;
+ break;
+ case SH_FormLayoutFormAlignment:
+ ret = Qt::AlignHCenter | Qt::AlignTop;
+ break;
+ case SH_FormLayoutLabelAlignment:
+ ret = Qt::AlignRight;
+ break;
+ case SH_ComboBox_PopupFrameStyle:
+ ret = QFrame::NoFrame | QFrame::Plain;
+ break;
+ case SH_MessageBox_TextInteractionFlags:
+ ret = Qt::TextSelectableByMouse | Qt::LinksAccessibleByMouse | Qt::TextSelectableByKeyboard;
+ break;
+ case SH_SpellCheckUnderlineStyle:
+ ret = QTextCharFormat::DashUnderline;
+ break;
+ case SH_MessageBox_CenterButtons:
+ ret = false;
+ break;
+ case SH_MenuBar_AltKeyNavigation:
+ ret = false;
+ break;
+ case SH_ItemView_MovementWithoutUpdatingSelection:
+ ret = false;
+ break;
+ case SH_FocusFrame_AboveWidget:
+ ret = true;
+ break;
+#ifndef QT_NO_WIZARD
+ case SH_WizardStyle:
+ ret = QWizard::MacStyle;
+ break;
+#endif
+ case SH_ItemView_ArrowKeysNavigateIntoChildren:
+ ret = false;
+ break;
+ case SH_Menu_FlashTriggeredItem:
+ ret = true;
+ break;
+ case SH_Menu_FadeOutOnHide:
+ ret = true;
+ break;
+ case SH_Menu_Mask:
+ if (opt) {
+ if (QStyleHintReturnMask *mask = qstyleoption_cast<QStyleHintReturnMask*>(hret)) {
+ ret = true;
+ HIRect menuRect = CGRectMake(opt->rect.x(), opt->rect.y() + 4,
+ opt->rect.width(), opt->rect.height() - 8);
+ HIThemeMenuDrawInfo mdi;
+ mdi.version = 0;
+#ifndef QT_NO_MENU
+ if (w && qobject_cast<QMenu *>(w->parentWidget()))
+ mdi.menuType = kThemeMenuTypeHierarchical;
+ else
+#endif
+ mdi.menuType = kThemeMenuTypePopUp;
+ QCFType<HIShapeRef> shape;
+ HIThemeGetMenuBackgroundShape(&menuRect, &mdi, &shape);
+
+ mask->region = qt_mac_fromHIShapeRef(shape);
+ }
+ }
+ break;
+ case SH_ItemView_PaintAlternatingRowColorsForEmptyArea:
+ ret = true;
+ break;
+#ifndef QT_NO_TABBAR
+ case SH_TabBar_CloseButtonPosition:
+ ret = QTabBar::LeftSide;
+ break;
+#endif
+ case SH_DockWidget_ButtonsHaveFrame:
+ ret = false;
+ break;
+ case SH_ScrollBar_Transient:
+ if ((qobject_cast<const QScrollBar *>(w) && w->parent() &&
+ qobject_cast<QAbstractScrollArea*>(w->parent()->parent()))
+#ifndef QT_NO_ACCESSIBILITY
+ || (opt && QStyleHelper::hasAncestor(opt->styleObject, QAccessible::ScrollBar))
+#endif
+ ) {
+ ret = [NSScroller preferredScrollerStyle] == NSScrollerStyleOverlay;
+ }
+ break;
+ case SH_ItemView_ScrollMode:
+ ret = QAbstractItemView::ScrollPerPixel;
+ break;
+ case SH_TitleBar_ShowToolTipsOnButtons:
+ // min/max/close buttons on windows don't show tool tips
+ ret = false;
+ break;
+ default:
+ ret = QCommonStyle::styleHint(sh, opt, w, hret);
+ break;
+ }
+ return ret;
+}
+
+QPixmap QMacStyle::generatedIconPixmap(QIcon::Mode iconMode, const QPixmap &pixmap,
+ const QStyleOption *opt) const
+{
+ switch (iconMode) {
+ case QIcon::Disabled: {
+ QImage img = pixmap.toImage().convertToFormat(QImage::Format_ARGB32);
+ int imgh = img.height();
+ int imgw = img.width();
+ QRgb pixel;
+ for (int y = 0; y < imgh; ++y) {
+ for (int x = 0; x < imgw; ++x) {
+ pixel = img.pixel(x, y);
+ img.setPixel(x, y, qRgba(qRed(pixel), qGreen(pixel), qBlue(pixel),
+ qAlpha(pixel) / 2));
+ }
+ }
+ return QPixmap::fromImage(img);
+ }
+ default:
+ ;
+ }
+ return QCommonStyle::generatedIconPixmap(iconMode, pixmap, opt);
+}
+
+
+QPixmap QMacStyle::standardPixmap(StandardPixmap standardPixmap, const QStyleOption *opt,
+ const QWidget *widget) const
+{
+ // The default implementation of QStyle::standardIconImplementation() is to call standardPixmap()
+ // I don't want infinite recursion so if we do get in that situation, just return the Window's
+ // standard pixmap instead (since there is no mac-specific icon then). This should be fine until
+ // someone changes how Windows standard
+ // pixmap works.
+ static bool recursionGuard = false;
+
+ if (recursionGuard)
+ return QCommonStyle::standardPixmap(standardPixmap, opt, widget);
+
+ recursionGuard = true;
+ QIcon icon = proxy()->standardIcon(standardPixmap, opt, widget);
+ recursionGuard = false;
+ int size;
+ switch (standardPixmap) {
+ default:
+ size = 32;
+ break;
+ case SP_MessageBoxCritical:
+ case SP_MessageBoxQuestion:
+ case SP_MessageBoxInformation:
+ case SP_MessageBoxWarning:
+ size = 64;
+ break;
+ }
+ return icon.pixmap(qt_getWindow(widget), QSize(size, size));
+}
+
+void QMacStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, QPainter *p,
+ const QWidget *w) const
+{
+ Q_D(const QMacStyle);
+ ThemeDrawState tds = d->getDrawState(opt->state);
+ QMacCGContext cg(p);
+ QWindow *window = w && w->window() ? w->window()->windowHandle() :
+ QStyleHelper::styleObjectWindow(opt->styleObject);
+ const_cast<QMacStylePrivate *>(d)->resolveCurrentNSView(window);
+ switch (pe) {
+ case PE_IndicatorArrowUp:
+ case PE_IndicatorArrowDown:
+ case PE_IndicatorArrowRight:
+ case PE_IndicatorArrowLeft: {
+ p->save();
+ p->setRenderHint(QPainter::Antialiasing);
+ int xOffset = opt->direction == Qt::LeftToRight ? 2 : -1;
+ QMatrix matrix;
+ matrix.translate(opt->rect.center().x() + xOffset, opt->rect.center().y() + 2);
+ QPainterPath path;
+ switch(pe) {
+ default:
+ case PE_IndicatorArrowDown:
+ break;
+ case PE_IndicatorArrowUp:
+ matrix.rotate(180);
+ break;
+ case PE_IndicatorArrowLeft:
+ matrix.rotate(90);
+ break;
+ case PE_IndicatorArrowRight:
+ matrix.rotate(-90);
+ break;
+ }
+ path.moveTo(0, 5);
+ path.lineTo(-4, -3);
+ path.lineTo(4, -3);
+ p->setMatrix(matrix);
+ p->setPen(Qt::NoPen);
+ p->setBrush(QColor(0, 0, 0, 135));
+ p->drawPath(path);
+ p->restore();
+ break; }
+#ifndef QT_NO_TABBAR
+ case PE_FrameTabBarBase:
+ if (const QStyleOptionTabBarBase *tbb
+ = qstyleoption_cast<const QStyleOptionTabBarBase *>(opt)) {
+ if (tbb->documentMode) {
+ p->save();
+ drawTabBase(p, tbb, w);
+ p->restore();
+ return;
+ }
+
+ QRegion region(tbb->rect);
+ region -= tbb->tabBarRect;
+ p->save();
+ p->setClipRegion(region);
+ QStyleOptionTabWidgetFrame twf;
+ twf.QStyleOption::operator=(*tbb);
+ twf.shape = tbb->shape;
+ switch (getTabDirection(twf.shape)) {
+ case kThemeTabNorth:
+ twf.rect = twf.rect.adjusted(0, 0, 0, 10);
+ break;
+ case kThemeTabSouth:
+ twf.rect = twf.rect.adjusted(0, -10, 0, 0);
+ break;
+ case kThemeTabWest:
+ twf.rect = twf.rect.adjusted(0, 0, 10, 0);
+ break;
+ case kThemeTabEast:
+ twf.rect = twf.rect.adjusted(0, -10, 0, 0);
+ break;
+ }
+ proxy()->drawPrimitive(PE_FrameTabWidget, &twf, p, w);
+ p->restore();
+ }
+ break;
+#endif
+ case PE_PanelTipLabel:
+ p->fillRect(opt->rect, opt->palette.brush(QPalette::ToolTipBase));
+ break;
+ case PE_FrameGroupBox:
+ if (const QStyleOptionFrame *groupBox = qstyleoption_cast<const QStyleOptionFrame *>(opt)) {
+ if (groupBox->features & QStyleOptionFrame::Flat) {
+ QCommonStyle::drawPrimitive(pe, groupBox, p, w);
+ } else {
+ HIThemeGroupBoxDrawInfo gdi;
+ gdi.version = qt_mac_hitheme_version;
+ gdi.state = tds;
+#ifndef QT_NO_GROUPBOX
+ if (w && qobject_cast<QGroupBox *>(w->parentWidget()))
+ gdi.kind = kHIThemeGroupBoxKindSecondary;
+ else
+#endif
+ gdi.kind = kHIThemeGroupBoxKindPrimary;
+ HIRect hirect = qt_hirectForQRect(opt->rect);
+ HIThemeDrawGroupBox(&hirect, &gdi, cg, kHIThemeOrientationNormal);
+ }
+ }
+ break;
+ case PE_IndicatorToolBarSeparator: {
+ QPainterPath path;
+ if (opt->state & State_Horizontal) {
+ int xpoint = opt->rect.center().x();
+ path.moveTo(xpoint + 0.5, opt->rect.top() + 1);
+ path.lineTo(xpoint + 0.5, opt->rect.bottom());
+ } else {
+ int ypoint = opt->rect.center().y();
+ path.moveTo(opt->rect.left() + 2 , ypoint + 0.5);
+ path.lineTo(opt->rect.right() + 1, ypoint + 0.5);
+ }
+ QPainterPathStroker theStroker;
+ theStroker.setCapStyle(Qt::FlatCap);
+ theStroker.setDashPattern(QVector<qreal>() << 1 << 2);
+ path = theStroker.createStroke(path);
+ p->fillPath(path, QColor(0, 0, 0, 119));
+ }
+ break;
+ case PE_FrameWindow:
+ break;
+ case PE_IndicatorDockWidgetResizeHandle: {
+ // The docwidget resize handle is drawn as a one-pixel wide line.
+ p->save();
+ if (opt->state & State_Horizontal) {
+ p->setPen(QColor(160, 160, 160));
+ p->drawLine(opt->rect.topLeft(), opt->rect.topRight());
+ } else {
+ p->setPen(QColor(145, 145, 145));
+ p->drawLine(opt->rect.topRight(), opt->rect.bottomRight());
+ }
+ p->restore();
+ } break;
+ case PE_IndicatorToolBarHandle: {
+ p->save();
+ QPainterPath path;
+ int x = opt->rect.x() + 6;
+ int y = opt->rect.y() + 7;
+ static const int RectHeight = 2;
+ if (opt->state & State_Horizontal) {
+ while (y < opt->rect.height() - RectHeight - 5) {
+ path.moveTo(x, y);
+ path.addEllipse(x, y, RectHeight, RectHeight);
+ y += 6;
+ }
+ } else {
+ while (x < opt->rect.width() - RectHeight - 5) {
+ path.moveTo(x, y);
+ path.addEllipse(x, y, RectHeight, RectHeight);
+ x += 6;
+ }
+ }
+ p->setPen(Qt::NoPen);
+ QColor dark = opt->palette.dark().color().darker();
+ dark.setAlphaF(0.50);
+ p->fillPath(path, dark);
+ p->restore();
+
+ break;
+ }
+ case PE_IndicatorHeaderArrow:
+ if (const QStyleOptionHeader *header = qstyleoption_cast<const QStyleOptionHeader *>(opt)) {
+ // In HITheme, up is down, down is up and hamburgers eat people.
+ if (header->sortIndicator != QStyleOptionHeader::None)
+ proxy()->drawPrimitive(
+ (header->sortIndicator == QStyleOptionHeader::SortDown) ?
+ PE_IndicatorArrowUp : PE_IndicatorArrowDown, header, p, w);
+ }
+ break;
+ case PE_IndicatorMenuCheckMark: {
+ if (!(opt->state & State_On))
+ break;
+ QColor pc;
+ if (opt->state & State_Selected)
+ pc = opt->palette.highlightedText().color();
+ else
+ pc = opt->palette.text().color();
+ QCFType<CGColorRef> checkmarkColor = CGColorCreateGenericRGB(static_cast<CGFloat>(pc.redF()),
+ static_cast<CGFloat>(pc.greenF()),
+ static_cast<CGFloat>(pc.blueF()),
+ static_cast<CGFloat>(pc.alphaF()));
+ // kCTFontUIFontSystem and others give the same result
+ // as kCTFontUIFontMenuItemMark. However, the latter is
+ // more reminiscent to HITheme's kThemeMenuItemMarkFont.
+ // See also the font for small- and mini-sized widgets,
+ // where we end up using the generic system font type.
+ const CTFontUIFontType fontType = (opt->state & State_Mini) ? kCTFontUIFontMiniSystem :
+ (opt->state & State_Small) ? kCTFontUIFontSmallSystem :
+ kCTFontUIFontMenuItemMark;
+ // Similarly for the font size, where there is a small difference
+ // between regular combobox and item view items, and and menu items.
+ // However, we ignore any difference for small- and mini-sized widgets.
+ const CGFloat fontSize = fontType == kCTFontUIFontMenuItemMark ? opt->fontMetrics.height() : 0.0;
+ QCFType<CTFontRef> checkmarkFont = CTFontCreateUIFontForLanguage(fontType, fontSize, NULL);
+
+ CGContextSaveGState(cg);
+ CGContextSetShouldSmoothFonts(cg, NO); // Same as HITheme and Cocoa menu checkmarks
+
+ // Baseline alignment tweaks for QComboBox and QMenu
+ const CGFloat vOffset = (opt->state & State_Mini) ? 0.0 :
+ (opt->state & State_Small) ? 1.0 :
+ 0.75;
+
+ CGContextTranslateCTM(cg, 0, opt->rect.bottom());
+ CGContextScaleCTM(cg, 1, -1);
+ // Translate back to the original position and add rect origin and offset
+ CGContextTranslateCTM(cg, opt->rect.x(), vOffset);
+
+ // CTFont has severe difficulties finding the checkmark character among its
+ // glyphs. Fortunately, CTLine knows its ways inside the Cocoa labyrinth.
+ static const CFStringRef keys[] = { kCTFontAttributeName, kCTForegroundColorAttributeName };
+ static const int numValues = sizeof(keys) / sizeof(keys[0]);
+ const CFTypeRef values[] = { (CFTypeRef)checkmarkFont, (CFTypeRef)checkmarkColor };
+ Q_STATIC_ASSERT((sizeof(values) / sizeof(values[0])) == numValues);
+ QCFType<CFDictionaryRef> attributes = CFDictionaryCreate(kCFAllocatorDefault, (const void **)keys, (const void **)values,
+ numValues, NULL, NULL);
+ // U+2713: CHECK MARK
+ QCFType<CFAttributedStringRef> checkmarkString = CFAttributedStringCreate(kCFAllocatorDefault, (CFStringRef)@"\u2713", attributes);
+ QCFType<CTLineRef> line = CTLineCreateWithAttributedString(checkmarkString);
+
+ CTLineDraw((CTLineRef)line, cg);
+ CGContextFlush(cg); // CTLineDraw's documentation says it doesn't flush
+
+ CGContextRestoreGState(cg);
+ break; }
+ case PE_IndicatorViewItemCheck:
+ case PE_IndicatorRadioButton:
+ case PE_IndicatorCheckBox: {
+ bool drawColorless = tds == kThemeStateInactive;
+ HIThemeButtonDrawInfo bdi;
+ bdi.version = qt_mac_hitheme_version;
+ bdi.state = tds;
+ if (drawColorless)
+ bdi.state = kThemeStateActive;
+ bdi.adornment = kThemeDrawIndicatorOnly;
+ if (opt->state & State_HasFocus)
+ bdi.adornment |= kThemeAdornmentFocus;
+ bool isRadioButton = (pe == PE_IndicatorRadioButton);
+ switch (d->aquaSizeConstrain(opt, w)) {
+ case QAquaSizeUnknown:
+ case QAquaSizeLarge:
+ if (isRadioButton)
+ bdi.kind = kThemeRadioButton;
+ else
+ bdi.kind = kThemeCheckBox;
+ break;
+ case QAquaSizeMini:
+ if (isRadioButton)
+ bdi.kind = kThemeMiniRadioButton;
+ else
+ bdi.kind = kThemeMiniCheckBox;
+ break;
+ case QAquaSizeSmall:
+ if (isRadioButton)
+ bdi.kind = kThemeSmallRadioButton;
+ else
+ bdi.kind = kThemeSmallCheckBox;
+ break;
+ }
+ if (opt->state & State_NoChange)
+ bdi.value = kThemeButtonMixed;
+ else if (opt->state & State_On)
+ bdi.value = kThemeButtonOn;
+ else
+ bdi.value = kThemeButtonOff;
+ HIRect macRect = qt_hirectForQRect(opt->rect);
+ if (!drawColorless)
+ HIThemeDrawButton(&macRect, &bdi, cg, kHIThemeOrientationNormal, 0);
+ else
+ d->drawColorlessButton(macRect, &bdi, p, opt);
+ break; }
+ case PE_FrameFocusRect:
+ // Use the our own focus widget stuff.
+ break;
+ case PE_IndicatorBranch: {
+ if (!(opt->state & State_Children))
+ break;
+ if (!d->indicatorBranchButtonCell)
+ const_cast<QMacStylePrivate *>(d)->indicatorBranchButtonCell = (void *)[[NSButtonCell alloc] init];
+ NSButtonCell *triangleCell = (NSButtonCell *)d->indicatorBranchButtonCell;
+ [triangleCell setButtonType:NSOnOffButton];
+ [triangleCell setState:(opt->state & State_Open) ? NSOnState : NSOffState];
+ [triangleCell setBezelStyle:NSDisclosureBezelStyle];
+ bool viewHasFocus = (w && w->hasFocus()) || (opt->state & State_HasFocus);
+ [triangleCell setBackgroundStyle:((opt->state & State_Selected) && viewHasFocus) ? NSBackgroundStyleDark : NSBackgroundStyleLight];
+
+ CGContextSaveGState(cg);
+ [NSGraphicsContext saveGraphicsState];
+
+ [NSGraphicsContext setCurrentContext:[NSGraphicsContext
+ graphicsContextWithGraphicsPort:(CGContextRef)cg flipped:NO]];
+
+ QRect qtRect = opt->rect.adjusted(DisclosureOffset, 0, -DisclosureOffset, 0);
+ CGRect rect = CGRectMake(qtRect.x() + 1, qtRect.y(), qtRect.width(), qtRect.height());
+ CGContextTranslateCTM(cg, rect.origin.x, rect.origin.y + rect.size.height);
+ CGContextScaleCTM(cg, 1, -1);
+ CGContextTranslateCTM(cg, -rect.origin.x, -rect.origin.y);
+
+ [triangleCell drawBezelWithFrame:NSRectFromCGRect(rect) inView:[triangleCell controlView]];
+
+ [NSGraphicsContext restoreGraphicsState];
+ CGContextRestoreGState(cg);
+ break; }
+
+ case PE_Frame: {
+ QPen oldPen = p->pen();
+ p->setPen(opt->palette.base().color().darker(140));
+ p->drawRect(opt->rect.adjusted(0, 0, -1, -1));
+ p->setPen(opt->palette.base().color().darker(180));
+ p->drawLine(opt->rect.topLeft(), opt->rect.topRight());
+ p->setPen(oldPen);
+ break; }
+
+ case PE_FrameLineEdit:
+ if (const QStyleOptionFrame *frame = qstyleoption_cast<const QStyleOptionFrame *>(opt)) {
+ if (frame->state & State_Sunken) {
+ QColor baseColor(frame->palette.background().color());
+ HIThemeFrameDrawInfo fdi;
+ fdi.version = qt_mac_hitheme_version;
+ fdi.state = tds;
+ SInt32 frame_size;
+ fdi.kind = frame->features & QStyleOptionFrame::Rounded ? kHIThemeFrameTextFieldRound :
+ kHIThemeFrameTextFieldSquare;
+ GetThemeMetric(kThemeMetricEditTextFrameOutset, &frame_size);
+ if ((frame->state & State_ReadOnly) || !(frame->state & State_Enabled))
+ fdi.state = kThemeStateInactive;
+ else if (fdi.state == kThemeStatePressed)
+ // This pressed state doesn't make sense for a line edit frame.
+ // And Yosemite agrees with us. Otherwise it starts showing yellow pixels.
+ fdi.state = kThemeStateActive;
+ fdi.isFocused = (frame->state & State_HasFocus);
+ int lw = frame->lineWidth;
+ if (lw <= 0)
+ lw = proxy()->pixelMetric(PM_DefaultFrameWidth, frame, w);
+ { //clear to base color
+ p->save();
+ p->setPen(QPen(baseColor, lw));
+ p->setBrush(Qt::NoBrush);
+ p->drawRect(frame->rect);
+ p->restore();
+ }
+ HIRect hirect = qt_hirectForQRect(frame->rect,
+ QRect(frame_size, frame_size,
+ frame_size * 2, frame_size * 2));
+
+ HIThemeDrawFrame(&hirect, &fdi, cg, kHIThemeOrientationNormal);
+ } else {
+ QCommonStyle::drawPrimitive(pe, opt, p, w);
+ }
+ }
+ break;
+ case PE_PanelLineEdit:
+ QCommonStyle::drawPrimitive(pe, opt, p, w);
+ // Draw the focus frame for widgets other than QLineEdit (e.g. for line edits in Webkit).
+ // Focus frame is drawn outside the rectangle passed in the option-rect.
+ if (const QStyleOptionFrame *panel = qstyleoption_cast<const QStyleOptionFrame *>(opt)) {
+#ifndef QT_NO_LINEEDIT
+ if ((opt->state & State_HasFocus) && !qobject_cast<const QLineEdit*>(w)) {
+ int vmargin = pixelMetric(QStyle::PM_FocusFrameVMargin);
+ int hmargin = pixelMetric(QStyle::PM_FocusFrameHMargin);
+ QStyleOptionFrame focusFrame = *panel;
+ focusFrame.rect = panel->rect.adjusted(-hmargin, -vmargin, hmargin, vmargin);
+ drawControl(CE_FocusFrame, &focusFrame, p, w);
+ }
+#endif
+ }
+
+ break;
+#ifndef QT_NO_TABWIDGET
+ case PE_FrameTabWidget:
+ if (const QStyleOptionTabWidgetFrame *twf
+ = qstyleoption_cast<const QStyleOptionTabWidgetFrame *>(opt)) {
+ HIRect hirect = qt_hirectForQRect(twf->rect);
+ HIThemeTabPaneDrawInfo tpdi;
+ tpdi.version = qt_mac_hitheme_tab_version();
+ tpdi.state = tds;
+ tpdi.direction = getTabDirection(twf->shape);
+ tpdi.size = kHIThemeTabSizeNormal;
+ tpdi.kind = kHIThemeTabKindNormal;
+ tpdi.adornment = kHIThemeTabPaneAdornmentNormal;
+ HIThemeDrawTabPane(&hirect, &tpdi, cg, kHIThemeOrientationNormal);
+ }
+ break;
+#endif
+ case PE_PanelScrollAreaCorner: {
+ const QBrush brush(opt->palette.brush(QPalette::Base));
+ p->fillRect(opt->rect, brush);
+ p->setPen(QPen(QColor(217, 217, 217)));
+ p->drawLine(opt->rect.topLeft(), opt->rect.topRight());
+ p->drawLine(opt->rect.topLeft(), opt->rect.bottomLeft());
+ } break;
+ case PE_FrameStatusBarItem:
+ break;
+ case PE_IndicatorTabClose: {
+ // Make close button visible only on the hovered tab.
+ if (QTabBar *tabBar = qobject_cast<QTabBar*>(w->parentWidget())) {
+ const QTabBarPrivate *tabBarPrivate = static_cast<QTabBarPrivate *>(QObjectPrivate::get(tabBar));
+ const int hoveredTabIndex = tabBarPrivate->hoveredTabIndex();
+ if (hoveredTabIndex != -1 && ((w == tabBar->tabButton(hoveredTabIndex, QTabBar::LeftSide)) ||
+ (w == tabBar->tabButton(hoveredTabIndex, QTabBar::RightSide)))) {
+ const bool hover = (opt->state & State_MouseOver);
+ const bool selected = (opt->state & State_Selected);
+ const bool pressed = (opt->state & State_Sunken);
+ drawTabCloseButton(p, hover, selected, pressed);
+ }
+ }
+ } break;
+ 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);
+
+ // Draw the black separator line at the top of the status bar.
+ if (w ? qt_macWindowMainWindow(w->window()) : (opt->state & QStyle::State_Active))
+ p->setPen(titlebarSeparatorLineActive);
+ else
+ p->setPen(titlebarSeparatorLineInactive);
+ p->drawLine(opt->rect.left(), opt->rect.top(), opt->rect.right(), opt->rect.top());
+
+ break;
+ }
+
+ default:
+ QCommonStyle::drawPrimitive(pe, opt, p, w);
+ break;
+ }
+}
+
+static inline QPixmap darkenPixmap(const QPixmap &pixmap)
+{
+ QImage img = pixmap.toImage().convertToFormat(QImage::Format_ARGB32);
+ int imgh = img.height();
+ int imgw = img.width();
+ int h, s, v, a;
+ QRgb pixel;
+ for (int y = 0; y < imgh; ++y) {
+ for (int x = 0; x < imgw; ++x) {
+ pixel = img.pixel(x, y);
+ a = qAlpha(pixel);
+ QColor hsvColor(pixel);
+ hsvColor.getHsv(&h, &s, &v);
+ s = qMin(100, s * 2);
+ v = v / 2;
+ hsvColor.setHsv(h, s, v);
+ pixel = hsvColor.rgb();
+ img.setPixel(x, y, qRgba(qRed(pixel), qGreen(pixel), qBlue(pixel), a));
+ }
+ }
+ return QPixmap::fromImage(img);
+}
+
+
+
+void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter *p,
+ const QWidget *w) const
+{
+ Q_D(const QMacStyle);
+ ThemeDrawState tds = d->getDrawState(opt->state);
+ QMacCGContext cg(p);
+ QWindow *window = w && w->window() ? w->window()->windowHandle() :
+ QStyleHelper::styleObjectWindow(opt->styleObject);
+ const_cast<QMacStylePrivate *>(d)->resolveCurrentNSView(window);
+ switch (ce) {
+ case CE_HeaderSection:
+ if (const QStyleOptionHeader *header = qstyleoption_cast<const QStyleOptionHeader *>(opt)) {
+ HIThemeButtonDrawInfo bdi;
+ bdi.version = qt_mac_hitheme_version;
+ State flags = header->state;
+ QRect ir = header->rect;
+ bdi.kind = kThemeListHeaderButton;
+ bdi.adornment = kThemeAdornmentNone;
+ bdi.state = kThemeStateActive;
+
+ if (flags & State_On)
+ bdi.value = kThemeButtonOn;
+ else
+ bdi.value = kThemeButtonOff;
+
+ if (header->orientation == Qt::Horizontal){
+ switch (header->position) {
+ case QStyleOptionHeader::Beginning:
+ ir.adjust(-1, -1, 0, 0);
+ break;
+ case QStyleOptionHeader::Middle:
+ ir.adjust(-1, -1, 0, 0);
+ break;
+ case QStyleOptionHeader::OnlyOneSection:
+ case QStyleOptionHeader::End:
+ ir.adjust(-1, -1, 1, 0);
+ break;
+ default:
+ break;
+ }
+
+ if (header->position != QStyleOptionHeader::Beginning
+ && header->position != QStyleOptionHeader::OnlyOneSection) {
+ bdi.adornment = header->direction == Qt::LeftToRight
+ ? kThemeAdornmentHeaderButtonLeftNeighborSelected
+ : kThemeAdornmentHeaderButtonRightNeighborSelected;
+ }
+ }
+
+ if (flags & State_Active) {
+ if (!(flags & State_Enabled))
+ bdi.state = kThemeStateUnavailable;
+ else if (flags & State_Sunken)
+ bdi.state = kThemeStatePressed;
+ } else {
+ if (flags & State_Enabled)
+ bdi.state = kThemeStateInactive;
+ else
+ bdi.state = kThemeStateUnavailableInactive;
+ }
+
+ if (header->sortIndicator != QStyleOptionHeader::None) {
+ bdi.value = kThemeButtonOn;
+ if (header->sortIndicator == QStyleOptionHeader::SortDown)
+ bdi.adornment = kThemeAdornmentHeaderButtonSortUp;
+ }
+ if (flags & State_HasFocus)
+ bdi.adornment = kThemeAdornmentFocus;
+
+ ir = visualRect(header->direction, header->rect, ir);
+ HIRect bounds = qt_hirectForQRect(ir);
+
+ bool noVerticalHeader = true;
+#ifndef QT_NO_TABLEVIEW
+ if (w)
+ if (const QTableView *table = qobject_cast<const QTableView *>(w->parentWidget()))
+ noVerticalHeader = !table->verticalHeader()->isVisible();
+#endif
+
+ bool drawTopBorder = header->orientation == Qt::Horizontal;
+ bool drawLeftBorder = header->orientation == Qt::Vertical
+ || header->position == QStyleOptionHeader::OnlyOneSection
+ || (header->position == QStyleOptionHeader::Beginning && noVerticalHeader);
+ d->drawTableHeader(bounds, drawTopBorder, drawLeftBorder, bdi, p);
+ }
+ 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(window, QSize(iconExtent, iconExtent), 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 (QStyleHelper::hasAncestor(opt->styleObject, QAccessible::ToolBar)) {
+ QRect cr = tb->rect;
+ int shiftX = 0;
+ int shiftY = 0;
+ bool needText = false;
+ int alignment = 0;
+ bool down = tb->state & (State_Sunken | State_On);
+ if (down) {
+ shiftX = proxy()->pixelMetric(PM_ButtonShiftHorizontal, tb, w);
+ shiftY = proxy()->pixelMetric(PM_ButtonShiftVertical, tb, w);
+ }
+ // The down state is special for QToolButtons in a toolbar on the Mac
+ // The text is a bit bolder and gets a drop shadow and the icons are also darkened.
+ // This doesn't really fit into any particular case in QIcon, so we
+ // do the majority of the work ourselves.
+ if (!(tb->features & QStyleOptionToolButton::Arrow)) {
+ Qt::ToolButtonStyle tbstyle = tb->toolButtonStyle;
+ if (tb->icon.isNull() && !tb->text.isEmpty())
+ tbstyle = Qt::ToolButtonTextOnly;
+
+ switch (tbstyle) {
+ case Qt::ToolButtonTextOnly: {
+ needText = true;
+ alignment = Qt::AlignCenter;
+ break; }
+ case Qt::ToolButtonIconOnly:
+ case Qt::ToolButtonTextBesideIcon:
+ case Qt::ToolButtonTextUnderIcon: {
+ QRect pr = cr;
+ QIcon::Mode iconMode = (tb->state & State_Enabled) ? QIcon::Normal
+ : QIcon::Disabled;
+ QIcon::State iconState = (tb->state & State_On) ? QIcon::On
+ : QIcon::Off;
+ QPixmap pixmap = tb->icon.pixmap(window,
+ tb->rect.size().boundedTo(tb->iconSize),
+ iconMode, iconState);
+
+ // Draw the text if it's needed.
+ if (tb->toolButtonStyle != Qt::ToolButtonIconOnly) {
+ needText = true;
+ if (tb->toolButtonStyle == Qt::ToolButtonTextUnderIcon) {
+ pr.setHeight(pixmap.size().height() / pixmap.devicePixelRatio() + 6);
+ cr.adjust(0, pr.bottom(), 0, -3);
+ alignment |= Qt::AlignCenter;
+ } else {
+ pr.setWidth(pixmap.width() / pixmap.devicePixelRatio() + 8);
+ cr.adjust(pr.right(), 0, 0, 0);
+ alignment |= Qt::AlignLeft | Qt::AlignVCenter;
+ }
+ }
+ if (opt->state & State_Sunken) {
+ pr.translate(shiftX, shiftY);
+ pixmap = darkenPixmap(pixmap);
+ }
+ proxy()->drawItemPixmap(p, pr, Qt::AlignCenter, pixmap);
+ break; }
+ default:
+ Q_ASSERT(false);
+ break;
+ }
+
+ if (needText) {
+ QPalette pal = tb->palette;
+ QPalette::ColorRole role = QPalette::NoRole;
+ if (!proxy()->styleHint(SH_UnderlineShortcut, tb, w))
+ alignment |= Qt::TextHideMnemonic;
+ if (down)
+ cr.translate(shiftX, shiftY);
+ if (tbstyle == Qt::ToolButtonTextOnly
+ || (tbstyle != Qt::ToolButtonTextOnly && !down)) {
+ QPen pen = p->pen();
+ QColor light = down ? Qt::black : Qt::white;
+ light.setAlphaF(0.375f);
+ p->setPen(light);
+ p->drawText(cr.adjusted(0, 1, 0, 1), alignment, tb->text);
+ p->setPen(pen);
+ if (down && tbstyle == Qt::ToolButtonTextOnly) {
+ pal = QApplication::palette("QMenu");
+ pal.setCurrentColorGroup(tb->palette.currentColorGroup());
+ role = QPalette::HighlightedText;
+ }
+ }
+ proxy()->drawItemText(p, cr, alignment, pal,
+ tb->state & State_Enabled, tb->text, role);
+ }
+ } else {
+ QCommonStyle::drawControl(ce, &myTb, p, w);
+ }
+ } else {
+ QCommonStyle::drawControl(ce, &myTb, p, w);
+ }
+#else
+ Q_UNUSED(tb)
+#endif
+ }
+ break;
+ case CE_ToolBoxTabShape:
+ QCommonStyle::drawControl(ce, opt, p, w);
+ break;
+ case CE_PushButtonBevel:
+ if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(opt)) {
+ if (!(btn->state & (State_Raised | State_Sunken | State_On)))
+ break;
+
+ if (btn->features & QStyleOptionButton::CommandLinkButton) {
+ QCommonStyle::drawControl(ce, opt, p, w);
+ break;
+ }
+
+ // No default button pulsating animation on Yosemite,
+ // so we have to do few things differently.
+
+ // a focused auto-default button within an active window
+ // takes precedence over a normal default button
+ if (btn->features & QStyleOptionButton::AutoDefaultButton
+ && opt->state & State_Active && opt->state & State_HasFocus) {
+ d->autoDefaultButton = opt->styleObject;
+ } else if (d->autoDefaultButton == opt->styleObject) {
+ d->setAutoDefaultButton(0);
+ }
+
+ if (!d->autoDefaultButton) {
+ if (btn->features & QStyleOptionButton::DefaultButton && opt->state & State_Active) {
+ d->defaultButton = opt->styleObject;
+ } else if (d->defaultButton == opt->styleObject) {
+ if (QStyleAnimation *animation = d->animation(opt->styleObject)) {
+ animation->updateTarget();
+ d->stopAnimation(opt->styleObject);
+ }
+ d->defaultButton = 0;
+ }
+ }
+
+ // TODO: find out the pressed button in a qwidget independent way
+ extern QWidget *qt_button_down; // qwidgetwindow.cpp
+ if (opt->styleObject == qt_button_down)
+ d->pressedButton = opt->styleObject;
+ else if (d->pressedButton == opt->styleObject)
+ d->pressedButton = 0;
+
+ bool hasMenu = btn->features & QStyleOptionButton::HasMenu;
+ HIThemeButtonDrawInfo bdi;
+ d->initHIThemePushButton(btn, w, tds, &bdi);
+
+ if (!hasMenu) {
+ // HITheme is not drawing a nice focus frame around buttons.
+ // We'll do it ourselves further down.
+ bdi.adornment &= ~kThemeAdornmentFocus;
+
+ // We can't rely on an animation existing to test for the default look. That means a bit
+ // more logic (notice that the logic is slightly different for the bevel and the label).
+ if (tds == kThemeStateActive
+ && (btn->features & QStyleOptionButton::DefaultButton
+ || (btn->features & QStyleOptionButton::AutoDefaultButton
+ && d->autoDefaultButton == btn->styleObject)))
+ bdi.adornment |= kThemeAdornmentDefault;
+ }
+
+ // Unlike Carbon, we want the button to always be drawn inside its bounds.
+ // Therefore, make the button a bit smaller, so that even if it got focus,
+ // the focus 'shadow' will be inside.
+ HIRect newRect = qt_hirectForQRect(btn->rect);
+ if (bdi.kind == kThemePushButton || bdi.kind == kThemePushButtonSmall) {
+ newRect.origin.x += QMacStylePrivate::PushButtonLeftOffset;
+ newRect.origin.y += QMacStylePrivate::PushButtonTopOffset;
+ newRect.size.width -= QMacStylePrivate::PushButtonRightOffset;
+ newRect.size.height -= QMacStylePrivate::PushButtonBottomOffset;
+ } else if (bdi.kind == kThemePushButtonMini) {
+ newRect.origin.x += QMacStylePrivate::PushButtonLeftOffset - 2;
+ newRect.origin.y += QMacStylePrivate::PushButtonTopOffset;
+ newRect.size.width -= QMacStylePrivate::PushButtonRightOffset - 4;
+ }
+
+ if (hasMenu && bdi.kind != kThemeBevelButton) {
+ QCocoaWidget cw = cocoaWidgetFromHIThemeButtonKind(bdi.kind);
+ cw.first = QCocoaPullDownButton;
+ NSPopUpButton *pdb = (NSPopUpButton *)d->cocoaControl(cw);
+ [pdb highlight:(bdi.state == kThemeStatePressed)];
+ pdb.enabled = bdi.state != kThemeStateUnavailable && bdi.state != kThemeStateUnavailableInactive;
+ QRect rect = opt->rect;
+ rect.adjust(0, 0, cw.second == QAquaSizeSmall ? -4 : cw.second == QAquaSizeMini ? -9 : -6, 0);
+ d->drawNSViewInRect(cw, pdb, rect, p, w != 0);
+ } else if (hasMenu && bdi.state == kThemeStatePressed)
+ d->drawColorlessButton(newRect, &bdi, p, opt);
+ else
+ HIThemeDrawButton(&newRect, &bdi, cg, kHIThemeOrientationNormal, 0);
+
+ if (btn->state & State_HasFocus) {
+ CGRect focusRect = newRect;
+ if (bdi.kind == kThemePushButton)
+ focusRect.size.height += 1; // Another thing HITheme and Cocoa seem to disagree about.
+ else if (bdi.kind == kThemePushButtonMini)
+ focusRect.size.height = 15; // Our QPushButton sizes are really weird
+
+ if (bdi.adornment & kThemeAdornmentDefault || bdi.state == kThemeStatePressed) {
+ if (bdi.kind == kThemePushButtonSmall) {
+ focusRect = CGRectInset(focusRect, -1, 0);
+ } else if (bdi.kind == kThemePushButtonMini) {
+ focusRect = CGRectInset(focusRect, 1, 0);
+ }
+ } else {
+ if (bdi.kind == kThemePushButton) {
+ focusRect = CGRectInset(focusRect, 1, 1);
+ } else if (bdi.kind == kThemePushButtonSmall) {
+ focusRect = CGRectInset(focusRect, 0, 2);
+ } else if (bdi.kind == kThemePushButtonMini) {
+ focusRect = CGRectInset(focusRect, 2, 1);
+ }
+ }
+
+ const qreal radius = bdi.kind == kThemeBevelButton ? 0 : 4;
+ const int hMargin = proxy()->pixelMetric(QStyle::PM_FocusFrameHMargin, btn, w);
+ const int vMargin = proxy()->pixelMetric(QStyle::PM_FocusFrameVMargin, btn, w);
+ const QRect focusTargetRect(focusRect.origin.x, focusRect.origin.y, focusRect.size.width, focusRect.size.height);
+ d->drawFocusRing(p, focusTargetRect.adjusted(-hMargin, -vMargin, hMargin, vMargin), hMargin, vMargin, radius);
+ }
+
+ if (hasMenu && bdi.kind == kThemeBevelButton) {
+ int mbi = proxy()->pixelMetric(QStyle::PM_MenuButtonIndicator, btn, w);
+ QRect ir = btn->rect;
+ int arrowXOffset = bdi.kind == kThemePushButton ? 6 :
+ bdi.kind == kThemePushButtonSmall ? 7 : 8;
+ int arrowYOffset = bdi.kind == kThemePushButton ? 3 :
+ bdi.kind == kThemePushButtonSmall ? 1 : 2;
+ if (!w) {
+ // adjustment for Qt Quick Controls
+ arrowYOffset -= ir.top();
+ if (bdi.kind == kThemePushButtonSmall)
+ arrowYOffset += 1;
+ }
+ QRect ar = QRect(ir.right() - mbi - QMacStylePrivate::PushButtonRightOffset,
+ ir.height() / 2 - arrowYOffset, mbi, ir.height() / 2);
+ ar = visualRect(btn->direction, ir, ar);
+ HIRect arrowRect = CGRectMake(ar.x() + arrowXOffset, ar.y(), ar.width(), ar.height());
+
+ HIThemePopupArrowDrawInfo pdi;
+ pdi.version = qt_mac_hitheme_version;
+ pdi.state = tds == kThemeStateInactive ? kThemeStateActive : tds;
+ pdi.orientation = kThemeArrowDown;
+ if (bdi.kind == kThemePushButtonMini)
+ pdi.size = kThemeArrow5pt;
+ else if (bdi.kind == kThemePushButton || bdi.kind == kThemePushButtonSmall)
+ pdi.size = kThemeArrow7pt;
+ HIThemeDrawPopupArrow(&arrowRect, &pdi, cg, kHIThemeOrientationNormal);
+ }
+ }
+ break;
+ case CE_PushButtonLabel:
+ if (const QStyleOptionButton *b = qstyleoption_cast<const QStyleOptionButton *>(opt)) {
+ QStyleOptionButton btn(*b);
+ // We really don't want the label to be drawn the same as on
+ // windows style if it has an icon and text, then it should be more like a
+ // tab. So, cheat a little here. However, if it *is* only an icon
+ // the windows style works great, so just use that implementation.
+ bool hasMenu = btn.features & QStyleOptionButton::HasMenu;
+ bool hasIcon = !btn.icon.isNull();
+ bool hasText = !btn.text.isEmpty();
+
+ if (!hasMenu) {
+ if (tds == kThemeStatePressed
+ || (tds == kThemeStateActive
+ && ((btn.features & QStyleOptionButton::DefaultButton && !d->autoDefaultButton)
+ || d->autoDefaultButton == btn.styleObject)))
+ btn.palette.setColor(QPalette::ButtonText, Qt::white);
+ }
+
+ if (!hasIcon && !hasMenu) {
+ // ### this is really overly difficult, simplify.
+ // It basically tries to get the right font for "small" and "mini" icons.
+ QFont oldFont = p->font();
+ QFont newFont = qt_app_fonts_hash()->value("QPushButton", QFont());
+ ThemeFontID themeId = kThemePushButtonFont;
+ if (oldFont == newFont) { // Yes, use HITheme to draw the text for small sizes.
+ switch (d->aquaSizeConstrain(opt, w)) {
+ default:
+ break;
+ case QAquaSizeSmall:
+ themeId = kThemeSmallSystemFont;
+ break;
+ case QAquaSizeMini:
+ themeId = kThemeMiniSystemFont;
+ break;
+ }
+ }
+
+ if (themeId == kThemePushButtonFont) {
+ QCommonStyle::drawControl(ce, &btn, p, w);
+ } else {
+ p->save();
+ CGContextSetShouldAntialias(cg, true);
+ CGContextSetShouldSmoothFonts(cg, true);
+ HIThemeTextInfo tti;
+ tti.version = qt_mac_hitheme_version;
+ tti.state = tds;
+ QColor textColor;
+ textColor = btn.palette.buttonText().color();
+ CGFloat colorComp[] = { static_cast<CGFloat>(textColor.redF()), static_cast<CGFloat>(textColor.greenF()),
+ static_cast<CGFloat>(textColor.blueF()), static_cast<CGFloat>(textColor.alphaF()) };
+ CGContextSetFillColorSpace(cg, qt_mac_genericColorSpace());
+ CGContextSetFillColor(cg, colorComp);
+ tti.fontID = themeId;
+ tti.horizontalFlushness = kHIThemeTextHorizontalFlushCenter;
+ tti.verticalFlushness = kHIThemeTextVerticalFlushCenter;
+ tti.options = kHIThemeTextBoxOptionNone;
+ tti.truncationPosition = kHIThemeTextTruncationNone;
+ tti.truncationMaxLines = 1 + btn.text.count(QLatin1Char('\n'));
+ QCFString buttonText = qt_mac_removeMnemonics(btn.text);
+ QRect r = btn.rect;
+ HIRect bounds = qt_hirectForQRect(r);
+ HIThemeDrawTextBox(buttonText, &bounds, &tti,
+ cg, kHIThemeOrientationNormal);
+ p->restore();
+ }
+ } else {
+ if (hasIcon && !hasText) {
+ QCommonStyle::drawControl(ce, &btn, p, w);
+ } else {
+ QRect freeContentRect = btn.rect;
+ QRect textRect = itemTextRect(
+ btn.fontMetrics, freeContentRect, Qt::AlignCenter, btn.state & State_Enabled, btn.text);
+ if (hasMenu) {
+ textRect.moveTo(w ? 15 : 11, textRect.top()); // Supports Qt Quick Controls
+ }
+ // Draw the icon:
+ if (hasIcon) {
+ int contentW = textRect.width();
+ if (hasMenu)
+ contentW += proxy()->pixelMetric(PM_MenuButtonIndicator) + 4;
+ QIcon::Mode mode = btn.state & State_Enabled ? QIcon::Normal : QIcon::Disabled;
+ if (mode == QIcon::Normal && btn.state & State_HasFocus)
+ mode = QIcon::Active;
+ // Decide if the icon is should be on or off:
+ QIcon::State state = QIcon::Off;
+ if (btn.state & State_On)
+ state = QIcon::On;
+ QPixmap pixmap = btn.icon.pixmap(window, btn.iconSize, mode, state);
+ int pixmapWidth = pixmap.width() / pixmap.devicePixelRatio();
+ int pixmapHeight = pixmap.height() / pixmap.devicePixelRatio();
+ contentW += pixmapWidth + QMacStylePrivate::PushButtonContentPadding;
+ int iconLeftOffset = freeContentRect.x() + (freeContentRect.width() - contentW) / 2;
+ int iconTopOffset = freeContentRect.y() + (freeContentRect.height() - pixmapHeight) / 2;
+ QRect iconDestRect(iconLeftOffset, iconTopOffset, pixmapWidth, pixmapHeight);
+ QRect visualIconDestRect = visualRect(btn.direction, freeContentRect, iconDestRect);
+ proxy()->drawItemPixmap(p, visualIconDestRect, Qt::AlignLeft | Qt::AlignVCenter, pixmap);
+ int newOffset = iconDestRect.x() + iconDestRect.width()
+ + QMacStylePrivate::PushButtonContentPadding - textRect.x();
+ textRect.adjust(newOffset, 0, newOffset, 0);
+ }
+ // Draw the text:
+ if (hasText) {
+ textRect = visualRect(btn.direction, freeContentRect, textRect);
+ proxy()->drawItemText(p, textRect, Qt::AlignLeft | Qt::AlignVCenter | Qt::TextShowMnemonic, btn.palette,
+ (btn.state & State_Enabled), btn.text, QPalette::ButtonText);
+ }
+ }
+ }
+ }
+ break;
+ case CE_ComboBoxLabel:
+ if (const QStyleOptionComboBox *cb = qstyleoption_cast<const QStyleOptionComboBox *>(opt)) {
+ QStyleOptionComboBox comboCopy = *cb;
+ comboCopy.direction = Qt::LeftToRight;
+ QCommonStyle::drawControl(CE_ComboBoxLabel, &comboCopy, p, w);
+ }
+ break;
+#ifndef QT_NO_TABBAR
+ case CE_TabBarTabShape:
+ if (const QStyleOptionTab *tabOpt = qstyleoption_cast<const QStyleOptionTab *>(opt)) {
+ if (tabOpt->documentMode) {
+ p->save();
+ bool isUnified = false;
+ if (w) {
+ QRect tabRect = tabOpt->rect;
+ QPoint windowTabStart = w->mapTo(w->window(), tabRect.topLeft());
+ isUnified = isInMacUnifiedToolbarArea(w->window()->windowHandle(), windowTabStart.y());
+ }
+
+ const int tabOverlap = proxy()->pixelMetric(PM_TabBarTabOverlap, opt, w);
+ drawTabShape(p, tabOpt, isUnified, tabOverlap);
+
+ p->restore();
+ return;
+ }
+
+ HIThemeTabDrawInfo tdi;
+ tdi.version = 1;
+ tdi.style = kThemeTabNonFront;
+ tdi.direction = getTabDirection(tabOpt->shape);
+ switch (d->aquaSizeConstrain(opt, w)) {
+ default:
+ case QAquaSizeUnknown:
+ case QAquaSizeLarge:
+ tdi.size = kHIThemeTabSizeNormal;
+ break;
+ case QAquaSizeSmall:
+ tdi.size = kHIThemeTabSizeSmall;
+ break;
+ case QAquaSizeMini:
+ tdi.size = kHIThemeTabSizeMini;
+ break;
+ }
+ bool verticalTabs = tdi.direction == kThemeTabWest || tdi.direction == kThemeTabEast;
+ QRect tabRect = tabOpt->rect;
+
+ bool selected = tabOpt->state & State_Selected;
+ if (selected) {
+ if (!(tabOpt->state & State_Active))
+ tdi.style = kThemeTabFrontUnavailable;
+ else if (!(tabOpt->state & State_Enabled))
+ tdi.style = kThemeTabFrontInactive;
+ else
+ tdi.style = kThemeTabFront;
+ } else if (!(tabOpt->state & State_Active)) {
+ tdi.style = kThemeTabNonFrontUnavailable;
+ } else if (!(tabOpt->state & State_Enabled)) {
+ tdi.style = kThemeTabNonFrontInactive;
+ } else if (tabOpt->state & State_Sunken) {
+ tdi.style = kThemeTabNonFrontPressed;
+ }
+ if (tabOpt->state & State_HasFocus)
+ tdi.adornment = kHIThemeTabAdornmentFocus;
+ else
+ tdi.adornment = kHIThemeTabAdornmentNone;
+ tdi.kind = kHIThemeTabKindNormal;
+
+ QStyleOptionTab::TabPosition tp = tabOpt->position;
+ QStyleOptionTab::SelectedPosition sp = tabOpt->selectedPosition;
+ if (tabOpt->direction == Qt::RightToLeft && !verticalTabs) {
+ if (sp == QStyleOptionTab::NextIsSelected)
+ sp = QStyleOptionTab::PreviousIsSelected;
+ else if (sp == QStyleOptionTab::PreviousIsSelected)
+ sp = QStyleOptionTab::NextIsSelected;
+ switch (tp) {
+ case QStyleOptionTab::Beginning:
+ tp = QStyleOptionTab::End;
+ break;
+ case QStyleOptionTab::End:
+ tp = QStyleOptionTab::Beginning;
+ break;
+ default:
+ break;
+ }
+ }
+ bool stretchTabs = (!verticalTabs && tabRect.height() > 22) || (verticalTabs && tabRect.width() > 22);
+
+ switch (tp) {
+ case QStyleOptionTab::Beginning:
+ tdi.position = kHIThemeTabPositionFirst;
+ if (sp != QStyleOptionTab::NextIsSelected || stretchTabs)
+ tdi.adornment |= kHIThemeTabAdornmentTrailingSeparator;
+ break;
+ case QStyleOptionTab::Middle:
+ tdi.position = kHIThemeTabPositionMiddle;
+ if (selected)
+ tdi.adornment |= kHIThemeTabAdornmentLeadingSeparator;
+ if (sp != QStyleOptionTab::NextIsSelected || stretchTabs) // Also when we're selected.
+ tdi.adornment |= kHIThemeTabAdornmentTrailingSeparator;
+ break;
+ case QStyleOptionTab::End:
+ tdi.position = kHIThemeTabPositionLast;
+ if (selected)
+ tdi.adornment |= kHIThemeTabAdornmentLeadingSeparator;
+ break;
+ case QStyleOptionTab::OnlyOneTab:
+ tdi.position = kHIThemeTabPositionOnly;
+ break;
+ }
+ // HITheme doesn't stretch its tabs. Therefore we have to cheat and do the job ourselves.
+ if (stretchTabs) {
+ HIRect hirect = CGRectMake(0, 0, 23, 23);
+ QPixmap pm(23, 23);
+ pm.fill(Qt::transparent);
+ {
+ QMacCGContext pmcg(&pm);
+ HIThemeDrawTab(&hirect, &tdi, pmcg, kHIThemeOrientationNormal, 0);
+ }
+ QStyleHelper::drawBorderPixmap(pm, p, tabRect, 7, 7, 7, 7);
+ } else {
+ HIRect hirect = qt_hirectForQRect(tabRect);
+ HIThemeDrawTab(&hirect, &tdi, cg, kHIThemeOrientationNormal, 0);
+ }
+ }
+ break;
+ case CE_TabBarTabLabel:
+ if (const QStyleOptionTab *tab = qstyleoption_cast<const QStyleOptionTab *>(opt)) {
+ QStyleOptionTab myTab = *tab;
+ ThemeTabDirection ttd = getTabDirection(myTab.shape);
+ bool verticalTabs = ttd == kThemeTabWest || ttd == kThemeTabEast;
+
+ // Check to see if we use have the same as the system font
+ // (QComboMenuItem is internal and should never be seen by the
+ // outside world, unless they read the source, in which case, it's
+ // their own fault).
+ bool nonDefaultFont = p->font() != qt_app_fonts_hash()->value("QComboMenuItem");
+ if (verticalTabs || nonDefaultFont || !tab->icon.isNull()
+ || !myTab.leftButtonSize.isEmpty() || !myTab.rightButtonSize.isEmpty()) {
+ int heightOffset = 0;
+ if (verticalTabs) {
+ heightOffset = -1;
+ } else if (nonDefaultFont) {
+ if (p->fontMetrics().height() == myTab.rect.height())
+ heightOffset = 2;
+ }
+ myTab.rect.setHeight(myTab.rect.height() + heightOffset);
+
+ QCommonStyle::drawControl(ce, &myTab, p, w);
+ } else {
+ p->save();
+ CGContextSetShouldAntialias(cg, true);
+ CGContextSetShouldSmoothFonts(cg, true);
+ HIThemeTextInfo tti;
+ tti.version = qt_mac_hitheme_version;
+ tti.state = tds;
+ QColor textColor = myTab.palette.windowText().color();
+ CGFloat colorComp[] = { static_cast<CGFloat>(textColor.redF()), static_cast<CGFloat>(textColor.greenF()),
+ static_cast<CGFloat>(textColor.blueF()), static_cast<CGFloat>(textColor.alphaF()) };
+ CGContextSetFillColorSpace(cg, qt_mac_genericColorSpace());
+ CGContextSetFillColor(cg, colorComp);
+ switch (d->aquaSizeConstrain(opt, w)) {
+ default:
+ case QAquaSizeUnknown:
+ case QAquaSizeLarge:
+ tti.fontID = kThemeSystemFont;
+ break;
+ case QAquaSizeSmall:
+ tti.fontID = kThemeSmallSystemFont;
+ break;
+ case QAquaSizeMini:
+ tti.fontID = kThemeMiniSystemFont;
+ break;
+ }
+ tti.horizontalFlushness = kHIThemeTextHorizontalFlushCenter;
+ tti.verticalFlushness = kHIThemeTextVerticalFlushCenter;
+ tti.options = verticalTabs ? kHIThemeTextBoxOptionStronglyVertical : kHIThemeTextBoxOptionNone;
+ tti.truncationPosition = kHIThemeTextTruncationNone;
+ tti.truncationMaxLines = 1 + myTab.text.count(QLatin1Char('\n'));
+ QCFString tabText = qt_mac_removeMnemonics(myTab.text);
+ QRect r = myTab.rect.adjusted(0, 0, 0, -1);
+ HIRect bounds = qt_hirectForQRect(r);
+ HIThemeDrawTextBox(tabText, &bounds, &tti, cg, kHIThemeOrientationNormal);
+ p->restore();
+ }
+ }
+ break;
+#endif
+#ifndef QT_NO_DOCKWIDGET
+ case CE_DockWidgetTitle:
+ if (const QDockWidget *dockWidget = qobject_cast<const QDockWidget *>(w)) {
+ bool floating = dockWidget->isFloating();
+ if (floating) {
+ ThemeDrawState tds = d->getDrawState(opt->state);
+ HIThemeWindowDrawInfo wdi;
+ wdi.version = qt_mac_hitheme_version;
+ wdi.state = tds;
+ wdi.windowType = kThemeMovableDialogWindow;
+ wdi.titleHeight = opt->rect.height();
+ wdi.titleWidth = opt->rect.width();
+ wdi.attributes = 0;
+
+ HIRect titleBarRect;
+ HIRect tmpRect = qt_hirectForQRect(opt->rect);
+ {
+ QCFType<HIShapeRef> titleRegion;
+ QRect newr = opt->rect.adjusted(0, 0, 2, 0);
+ HIThemeGetWindowShape(&tmpRect, &wdi, kWindowTitleBarRgn, &titleRegion);
+ ptrHIShapeGetBounds(titleRegion, &tmpRect);
+ newr.translate(newr.x() - int(tmpRect.origin.x), newr.y() - int(tmpRect.origin.y));
+ titleBarRect = qt_hirectForQRect(newr);
+ }
+ QMacCGContext cg(p);
+ HIThemeDrawWindowFrame(&titleBarRect, &wdi, cg, kHIThemeOrientationNormal, 0);
+ } else {
+ // fill title bar background
+ QLinearGradient linearGrad(0, opt->rect.top(), 0, opt->rect.bottom());
+ linearGrad.setColorAt(0, mainWindowGradientBegin);
+ linearGrad.setColorAt(1, mainWindowGradientEnd);
+ p->fillRect(opt->rect, linearGrad);
+
+ // draw horizontal lines at top and bottom
+ p->save();
+ p->setPen(mainWindowGradientBegin.lighter(114));
+ p->drawLine(opt->rect.topLeft(), opt->rect.topRight());
+ p->setPen(mainWindowGradientEnd.darker(114));
+ p->drawLine(opt->rect.bottomLeft(), opt->rect.bottomRight());
+ p->restore();
+ }
+ }
+
+ // Draw the text...
+ if (const QStyleOptionDockWidget *dwOpt = qstyleoption_cast<const QStyleOptionDockWidget *>(opt)) {
+ if (!dwOpt->title.isEmpty()) {
+
+ const bool verticalTitleBar = dwOpt->verticalTitleBar;
+
+ QRect titleRect = subElementRect(SE_DockWidgetTitleBarText, opt, w);
+ if (verticalTitleBar) {
+ QRect rect = dwOpt->rect;
+ QRect r = rect.transposed();
+
+ titleRect = QRect(r.left() + rect.bottom()
+ - titleRect.bottom(),
+ r.top() + titleRect.left() - rect.left(),
+ titleRect.height(), titleRect.width());
+
+ p->translate(r.left(), r.top() + r.width());
+ p->rotate(-90);
+ p->translate(-r.left(), -r.top());
+ }
+
+ QFont oldFont = p->font();
+ p->setFont(qt_app_fonts_hash()->value("QToolButton", p->font()));
+ QString text = p->fontMetrics().elidedText(dwOpt->title, Qt::ElideRight,
+ titleRect.width());
+ drawItemText(p, titleRect,
+ Qt::AlignCenter | Qt::TextShowMnemonic, dwOpt->palette,
+ dwOpt->state & State_Enabled, text,
+ QPalette::WindowText);
+ p->setFont(oldFont);
+ }
+ }
+ break;
+#endif
+ case CE_FocusFrame: {
+ 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);
+ break; }
+ case CE_MenuItem:
+ case CE_MenuEmptyArea:
+ if (const QStyleOptionMenuItem *mi = qstyleoption_cast<const QStyleOptionMenuItem *>(opt)) {
+ p->fillRect(mi->rect, opt->palette.background());
+ QAquaWidgetSize widgetSize = d->aquaSizeConstrain(opt, w);
+ int tabwidth = mi->tabWidth;
+ int maxpmw = mi->maxIconWidth;
+ bool active = mi->state & State_Selected;
+ bool enabled = mi->state & State_Enabled;
+ HIRect menuRect = qt_hirectForQRect(mi->menuRect);
+ HIRect itemRect = qt_hirectForQRect(mi->rect);
+ HIThemeMenuItemDrawInfo mdi;
+ mdi.version = qt_mac_hitheme_version;
+ mdi.itemType = kThemeMenuItemPlain;
+ if (!mi->icon.isNull())
+ mdi.itemType |= kThemeMenuItemHasIcon;
+ if (mi->menuItemType == QStyleOptionMenuItem::SubMenu)
+ mdi.itemType |= kThemeMenuItemHierarchical | kThemeMenuItemHierBackground;
+ else
+ mdi.itemType |= kThemeMenuItemPopUpBackground;
+ if (enabled)
+ mdi.state = kThemeMenuActive;
+ else
+ mdi.state = kThemeMenuDisabled;
+ if (active)
+ mdi.state |= kThemeMenuSelected;
+ QRect contentRect;
+ if (mi->menuItemType == QStyleOptionMenuItem::Separator) {
+ // First arg should be &menurect, but wacky stuff happens then.
+ HIThemeDrawMenuSeparator(&itemRect, &itemRect, &mdi,
+ cg, kHIThemeOrientationNormal);
+ break;
+ } else {
+ HIRect cr;
+ bool needAlpha = mi->palette.color(QPalette::Button) == Qt::transparent;
+ if (needAlpha) {
+ CGContextSaveGState(cg);
+ CGContextSetAlpha(cg, 0.0);
+ }
+ HIThemeDrawMenuItem(&menuRect, &itemRect, &mdi,
+ cg, kHIThemeOrientationNormal, &cr);
+ if (needAlpha)
+ CGContextRestoreGState(cg);
+ if (ce == CE_MenuEmptyArea)
+ break;
+ contentRect = qt_qrectForHIRect(cr);
+ }
+ int xpos = contentRect.x() + 18;
+ int checkcol = maxpmw;
+ if (!enabled)
+ p->setPen(mi->palette.text().color());
+ else if (active)
+ p->setPen(mi->palette.highlightedText().color());
+ else
+ p->setPen(mi->palette.buttonText().color());
+
+ if (mi->checked) {
+ QStyleOption checkmarkOpt;
+ checkmarkOpt.initFrom(w);
+
+ const int mw = checkcol + macItemFrame;
+ const int mh = contentRect.height() + macItemFrame;
+ const int xp = contentRect.x() + macItemFrame;
+ checkmarkOpt.rect = QRect(xp, contentRect.y() - checkmarkOpt.fontMetrics.descent(), mw, mh);
+
+ checkmarkOpt.state |= State_On; // Always on. Never rendered when off.
+ checkmarkOpt.state.setFlag(State_Selected, active);
+ checkmarkOpt.state.setFlag(State_Enabled, enabled);
+ if (widgetSize == QAquaSizeMini)
+ checkmarkOpt.state |= State_Mini;
+ else if (widgetSize == QAquaSizeSmall)
+ checkmarkOpt.state |= State_Small;
+
+ // We let drawPrimitive(PE_IndicatorMenuCheckMark) pick the right color
+ checkmarkOpt.palette.setColor(QPalette::HighlightedText, p->pen().color());
+ checkmarkOpt.palette.setColor(QPalette::Text, p->pen().color());
+
+ proxy()->drawPrimitive(PE_IndicatorMenuCheckMark, &checkmarkOpt, p, w);
+ }
+ if (!mi->icon.isNull()) {
+ QIcon::Mode mode = (mi->state & State_Enabled) ? QIcon::Normal
+ : QIcon::Disabled;
+ // Always be normal or disabled to follow the Mac style.
+ int smallIconSize = proxy()->pixelMetric(PM_SmallIconSize);
+ QSize iconSize(smallIconSize, smallIconSize);
+#ifndef QT_NO_COMBOBOX
+ if (const QComboBox *comboBox = qobject_cast<const QComboBox *>(w)) {
+ iconSize = comboBox->iconSize();
+ }
+#endif
+ QPixmap pixmap = mi->icon.pixmap(window, iconSize, mode);
+ int pixw = pixmap.width() / pixmap.devicePixelRatio();
+ int pixh = pixmap.height() / pixmap.devicePixelRatio();
+ QRect cr(xpos, contentRect.y(), checkcol, contentRect.height());
+ QRect pmr(0, 0, pixw, pixh);
+ pmr.moveCenter(cr.center());
+ p->drawPixmap(pmr.topLeft(), pixmap);
+ xpos += pixw + 6;
+ }
+
+ QString s = mi->text;
+ if (!s.isEmpty()) {
+ int t = s.indexOf(QLatin1Char('\t'));
+ int text_flags = Qt::AlignRight | Qt::AlignVCenter | Qt::TextHideMnemonic
+ | Qt::TextSingleLine | Qt::AlignAbsolute;
+ int yPos = contentRect.y();
+ if (widgetSize == QAquaSizeMini)
+ yPos += 1;
+ p->save();
+ if (t >= 0) {
+ p->setFont(qt_app_fonts_hash()->value("QMenuItem", p->font()));
+ int xp = contentRect.right() - tabwidth - macRightBorder
+ - macItemHMargin - macItemFrame + 1;
+ p->drawText(xp, yPos, tabwidth, contentRect.height(), text_flags,
+ s.mid(t + 1));
+ s = s.left(t);
+ }
+
+ const int xm = macItemFrame + maxpmw + macItemHMargin;
+ QFont myFont = mi->font;
+ // myFont may not have any "hard" flags set. We override
+ // the point size so that when it is resolved against the device, this font will win.
+ // This is mainly to handle cases where someone sets the font on the window
+ // and then the combo inherits it and passes it onward. At that point the resolve mask
+ // is very, very weak. This makes it stonger.
+ myFont.setPointSizeF(QFontInfo(mi->font).pointSizeF());
+ p->setFont(myFont);
+ p->drawText(xpos, yPos, contentRect.width() - xm - tabwidth + 1,
+ contentRect.height(), text_flags ^ Qt::AlignRight, s);
+ p->restore();
+ }
+ }
+ break;
+ case CE_MenuHMargin:
+ case CE_MenuVMargin:
+ case CE_MenuTearoff:
+ case CE_MenuScroller:
+ if (const QStyleOptionMenuItem *mi = qstyleoption_cast<const QStyleOptionMenuItem *>(opt)) {
+ p->fillRect(mi->rect, opt->palette.background());
+
+ HIRect menuRect = qt_hirectForQRect(mi->menuRect);
+ HIRect itemRect = qt_hirectForQRect(mi->rect);
+ HIThemeMenuItemDrawInfo mdi;
+ mdi.version = qt_mac_hitheme_version;
+ if (!(opt->state & State_Enabled))
+ mdi.state = kThemeMenuDisabled;
+ else if (opt->state & State_Selected)
+ mdi.state = kThemeMenuSelected;
+ else
+ mdi.state = kThemeMenuActive;
+ if (ce == CE_MenuScroller) {
+ if (opt->state & State_DownArrow)
+ mdi.itemType = kThemeMenuItemScrollDownArrow;
+ else
+ mdi.itemType = kThemeMenuItemScrollUpArrow;
+ } else {
+ mdi.itemType = kThemeMenuItemPlain;
+ }
+ HIThemeDrawMenuItem(&menuRect, &itemRect, &mdi,
+ cg,
+ kHIThemeOrientationNormal, 0);
+ if (ce == CE_MenuTearoff) {
+ p->setPen(QPen(mi->palette.dark().color(), 1, Qt::DashLine));
+ p->drawLine(mi->rect.x() + 2, mi->rect.y() + mi->rect.height() / 2 - 1,
+ mi->rect.x() + mi->rect.width() - 4,
+ mi->rect.y() + mi->rect.height() / 2 - 1);
+ p->setPen(QPen(mi->palette.light().color(), 1, Qt::DashLine));
+ p->drawLine(mi->rect.x() + 2, mi->rect.y() + mi->rect.height() / 2,
+ mi->rect.x() + mi->rect.width() - 4,
+ mi->rect.y() + mi->rect.height() / 2);
+ }
+ }
+ break;
+ case CE_MenuBarItem:
+ if (const QStyleOptionMenuItem *mi = qstyleoption_cast<const QStyleOptionMenuItem *>(opt)) {
+ HIRect menuRect = qt_hirectForQRect(mi->menuRect);
+ HIRect itemRect = qt_hirectForQRect(mi->rect);
+
+ const bool selected = (opt->state & State_Selected) && (opt->state & State_Enabled) && (opt->state & State_Sunken);
+ if (selected) {
+ // Draw a selected menu item background:
+ HIThemeMenuItemDrawInfo mdi;
+ mdi.version = qt_mac_hitheme_version;
+ mdi.state = kThemeMenuSelected;
+ mdi.itemType = kThemeMenuItemPlain;
+ HIThemeDrawMenuItem(&menuRect, &itemRect, &mdi, cg, kHIThemeOrientationNormal, 0);
+ } else {
+ // Draw the toolbar background:
+ HIThemeMenuBarDrawInfo bdi;
+ bdi.version = qt_mac_hitheme_version;
+ bdi.state = kThemeMenuBarNormal;
+ bdi.attributes = 0;
+ HIThemeDrawMenuBarBackground(&menuRect, &bdi, cg, kHIThemeOrientationNormal);
+ }
+
+ if (!mi->icon.isNull()) {
+ int iconExtent = proxy()->pixelMetric(PM_SmallIconSize);
+ drawItemPixmap(p, mi->rect,
+ Qt::AlignCenter | Qt::TextHideMnemonic | Qt::TextDontClip
+ | Qt::TextSingleLine,
+ mi->icon.pixmap(window, QSize(iconExtent, iconExtent),
+ (mi->state & State_Enabled) ? QIcon::Normal : QIcon::Disabled));
+ } else {
+ drawItemText(p, mi->rect,
+ Qt::AlignCenter | Qt::TextHideMnemonic | Qt::TextDontClip
+ | Qt::TextSingleLine,
+ mi->palette, mi->state & State_Enabled,
+ mi->text, selected ? QPalette::HighlightedText : QPalette::ButtonText);
+ }
+ }
+ break;
+ case CE_MenuBarEmptyArea:
+ if (const QStyleOptionMenuItem *mi = qstyleoption_cast<const QStyleOptionMenuItem *>(opt)) {
+ HIThemeMenuBarDrawInfo bdi;
+ bdi.version = qt_mac_hitheme_version;
+ bdi.state = kThemeMenuBarNormal;
+ bdi.attributes = 0;
+ HIRect hirect = qt_hirectForQRect(mi->rect);
+ HIThemeDrawMenuBarBackground(&hirect, &bdi, cg,
+ kHIThemeOrientationNormal);
+ break;
+ }
+ case CE_ProgressBarContents:
+ if (const QStyleOptionProgressBar *pb = qstyleoption_cast<const QStyleOptionProgressBar *>(opt)) {
+ HIThemeTrackDrawInfo tdi;
+ tdi.version = qt_mac_hitheme_version;
+ tdi.reserved = 0;
+ bool isIndeterminate = (pb->minimum == 0 && pb->maximum == 0);
+ const bool vertical = pb->orientation == Qt::Vertical;
+ const bool inverted = pb->invertedAppearance;
+ bool reverse = (!vertical && (pb->direction == Qt::RightToLeft));
+ if (inverted)
+ reverse = !reverse;
+ switch (d->aquaSizeConstrain(opt, w)) {
+ case QAquaSizeUnknown:
+ case QAquaSizeLarge:
+ tdi.kind = !isIndeterminate ? kThemeLargeProgressBar
+ : kThemeLargeIndeterminateBar;
+ break;
+ case QAquaSizeMini:
+ case QAquaSizeSmall:
+ tdi.kind = !isIndeterminate ? kThemeProgressBar : kThemeIndeterminateBar;
+ break;
+ }
+ tdi.bounds = qt_hirectForQRect(pb->rect);
+ tdi.max = pb->maximum;
+ tdi.min = pb->minimum;
+ tdi.value = pb->progress;
+ tdi.attributes = vertical ? 0 : kThemeTrackHorizontal;
+
+ if (isIndeterminate) {
+ if (QProgressStyleAnimation *animation = qobject_cast<QProgressStyleAnimation*>(d->animation(opt->styleObject)))
+ tdi.trackInfo.progress.phase = animation->animationStep();
+ else if (opt->styleObject)
+ d->startAnimation(new QProgressStyleAnimation(d->animateSpeed(QMacStylePrivate::AquaProgressBar), opt->styleObject));
+ } else {
+ d->stopAnimation(opt->styleObject);
+ }
+ if (!(pb->state & State_Active))
+ tdi.enableState = kThemeTrackInactive;
+ else if (!(pb->state & State_Enabled))
+ tdi.enableState = kThemeTrackDisabled;
+ else
+ tdi.enableState = kThemeTrackActive;
+ HIThemeOrientation drawOrientation = kHIThemeOrientationNormal;
+ if (reverse) {
+ if (vertical) {
+ drawOrientation = kHIThemeOrientationInverted;
+ } else {
+ CGContextSaveGState(cg);
+ CGContextTranslateCTM(cg, pb->rect.width(), 0);
+ CGContextScaleCTM(cg, -1, 1);
+ }
+ }
+ HIThemeDrawTrack(&tdi, 0, cg, drawOrientation);
+ if (reverse && !vertical)
+ CGContextRestoreGState(cg);
+ }
+ break;
+ case CE_ProgressBarLabel:
+ case CE_ProgressBarGroove:
+ break;
+ case CE_SizeGrip: {
+ if (w && w->testAttribute(Qt::WA_MacOpaqueSizeGrip)) {
+ HIThemeGrowBoxDrawInfo gdi;
+ gdi.version = qt_mac_hitheme_version;
+ gdi.state = tds;
+ gdi.kind = kHIThemeGrowBoxKindNormal;
+ gdi.direction = kThemeGrowRight | kThemeGrowDown;
+ gdi.size = kHIThemeGrowBoxSizeNormal;
+ HIPoint pt = CGPointMake(opt->rect.x(), opt->rect.y());
+ HIThemeDrawGrowBox(&pt, &gdi, cg, kHIThemeOrientationNormal);
+ } else {
+ // It isn't possible to draw a transparent size grip with the
+ // native API, so we do it ourselves here.
+ const bool metal = qt_mac_is_metal(w);
+ QPen lineColor = metal ? QColor(236, 236, 236) : QColor(82, 82, 82, 192);
+ QPen metalHighlight = QColor(5, 5, 5, 192);
+ lineColor.setWidth(1);
+ p->save();
+ p->setRenderHint(QPainter::Antialiasing);
+ p->setPen(lineColor);
+ const Qt::LayoutDirection layoutDirection = w ? w->layoutDirection() : qApp->layoutDirection();
+ const int NumLines = metal ? 4 : 3;
+ for (int l = 0; l < NumLines; ++l) {
+ const int offset = (l * 4 + (metal ? 2 : 3));
+ QPoint start, end;
+ if (layoutDirection == Qt::LeftToRight) {
+ start = QPoint(opt->rect.width() - offset, opt->rect.height() - 1);
+ end = QPoint(opt->rect.width() - 1, opt->rect.height() - offset);
+ } else {
+ start = QPoint(offset, opt->rect.height() - 1);
+ end = QPoint(1, opt->rect.height() - offset);
+ }
+ p->drawLine(start, end);
+ if (metal) {
+ p->setPen(metalHighlight);
+ p->setRenderHint(QPainter::Antialiasing, false);
+ p->drawLine(start + QPoint(0, -1), end + QPoint(0, -1));
+ p->setRenderHint(QPainter::Antialiasing, true);
+ p->setPen(lineColor);
+ }
+ }
+ p->restore();
+ }
+ break;
+ }
+ case CE_Splitter:
+ if (opt->rect.width() > 1 && opt->rect.height() > 1){
+ HIThemeSplitterDrawInfo sdi;
+ sdi.version = qt_mac_hitheme_version;
+ sdi.state = tds;
+ sdi.adornment = kHIThemeSplitterAdornmentMetal;
+ HIRect hirect = qt_hirectForQRect(opt->rect);
+ HIThemeDrawPaneSplitter(&hirect, &sdi, cg, kHIThemeOrientationNormal);
+ } else {
+ QPen oldPen = p->pen();
+ p->setPen(opt->palette.dark().color());
+ if (opt->state & QStyle::State_Horizontal)
+ p->drawLine(opt->rect.topLeft(), opt->rect.bottomLeft());
+ else
+ p->drawLine(opt->rect.topLeft(), opt->rect.topRight());
+ p->setPen(oldPen);
+ }
+ break;
+ case CE_RubberBand:
+ if (const QStyleOptionRubberBand *rubber = qstyleoption_cast<const QStyleOptionRubberBand *>(opt)) {
+ QColor fillColor(opt->palette.color(QPalette::Disabled, QPalette::Highlight));
+ if (!rubber->opaque) {
+ QColor strokeColor;
+ // I retrieved these colors from the Carbon-Dev mailing list
+ strokeColor.setHsvF(0, 0, 0.86, 1.0);
+ fillColor.setHsvF(0, 0, 0.53, 0.25);
+ if (opt->rect.width() * opt->rect.height() <= 3) {
+ p->fillRect(opt->rect, strokeColor);
+ } else {
+ QPen oldPen = p->pen();
+ QBrush oldBrush = p->brush();
+ QPen pen(strokeColor);
+ p->setPen(pen);
+ p->setBrush(fillColor);
+ QRect adjusted = opt->rect.adjusted(1, 1, -1, -1);
+ if (adjusted.isValid())
+ p->drawRect(adjusted);
+ p->setPen(oldPen);
+ p->setBrush(oldBrush);
+ }
+ } else {
+ p->fillRect(opt->rect, fillColor);
+ }
+ }
+ break;
+#ifndef QT_NO_TOOLBAR
+ case CE_ToolBar: {
+ const QStyleOptionToolBar *toolBar = qstyleoption_cast<const QStyleOptionToolBar *>(opt);
+
+ // 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
+ // the title bar. The following code fills the toolBar area with transparent pixels
+ // to make that gradient visible.
+ if (w) {
+#ifndef QT_NO_MAINWINDOW
+ if (QMainWindow * mainWindow = qobject_cast<QMainWindow *>(w->window())) {
+ if (toolBar && toolBar->toolBarArea == Qt::TopToolBarArea && mainWindow->unifiedTitleAndToolBarOnMac()) {
+
+ // fill with transparent pixels.
+ p->save();
+ p->setCompositionMode(QPainter::CompositionMode_Source);
+ p->fillRect(opt->rect, Qt::transparent);
+ p->restore();
+
+ // Drow a horizontal sepearator line at the toolBar bottom if the "unified" area ends here.
+ // There might be additional toolbars or other widgets such as tab bars in document
+ // mode below. Determine this by making a unified toolbar area test for the row below
+ // this toolbar.
+ QPoint windowToolbarEnd = w->mapTo(w->window(), opt->rect.bottomLeft());
+ bool isEndOfUnifiedArea = !isInMacUnifiedToolbarArea(w->window()->windowHandle(), windowToolbarEnd.y() + 1);
+ if (isEndOfUnifiedArea) {
+ SInt32 margin;
+ GetThemeMetric(kThemeMetricSeparatorSize, &margin);
+ CGRect separatorRect = CGRectMake(opt->rect.left(), opt->rect.bottom(), opt->rect.width(), margin);
+ HIThemeSeparatorDrawInfo separatorDrawInfo;
+ separatorDrawInfo.version = 0;
+ separatorDrawInfo.state = qt_macWindowMainWindow(mainWindow) ? kThemeStateActive : kThemeStateInactive;
+ QMacCGContext cg(p);
+ HIThemeDrawSeparator(&separatorRect, &separatorDrawInfo, cg, kHIThemeOrientationNormal);
+ }
+ break;
+ }
+ }
+#endif
+ }
+
+ // draw background gradient
+ QLinearGradient linearGrad;
+ if (opt->state & State_Horizontal)
+ linearGrad = QLinearGradient(0, opt->rect.top(), 0, opt->rect.bottom());
+ else
+ linearGrad = QLinearGradient(opt->rect.left(), 0, opt->rect.right(), 0);
+
+ linearGrad.setColorAt(0, mainWindowGradientBegin);
+ linearGrad.setColorAt(1, mainWindowGradientEnd);
+ p->fillRect(opt->rect, linearGrad);
+
+ p->save();
+ if (opt->state & State_Horizontal) {
+ p->setPen(mainWindowGradientBegin.lighter(114));
+ p->drawLine(opt->rect.topLeft(), opt->rect.topRight());
+ p->setPen(mainWindowGradientEnd.darker(114));
+ p->drawLine(opt->rect.bottomLeft(), opt->rect.bottomRight());
+
+ } else {
+ p->setPen(mainWindowGradientBegin.lighter(114));
+ p->drawLine(opt->rect.topLeft(), opt->rect.bottomLeft());
+ p->setPen(mainWindowGradientEnd.darker(114));
+ p->drawLine(opt->rect.topRight(), opt->rect.bottomRight());
+ }
+ p->restore();
+
+
+ } break;
+#endif
+ default:
+ QCommonStyle::drawControl(ce, opt, p, w);
+ break;
+ }
+}
+
+static void setLayoutItemMargins(int left, int top, int right, int bottom, QRect *rect, Qt::LayoutDirection dir)
+{
+ if (dir == Qt::RightToLeft) {
+ rect->adjust(-right, top, -left, bottom);
+ } else {
+ rect->adjust(left, top, right, bottom);
+ }
+}
+
+QRect QMacStyle::subElementRect(SubElement sr, const QStyleOption *opt,
+ const QWidget *widget) const
+{
+ Q_D(const QMacStyle);
+ QRect rect;
+ int controlSize = getControlSize(opt, widget);
+
+ switch (sr) {
+ case SE_ItemViewItemText:
+ if (const QStyleOptionViewItem *vopt = qstyleoption_cast<const QStyleOptionViewItem *>(opt)) {
+ int fw = proxy()->pixelMetric(PM_FocusFrameHMargin, opt, widget);
+ // We add the focusframeargin between icon and text in commonstyle
+ rect = QCommonStyle::subElementRect(sr, opt, widget);
+ if (vopt->features & QStyleOptionViewItem::HasDecoration)
+ rect.adjust(-fw, 0, 0, 0);
+ }
+ break;
+ case SE_ToolBoxTabContents:
+ rect = QCommonStyle::subElementRect(sr, opt, widget);
+ break;
+ case SE_PushButtonContents:
+ if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(opt)) {
+ // Unlike Carbon, we want the button to always be drawn inside its bounds.
+ // Therefore, the button is a bit smaller, so that even if it got focus,
+ // the focus 'shadow' will be inside. Adjust the content rect likewise.
+ HIThemeButtonDrawInfo bdi;
+ d->initHIThemePushButton(btn, widget, d->getDrawState(opt->state), &bdi);
+ HIRect contentRect = d->pushButtonContentBounds(btn, &bdi);
+ rect = qt_qrectForHIRect(contentRect);
+ }
+ break;
+ case SE_HeaderLabel: {
+ int margin = proxy()->pixelMetric(QStyle::PM_HeaderMargin, opt, widget);
+ rect.setRect(opt->rect.x() + margin, opt->rect.y(),
+ opt->rect.width() - margin * 2, opt->rect.height() - 2);
+ if (const QStyleOptionHeader *header = qstyleoption_cast<const QStyleOptionHeader *>(opt)) {
+ // Subtract width needed for arrow, if there is one
+ if (header->sortIndicator != QStyleOptionHeader::None) {
+ if (opt->state & State_Horizontal)
+ rect.setWidth(rect.width() - (opt->rect.height() / 2) - (margin * 2));
+ else
+ rect.setHeight(rect.height() - (opt->rect.width() / 2) - (margin * 2));
+ }
+ }
+ rect = visualRect(opt->direction, opt->rect, rect);
+ break;
+ }
+ case SE_ProgressBarGroove:
+ // Wrong in the secondary dimension, but accurate enough in the main dimension.
+ rect = opt->rect;
+ break;
+ case SE_ProgressBarLabel:
+ break;
+ case SE_ProgressBarContents:
+ rect = opt->rect;
+ break;
+ case SE_TreeViewDisclosureItem: {
+ HIRect inRect = CGRectMake(opt->rect.x(), opt->rect.y(),
+ opt->rect.width(), opt->rect.height());
+ HIThemeButtonDrawInfo bdi;
+ bdi.version = qt_mac_hitheme_version;
+ bdi.state = kThemeStateActive;
+ bdi.kind = kThemeDisclosureButton;
+ bdi.value = kThemeDisclosureRight;
+ bdi.adornment = kThemeAdornmentNone;
+ HIRect contentRect;
+ HIThemeGetButtonContentBounds(&inRect, &bdi, &contentRect);
+ QCFType<HIShapeRef> shape;
+ HIRect outRect;
+ HIThemeGetButtonShape(&inRect, &bdi, &shape);
+ ptrHIShapeGetBounds(shape, &outRect);
+ rect = QRect(int(outRect.origin.x + DisclosureOffset), int(outRect.origin.y),
+ int(contentRect.origin.x - outRect.origin.x + DisclosureOffset),
+ int(outRect.size.height));
+ break;
+ }
+#ifndef QT_NO_TABWIDGET
+ case SE_TabWidgetLeftCorner:
+ if (const QStyleOptionTabWidgetFrame *twf
+ = qstyleoption_cast<const QStyleOptionTabWidgetFrame *>(opt)) {
+ switch (twf->shape) {
+ case QTabBar::RoundedNorth:
+ case QTabBar::TriangularNorth:
+ rect = QRect(QPoint(0, 0), twf->leftCornerWidgetSize);
+ break;
+ case QTabBar::RoundedSouth:
+ case QTabBar::TriangularSouth:
+ rect = QRect(QPoint(0, twf->rect.height() - twf->leftCornerWidgetSize.height()),
+ twf->leftCornerWidgetSize);
+ break;
+ default:
+ break;
+ }
+ rect = visualRect(twf->direction, twf->rect, rect);
+ }
+ break;
+ case SE_TabWidgetRightCorner:
+ if (const QStyleOptionTabWidgetFrame *twf
+ = qstyleoption_cast<const QStyleOptionTabWidgetFrame *>(opt)) {
+ switch (twf->shape) {
+ case QTabBar::RoundedNorth:
+ case QTabBar::TriangularNorth:
+ rect = QRect(QPoint(twf->rect.width() - twf->rightCornerWidgetSize.width(), 0),
+ twf->rightCornerWidgetSize);
+ break;
+ case QTabBar::RoundedSouth:
+ case QTabBar::TriangularSouth:
+ rect = QRect(QPoint(twf->rect.width() - twf->rightCornerWidgetSize.width(),
+ twf->rect.height() - twf->rightCornerWidgetSize.height()),
+ twf->rightCornerWidgetSize);
+ break;
+ default:
+ break;
+ }
+ rect = visualRect(twf->direction, twf->rect, rect);
+ }
+ break;
+ case SE_TabWidgetTabContents:
+ rect = QCommonStyle::subElementRect(sr, opt, widget);
+ if (const QStyleOptionTabWidgetFrame *twf
+ = qstyleoption_cast<const QStyleOptionTabWidgetFrame *>(opt)) {
+ if (twf->lineWidth != 0) {
+ switch (getTabDirection(twf->shape)) {
+ case kThemeTabNorth:
+ rect.adjust(+1, +14, -1, -1);
+ break;
+ case kThemeTabSouth:
+ rect.adjust(+1, +1, -1, -14);
+ break;
+ case kThemeTabWest:
+ rect.adjust(+14, +1, -1, -1);
+ break;
+ case kThemeTabEast:
+ rect.adjust(+1, +1, -14, -1);
+ }
+ }
+ }
+ break;
+ case SE_TabBarTabText:
+ if (const QStyleOptionTab *tab = qstyleoption_cast<const QStyleOptionTab *>(opt)) {
+ d->tabLayout(tab, widget, &rect);
+ }
+ break;
+ case SE_TabBarTabLeftButton:
+ case SE_TabBarTabRightButton:
+ if (const QStyleOptionTab *tab = qstyleoption_cast<const QStyleOptionTab *>(opt)) {
+ bool selected = tab->state & State_Selected;
+ int verticalShift = proxy()->pixelMetric(QStyle::PM_TabBarTabShiftVertical, tab, widget);
+ int horizontalShift = proxy()->pixelMetric(QStyle::PM_TabBarTabShiftHorizontal, tab, widget);
+ int hpadding = 5;
+
+ bool verticalTabs = tab->shape == QTabBar::RoundedEast
+ || tab->shape == QTabBar::RoundedWest
+ || tab->shape == QTabBar::TriangularEast
+ || tab->shape == QTabBar::TriangularWest;
+
+ QRect tr = tab->rect;
+ if (tab->shape == QTabBar::RoundedSouth || tab->shape == QTabBar::TriangularSouth)
+ verticalShift = -verticalShift;
+ if (verticalTabs) {
+ qSwap(horizontalShift, verticalShift);
+ horizontalShift *= -1;
+ verticalShift *= -1;
+ }
+ if (tab->shape == QTabBar::RoundedWest || tab->shape == QTabBar::TriangularWest)
+ horizontalShift = -horizontalShift;
+
+ tr.adjust(0, 0, horizontalShift, verticalShift);
+ if (selected)
+ {
+ tr.setBottom(tr.bottom() - verticalShift);
+ tr.setRight(tr.right() - horizontalShift);
+ }
+
+ QSize size = (sr == SE_TabBarTabLeftButton) ? tab->leftButtonSize : tab->rightButtonSize;
+ int w = size.width();
+ int h = size.height();
+ int midHeight = static_cast<int>(qCeil(float(tr.height() - h) / 2));
+ int midWidth = ((tr.width() - w) / 2);
+
+ bool atTheTop = true;
+ switch (tab->shape) {
+ case QTabBar::RoundedWest:
+ case QTabBar::TriangularWest:
+ atTheTop = (sr == SE_TabBarTabLeftButton);
+ break;
+ case QTabBar::RoundedEast:
+ case QTabBar::TriangularEast:
+ atTheTop = (sr == SE_TabBarTabRightButton);
+ break;
+ default:
+ if (sr == SE_TabBarTabLeftButton)
+ rect = QRect(tab->rect.x() + hpadding, midHeight, w, h);
+ else
+ rect = QRect(tab->rect.right() - w - hpadding, midHeight, w, h);
+ rect = visualRect(tab->direction, tab->rect, rect);
+ }
+ if (verticalTabs) {
+ if (atTheTop)
+ rect = QRect(midWidth, tr.y() + tab->rect.height() - hpadding - h, w, h);
+ else
+ rect = QRect(midWidth, tr.y() + hpadding, w, h);
+ }
+ }
+ break;
+#endif
+ case SE_LineEditContents:
+ rect = QCommonStyle::subElementRect(sr, opt, widget);
+#ifndef QT_NO_COMBOBOX
+ if (widget && qobject_cast<const QComboBox*>(widget->parentWidget()))
+ rect.adjust(-1, -2, 0, 0);
+ else
+#endif
+ rect.adjust(-1, -1, 0, +1);
+ break;
+ case SE_CheckBoxLayoutItem:
+ rect = opt->rect;
+ if (controlSize == QAquaSizeLarge) {
+ setLayoutItemMargins(+2, +3, -9, -4, &rect, opt->direction);
+ } else if (controlSize == QAquaSizeSmall) {
+ setLayoutItemMargins(+1, +5, 0 /* fix */, -6, &rect, opt->direction);
+ } else {
+ setLayoutItemMargins(0, +7, 0 /* fix */, -6, &rect, opt->direction);
+ }
+ break;
+ case SE_ComboBoxLayoutItem:
+#ifndef QT_NO_TOOLBAR
+ if (widget && qobject_cast<QToolBar *>(widget->parentWidget())) {
+ // Do nothing, because QToolbar needs the entire widget rect.
+ // Otherwise it will be clipped. Equivalent to
+ // widget->setAttribute(Qt::WA_LayoutUsesWidgetRect), but without
+ // all the hassle.
+ } else
+#endif
+ {
+ rect = opt->rect;
+ if (controlSize == QAquaSizeLarge) {
+ rect.adjust(+3, +2, -3, -4);
+ } else if (controlSize == QAquaSizeSmall) {
+ setLayoutItemMargins(+2, +1, -3, -4, &rect, opt->direction);
+ } else {
+ setLayoutItemMargins(+1, 0, -2, 0, &rect, opt->direction);
+ }
+ }
+ break;
+ case SE_LabelLayoutItem:
+ rect = opt->rect;
+ setLayoutItemMargins(+1, 0 /* SHOULD be -1, done for alignment */, 0, 0 /* SHOULD be -1, done for alignment */, &rect, opt->direction);
+ break;
+ case SE_ProgressBarLayoutItem: {
+ rect = opt->rect;
+ int bottom = SIZE(3, 8, 8);
+ if (opt->state & State_Horizontal) {
+ rect.adjust(0, +1, 0, -bottom);
+ } else {
+ setLayoutItemMargins(+1, 0, -bottom, 0, &rect, opt->direction);
+ }
+ break;
+ }
+ case SE_PushButtonLayoutItem:
+ if (const QStyleOptionButton *buttonOpt
+ = qstyleoption_cast<const QStyleOptionButton *>(opt)) {
+ if ((buttonOpt->features & QStyleOptionButton::Flat))
+ break; // leave rect alone
+ }
+ rect = opt->rect;
+ if (controlSize == QAquaSizeLarge) {
+ rect.adjust(+6, +4, -6, -8);
+ } else if (controlSize == QAquaSizeSmall) {
+ rect.adjust(+5, +4, -5, -6);
+ } else {
+ rect.adjust(+1, 0, -1, -2);
+ }
+ break;
+ case SE_RadioButtonLayoutItem:
+ rect = opt->rect;
+ if (controlSize == QAquaSizeLarge) {
+ setLayoutItemMargins(+2, +2 /* SHOULD BE +3, done for alignment */,
+ 0, -4 /* SHOULD BE -3, done for alignment */, &rect, opt->direction);
+ } else if (controlSize == QAquaSizeSmall) {
+ rect.adjust(0, +6, 0 /* fix */, -5);
+ } else {
+ rect.adjust(0, +6, 0 /* fix */, -7);
+ }
+ break;
+ case SE_SliderLayoutItem:
+ if (const QStyleOptionSlider *sliderOpt
+ = qstyleoption_cast<const QStyleOptionSlider *>(opt)) {
+ rect = opt->rect;
+ if (sliderOpt->tickPosition == QSlider::NoTicks) {
+ int above = SIZE(3, 0, 2);
+ int below = SIZE(4, 3, 0);
+ if (sliderOpt->orientation == Qt::Horizontal) {
+ rect.adjust(0, +above, 0, -below);
+ } else {
+ rect.adjust(+above, 0, -below, 0); //### Seems that QSlider flip the position of the ticks in reverse mode.
+ }
+ } else if (sliderOpt->tickPosition == QSlider::TicksAbove) {
+ int below = SIZE(3, 2, 0);
+ if (sliderOpt->orientation == Qt::Horizontal) {
+ rect.setHeight(rect.height() - below);
+ } else {
+ rect.setWidth(rect.width() - below);
+ }
+ } else if (sliderOpt->tickPosition == QSlider::TicksBelow) {
+ int above = SIZE(3, 2, 0);
+ if (sliderOpt->orientation == Qt::Horizontal) {
+ rect.setTop(rect.top() + above);
+ } else {
+ rect.setLeft(rect.left() + above);
+ }
+ }
+ }
+ break;
+ case SE_FrameLayoutItem:
+ // hack because QStyleOptionFrame doesn't have a frameStyle member
+ if (const QFrame *frame = qobject_cast<const QFrame *>(widget)) {
+ rect = opt->rect;
+ switch (frame->frameStyle() & QFrame::Shape_Mask) {
+ case QFrame::HLine:
+ rect.adjust(0, +1, 0, -1);
+ break;
+ case QFrame::VLine:
+ rect.adjust(+1, 0, -1, 0);
+ break;
+ default:
+ ;
+ }
+ }
+ break;
+ case SE_GroupBoxLayoutItem:
+ rect = opt->rect;
+ if (const QStyleOptionGroupBox *groupBoxOpt =
+ qstyleoption_cast<const QStyleOptionGroupBox *>(opt)) {
+ /*
+ AHIG is very inconsistent when it comes to group boxes.
+ Basically, we make sure that (non-checkable) group boxes
+ and tab widgets look good when laid out side by side.
+ */
+ if (groupBoxOpt->subControls & (QStyle::SC_GroupBoxCheckBox
+ | QStyle::SC_GroupBoxLabel)) {
+ int delta;
+ if (groupBoxOpt->subControls & QStyle::SC_GroupBoxCheckBox) {
+ delta = SIZE(8, 4, 4); // guess
+ } else {
+ delta = SIZE(15, 12, 12); // guess
+ }
+ rect.setTop(rect.top() + delta);
+ }
+ }
+ rect.setBottom(rect.bottom() - 1);
+ break;
+#ifndef QT_NO_TABWIDGET
+ case SE_TabWidgetLayoutItem:
+ if (const QStyleOptionTabWidgetFrame *tabWidgetOpt =
+ qstyleoption_cast<const QStyleOptionTabWidgetFrame *>(opt)) {
+ /*
+ AHIG specifies "12 or 14" as the distance from the window
+ edge. We choose 14 and since the default top margin is 20,
+ the overlap is 6.
+ */
+ rect = tabWidgetOpt->rect;
+ if (tabWidgetOpt->shape == QTabBar::RoundedNorth)
+ rect.setTop(rect.top() + SIZE(6 /* AHIG */, 3 /* guess */, 2 /* AHIG */));
+ }
+ break;
+#endif
+#ifndef QT_NO_DOCKWIDGET
+ case SE_DockWidgetCloseButton:
+ case SE_DockWidgetFloatButton:
+ case SE_DockWidgetTitleBarText:
+ case SE_DockWidgetIcon: {
+ int iconSize = proxy()->pixelMetric(PM_SmallIconSize, opt, widget);
+ int buttonMargin = proxy()->pixelMetric(PM_DockWidgetTitleBarButtonMargin, opt, widget);
+ QRect srect = opt->rect;
+
+ const QStyleOptionDockWidget *dwOpt
+ = qstyleoption_cast<const QStyleOptionDockWidget*>(opt);
+ bool canClose = dwOpt == 0 ? true : dwOpt->closable;
+ bool canFloat = dwOpt == 0 ? false : dwOpt->floatable;
+
+ const bool verticalTitleBar = dwOpt->verticalTitleBar;
+
+ // If this is a vertical titlebar, we transpose and work as if it was
+ // horizontal, then transpose again.
+ if (verticalTitleBar)
+ srect = srect.transposed();
+
+ do {
+ int right = srect.right();
+ int left = srect.left();
+
+ QRect closeRect;
+ if (canClose) {
+ QSize sz = proxy()->standardIcon(QStyle::SP_TitleBarCloseButton,
+ opt, widget).actualSize(QSize(iconSize, iconSize));
+ sz += QSize(buttonMargin, buttonMargin);
+ if (verticalTitleBar)
+ sz = sz.transposed();
+ closeRect = QRect(left,
+ srect.center().y() - sz.height()/2,
+ sz.width(), sz.height());
+ left = closeRect.right() + 1;
+ }
+ if (sr == SE_DockWidgetCloseButton) {
+ rect = closeRect;
+ break;
+ }
+
+ QRect floatRect;
+ if (canFloat) {
+ QSize sz = proxy()->standardIcon(QStyle::SP_TitleBarNormalButton,
+ opt, widget).actualSize(QSize(iconSize, iconSize));
+ sz += QSize(buttonMargin, buttonMargin);
+ if (verticalTitleBar)
+ sz = sz.transposed();
+ floatRect = QRect(left,
+ srect.center().y() - sz.height()/2,
+ sz.width(), sz.height());
+ left = floatRect.right() + 1;
+ }
+ if (sr == SE_DockWidgetFloatButton) {
+ rect = floatRect;
+ break;
+ }
+
+ QRect iconRect;
+ if (const QDockWidget *dw = qobject_cast<const QDockWidget*>(widget)) {
+ QIcon icon;
+ if (dw->isFloating())
+ icon = dw->windowIcon();
+ if (!icon.isNull()
+ && icon.cacheKey() != QApplication::windowIcon().cacheKey()) {
+ QSize sz = icon.actualSize(QSize(rect.height(), rect.height()));
+ if (verticalTitleBar)
+ sz = sz.transposed();
+ iconRect = QRect(right - sz.width(), srect.center().y() - sz.height()/2,
+ sz.width(), sz.height());
+ right = iconRect.left() - 1;
+ }
+ }
+ if (sr == SE_DockWidgetIcon) {
+ rect = iconRect;
+ break;
+ }
+
+ QRect textRect = QRect(left, srect.top(),
+ right - left, srect.height());
+ if (sr == SE_DockWidgetTitleBarText) {
+ rect = textRect;
+ break;
+ }
+ } while (false);
+
+ if (verticalTitleBar) {
+ rect = QRect(srect.left() + rect.top() - srect.top(),
+ srect.top() + srect.right() - rect.right(),
+ rect.height(), rect.width());
+ } else {
+ rect = visualRect(opt->direction, srect, rect);
+ }
+ break;
+ }
+#endif
+ default:
+ rect = QCommonStyle::subElementRect(sr, opt, widget);
+ break;
+ }
+ return rect;
+}
+
+static inline void drawToolbarButtonArrow(const QRect &toolButtonRect, ThemeDrawState tds, CGContextRef cg)
+{
+ QRect arrowRect = QRect(toolButtonRect.right() - 9, toolButtonRect.bottom() - 9, 7, 5);
+ HIThemePopupArrowDrawInfo padi;
+ padi.version = qt_mac_hitheme_version;
+ padi.state = tds;
+ padi.orientation = kThemeArrowDown;
+ padi.size = kThemeArrow7pt;
+ HIRect hirect = qt_hirectForQRect(arrowRect);
+ HIThemeDrawPopupArrow(&hirect, &padi, cg, kHIThemeOrientationNormal);
+}
+
+void QMacStyle::drawComplexControl(ComplexControl cc, const QStyleOptionComplex *opt, QPainter *p,
+ const QWidget *widget) const
+{
+ Q_D(const QMacStyle);
+ ThemeDrawState tds = d->getDrawState(opt->state);
+ QMacCGContext cg(p);
+ QWindow *window = widget && widget->window() ? widget->window()->windowHandle() :
+ QStyleHelper::styleObjectWindow(opt->styleObject);
+ const_cast<QMacStylePrivate *>(d)->resolveCurrentNSView(window);
+ switch (cc) {
+ case CC_Slider:
+ case CC_ScrollBar:
+ if (const QStyleOptionSlider *slider = qstyleoption_cast<const QStyleOptionSlider *>(opt)) {
+ HIThemeTrackDrawInfo tdi;
+ d->getSliderInfo(cc, slider, &tdi, widget);
+ if (slider->state & State_Sunken) {
+ if (cc == CC_Slider) {
+ if (slider->activeSubControls == SC_SliderHandle)
+ tdi.trackInfo.slider.pressState = kThemeThumbPressed;
+ else if (slider->activeSubControls == SC_SliderGroove)
+ tdi.trackInfo.slider.pressState = kThemeLeftTrackPressed;
+ } else {
+ if (slider->activeSubControls == SC_ScrollBarSubLine
+ || slider->activeSubControls == SC_ScrollBarAddLine) {
+ // This test looks complex but it basically boils down
+ // to the following: The "RTL look" on the mac also
+ // changed the directions of the controls, that's not
+ // what people expect (an arrow is an arrow), so we
+ // kind of fake and say the opposite button is hit.
+ // This works great, up until 10.4 which broke the
+ // scroll bars, so I also have actually do something
+ // similar when I have an upside down scroll bar
+ // because on Tiger I only "fake" the reverse stuff.
+ bool reverseHorizontal = (slider->direction == Qt::RightToLeft
+ && slider->orientation == Qt::Horizontal);
+
+ if ((reverseHorizontal
+ && slider->activeSubControls == SC_ScrollBarAddLine)
+ || (!reverseHorizontal
+ && slider->activeSubControls == SC_ScrollBarSubLine)) {
+ tdi.trackInfo.scrollbar.pressState = kThemeRightInsideArrowPressed
+ | kThemeLeftOutsideArrowPressed;
+ } else {
+ tdi.trackInfo.scrollbar.pressState = kThemeLeftInsideArrowPressed
+ | kThemeRightOutsideArrowPressed;
+ }
+ } else if (slider->activeSubControls == SC_ScrollBarAddPage) {
+ tdi.trackInfo.scrollbar.pressState = kThemeRightTrackPressed;
+ } else if (slider->activeSubControls == SC_ScrollBarSubPage) {
+ tdi.trackInfo.scrollbar.pressState = kThemeLeftTrackPressed;
+ } else if (slider->activeSubControls == SC_ScrollBarSlider) {
+ tdi.trackInfo.scrollbar.pressState = kThemeThumbPressed;
+ }
+ }
+ }
+ HIRect macRect;
+ bool tracking = slider->sliderPosition == slider->sliderValue;
+ if (!tracking) {
+ // Small optimization, the same as q->subControlRect
+ QCFType<HIShapeRef> shape;
+ HIThemeGetTrackThumbShape(&tdi, &shape);
+ ptrHIShapeGetBounds(shape, &macRect);
+ tdi.value = slider->sliderValue;
+ }
+
+ // Remove controls from the scroll bar if it is to short to draw them correctly.
+ // This is done in two stages: first the thumb indicator is removed when it is
+ // no longer possible to move it, second the up/down buttons are removed when
+ // there is not enough space for them.
+ if (cc == CC_ScrollBar) {
+ if (opt && opt->styleObject && !QMacStylePrivate::scrollBars.contains(opt->styleObject))
+ QMacStylePrivate::scrollBars.append(QPointer<QObject>(opt->styleObject));
+ const int scrollBarLength = (slider->orientation == Qt::Horizontal)
+ ? slider->rect.width() : slider->rect.height();
+ const QStyleHelper::WidgetSizePolicy sizePolicy = QStyleHelper::widgetSizePolicy(widget, opt);
+ if (scrollBarLength < scrollButtonsCutoffSize(thumbIndicatorCutoff, sizePolicy))
+ tdi.attributes &= ~kThemeTrackShowThumb;
+ if (scrollBarLength < scrollButtonsCutoffSize(scrollButtonsCutoff, sizePolicy))
+ tdi.enableState = kThemeTrackNothingToScroll;
+ } else {
+ if (!(slider->subControls & SC_SliderHandle))
+ tdi.attributes &= ~kThemeTrackShowThumb;
+ if (!(slider->subControls & SC_SliderGroove))
+ tdi.attributes |= kThemeTrackHideTrack;
+ }
+
+ const bool isHorizontal = slider->orientation == Qt::Horizontal;
+
+ if (cc == CC_ScrollBar && proxy()->styleHint(SH_ScrollBar_Transient, opt, widget)) {
+ bool wasActive = false;
+ CGFloat opacity = 0.0;
+ CGFloat expandScale = 1.0;
+ CGFloat expandOffset = -1.0;
+ bool shouldExpand = false;
+ const CGFloat maxExpandScale = tdi.kind == kThemeSmallScrollBar ? 11.0 / 7.0 : 13.0 / 9.0;
+
+ if (QObject *styleObject = opt->styleObject) {
+ int oldPos = styleObject->property("_q_stylepos").toInt();
+ int oldMin = styleObject->property("_q_stylemin").toInt();
+ int oldMax = styleObject->property("_q_stylemax").toInt();
+ QRect oldRect = styleObject->property("_q_stylerect").toRect();
+ QStyle::State oldState = static_cast<QStyle::State>(styleObject->property("_q_stylestate").value<QStyle::State::Int>());
+ uint oldActiveControls = styleObject->property("_q_stylecontrols").toUInt();
+
+ // a scrollbar is transient when the scrollbar itself and
+ // its sibling are both inactive (ie. not pressed/hovered/moved)
+ bool transient = !opt->activeSubControls && !(slider->state & State_On);
+
+ if (!transient ||
+ oldPos != slider->sliderPosition ||
+ oldMin != slider->minimum ||
+ oldMax != slider->maximum ||
+ oldRect != slider->rect ||
+ oldState != slider->state ||
+ oldActiveControls != slider->activeSubControls) {
+
+ // if the scrollbar is transient or its attributes, geometry or
+ // state has changed, the opacity is reset back to 100% opaque
+ opacity = 1.0;
+
+ styleObject->setProperty("_q_stylepos", slider->sliderPosition);
+ styleObject->setProperty("_q_stylemin", slider->minimum);
+ styleObject->setProperty("_q_stylemax", slider->maximum);
+ styleObject->setProperty("_q_stylerect", slider->rect);
+ styleObject->setProperty("_q_stylestate", static_cast<QStyle::State::Int>(slider->state));
+ styleObject->setProperty("_q_stylecontrols", static_cast<uint>(slider->activeSubControls));
+
+ QScrollbarStyleAnimation *anim = qobject_cast<QScrollbarStyleAnimation *>(d->animation(styleObject));
+ if (transient) {
+ if (!anim) {
+ anim = new QScrollbarStyleAnimation(QScrollbarStyleAnimation::Deactivating, styleObject);
+ d->startAnimation(anim);
+ } else if (anim->mode() == QScrollbarStyleAnimation::Deactivating) {
+ // the scrollbar was already fading out while the
+ // state changed -> restart the fade out animation
+ anim->setCurrentTime(0);
+ }
+ } else if (anim && anim->mode() == QScrollbarStyleAnimation::Deactivating) {
+ d->stopAnimation(styleObject);
+ }
+ }
+
+ QScrollbarStyleAnimation *anim = qobject_cast<QScrollbarStyleAnimation *>(d->animation(styleObject));
+ if (anim && anim->mode() == QScrollbarStyleAnimation::Deactivating) {
+ // once a scrollbar was active (hovered/pressed), it retains
+ // the active look even if it's no longer active while fading out
+ if (oldActiveControls)
+ anim->setActive(true);
+
+ wasActive = anim->wasActive();
+ opacity = anim->currentValue();
+ }
+
+ shouldExpand = (opt->activeSubControls || wasActive);
+ if (shouldExpand) {
+ if (!anim && !oldActiveControls) {
+ // Start expand animation only once and when entering
+ anim = new QScrollbarStyleAnimation(QScrollbarStyleAnimation::Activating, styleObject);
+ d->startAnimation(anim);
+ }
+ if (anim && anim->mode() == QScrollbarStyleAnimation::Activating) {
+ expandScale = 1.0 + (maxExpandScale - 1.0) * anim->currentValue();
+ expandOffset = 5.5 * anim->currentValue() - 1;
+ } else {
+ // Keep expanded state after the animation ends, and when fading out
+ expandScale = maxExpandScale;
+ expandOffset = 4.5;
+ }
+ }
+ }
+
+ CGContextSaveGState(cg);
+ [NSGraphicsContext saveGraphicsState];
+
+ [NSGraphicsContext setCurrentContext:[NSGraphicsContext
+ graphicsContextWithGraphicsPort:(CGContextRef)cg flipped:NO]];
+ NSScroller *scroller = reinterpret_cast<NSScroller*>(d->nsscroller);
+ [scroller initWithFrame:NSMakeRect(0, 0, slider->rect.width(), slider->rect.height())];
+ // mac os behaviour: as soon as one color channel is >= 128,
+ // the bg is considered bright, scroller is dark
+ const QColor bgColor = QStyleHelper::backgroundColor(opt->palette, widget);
+ const bool isDarkBg = bgColor.red() < 128 && bgColor.green() < 128 &&
+ bgColor.blue() < 128;
+ if (isDarkBg)
+ [scroller setKnobStyle:NSScrollerKnobStyleLight];
+ else
+ [scroller setKnobStyle:NSScrollerKnobStyleDefault];
+
+ [scroller setControlSize:(tdi.kind == kThemeSmallScrollBar ? NSMiniControlSize
+ : NSRegularControlSize)];
+ [scroller setBounds:NSMakeRect(0, 0, slider->rect.width(), slider->rect.height())];
+ [scroller setScrollerStyle:NSScrollerStyleOverlay];
+
+ CGContextBeginTransparencyLayer(cg, NULL);
+ CGContextSetAlpha(cg, opacity);
+
+ // Draw the track when hovering
+ if (opt->activeSubControls || wasActive) {
+ NSRect rect = [scroller bounds];
+ if (shouldExpand) {
+ if (isHorizontal)
+ rect.origin.y += 4.5 - expandOffset;
+ else
+ rect.origin.x += 4.5 - expandOffset;
+ }
+ [scroller drawKnobSlotInRect:rect highlight:YES];
+ }
+
+ const qreal length = slider->maximum - slider->minimum + slider->pageStep;
+ const qreal proportion = slider->pageStep / length;
+ qreal value = (slider->sliderValue - slider->minimum) / length;
+ if (isHorizontal && slider->direction == Qt::RightToLeft)
+ value = 1.0 - value - proportion;
+
+ [scroller setKnobProportion:1.0];
+
+ const int minKnobWidth = 26;
+
+ if (isHorizontal) {
+ const qreal plannedWidth = proportion * slider->rect.width();
+ const qreal width = qMax<qreal>(minKnobWidth, plannedWidth);
+ const qreal totalWidth = slider->rect.width() + plannedWidth - width;
+ [scroller setFrame:NSMakeRect(0, 0, width, slider->rect.height())];
+ if (shouldExpand) {
+ CGContextScaleCTM(cg, 1, expandScale);
+ CGContextTranslateCTM(cg, value * totalWidth, -expandOffset);
+ } else {
+ CGContextTranslateCTM(cg, value * totalWidth, 1);
+ }
+ } else {
+ const qreal plannedHeight = proportion * slider->rect.height();
+ const qreal height = qMax<qreal>(minKnobWidth, plannedHeight);
+ const qreal totalHeight = slider->rect.height() + plannedHeight - height;
+ [scroller setFrame:NSMakeRect(0, 0, slider->rect.width(), height)];
+ if (shouldExpand) {
+ CGContextScaleCTM(cg, expandScale, 1);
+ CGContextTranslateCTM(cg, -expandOffset, value * totalHeight);
+ } else {
+ CGContextTranslateCTM(cg, 1, value * totalHeight);
+ }
+ }
+ if (length > 0.0) {
+ [scroller layout];
+ [scroller drawKnob];
+ }
+
+ CGContextEndTransparencyLayer(cg);
+
+ [NSGraphicsContext restoreGraphicsState];
+ CGContextRestoreGState(cg);
+ } else {
+ d->stopAnimation(opt->styleObject);
+
+ if (cc == CC_Slider) {
+ // Fix min and max positions. (See also getSliderInfo()
+ // for the slider values adjustments.)
+ // HITheme seems to have forgotten how to render
+ // a slide at those positions, leaving a gap between
+ // the knob and the ends of the track.
+ // We fix this by rendering the track first, and then
+ // the knob on top. However, in order to not clip the
+ // knob, we reduce the the drawing rect for the track.
+ HIRect bounds = tdi.bounds;
+ if (isHorizontal) {
+ tdi.bounds.size.width -= 2;
+ tdi.bounds.origin.x += 1;
+ if (tdi.trackInfo.slider.thumbDir == kThemeThumbDownward)
+ tdi.bounds.origin.y -= 2;
+ else if (tdi.trackInfo.slider.thumbDir == kThemeThumbUpward)
+ tdi.bounds.origin.y += 3;
+ } else {
+ tdi.bounds.size.height -= 2;
+ tdi.bounds.origin.y += 1;
+ if (tdi.trackInfo.slider.thumbDir == kThemeThumbDownward) // pointing right
+ tdi.bounds.origin.x -= 4;
+ else if (tdi.trackInfo.slider.thumbDir == kThemeThumbUpward) // pointing left
+ tdi.bounds.origin.x += 2;
+ }
+
+ // Yosemite demands its blue progress track when no tickmarks are present
+ if (!(slider->subControls & SC_SliderTickmarks)) {
+ QCocoaWidgetKind sliderKind = slider->orientation == Qt::Horizontal ? QCocoaHorizontalSlider : QCocoaVerticalSlider;
+ QCocoaWidget cw = QCocoaWidget(sliderKind, QAquaSizeLarge);
+ NSSlider *sl = (NSSlider *)d->cocoaControl(cw);
+ sl.minValue = slider->minimum;
+ sl.maxValue = slider->maximum;
+ sl.intValue = slider->sliderValue;
+ sl.enabled = slider->state & QStyle::State_Enabled;
+ d->drawNSViewInRect(cw, sl, opt->rect, p, widget != 0, ^(NSRect rect, CGContextRef ctx) {
+ if (slider->upsideDown) {
+ if (isHorizontal) {
+ CGContextTranslateCTM(ctx, rect.size.width, 0);
+ CGContextScaleCTM(ctx, -1, 1);
+ }
+ } else if (!isHorizontal) {
+ CGContextTranslateCTM(ctx, 0, rect.size.height);
+ CGContextScaleCTM(ctx, 1, -1);
+ }
+ [sl.cell drawBarInside:NSRectFromCGRect(tdi.bounds) flipped:NO];
+ // No need to restore the CTM later, the context has been saved
+ // and will be restored at the end of drawNSViewInRect()
+ });
+ tdi.attributes |= kThemeTrackHideTrack;
+ } else {
+ tdi.attributes &= ~(kThemeTrackShowThumb | kThemeTrackHasFocus);
+ HIThemeDrawTrack(&tdi, tracking ? 0 : &macRect, cg,
+ kHIThemeOrientationNormal);
+ tdi.attributes |= kThemeTrackHideTrack | kThemeTrackShowThumb;
+ }
+
+ tdi.bounds = bounds;
+ }
+
+ if (cc == CC_Slider && slider->subControls & SC_SliderTickmarks) {
+
+ HIRect bounds;
+ // As part of fixing the min and max positions,
+ // we need to adjust the tickmarks as well
+ bounds = tdi.bounds;
+ if (slider->orientation == Qt::Horizontal) {
+ tdi.bounds.size.width += 2;
+ tdi.bounds.origin.x -= 1;
+ if (tdi.trackInfo.slider.thumbDir == kThemeThumbUpward)
+ tdi.bounds.origin.y -= 2;
+ } else {
+ tdi.bounds.size.height += 3;
+ tdi.bounds.origin.y -= 3;
+ tdi.bounds.origin.y += 1;
+ if (tdi.trackInfo.slider.thumbDir == kThemeThumbUpward) // pointing left
+ tdi.bounds.origin.x -= 2;
+ }
+
+ if (qt_mac_is_metal(widget)) {
+ if (tdi.enableState == kThemeTrackInactive)
+ tdi.enableState = kThemeTrackActive; // Looks more Cocoa-like
+ }
+ int interval = slider->tickInterval;
+ if (interval == 0) {
+ interval = slider->pageStep;
+ if (interval == 0)
+ interval = slider->singleStep;
+ if (interval == 0)
+ interval = 1;
+ }
+ int numMarks = 1 + ((slider->maximum - slider->minimum) / interval);
+
+ if (tdi.trackInfo.slider.thumbDir == kThemeThumbPlain) {
+ // They asked for both, so we'll give it to them.
+ tdi.trackInfo.slider.thumbDir = kThemeThumbDownward;
+ HIThemeDrawTrackTickMarks(&tdi, numMarks,
+ cg,
+ kHIThemeOrientationNormal);
+ tdi.trackInfo.slider.thumbDir = kThemeThumbUpward;
+ // 10.10 and above need a slight shift
+ if (slider->orientation == Qt::Vertical)
+ tdi.bounds.origin.x -= 2;
+ HIThemeDrawTrackTickMarks(&tdi, numMarks,
+ cg,
+ kHIThemeOrientationNormal);
+ // Reset to plain thumb to be drawn further down
+ tdi.trackInfo.slider.thumbDir = kThemeThumbPlain;
+ } else {
+ HIThemeDrawTrackTickMarks(&tdi, numMarks,
+ cg,
+ kHIThemeOrientationNormal);
+ }
+
+ tdi.bounds = bounds;
+ }
+
+ if (cc == CC_Slider) {
+ // Still as part of fixing the min and max positions,
+ // we also adjust the knob position. We can do this
+ // because it's rendered separately from the track.
+ if (slider->orientation == Qt::Vertical) {
+ if (tdi.trackInfo.slider.thumbDir == kThemeThumbDownward) // pointing right
+ tdi.bounds.origin.x -= 2;
+ }
+ }
+
+ HIThemeDrawTrack(&tdi, tracking ? 0 : &macRect, cg,
+ kHIThemeOrientationNormal);
+ }
+ }
+ break;
+#ifndef QT_NO_SPINBOX
+ case CC_SpinBox:
+ if (const QStyleOptionSpinBox *sb = qstyleoption_cast<const QStyleOptionSpinBox *>(opt)) {
+ QStyleOptionSpinBox newSB = *sb;
+ if (sb->frame && (sb->subControls & SC_SpinBoxFrame)) {
+ SInt32 frame_size;
+ GetThemeMetric(kThemeMetricEditTextFrameOutset, &frame_size);
+
+ QRect lineeditRect = proxy()->subControlRect(CC_SpinBox, sb, SC_SpinBoxEditField, widget);
+ lineeditRect.adjust(-frame_size, -frame_size, +frame_size, +frame_size);
+
+ HIThemeFrameDrawInfo fdi;
+ fdi.version = qt_mac_hitheme_version;
+ fdi.state = tds == kThemeStateInactive ? kThemeStateActive : tds;
+ fdi.kind = kHIThemeFrameTextFieldSquare;
+ fdi.isFocused = false;
+ HIRect hirect = qt_hirectForQRect(lineeditRect);
+ HIThemeDrawFrame(&hirect, &fdi, cg, kHIThemeOrientationNormal);
+ }
+ if (sb->subControls & (SC_SpinBoxUp | SC_SpinBoxDown)) {
+ HIThemeButtonDrawInfo bdi;
+ bdi.version = qt_mac_hitheme_version;
+ QAquaWidgetSize aquaSize = d->aquaSizeConstrain(opt, widget);
+ switch (aquaSize) {
+ case QAquaSizeUnknown:
+ case QAquaSizeLarge:
+ bdi.kind = kThemeIncDecButton;
+ break;
+ case QAquaSizeMini:
+ bdi.kind = kThemeIncDecButtonMini;
+ break;
+ case QAquaSizeSmall:
+ bdi.kind = kThemeIncDecButtonSmall;
+ break;
+ }
+ if (!(sb->stepEnabled & (QAbstractSpinBox::StepUpEnabled
+ | QAbstractSpinBox::StepDownEnabled)))
+ tds = kThemeStateUnavailable;
+ if (sb->activeSubControls == SC_SpinBoxDown
+ && (sb->state & State_Sunken))
+ tds = kThemeStatePressedDown;
+ else if (sb->activeSubControls == SC_SpinBoxUp
+ && (sb->state & State_Sunken))
+ tds = kThemeStatePressedUp;
+ if (tds == kThemeStateInactive)
+ bdi.state = kThemeStateActive;
+ else
+ bdi.state = tds;
+ bdi.value = kThemeButtonOff;
+ bdi.adornment = kThemeAdornmentNone;
+
+ QRect updown = proxy()->subControlRect(CC_SpinBox, sb, SC_SpinBoxUp, widget);
+
+ updown |= proxy()->subControlRect(CC_SpinBox, sb, SC_SpinBoxDown, widget);
+ HIRect newRect = qt_hirectForQRect(updown);
+ QRect off_rct;
+ HIRect outRect;
+ HIThemeGetButtonBackgroundBounds(&newRect, &bdi, &outRect);
+ off_rct.setRect(int(newRect.origin.x - outRect.origin.x),
+ int(newRect.origin.y - outRect.origin.y),
+ int(outRect.size.width - newRect.size.width),
+ int(outRect.size.height - newRect.size.height));
+
+ newRect = qt_hirectForQRect(updown, off_rct);
+ if (tds == kThemeStateInactive)
+ d->drawColorlessButton(newRect, &bdi, p, sb);
+ else
+ HIThemeDrawButton(&newRect, &bdi, cg, kHIThemeOrientationNormal, 0);
+ }
+ }
+ break;
+#endif
+ case CC_ComboBox:
+ if (const QStyleOptionComboBox *combo = qstyleoption_cast<const QStyleOptionComboBox *>(opt)){
+ HIThemeButtonDrawInfo bdi;
+ d->initComboboxBdi(combo, &bdi, widget, tds);
+ HIRect rect = qt_hirectForQRect(combo->rect);
+ if (combo->editable)
+ rect.origin.y += tds == kThemeStateInactive ? 1 : 2;
+ if (tds != kThemeStateInactive)
+ QMacStylePrivate::drawCombobox(rect, bdi, p);
+ else if (!widget && combo->editable) {
+ QCocoaWidget cw = cocoaWidgetFromHIThemeButtonKind(bdi.kind);
+ NSView *cb = d->cocoaControl(cw);
+ QRect r = combo->rect.adjusted(3, 0, 0, 0);
+ d->drawNSViewInRect(cw, cb, r, p, widget != 0);
+ } else
+ d->drawColorlessButton(rect, &bdi, p, opt);
+ }
+ break;
+ case CC_TitleBar:
+ if (const QStyleOptionTitleBar *titlebar
+ = qstyleoption_cast<const QStyleOptionTitleBar *>(opt)) {
+ if (titlebar->state & State_Active) {
+ if (titlebar->titleBarState & State_Active)
+ tds = kThemeStateActive;
+ else
+ tds = kThemeStateInactive;
+ } else {
+ tds = kThemeStateInactive;
+ }
+
+ HIThemeWindowDrawInfo wdi;
+ wdi.version = qt_mac_hitheme_version;
+ wdi.state = tds;
+ wdi.windowType = QtWinType;
+ wdi.titleHeight = titlebar->rect.height();
+ wdi.titleWidth = titlebar->rect.width();
+ wdi.attributes = kThemeWindowHasTitleText;
+ // It seems HIThemeDrawTitleBarWidget is not able to draw a dirty
+ // close button, so use HIThemeDrawWindowFrame instead.
+ if (widget && widget->isWindowModified() && titlebar->subControls & SC_TitleBarCloseButton)
+ wdi.attributes |= kThemeWindowHasCloseBox | kThemeWindowHasDirty;
+
+ HIRect titleBarRect;
+ HIRect tmpRect = qt_hirectForQRect(titlebar->rect);
+ {
+ QCFType<HIShapeRef> titleRegion;
+ QRect newr = titlebar->rect.adjusted(0, 0, 2, 0);
+ HIThemeGetWindowShape(&tmpRect, &wdi, kWindowTitleBarRgn, &titleRegion);
+ ptrHIShapeGetBounds(titleRegion, &tmpRect);
+ newr.translate(newr.x() - int(tmpRect.origin.x), newr.y() - int(tmpRect.origin.y));
+ titleBarRect = qt_hirectForQRect(newr);
+ }
+ HIThemeDrawWindowFrame(&titleBarRect, &wdi, cg, kHIThemeOrientationNormal, 0);
+ if (titlebar->subControls & (SC_TitleBarCloseButton
+ | SC_TitleBarMaxButton
+ | SC_TitleBarMinButton
+ | SC_TitleBarNormalButton)) {
+ HIThemeWindowWidgetDrawInfo wwdi;
+ wwdi.version = qt_mac_hitheme_version;
+ wwdi.widgetState = tds;
+ if (titlebar->state & State_MouseOver)
+ wwdi.widgetState = kThemeStateRollover;
+ wwdi.windowType = QtWinType;
+ wwdi.attributes = wdi.attributes | kThemeWindowHasFullZoom | kThemeWindowHasCloseBox | kThemeWindowHasCollapseBox;
+ wwdi.windowState = wdi.state;
+ wwdi.titleHeight = wdi.titleHeight;
+ wwdi.titleWidth = wdi.titleWidth;
+ ThemeDrawState savedControlState = wwdi.widgetState;
+ uint sc = SC_TitleBarMinButton;
+ ThemeTitleBarWidget tbw = kThemeWidgetCollapseBox;
+ bool active = titlebar->state & State_Active;
+
+ while (sc <= SC_TitleBarCloseButton) {
+ if (sc & titlebar->subControls) {
+ uint tmp = sc;
+ wwdi.widgetState = savedControlState;
+ wwdi.widgetType = tbw;
+ if (sc == SC_TitleBarMinButton)
+ tmp |= SC_TitleBarNormalButton;
+ if (active && (titlebar->activeSubControls & tmp)
+ && (titlebar->state & State_Sunken))
+ wwdi.widgetState = kThemeStatePressed;
+ // Draw all sub controllers except the dirty close button
+ // (it is already handled by HIThemeDrawWindowFrame).
+ if (!(widget && widget->isWindowModified() && tbw == kThemeWidgetCloseBox)) {
+ HIThemeDrawTitleBarWidget(&titleBarRect, &wwdi, cg, kHIThemeOrientationNormal);
+ p->paintEngine()->syncState();
+ }
+ }
+ sc = sc << 1;
+ tbw = tbw >> 1;
+ }
+ }
+ p->paintEngine()->syncState();
+ if (titlebar->subControls & SC_TitleBarLabel) {
+ int iw = 0;
+ if (!titlebar->icon.isNull()) {
+ QCFType<HIShapeRef> titleRegion2;
+ HIThemeGetWindowShape(&titleBarRect, &wdi, kWindowTitleProxyIconRgn,
+ &titleRegion2);
+ ptrHIShapeGetBounds(titleRegion2, &tmpRect);
+ if (tmpRect.size.width != 1) {
+ int iconExtent = proxy()->pixelMetric(PM_SmallIconSize);
+ iw = titlebar->icon.actualSize(QSize(iconExtent, iconExtent)).width();
+ }
+ }
+ if (!titlebar->text.isEmpty()) {
+ p->save();
+ QCFType<HIShapeRef> titleRegion3;
+ HIThemeGetWindowShape(&titleBarRect, &wdi, kWindowTitleTextRgn, &titleRegion3);
+ ptrHIShapeGetBounds(titleRegion3, &tmpRect);
+ p->setClipRect(qt_qrectForHIRect(tmpRect));
+ QRect br = p->clipRegion().boundingRect();
+ int x = br.x(),
+ y = br.y() + (titlebar->rect.height() / 2 - p->fontMetrics().height() / 2);
+ if (br.width() <= (p->fontMetrics().width(titlebar->text) + iw * 2))
+ x += iw;
+ else
+ x += br.width() / 2 - p->fontMetrics().width(titlebar->text) / 2;
+ if (iw) {
+ int iconExtent = proxy()->pixelMetric(PM_SmallIconSize);
+ p->drawPixmap(x - iw, y,
+ titlebar->icon.pixmap(window, QSize(iconExtent, iconExtent), QIcon::Normal));
+ }
+ drawItemText(p, br, Qt::AlignCenter, opt->palette, tds == kThemeStateActive,
+ titlebar->text, QPalette::Text);
+ p->restore();
+ }
+ }
+ }
+ break;
+ case CC_GroupBox:
+ if (const QStyleOptionGroupBox *gb
+ = qstyleoption_cast<const QStyleOptionGroupBox *>(opt)) {
+
+ QStyleOptionGroupBox groupBox(*gb);
+ const bool flat = groupBox.features & QStyleOptionFrame::Flat;
+ if (!flat)
+ groupBox.state |= QStyle::State_Mini; // Force mini-sized checkbox to go with small-sized label
+ else
+ groupBox.subControls = groupBox.subControls & ~SC_GroupBoxFrame; // We don't like frames and ugly lines
+
+ bool didModifySubControls = false;
+ if ((!widget || !widget->testAttribute(Qt::WA_SetFont))
+ && QApplication::desktopSettingsAware()) {
+ groupBox.subControls = groupBox.subControls & ~SC_GroupBoxLabel;
+ didModifySubControls = true;
+ }
+ QCommonStyle::drawComplexControl(cc, &groupBox, p, widget);
+ if (didModifySubControls) {
+ p->save();
+ CGContextSetShouldAntialias(cg, true);
+ CGContextSetShouldSmoothFonts(cg, true);
+ HIThemeTextInfo tti;
+ tti.version = qt_mac_hitheme_version;
+ tti.state = tds;
+ QColor textColor = groupBox.palette.windowText().color();
+ CGFloat colorComp[] = { static_cast<CGFloat>(textColor.redF()), static_cast<CGFloat>(textColor.greenF()),
+ static_cast<CGFloat>(textColor.blueF()), static_cast<CGFloat>(textColor.alphaF()) };
+ CGContextSetFillColorSpace(cg, qt_mac_genericColorSpace());
+ CGContextSetFillColor(cg, colorComp);
+ tti.fontID = flat ? kThemeSystemFont : kThemeSmallSystemFont;
+ tti.horizontalFlushness = kHIThemeTextHorizontalFlushCenter;
+ tti.verticalFlushness = kHIThemeTextVerticalFlushCenter;
+ tti.options = kHIThemeTextBoxOptionNone;
+ tti.truncationPosition = kHIThemeTextTruncationNone;
+ tti.truncationMaxLines = 1 + groupBox.text.count(QLatin1Char('\n'));
+ QCFString groupText = qt_mac_removeMnemonics(groupBox.text);
+ QRect r = proxy()->subControlRect(CC_GroupBox, &groupBox, SC_GroupBoxLabel, widget);
+ HIRect bounds = qt_hirectForQRect(r);
+ HIThemeDrawTextBox(groupText, &bounds, &tti, cg, kHIThemeOrientationNormal);
+ p->restore();
+ }
+ }
+ break;
+ case CC_ToolButton:
+ if (const QStyleOptionToolButton *tb
+ = qstyleoption_cast<const QStyleOptionToolButton *>(opt)) {
+#ifndef QT_NO_ACCESSIBILITY
+ if (QStyleHelper::hasAncestor(opt->styleObject, QAccessible::ToolBar)) {
+ if (tb->subControls & SC_ToolButtonMenu) {
+ QStyleOption arrowOpt = *tb;
+ arrowOpt.rect = proxy()->subControlRect(cc, tb, SC_ToolButtonMenu, widget);
+ arrowOpt.rect.setY(arrowOpt.rect.y() + arrowOpt.rect.height() / 2);
+ arrowOpt.rect.setHeight(arrowOpt.rect.height() / 2);
+ proxy()->drawPrimitive(PE_IndicatorArrowDown, &arrowOpt, p, widget);
+ } else if ((tb->features & QStyleOptionToolButton::HasMenu)
+ && (tb->toolButtonStyle != Qt::ToolButtonTextOnly && !tb->icon.isNull())) {
+ drawToolbarButtonArrow(tb->rect, tds, cg);
+ }
+ if (tb->state & State_On) {
+ QWindow *window = 0;
+ if (widget && widget->window())
+ window = widget->window()->windowHandle();
+ else if (opt->styleObject)
+ window = opt->styleObject->property("_q_styleObjectWindow").value<QWindow *>();
+
+ NSView *view = window ? (NSView *)window->winId() : nil;
+ bool isKey = false;
+ if (view)
+ isKey = [view.window isKeyWindow];
+
+ QBrush brush(isKey ? QColor(0, 0, 0, 28)
+ : QColor(0, 0, 0, 21));
+ QPainterPath path;
+ path.addRoundedRect(QRectF(tb->rect.x(), tb->rect.y(), tb->rect.width(), tb->rect.height() + 4), 4, 4);
+ p->setRenderHint(QPainter::Antialiasing);
+ p->fillPath(path, brush);
+ }
+ proxy()->drawControl(CE_ToolButtonLabel, opt, p, widget);
+ } else {
+ ThemeButtonKind bkind = kThemeBevelButton;
+ switch (d->aquaSizeConstrain(opt, widget)) {
+ case QAquaSizeUnknown:
+ case QAquaSizeLarge:
+ bkind = kThemeBevelButton;
+ break;
+ case QAquaSizeMini:
+ case QAquaSizeSmall:
+ bkind = kThemeSmallBevelButton;
+ break;
+ }
+
+ QRect button, menuarea;
+ button = proxy()->subControlRect(cc, tb, SC_ToolButton, widget);
+ menuarea = proxy()->subControlRect(cc, tb, SC_ToolButtonMenu, widget);
+ State bflags = tb->state,
+ mflags = tb->state;
+ if (tb->subControls & SC_ToolButton)
+ bflags |= State_Sunken;
+ if (tb->subControls & SC_ToolButtonMenu)
+ mflags |= State_Sunken;
+
+ if (tb->subControls & SC_ToolButton) {
+ if (bflags & (State_Sunken | State_On | State_Raised)) {
+ HIThemeButtonDrawInfo bdi;
+ bdi.version = qt_mac_hitheme_version;
+ bdi.state = tds;
+ bdi.adornment = kThemeAdornmentNone;
+ bdi.kind = bkind;
+ bdi.value = kThemeButtonOff;
+ if (tb->state & State_HasFocus)
+ bdi.adornment = kThemeAdornmentFocus;
+ if (tb->state & State_Sunken)
+ bdi.state = kThemeStatePressed;
+ if (tb->state & State_On)
+ bdi.value = kThemeButtonOn;
+
+ QRect off_rct(0, 0, 0, 0);
+ HIRect myRect, macRect;
+ myRect = CGRectMake(tb->rect.x(), tb->rect.y(),
+ tb->rect.width(), tb->rect.height());
+ HIThemeGetButtonBackgroundBounds(&myRect, &bdi, &macRect);
+ off_rct.setRect(int(myRect.origin.x - macRect.origin.x),
+ int(myRect.origin.y - macRect.origin.y),
+ int(macRect.size.width - myRect.size.width),
+ int(macRect.size.height - myRect.size.height));
+
+ myRect = qt_hirectForQRect(button, off_rct);
+ HIThemeDrawButton(&myRect, &bdi, cg, kHIThemeOrientationNormal, 0);
+ }
+ }
+
+ if (tb->subControls & SC_ToolButtonMenu) {
+ HIThemeButtonDrawInfo bdi;
+ bdi.version = qt_mac_hitheme_version;
+ bdi.state = tds;
+ bdi.value = kThemeButtonOff;
+ bdi.adornment = kThemeAdornmentNone;
+ bdi.kind = bkind;
+ if (tb->state & State_HasFocus)
+ bdi.adornment = kThemeAdornmentFocus;
+ if (tb->state & (State_On | State_Sunken)
+ || (tb->activeSubControls & SC_ToolButtonMenu))
+ bdi.state = kThemeStatePressed;
+ HIRect hirect = qt_hirectForQRect(menuarea);
+ HIThemeDrawButton(&hirect, &bdi, cg, kHIThemeOrientationNormal, 0);
+ QRect r(menuarea.x() + ((menuarea.width() / 2) - 3), menuarea.height() - 8, 8, 8);
+ HIThemePopupArrowDrawInfo padi;
+ padi.version = qt_mac_hitheme_version;
+ padi.state = tds;
+ padi.orientation = kThemeArrowDown;
+ padi.size = kThemeArrow7pt;
+ hirect = qt_hirectForQRect(r);
+ HIThemeDrawPopupArrow(&hirect, &padi, cg, kHIThemeOrientationNormal);
+ } else if (tb->features & QStyleOptionToolButton::HasMenu) {
+ drawToolbarButtonArrow(tb->rect, tds, cg);
+ }
+ QRect buttonRect = proxy()->subControlRect(CC_ToolButton, tb, SC_ToolButton, widget);
+ int fw = proxy()->pixelMetric(PM_DefaultFrameWidth, opt, widget);
+ QStyleOptionToolButton label = *tb;
+ label.rect = buttonRect.adjusted(fw, fw, -fw, -fw);
+ proxy()->drawControl(CE_ToolButtonLabel, &label, p, widget);
+ }
+#endif
+ }
+ break;
+#ifndef QT_NO_DIAL
+ case CC_Dial:
+ if (const QStyleOptionSlider *dial = qstyleoption_cast<const QStyleOptionSlider *>(opt))
+ QStyleHelper::drawDial(dial, p);
+ break;
+#endif
+ default:
+ QCommonStyle::drawComplexControl(cc, opt, p, widget);
+ break;
+ }
+}
+
+QStyle::SubControl QMacStyle::hitTestComplexControl(ComplexControl cc,
+ const QStyleOptionComplex *opt,
+ const QPoint &pt, const QWidget *widget) const
+{
+ Q_D(const QMacStyle);
+ SubControl sc = QStyle::SC_None;
+ switch (cc) {
+ case CC_ComboBox:
+ if (const QStyleOptionComboBox *cmb = qstyleoption_cast<const QStyleOptionComboBox *>(opt)) {
+ sc = QCommonStyle::hitTestComplexControl(cc, cmb, pt, widget);
+ if (!cmb->editable && sc != QStyle::SC_None)
+ sc = SC_ComboBoxArrow; // A bit of a lie, but what we want
+ }
+ break;
+ case CC_Slider:
+ if (const QStyleOptionSlider *slider = qstyleoption_cast<const QStyleOptionSlider *>(opt)) {
+ HIThemeTrackDrawInfo tdi;
+ d->getSliderInfo(cc, slider, &tdi, widget);
+ ControlPartCode part;
+ HIPoint pos = CGPointMake(pt.x(), pt.y());
+ if (HIThemeHitTestTrack(&tdi, &pos, &part)) {
+ if (part == kControlPageUpPart || part == kControlPageDownPart)
+ sc = SC_SliderGroove;
+ else
+ sc = SC_SliderHandle;
+ }
+ }
+ break;
+ case CC_ScrollBar:
+ if (const QStyleOptionSlider *sb = qstyleoption_cast<const QStyleOptionSlider *>(opt)) {
+ HIScrollBarTrackInfo sbi;
+ sbi.version = qt_mac_hitheme_version;
+ if (!(sb->state & State_Active))
+ sbi.enableState = kThemeTrackInactive;
+ else if (sb->state & State_Enabled)
+ sbi.enableState = kThemeTrackActive;
+ else
+ sbi.enableState = kThemeTrackDisabled;
+
+ // The arrow buttons are not drawn if the scroll bar is to short,
+ // exclude them from the hit test.
+ const int scrollBarLength = (sb->orientation == Qt::Horizontal)
+ ? sb->rect.width() : sb->rect.height();
+ if (scrollBarLength < scrollButtonsCutoffSize(scrollButtonsCutoff, QStyleHelper::widgetSizePolicy(widget, opt)))
+ sbi.enableState = kThemeTrackNothingToScroll;
+
+ sbi.viewsize = sb->pageStep;
+ HIPoint pos = CGPointMake(pt.x(), pt.y());
+
+ HIRect macSBRect = qt_hirectForQRect(sb->rect);
+ ControlPartCode part;
+ bool reverseHorizontal = (sb->direction == Qt::RightToLeft
+ && sb->orientation == Qt::Horizontal);
+ if (HIThemeHitTestScrollBarArrows(&macSBRect, &sbi, sb->orientation == Qt::Horizontal,
+ &pos, 0, &part)) {
+ if (part == kControlUpButtonPart)
+ sc = reverseHorizontal ? SC_ScrollBarAddLine : SC_ScrollBarSubLine;
+ else if (part == kControlDownButtonPart)
+ sc = reverseHorizontal ? SC_ScrollBarSubLine : SC_ScrollBarAddLine;
+ } else {
+ HIThemeTrackDrawInfo tdi;
+ d->getSliderInfo(cc, sb, &tdi, widget);
+ if(tdi.enableState == kThemeTrackInactive)
+ tdi.enableState = kThemeTrackActive;
+ if (HIThemeHitTestTrack(&tdi, &pos, &part)) {
+ if (part == kControlPageUpPart)
+ sc = reverseHorizontal ? SC_ScrollBarAddPage
+ : SC_ScrollBarSubPage;
+ else if (part == kControlPageDownPart)
+ sc = reverseHorizontal ? SC_ScrollBarSubPage
+ : SC_ScrollBarAddPage;
+ else
+ sc = SC_ScrollBarSlider;
+ }
+ }
+ }
+ break;
+/*
+ I don't know why, but we only get kWindowContentRgn here, which isn't what we want at all.
+ It would be very nice if this would work.
+ case QStyle::CC_TitleBar:
+ if (const QStyleOptionTitleBar *tbar = qstyleoption_cast<const QStyleOptionTitleBar *>(opt)) {
+ HIThemeWindowDrawInfo wdi;
+ memset(&wdi, 0, sizeof(wdi));
+ wdi.version = qt_mac_hitheme_version;
+ wdi.state = kThemeStateActive;
+ wdi.windowType = QtWinType;
+ wdi.titleWidth = tbar->rect.width();
+ wdi.titleHeight = tbar->rect.height();
+ if (tbar->titleBarState)
+ wdi.attributes |= kThemeWindowHasFullZoom | kThemeWindowHasCloseBox
+ | kThemeWindowHasCollapseBox;
+ else if (tbar->titleBarFlags & Qt::WindowSystemMenuHint)
+ wdi.attributes |= kThemeWindowHasCloseBox;
+ QRect tmpRect = tbar->rect;
+ tmpRect.setHeight(tmpRect.height() + 100);
+ HIRect hirect = qt_hirectForQRect(tmpRect);
+ WindowRegionCode hit;
+ HIPoint hipt = CGPointMake(pt.x(), pt.y());
+ if (HIThemeGetWindowRegionHit(&hirect, &wdi, &hipt, &hit)) {
+ switch (hit) {
+ case kWindowCloseBoxRgn:
+ sc = QStyle::SC_TitleBarCloseButton;
+ break;
+ case kWindowCollapseBoxRgn:
+ sc = QStyle::SC_TitleBarMinButton;
+ break;
+ case kWindowZoomBoxRgn:
+ sc = QStyle::SC_TitleBarMaxButton;
+ break;
+ case kWindowTitleTextRgn:
+ sc = QStyle::SC_TitleBarLabel;
+ break;
+ default:
+ qDebug("got something else %d", hit);
+ break;
+ }
+ }
+ }
+ break;
+*/
+ default:
+ sc = QCommonStyle::hitTestComplexControl(cc, opt, pt, widget);
+ break;
+ }
+ return sc;
+}
+
+QRect QMacStyle::subControlRect(ComplexControl cc, const QStyleOptionComplex *opt, SubControl sc,
+ const QWidget *widget) const
+{
+ Q_D(const QMacStyle);
+ QRect ret;
+ switch (cc) {
+ case CC_Slider:
+ case CC_ScrollBar:
+ if (const QStyleOptionSlider *slider = qstyleoption_cast<const QStyleOptionSlider *>(opt)) {
+ HIThemeTrackDrawInfo tdi;
+ d->getSliderInfo(cc, slider, &tdi, widget);
+ HIRect macRect;
+ QCFType<HIShapeRef> shape;
+ bool scrollBar = cc == CC_ScrollBar;
+ if ((scrollBar && sc == SC_ScrollBarSlider)
+ || (!scrollBar && sc == SC_SliderHandle)) {
+ HIThemeGetTrackThumbShape(&tdi, &shape);
+ ptrHIShapeGetBounds(shape, &macRect);
+ } else if (!scrollBar && sc == SC_SliderGroove) {
+ HIThemeGetTrackBounds(&tdi, &macRect);
+ } else if (sc == SC_ScrollBarGroove) { // Only scroll bar parts available...
+ HIThemeGetTrackDragRect(&tdi, &macRect);
+ } else {
+ ControlPartCode cpc;
+ if (sc == SC_ScrollBarSubPage || sc == SC_ScrollBarAddPage) {
+ cpc = sc == SC_ScrollBarSubPage ? kControlPageDownPart
+ : kControlPageUpPart;
+ } else {
+ cpc = sc == SC_ScrollBarSubLine ? kControlUpButtonPart
+ : kControlDownButtonPart;
+ if (slider->direction == Qt::RightToLeft
+ && slider->orientation == Qt::Horizontal) {
+ if (cpc == kControlDownButtonPart)
+ cpc = kControlUpButtonPart;
+ else if (cpc == kControlUpButtonPart)
+ cpc = kControlDownButtonPart;
+ }
+ }
+ HIThemeGetTrackPartBounds(&tdi, cpc, &macRect);
+ }
+ ret = qt_qrectForHIRect(macRect);
+
+ // Tweak: the dark line between the sub/add line buttons belong to only one of the buttons
+ // when doing hit-testing, but both of them have to repaint it. Extend the rect to cover
+ // the line in the cases where HIThemeGetTrackPartBounds returns a rect that doesn't.
+ if (slider->orientation == Qt::Horizontal) {
+ if (slider->direction == Qt::LeftToRight && sc == SC_ScrollBarSubLine)
+ ret.adjust(0, 0, 1, 0);
+ else if (slider->direction == Qt::RightToLeft && sc == SC_ScrollBarAddLine)
+ ret.adjust(-1, 0, 1, 0);
+ } else if (sc == SC_ScrollBarAddLine) {
+ ret.adjust(0, -1, 0, 1);
+ }
+ }
+ break;
+ case CC_TitleBar:
+ if (const QStyleOptionTitleBar *titlebar
+ = qstyleoption_cast<const QStyleOptionTitleBar *>(opt)) {
+ HIThemeWindowDrawInfo wdi;
+ memset(&wdi, 0, sizeof(wdi));
+ wdi.version = qt_mac_hitheme_version;
+ wdi.state = kThemeStateActive;
+ wdi.windowType = QtWinType;
+ wdi.titleHeight = titlebar->rect.height();
+ wdi.titleWidth = titlebar->rect.width();
+ wdi.attributes = kThemeWindowHasTitleText;
+ if (titlebar->subControls & SC_TitleBarCloseButton)
+ wdi.attributes |= kThemeWindowHasCloseBox;
+ if (titlebar->subControls & SC_TitleBarMaxButton
+ | SC_TitleBarNormalButton)
+ wdi.attributes |= kThemeWindowHasFullZoom;
+ if (titlebar->subControls & SC_TitleBarMinButton)
+ wdi.attributes |= kThemeWindowHasCollapseBox;
+ WindowRegionCode wrc = kWindowGlobalPortRgn;
+
+ if (sc == SC_TitleBarCloseButton)
+ wrc = kWindowCloseBoxRgn;
+ else if (sc == SC_TitleBarMinButton)
+ wrc = kWindowCollapseBoxRgn;
+ else if (sc == SC_TitleBarMaxButton)
+ wrc = kWindowZoomBoxRgn;
+ else if (sc == SC_TitleBarLabel)
+ wrc = kWindowTitleTextRgn;
+ else if (sc == SC_TitleBarSysMenu)
+ ret.setRect(-1024, -1024, 10, proxy()->pixelMetric(PM_TitleBarHeight,
+ titlebar, widget));
+ if (wrc != kWindowGlobalPortRgn) {
+ QCFType<HIShapeRef> region;
+ QRect tmpRect = titlebar->rect;
+ HIRect titleRect = qt_hirectForQRect(tmpRect);
+ HIThemeGetWindowShape(&titleRect, &wdi, kWindowTitleBarRgn, &region);
+ ptrHIShapeGetBounds(region, &titleRect);
+ CFRelease(region);
+ tmpRect.translate(tmpRect.x() - int(titleRect.origin.x),
+ tmpRect.y() - int(titleRect.origin.y));
+ titleRect = qt_hirectForQRect(tmpRect);
+ HIThemeGetWindowShape(&titleRect, &wdi, wrc, &region);
+ ptrHIShapeGetBounds(region, &titleRect);
+ ret = qt_qrectForHIRect(titleRect);
+ }
+ }
+ break;
+ case CC_ComboBox:
+ if (const QStyleOptionComboBox *combo = qstyleoption_cast<const QStyleOptionComboBox *>(opt)) {
+ HIThemeButtonDrawInfo bdi;
+ d->initComboboxBdi(combo, &bdi, widget, d->getDrawState(opt->state));
+
+ switch (sc) {
+ case SC_ComboBoxEditField:{
+ ret = QMacStylePrivate::comboboxEditBounds(combo->rect, bdi);
+ // 10.10 and above need a slight shift
+ ret.setHeight(ret.height() - 1);
+ break; }
+ case SC_ComboBoxArrow:{
+ ret = QMacStylePrivate::comboboxEditBounds(combo->rect, bdi);
+ ret.setX(ret.x() + ret.width());
+ ret.setWidth(combo->rect.right() - ret.right());
+ break; }
+ case SC_ComboBoxListBoxPopup:{
+ if (combo->editable) {
+ HIRect inner = QMacStylePrivate::comboboxInnerBounds(qt_hirectForQRect(combo->rect), bdi.kind);
+ QRect editRect = QMacStylePrivate::comboboxEditBounds(combo->rect, bdi);
+ const int comboTop = combo->rect.top();
+ ret = QRect(qRound(inner.origin.x),
+ comboTop,
+ qRound(inner.origin.x - combo->rect.left() + inner.size.width),
+ editRect.bottom() - comboTop + 2);
+ } else {
+ QRect editRect = QMacStylePrivate::comboboxEditBounds(combo->rect, bdi);
+ ret = QRect(combo->rect.x() + 4 - 11,
+ combo->rect.y() + 1,
+ editRect.width() + 10 + 11,
+ 1);
+ }
+ break; }
+ default:
+ break;
+ }
+ }
+ break;
+ case CC_GroupBox:
+ if (const QStyleOptionGroupBox *groupBox = qstyleoption_cast<const QStyleOptionGroupBox *>(opt)) {
+ bool checkable = groupBox->subControls & SC_GroupBoxCheckBox;
+ const bool flat = groupBox->features & QStyleOptionFrame::Flat;
+ bool hasNoText = !checkable && groupBox->text.isEmpty();
+ switch (sc) {
+ case SC_GroupBoxLabel:
+ case SC_GroupBoxCheckBox: {
+ // Cheat and use the smaller font if we need to
+ bool checkable = groupBox->subControls & SC_GroupBoxCheckBox;
+ bool fontIsSet = (widget && widget->testAttribute(Qt::WA_SetFont))
+ || !QApplication::desktopSettingsAware();
+ int tw;
+ int h;
+ int margin = flat || hasNoText ? 0 : 9;
+ ret = groupBox->rect.adjusted(margin, 0, -margin, 0);
+
+ if (!fontIsSet) {
+ HIThemeTextInfo tti;
+ tti.version = qt_mac_hitheme_version;
+ tti.state = kThemeStateActive;
+ tti.fontID = flat ? kThemeSystemFont : kThemeSmallSystemFont;
+ tti.horizontalFlushness = kHIThemeTextHorizontalFlushCenter;
+ tti.verticalFlushness = kHIThemeTextVerticalFlushCenter;
+ tti.options = kHIThemeTextBoxOptionNone;
+ tti.truncationPosition = kHIThemeTextTruncationNone;
+ tti.truncationMaxLines = 1 + groupBox->text.count(QLatin1Char('\n'));
+ CGFloat width;
+ CGFloat height;
+ QCFString groupText = qt_mac_removeMnemonics(groupBox->text);
+ HIThemeGetTextDimensions(groupText, 0, &tti, &width, &height, 0);
+ tw = qRound(width);
+ h = qCeil(height);
+ } else {
+ QFontMetricsF fm = QFontMetricsF(groupBox->fontMetrics);
+ h = qCeil(fm.height());
+ tw = qCeil(fm.size(Qt::TextShowMnemonic, groupBox->text).width());
+ }
+ ret.setHeight(h);
+
+ QRect labelRect = alignedRect(groupBox->direction, groupBox->textAlignment,
+ QSize(tw, h), ret);
+ if (flat && checkable)
+ labelRect.moveLeft(labelRect.left() + 4);
+ int indicatorWidth = proxy()->pixelMetric(PM_IndicatorWidth, opt, widget);
+ bool rtl = groupBox->direction == Qt::RightToLeft;
+ if (sc == SC_GroupBoxLabel) {
+ if (checkable) {
+ int newSum = indicatorWidth + 1;
+ int newLeft = labelRect.left() + (rtl ? -newSum : newSum);
+ labelRect.moveLeft(newLeft);
+ if (flat)
+ labelRect.moveTop(labelRect.top() + 3);
+ else
+ labelRect.moveTop(labelRect.top() + 4);
+ } else if (flat) {
+ int newLeft = labelRect.left() - (rtl ? 3 : -3);
+ labelRect.moveLeft(newLeft);
+ labelRect.moveTop(labelRect.top() + 3);
+ } else {
+ int newLeft = labelRect.left() - (rtl ? 3 : 2);
+ labelRect.moveLeft(newLeft);
+ labelRect.moveTop(labelRect.top() + 4);
+ }
+ ret = labelRect;
+ }
+
+ if (sc == SC_GroupBoxCheckBox) {
+ int left = rtl ? labelRect.right() - indicatorWidth : labelRect.left() - 1;
+ int top = flat ? ret.top() + 1 : ret.top() + 5;
+ ret.setRect(left, top,
+ indicatorWidth, proxy()->pixelMetric(PM_IndicatorHeight, opt, widget));
+ }
+ break;
+ }
+ case SC_GroupBoxContents:
+ case SC_GroupBoxFrame: {
+ QFontMetrics fm = groupBox->fontMetrics;
+ int yOffset = 3;
+ if (!flat) {
+ if (widget && !widget->testAttribute(Qt::WA_SetFont)
+ && QApplication::desktopSettingsAware())
+ fm = QFontMetrics(qt_app_fonts_hash()->value("QSmallFont", QFont()));
+ yOffset = 5;
+ }
+
+ if (hasNoText)
+ yOffset = -qCeil(QFontMetricsF(fm).height());
+ ret = opt->rect.adjusted(0, qCeil(QFontMetricsF(fm).height()) + yOffset, 0, 0);
+ if (sc == SC_GroupBoxContents) {
+ if (flat)
+ ret.adjust(3, -5, -3, -4); // guess too
+ else
+ ret.adjust(3, 3, -3, -4); // guess
+ }
+ }
+ break;
+ default:
+ ret = QCommonStyle::subControlRect(cc, groupBox, sc, widget);
+ break;
+ }
+ }
+ break;
+#ifndef QT_NO_SPINBOX
+ case CC_SpinBox:
+ if (const QStyleOptionSpinBox *spin = qstyleoption_cast<const QStyleOptionSpinBox *>(opt)) {
+ QAquaWidgetSize aquaSize = d->aquaSizeConstrain(spin, widget);
+ int spinner_w;
+ int spinBoxSep;
+ int fw = proxy()->pixelMetric(PM_SpinBoxFrameWidth, spin, widget);
+ switch (aquaSize) {
+ default:
+ case QAquaSizeUnknown:
+ case QAquaSizeLarge:
+ spinner_w = 14;
+ spinBoxSep = 2;
+ break;
+ case QAquaSizeSmall:
+ spinner_w = 12;
+ spinBoxSep = 2;
+ break;
+ case QAquaSizeMini:
+ spinner_w = 10;
+ spinBoxSep = 1;
+ break;
+ }
+
+ switch (sc) {
+ case SC_SpinBoxUp:
+ case SC_SpinBoxDown: {
+ if (spin->buttonSymbols == QAbstractSpinBox::NoButtons)
+ break;
+
+ const int y = fw;
+ const int x = spin->rect.width() - spinner_w;
+ ret.setRect(x + spin->rect.x(), y + spin->rect.y(), spinner_w, spin->rect.height() - y * 2);
+ HIThemeButtonDrawInfo bdi;
+ bdi.version = qt_mac_hitheme_version;
+ bdi.kind = kThemeIncDecButton;
+ int hackTranslateX;
+ switch (aquaSize) {
+ default:
+ case QAquaSizeUnknown:
+ case QAquaSizeLarge:
+ bdi.kind = kThemeIncDecButton;
+ hackTranslateX = 0;
+ break;
+ case QAquaSizeSmall:
+ bdi.kind = kThemeIncDecButtonSmall;
+ hackTranslateX = -2;
+ break;
+ case QAquaSizeMini:
+ bdi.kind = kThemeIncDecButtonMini;
+ hackTranslateX = -1;
+ break;
+ }
+ bdi.state = kThemeStateActive;
+ bdi.value = kThemeButtonOff;
+ bdi.adornment = kThemeAdornmentNone;
+ HIRect hirect = qt_hirectForQRect(ret);
+
+ HIRect outRect;
+ HIThemeGetButtonBackgroundBounds(&hirect, &bdi, &outRect);
+ ret = qt_qrectForHIRect(outRect);
+ switch (sc) {
+ case SC_SpinBoxUp:
+ ret.setHeight(ret.height() / 2);
+ break;
+ case SC_SpinBoxDown:
+ ret.setY(ret.y() + ret.height() / 2);
+ break;
+ default:
+ Q_ASSERT(0);
+ break;
+ }
+ ret.translate(hackTranslateX, 0); // hack: position the buttons correctly (weird that we need this)
+ ret = visualRect(spin->direction, spin->rect, ret);
+ break;
+ }
+ case SC_SpinBoxEditField:
+ if (spin->buttonSymbols == QAbstractSpinBox::NoButtons) {
+ ret.setRect(fw, fw,
+ spin->rect.width() - fw * 2,
+ spin->rect.height() - fw * 2);
+ } else {
+ ret.setRect(fw, fw,
+ spin->rect.width() - fw * 2 - spinBoxSep - spinner_w,
+ spin->rect.height() - fw * 2);
+ }
+ ret = visualRect(spin->direction, spin->rect, ret);
+ break;
+ default:
+ ret = QCommonStyle::subControlRect(cc, spin, sc, widget);
+ break;
+ }
+ }
+ break;
+#endif
+ case CC_ToolButton:
+ ret = QCommonStyle::subControlRect(cc, opt, sc, widget);
+ if (sc == SC_ToolButtonMenu
+#ifndef QT_NO_ACCESSIBILITY
+ && !QStyleHelper::hasAncestor(opt->styleObject, QAccessible::ToolBar)
+#endif
+ ) {
+ ret.adjust(-1, 0, 0, 0);
+ }
+ break;
+ default:
+ ret = QCommonStyle::subControlRect(cc, opt, sc, widget);
+ break;
+ }
+ return ret;
+}
+
+QSize QMacStyle::sizeFromContents(ContentsType ct, const QStyleOption *opt,
+ const QSize &csz, const QWidget *widget) const
+{
+ Q_D(const QMacStyle);
+ QSize sz(csz);
+ bool useAquaGuideline = true;
+
+ switch (ct) {
+#ifndef QT_NO_SPINBOX
+ case CT_SpinBox:
+ if (const QStyleOptionSpinBox *vopt = qstyleoption_cast<const QStyleOptionSpinBox *>(opt)) {
+ // Add button + frame widths
+ int buttonWidth = 20;
+ int fw = proxy()->pixelMetric(PM_SpinBoxFrameWidth, vopt, widget);
+ sz += QSize(buttonWidth + 2*fw, 2*fw - 3);
+ }
+ break;
+#endif
+ case QStyle::CT_TabWidget:
+ // the size between the pane and the "contentsRect" (+4,+4)
+ // (the "contentsRect" is on the inside of the pane)
+ sz = QCommonStyle::sizeFromContents(ct, opt, csz, widget);
+ /**
+ This is supposed to show the relationship between the tabBar and
+ the stack widget of a QTabWidget.
+ Unfortunately ascii is not a good way of representing graphics.....
+ PS: The '=' line is the painted frame.
+
+ top ---+
+ |
+ |
+ |
+ | vvv just outside the painted frame is the "pane"
+ - -|- - - - - - - - - - <-+
+ TAB BAR +=====^============ | +2 pixels
+ - - -|- - -|- - - - - - - <-+
+ | | ^ ^^^ just inside the painted frame is the "contentsRect"
+ | | |
+ | overlap |
+ | | |
+ bottom ------+ <-+ +14 pixels
+ |
+ v
+ ------------------------------ <- top of stack widget
+
+
+ To summarize:
+ * 2 is the distance between the pane and the contentsRect
+ * The 14 and the 1's are the distance from the contentsRect to the stack widget.
+ (same value as used in SE_TabWidgetTabContents)
+ * overlap is how much the pane should overlap the tab bar
+ */
+ // then add the size between the stackwidget and the "contentsRect"
+#ifndef QT_NO_TABWIDGET
+ if (const QStyleOptionTabWidgetFrame *twf
+ = qstyleoption_cast<const QStyleOptionTabWidgetFrame *>(opt)) {
+ QSize extra(0,0);
+ const int overlap = pixelMetric(PM_TabBarBaseOverlap, opt, widget);
+ const int gapBetweenTabbarAndStackWidget = 2 + 14 - overlap;
+
+ if (getTabDirection(twf->shape) == kThemeTabNorth || getTabDirection(twf->shape) == kThemeTabSouth) {
+ extra = QSize(2, gapBetweenTabbarAndStackWidget + 1);
+ } else {
+ extra = QSize(gapBetweenTabbarAndStackWidget + 1, 2);
+ }
+ sz+= extra;
+ }
+#endif
+ break;
+#ifndef QT_NO_TABBAR
+ case QStyle::CT_TabBarTab:
+ if (const QStyleOptionTab *tab = qstyleoption_cast<const QStyleOptionTab *>(opt)) {
+ const QAquaWidgetSize AquaSize = d->aquaSizeConstrain(opt, widget);
+ const bool differentFont = (widget && widget->testAttribute(Qt::WA_SetFont))
+ || !QApplication::desktopSettingsAware();
+ ThemeTabDirection ttd = getTabDirection(tab->shape);
+ bool vertTabs = ttd == kThemeTabWest || ttd == kThemeTabEast;
+ if (vertTabs)
+ sz = sz.transposed();
+ int defaultTabHeight;
+ int extraHSpace = proxy()->pixelMetric(PM_TabBarTabHSpace, tab, widget);
+ QFontMetrics fm = opt->fontMetrics;
+ switch (AquaSize) {
+ case QAquaSizeUnknown:
+ case QAquaSizeLarge:
+ if (tab->documentMode)
+ defaultTabHeight = 24;
+ else
+ defaultTabHeight = 21;
+ break;
+ case QAquaSizeSmall:
+ defaultTabHeight = 18;
+ break;
+ case QAquaSizeMini:
+ defaultTabHeight = 16;
+ break;
+ }
+ bool setWidth = false;
+ if (differentFont || !tab->icon.isNull()) {
+ sz.rheight() = qMax(defaultTabHeight, sz.height());
+ } else {
+ QSize textSize = fm.size(Qt::TextShowMnemonic, tab->text);
+ sz.rheight() = qMax(defaultTabHeight, textSize.height());
+ sz.rwidth() = textSize.width();
+ setWidth = true;
+ }
+ sz.rwidth() += extraHSpace;
+
+ if (vertTabs)
+ sz = sz.transposed();
+
+ int maxWidgetHeight = qMax(tab->leftButtonSize.height(), tab->rightButtonSize.height());
+ int maxWidgetWidth = qMax(tab->leftButtonSize.width(), tab->rightButtonSize.width());
+
+ int widgetWidth = 0;
+ int widgetHeight = 0;
+ int padding = 0;
+ if (tab->leftButtonSize.isValid()) {
+ padding += 8;
+ widgetWidth += tab->leftButtonSize.width();
+ widgetHeight += tab->leftButtonSize.height();
+ }
+ if (tab->rightButtonSize.isValid()) {
+ padding += 8;
+ widgetWidth += tab->rightButtonSize.width();
+ widgetHeight += tab->rightButtonSize.height();
+ }
+
+ if (vertTabs) {
+ sz.setHeight(sz.height() + widgetHeight + padding);
+ sz.setWidth(qMax(sz.width(), maxWidgetWidth));
+ } else {
+ if (setWidth)
+ sz.setWidth(sz.width() + widgetWidth + padding);
+ sz.setHeight(qMax(sz.height(), maxWidgetHeight));
+ }
+ }
+ break;
+#endif
+ case QStyle::CT_PushButton:
+ // 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.
+ sz.rwidth() += QMacStylePrivate::PushButtonLeftOffset + QMacStylePrivate::PushButtonRightOffset + 12;
+ if (opt->state & QStyle::State_Small)
+ sz.rheight() += 14;
+ else
+ sz.rheight() += 4;
+ break;
+ case QStyle::CT_MenuItem:
+ if (const QStyleOptionMenuItem *mi = qstyleoption_cast<const QStyleOptionMenuItem *>(opt)) {
+ int maxpmw = mi->maxIconWidth;
+#ifndef QT_NO_COMBOBOX
+ const QComboBox *comboBox = qobject_cast<const QComboBox *>(widget);
+#endif
+ int w = sz.width(),
+ h = sz.height();
+ if (mi->menuItemType == QStyleOptionMenuItem::Separator) {
+ w = 10;
+ SInt16 ash;
+ GetThemeMenuSeparatorHeight(&ash);
+ h = ash;
+ } else {
+ h = mi->fontMetrics.height() + 2;
+ if (!mi->icon.isNull()) {
+#ifndef QT_NO_COMBOBOX
+ if (comboBox) {
+ const QSize &iconSize = comboBox->iconSize();
+ h = qMax(h, iconSize.height() + 4);
+ maxpmw = qMax(maxpmw, iconSize.width());
+ } else
+#endif
+ {
+ int iconExtent = proxy()->pixelMetric(PM_SmallIconSize);
+ h = qMax(h, mi->icon.actualSize(QSize(iconExtent, iconExtent)).height() + 4);
+ }
+ }
+ }
+ if (mi->text.contains(QLatin1Char('\t')))
+ w += 12;
+ if (mi->menuItemType == QStyleOptionMenuItem::SubMenu)
+ w += 20;
+ if (maxpmw)
+ w += maxpmw + 6;
+ // add space for a check. All items have place for a check too.
+ w += 20;
+#ifndef QT_NO_COMBOBOX
+ if (comboBox && comboBox->isVisible()) {
+ QStyleOptionComboBox cmb;
+ cmb.initFrom(comboBox);
+ cmb.editable = false;
+ cmb.subControls = QStyle::SC_ComboBoxEditField;
+ cmb.activeSubControls = QStyle::SC_None;
+ w = qMax(w, subControlRect(QStyle::CC_ComboBox, &cmb,
+ QStyle::SC_ComboBoxEditField,
+ comboBox).width());
+ } else
+#endif
+ {
+ w += 12;
+ }
+ sz = QSize(w, h);
+ }
+ break;
+ case CT_MenuBarItem:
+ if (!sz.isEmpty())
+ sz += QSize(12, 4); // Constants from QWindowsStyle
+ break;
+ case CT_ToolButton:
+ sz.rwidth() += 10;
+ sz.rheight() += 10;
+ return sz;
+ case CT_ComboBox: {
+ sz.rwidth() += 50;
+ const QStyleOptionComboBox *cb = qstyleoption_cast<const QStyleOptionComboBox *>(opt);
+ if (cb && !cb->editable)
+ sz.rheight() += 2;
+ break;
+ }
+ case CT_Menu: {
+ if (proxy() == this) {
+ sz = csz;
+ } else {
+ QStyleHintReturnMask menuMask;
+ QStyleOption myOption = *opt;
+ myOption.rect.setSize(sz);
+ if (proxy()->styleHint(SH_Menu_Mask, &myOption, widget, &menuMask))
+ sz = menuMask.region.boundingRect().size();
+ }
+ break; }
+ case CT_HeaderSection:{
+ const QStyleOptionHeader *header = qstyleoption_cast<const QStyleOptionHeader *>(opt);
+ sz = QCommonStyle::sizeFromContents(ct, opt, csz, widget);
+ if (header->text.contains(QLatin1Char('\n')))
+ useAquaGuideline = false;
+ break; }
+ case CT_ScrollBar :
+ // Make sure that the scroll bar is large enough to display the thumb indicator.
+ if (const QStyleOptionSlider *slider = qstyleoption_cast<const QStyleOptionSlider *>(opt)) {
+ const int minimumSize = scrollButtonsCutoffSize(thumbIndicatorCutoff, QStyleHelper::widgetSizePolicy(widget, opt));
+ if (slider->orientation == Qt::Horizontal)
+ sz = sz.expandedTo(QSize(minimumSize, sz.height()));
+ else
+ sz = sz.expandedTo(QSize(sz.width(), minimumSize));
+ }
+ break;
+ case CT_ItemViewItem:
+ if (const QStyleOptionViewItem *vopt = qstyleoption_cast<const QStyleOptionViewItem *>(opt)) {
+ sz = QCommonStyle::sizeFromContents(ct, vopt, csz, widget);
+ sz.setHeight(sz.height() + 2);
+ }
+ break;
+
+ default:
+ sz = QCommonStyle::sizeFromContents(ct, opt, csz, widget);
+ }
+
+ if (useAquaGuideline){
+ QSize macsz;
+ if (d->aquaSizeConstrain(opt, widget, ct, sz, &macsz) != QAquaSizeUnknown) {
+ if (macsz.width() != -1)
+ sz.setWidth(macsz.width());
+ if (macsz.height() != -1)
+ sz.setHeight(macsz.height());
+ }
+ }
+
+ // The sizes that Carbon and the guidelines gives us excludes the focus frame.
+ // We compensate for this by adding some extra space here to make room for the frame when drawing:
+ if (const QStyleOptionComboBox *combo = qstyleoption_cast<const QStyleOptionComboBox *>(opt)){
+ QAquaWidgetSize widgetSize = d->aquaSizeConstrain(opt, widget);
+ int bkind = 0;
+ switch (widgetSize) {
+ default:
+ case QAquaSizeLarge:
+ bkind = combo->editable ? kThemeComboBox : kThemePopupButton;
+ break;
+ case QAquaSizeSmall:
+ bkind = combo->editable ? int(kThemeComboBoxSmall) : int(kThemePopupButtonSmall);
+ break;
+ case QAquaSizeMini:
+ bkind = combo->editable ? kThemeComboBoxMini : kThemePopupButtonMini;
+ break;
+ }
+ HIRect tmpRect = {{0, 0}, {0, 0}};
+ HIRect diffRect = QMacStylePrivate::comboboxInnerBounds(tmpRect, bkind);
+ sz.rwidth() -= qRound(diffRect.size.width);
+ sz.rheight() -= qRound(diffRect.size.height);
+ } else if (ct == CT_PushButton || ct == CT_ToolButton){
+ ThemeButtonKind bkind;
+ QAquaWidgetSize widgetSize = d->aquaSizeConstrain(opt, widget);
+ switch (ct) {
+ default:
+ case CT_PushButton:
+ if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(opt)) {
+ if (btn->features & QStyleOptionButton::CommandLinkButton) {
+ return QCommonStyle::sizeFromContents(ct, opt, sz, widget);
+ }
+ }
+
+ switch (widgetSize) {
+ default:
+ case QAquaSizeLarge:
+ bkind = kThemePushButton;
+ break;
+ case QAquaSizeSmall:
+ bkind = kThemePushButtonSmall;
+ break;
+ case QAquaSizeMini:
+ bkind = kThemePushButtonMini;
+ break;
+ }
+ break;
+ case CT_ToolButton:
+ switch (widgetSize) {
+ default:
+ case QAquaSizeLarge:
+ bkind = kThemeLargeBevelButton;
+ break;
+ case QAquaSizeMini:
+ case QAquaSizeSmall:
+ bkind = kThemeSmallBevelButton;
+ }
+ break;
+ }
+
+ HIThemeButtonDrawInfo bdi;
+ bdi.version = qt_mac_hitheme_version;
+ bdi.state = kThemeStateActive;
+ bdi.kind = bkind;
+ bdi.value = kThemeButtonOff;
+ bdi.adornment = kThemeAdornmentNone;
+ HIRect macRect, myRect;
+ myRect = CGRectMake(0, 0, sz.width(), sz.height());
+ HIThemeGetButtonBackgroundBounds(&myRect, &bdi, &macRect);
+ // Mini buttons only return their actual size in HIThemeGetButtonBackgroundBounds, so help them out a bit (guess),
+ if (bkind == kThemePushButtonMini)
+ macRect.size.height += 8.;
+ else if (bkind == kThemePushButtonSmall)
+ macRect.size.height -= 10;
+ sz.setWidth(sz.width() + int(macRect.size.width - myRect.size.width));
+ sz.setHeight(sz.height() + int(macRect.size.height - myRect.size.height));
+ }
+ return sz;
+}
+
+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)
+ flags |= Qt::TextHideMnemonic;
+ QCommonStyle::drawItemText(p, r, flags, pal, enabled, text, textRole);
+}
+
+bool QMacStyle::event(QEvent *e)
+{
+ Q_D(QMacStyle);
+ if(e->type() == QEvent::FocusIn) {
+ QWidget *f = 0;
+ QWidget *focusWidget = QApplication::focusWidget();
+#ifndef QT_NO_GRAPHICSVIEW
+ if (QGraphicsView *graphicsView = qobject_cast<QGraphicsView *>(focusWidget)) {
+ QGraphicsItem *focusItem = graphicsView->scene() ? graphicsView->scene()->focusItem() : 0;
+ if (focusItem && focusItem->type() == QGraphicsProxyWidget::Type) {
+ QGraphicsProxyWidget *proxy = static_cast<QGraphicsProxyWidget *>(focusItem);
+ if (proxy->widget())
+ focusWidget = proxy->widget()->focusWidget();
+ }
+ }
+#endif
+ if (focusWidget && focusWidget->testAttribute(Qt::WA_MacShowFocusRect)) {
+ f = focusWidget;
+ QWidget *top = f->parentWidget();
+ while (top && !top->isWindow() && !(top->windowType() == Qt::SubWindow))
+ top = top->parentWidget();
+#ifndef QT_NO_MAINWINDOW
+ if (qobject_cast<QMainWindow *>(top)) {
+ QWidget *central = static_cast<QMainWindow *>(top)->centralWidget();
+ for (const QWidget *par = f; par; par = par->parentWidget()) {
+ if (par == central) {
+ top = central;
+ break;
+ }
+ if (par->isWindow())
+ break;
+ }
+ }
+#endif
+ }
+ if (f) {
+ if(!d->focusWidget)
+ d->focusWidget = new QFocusFrame(f);
+ d->focusWidget->setWidget(f);
+ } else if(d->focusWidget) {
+ d->focusWidget->setWidget(0);
+ }
+ } else if(e->type() == QEvent::FocusOut) {
+ if(d->focusWidget)
+ d->focusWidget->setWidget(0);
+ }
+ return false;
+}
+
+QIcon QMacStyle::standardIcon(StandardPixmap standardIcon, const QStyleOption *opt,
+ const QWidget *widget) const
+{
+ switch (standardIcon) {
+ default:
+ return QCommonStyle::standardIcon(standardIcon, opt, widget);
+ case SP_ToolBarHorizontalExtensionButton:
+ case SP_ToolBarVerticalExtensionButton: {
+ QPixmap pixmap(QLatin1String(":/qt-project.org/styles/macstyle/images/toolbar-ext.png"));
+ if (standardIcon == SP_ToolBarVerticalExtensionButton) {
+ QPixmap pix2(pixmap.height(), pixmap.width());
+ pix2.setDevicePixelRatio(pixmap.devicePixelRatio());
+ pix2.fill(Qt::transparent);
+ QPainter p(&pix2);
+ p.translate(pix2.width(), 0);
+ p.rotate(90);
+ p.drawPixmap(0, 0, pixmap);
+ return pix2;
+ }
+ return pixmap;
+ }
+ }
+}
+
+int QMacStyle::layoutSpacing(QSizePolicy::ControlType control1,
+ QSizePolicy::ControlType control2,
+ Qt::Orientation orientation,
+ const QStyleOption *option,
+ const QWidget *widget) const
+{
+ const int ButtonMask = QSizePolicy::ButtonBox | QSizePolicy::PushButton;
+ bool isMetal = (widget && widget->testAttribute(Qt::WA_MacBrushedMetal));
+ int controlSize = getControlSize(option, widget);
+
+ if (control2 == QSizePolicy::ButtonBox) {
+ /*
+ AHIG seems to prefer a 12-pixel margin between group
+ boxes and the row of buttons. The 20 pixel comes from
+ Builder.
+ */
+ if (isMetal // (AHIG, guess, guess)
+ || (control1 & (QSizePolicy::Frame // guess
+ | QSizePolicy::GroupBox // (AHIG, guess, guess)
+ | QSizePolicy::TabWidget // guess
+ | ButtonMask))) { // AHIG
+ return_SIZE(14, 8, 8);
+ } else if (control1 == QSizePolicy::LineEdit) {
+ return_SIZE(8, 8, 8); // Interface Builder
+ } else {
+ return_SIZE(20, 7, 7); // Interface Builder
+ }
+ }
+
+ if ((control1 | control2) & ButtonMask) {
+ if (control1 == QSizePolicy::LineEdit)
+ return_SIZE(8, 8, 8); // Interface Builder
+ else if (control2 == QSizePolicy::LineEdit) {
+ if (orientation == Qt::Vertical)
+ return_SIZE(20, 7, 7); // Interface Builder
+ else
+ return_SIZE(20, 8, 8);
+ }
+ return_SIZE(14, 8, 8); // Interface Builder
+ }
+
+ switch (CT2(control1, control2)) {
+ case CT1(QSizePolicy::Label): // guess
+ case CT2(QSizePolicy::Label, QSizePolicy::DefaultType): // guess
+ case CT2(QSizePolicy::Label, QSizePolicy::CheckBox): // AHIG
+ case CT2(QSizePolicy::Label, QSizePolicy::ComboBox): // AHIG
+ case CT2(QSizePolicy::Label, QSizePolicy::LineEdit): // guess
+ case CT2(QSizePolicy::Label, QSizePolicy::RadioButton): // AHIG
+ case CT2(QSizePolicy::Label, QSizePolicy::Slider): // guess
+ case CT2(QSizePolicy::Label, QSizePolicy::SpinBox): // guess
+ case CT2(QSizePolicy::Label, QSizePolicy::ToolButton): // guess
+ return_SIZE(8, 6, 5);
+ case CT1(QSizePolicy::ToolButton):
+ return 8; // AHIG
+ case CT1(QSizePolicy::CheckBox):
+ case CT2(QSizePolicy::CheckBox, QSizePolicy::RadioButton):
+ case CT2(QSizePolicy::RadioButton, QSizePolicy::CheckBox):
+ if (orientation == Qt::Vertical)
+ return_SIZE(8, 8, 7); // AHIG and Builder
+ break;
+ case CT1(QSizePolicy::RadioButton):
+ if (orientation == Qt::Vertical)
+ return 5; // (Builder, guess, AHIG)
+ }
+
+ if (orientation == Qt::Horizontal
+ && (control2 & (QSizePolicy::CheckBox | QSizePolicy::RadioButton)))
+ return_SIZE(12, 10, 8); // guess
+
+ if ((control1 | control2) & (QSizePolicy::Frame
+ | QSizePolicy::GroupBox
+ | QSizePolicy::TabWidget)) {
+ /*
+ These values were chosen so that nested container widgets
+ look good side by side. Builder uses 8, which looks way
+ too small, and AHIG doesn't say anything.
+ */
+ return_SIZE(16, 10, 10); // guess
+ }
+
+ if ((control1 | control2) & (QSizePolicy::Line | QSizePolicy::Slider))
+ return_SIZE(12, 10, 8); // AHIG
+
+ if ((control1 | control2) & QSizePolicy::LineEdit)
+ return_SIZE(10, 8, 8); // AHIG
+
+ /*
+ AHIG and Builder differ by up to 4 pixels for stacked editable
+ comboboxes. We use some values that work fairly well in all
+ cases.
+ */
+ if ((control1 | control2) & QSizePolicy::ComboBox)
+ return_SIZE(10, 8, 7); // guess
+
+ /*
+ Builder defaults to 8, 6, 5 in lots of cases, but most of the time the
+ result looks too cramped.
+ */
+ return_SIZE(10, 8, 6); // guess
+}
+
+/*
+FontHash::FontHash()
+{
+ QHash<QByteArray, QFont>::operator=(QGuiApplicationPrivate::platformIntegration()->fontDatabase()->defaultFonts());
+}
+
+Q_GLOBAL_STATIC(FontHash, app_fonts)
+FontHash *qt_app_fonts_hash()
+{
+ return app_fonts();
+}
+*/
+QT_END_NAMESPACE
diff --git a/src/plugins/styles/mac/qmacstyle_mac_p.h b/src/plugins/styles/mac/qmacstyle_mac_p.h
new file mode 100644
index 0000000000..6011baeea2
--- /dev/null
+++ b/src/plugins/styles/mac/qmacstyle_mac_p.h
@@ -0,0 +1,128 @@
+/****************************************************************************
+**
+** 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 QMACSTYLE_MAC_P_H
+#define QMACSTYLE_MAC_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/qcommonstyle.h>
+
+QT_BEGIN_NAMESPACE
+
+class QPalette;
+
+class QPushButton;
+class QStyleOptionButton;
+class QMacStylePrivate;
+class QMacStyle : public QCommonStyle
+{
+ Q_OBJECT
+public:
+ QMacStyle();
+ virtual ~QMacStyle();
+
+ void polish(QWidget *w);
+ void unpolish(QWidget *w);
+
+ void polish(QApplication*);
+ void unpolish(QApplication*);
+
+ void polish(QPalette &pal);
+
+ void drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, QPainter *p,
+ const QWidget *w = 0) 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;
+ void drawComplexControl(ComplexControl cc, const QStyleOptionComplex *opt, QPainter *p,
+ const QWidget *w = 0) const;
+ SubControl hitTestComplexControl(ComplexControl cc, const QStyleOptionComplex *opt,
+ const QPoint &pt, const QWidget *w = 0) const;
+ QRect subControlRect(ComplexControl cc, const QStyleOptionComplex *opt, SubControl sc,
+ const QWidget *w = 0) const;
+ QSize sizeFromContents(ContentsType ct, const QStyleOption *opt,
+ const QSize &contentsSize, const QWidget *w = 0) const;
+
+ int pixelMetric(PixelMetric pm, const QStyleOption *opt = 0, const QWidget *widget = 0) const;
+
+ QPalette standardPalette() const;
+
+ virtual int styleHint(StyleHint sh, const QStyleOption *opt = 0, const QWidget *w = 0,
+ QStyleHintReturn *shret = 0) const;
+
+ QPixmap standardPixmap(StandardPixmap sp, const QStyleOption *opt,
+ const QWidget *widget = 0) const;
+
+ QPixmap generatedIconPixmap(QIcon::Mode iconMode, const QPixmap &pixmap,
+ const QStyleOption *opt) const;
+
+ virtual void drawItemText(QPainter *p, const QRect &r, int flags, const QPalette &pal,
+ bool enabled, const QString &text, QPalette::ColorRole textRole = QPalette::NoRole) const;
+
+ bool event(QEvent *e);
+
+ QIcon standardIcon(StandardPixmap standardIcon, const QStyleOption *opt = 0,
+ const QWidget *widget = 0) const;
+ int layoutSpacing(QSizePolicy::ControlType control1, QSizePolicy::ControlType control2,
+ Qt::Orientation orientation, const QStyleOption *option = 0,
+ const QWidget *widget = 0) const;
+
+private:
+ Q_DISABLE_COPY(QMacStyle)
+ Q_DECLARE_PRIVATE(QMacStyle)
+
+#if QT_CONFIG(pushbutton)
+ friend bool qt_mac_buttonIsRenderedFlat(const QPushButton *pushButton, const QStyleOptionButton *option);
+#endif
+};
+
+QT_END_NAMESPACE
+
+#endif // QMACSTYLE_MAC_P_H
diff --git a/src/plugins/styles/mac/qmacstyle_mac_p_p.h b/src/plugins/styles/mac/qmacstyle_mac_p_p.h
new file mode 100644
index 0000000000..6fd6d0c900
--- /dev/null
+++ b/src/plugins/styles/mac/qmacstyle_mac_p_p.h
@@ -0,0 +1,257 @@
+/****************************************************************************
+**
+** 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 QMACSTYLE_MAC_P_P_H
+#define QMACSTYLE_MAC_P_P_H
+
+#include <Carbon/Carbon.h>
+#undef check
+
+#include <QtWidgets/private/qtwidgetsglobal_p.h>
+#include <QtWidgets/private/qcommonstyle_p.h>
+#include "qmacstyle_mac_p.h"
+#include <private/qapplication_p.h>
+#include <private/qcombobox_p.h>
+#include <private/qpainter_p.h>
+#include <private/qstylehelper_p.h>
+#include <qapplication.h>
+#include <qbitmap.h>
+#if QT_CONFIG(checkbox)
+#include <qcheckbox.h>
+#endif
+#include <qcombobox.h>
+#if QT_CONFIG(dialogbuttonbox)
+#include <qdialogbuttonbox.h>
+#endif
+#include <qdockwidget.h>
+#include <qevent.h>
+#include <qfocusframe.h>
+#include <qformlayout.h>
+#include <qgroupbox.h>
+#include <qhash.h>
+#include <qheaderview.h>
+#include <qlayout.h>
+#include <qlineedit.h>
+#include <qlistview.h>
+#include <qmainwindow.h>
+#include <qmap.h>
+#include <qmenubar.h>
+#include <qpaintdevice.h>
+#include <qpainter.h>
+#include <qpixmapcache.h>
+#include <qpointer.h>
+#include <qprogressbar.h>
+#if QT_CONFIG(pushbutton)
+#include <qpushbutton.h>
+#endif
+#include <qradiobutton.h>
+#include <qrubberband.h>
+#include <qsizegrip.h>
+#include <qspinbox.h>
+#include <qsplitter.h>
+#include <qstyleoption.h>
+#include <qtextedit.h>
+#include <qtextstream.h>
+#include <qtoolbar.h>
+#include <qtoolbutton.h>
+#include <qtreeview.h>
+#include <qtableview.h>
+#include <qwizard.h>
+#include <qdebug.h>
+#include <qdatetimeedit.h>
+#include <qmath.h>
+#include <qpair.h>
+#include <qvector.h>
+#include <QtWidgets/qgraphicsproxywidget.h>
+#include <QtWidgets/qgraphicsview.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.
+//
+
+QT_BEGIN_NAMESPACE
+
+/*
+ AHIG:
+ Apple Human Interface Guidelines
+ http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/
+
+ Builder:
+ Apple Interface Builder v. 3.1.1
+*/
+
+// this works as long as we have at most 16 different control types
+#define CT1(c) CT2(c, c)
+#define CT2(c1, c2) ((uint(c1) << 16) | uint(c2))
+
+enum QAquaWidgetSize { QAquaSizeLarge = 0, QAquaSizeSmall = 1, QAquaSizeMini = 2,
+ QAquaSizeUnknown = -1 };
+
+enum QCocoaWidgetKind {
+ QCocoaArrowButton, // Disclosure triangle, like in QTreeView
+ QCocoaCheckBox,
+ QCocoaComboBox, // Editable QComboBox
+ QCocoaPopupButton, // Non-editable QComboBox
+ QCocoaPullDownButton, // QPushButton with menu
+ QCocoaPushButton,
+ QCocoaRadioButton,
+ QCocoaHorizontalSlider,
+ QCocoaVerticalSlider
+};
+
+typedef QPair<QCocoaWidgetKind, QAquaWidgetSize> QCocoaWidget;
+
+typedef void (^QCocoaDrawRectBlock)(NSRect, CGContextRef);
+
+#define SIZE(large, small, mini) \
+ (controlSize == QAquaSizeLarge ? (large) : controlSize == QAquaSizeSmall ? (small) : (mini))
+
+// same as return SIZE(...) but optimized
+#define return_SIZE(large, small, mini) \
+ do { \
+ static const int sizes[] = { (large), (small), (mini) }; \
+ return sizes[controlSize]; \
+ } while (0)
+
+#if QT_CONFIG(pushbutton)
+bool qt_mac_buttonIsRenderedFlat(const QPushButton *pushButton, const QStyleOptionButton *option);
+#endif
+
+class QMacStylePrivate : public QCommonStylePrivate
+{
+ Q_DECLARE_PUBLIC(QMacStyle)
+public:
+ QMacStylePrivate();
+ ~QMacStylePrivate();
+
+ // Ideally these wouldn't exist, but since they already exist we need some accessors.
+ static const int PushButtonLeftOffset;
+ static const int PushButtonTopOffset;
+ static const int PushButtonRightOffset;
+ static const int PushButtonBottomOffset;
+ static const int MiniButtonH;
+ static const int SmallButtonH;
+ static const int BevelButtonW;
+ static const int BevelButtonH;
+ static const int PushButtonContentPadding;
+
+ enum Animates { AquaPushButton, AquaProgressBar, AquaListViewItemOpen, AquaScrollBar };
+ static ThemeDrawState getDrawState(QStyle::State flags);
+ QAquaWidgetSize aquaSizeConstrain(const QStyleOption *option, const QWidget *widg,
+ QStyle::ContentsType ct = QStyle::CT_CustomBase,
+ QSize szHint=QSize(-1, -1), QSize *insz = 0) const;
+ QAquaWidgetSize effectiveAquaSizeConstrain(const QStyleOption *option, const QWidget *widg,
+ QStyle::ContentsType ct = QStyle::CT_CustomBase,
+ QSize szHint=QSize(-1, -1), QSize *insz = 0) const;
+ void getSliderInfo(QStyle::ComplexControl cc, const QStyleOptionSlider *slider,
+ HIThemeTrackDrawInfo *tdi, const QWidget *needToRemoveMe) const;
+ inline int animateSpeed(Animates) const { return 33; }
+
+ // Utility functions
+ void drawColorlessButton(const HIRect &macRect, HIThemeButtonDrawInfo *bdi,
+ QPainter *p, const QStyleOption *opt) const;
+
+ QSize pushButtonSizeFromContents(const QStyleOptionButton *btn) const;
+
+ HIRect pushButtonContentBounds(const QStyleOptionButton *btn,
+ const HIThemeButtonDrawInfo *bdi) const;
+
+ void initComboboxBdi(const QStyleOptionComboBox *combo, HIThemeButtonDrawInfo *bdi,
+ const QWidget *widget, const ThemeDrawState &tds) const;
+
+ static HIRect comboboxInnerBounds(const HIRect &outerBounds, int buttonKind);
+
+ static QRect comboboxEditBounds(const QRect &outerBounds, const HIThemeButtonDrawInfo &bdi);
+
+ static void drawCombobox(const HIRect &outerBounds, const HIThemeButtonDrawInfo &bdi, QPainter *p);
+ static void drawTableHeader(const HIRect &outerBounds, bool drawTopBorder, bool drawLeftBorder,
+ const HIThemeButtonDrawInfo &bdi, QPainter *p);
+ bool contentFitsInPushButton(const QStyleOptionButton *btn, HIThemeButtonDrawInfo *bdi,
+ ThemeButtonKind buttonKindToCheck) const;
+ void initHIThemePushButton(const QStyleOptionButton *btn, const QWidget *widget,
+ const ThemeDrawState tds,
+ HIThemeButtonDrawInfo *bdi) const;
+ QPixmap generateBackgroundPattern() const;
+
+ void setAutoDefaultButton(QObject *button) const;
+
+ NSView *cocoaControl(QCocoaWidget widget) const;
+
+ void drawNSViewInRect(QCocoaWidget widget, NSView *view, const QRect &rect, QPainter *p, bool isQWidget = true, QCocoaDrawRectBlock drawRectBlock = nil) const;
+ void resolveCurrentNSView(QWindow *window);
+
+ void drawFocusRing(QPainter *p, const QRect &targetRect, int hMargin, int vMargin, qreal radius = 0) const;
+
+#ifndef QT_NO_TABBAR
+ void tabLayout(const QStyleOptionTab *opt, const QWidget *widget, QRect *textRect) const;
+#endif
+
+public:
+ mutable QPointer<QObject> pressedButton;
+ mutable QPointer<QObject> defaultButton;
+ mutable QPointer<QObject> autoDefaultButton;
+ static QVector<QPointer<QObject> > scrollBars;
+
+ struct ButtonState {
+ int frame;
+ enum { ButtonDark, ButtonLight } dir;
+ } buttonState;
+ mutable QPointer<QFocusFrame> focusWidget;
+ CFAbsoluteTime defaultButtonStart;
+ bool mouseDown;
+ void* receiver;
+ void *nsscroller;
+ void *indicatorBranchButtonCell;
+ NSView *backingStoreNSView;
+ QHash<QCocoaWidget, NSView *> cocoaControls;
+};
+
+QT_END_NAMESPACE
+
+#endif // QMACSTYLE_MAC_P_P_H
diff --git a/src/plugins/styles/styles.pro b/src/plugins/styles/styles.pro
new file mode 100644
index 0000000000..542ad1329a
--- /dev/null
+++ b/src/plugins/styles/styles.pro
@@ -0,0 +1,8 @@
+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
new file mode 100644
index 0000000000..d5048e45b7
--- /dev/null
+++ b/src/plugins/styles/windowsvista/main.cpp
@@ -0,0 +1,64 @@
+/****************************************************************************
+**
+** 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);
+};
+
+QStyle *QWindowsVistaStylePlugin::create(const QString &key)
+{
+ if (key.compare(QLatin1String("windowsvista"), Qt::CaseInsensitive) == 0)
+ return new QWindowsVistaStyle();
+
+ return 0;
+}
+
+QT_END_NAMESPACE
+
+#include "main.moc"
diff --git a/src/plugins/styles/windowsvista/qwindowsvistastyle.cpp b/src/plugins/styles/windowsvista/qwindowsvistastyle.cpp
new file mode 100644
index 0000000000..1a1094a934
--- /dev/null
+++ b/src/plugins/styles/windowsvista/qwindowsvistastyle.cpp
@@ -0,0 +1,2487 @@
+/****************************************************************************
+**
+** 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 <private/qapplication_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 (QSysInfo::WindowsVersion >= QSysInfo::WV_VISTA
+ && (QSysInfo::WindowsVersion & QSysInfo::WV_NT_based))
+ && QWindowsVistaStylePrivate::useXP();
+}
+
+/* \internal
+ Checks and returns the style object
+*/
+inline QObject *styleObject(const QStyleOption *option) {
+ return option ? option->styleObject : 0;
+}
+
+/* \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 int devicePixelRatio = widget ? widget->devicePixelRatio() : 1;
+ 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 = 0;
+ 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()
+{
+}
+
+//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 = 0;
+ 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(0, 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(0, 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(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(0, 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;
+ } else {
+ 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.background().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 = 0;
+
+ if (qobject_cast<const QMessageBox *> (widget))
+ buttonBox = widget->findChild<const QDialogButtonBox *>(QLatin1String("qt_msgbox_buttonbox"));
+#ifndef QT_NO_INPUTDIALOG
+ else if (qobject_cast<const QInputDialog *> (widget))
+ buttonBox = widget->findChild<const QDialogButtonBox *>(QLatin1String("qt_inputdlg_buttonbox"));
+#endif // QT_NO_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(0, 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(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, 0, QWindowsXPStylePrivate::ToolBarTheme,
+ TP_DROPDOWNBUTTON);
+ if (theme.isValid()) {
+ const QSizeF size = theme.size() * QWindowsStylePrivate::nativeMetricScaleFactor(widget);
+ if (!size.isEmpty()) {
+ mbiw = qRound(size.width());
+ mbih = qRound(size.height());
+ }
+ }
+ QRect ir = subElementRect(SE_PushButtonContents, option, 0);
+ 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->orientation == Qt::Vertical;
+ 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);
+
+ uint 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;
+#ifndef QT_NO_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, 0, 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->tabWidth;
+ 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_NO_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.background().color().darker(130));
+ QStyleOptionToolBar copyOpt = *toolbar;
+ copyOpt.palette = pal;
+ QWindowsStyle::drawControl(element, &copyOpt, painter, widget);
+ }
+ break;
+ 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.background().color().darker(110));
+ painter->setPen(option->palette.background().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 != 0 && 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 != 0 && 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, dwOpt->palette,
+ dwOpt->state & State_Enabled, titleText,
+ QPalette::WindowText);
+ }
+ }
+ break;
+#ifndef QT_NO_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_NO_ITEMVIEWS
+#ifndef QT_NO_COMBOBOX
+ case CE_ComboBoxLabel:
+ QCommonStyle::drawControl(element, option, painter, widget);
+ break;
+#endif // QT_NO_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 ) {
+
+ 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) {
+ QStyleOptionButton btn;
+ btn.QStyleOption::operator=(*option);
+ btn.rect = option->rect.adjusted(-1, -1, 1, 1);
+ if (sub & SC_ComboBoxArrow)
+ btn.features = QStyleOptionButton::HasMenu;
+ proxy()->drawControl(QStyle::CE_PushButton, &btn, 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;
+#ifndef QT_NO_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 != NULL);
+ 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_NO_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, 0,
+ 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;
+#ifndef QT_NO_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) : 0, 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, NULL, BP_PUSHBUTTON, stateId, TMT_CONTENTMARGINS, NULL, &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, 0,
+ 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 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;
+}
+
+
+/*! \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, 0,
+ QWindowsXPStylePrivate::ToolTipTheme,
+ TTP_STANDARD, TTSS_NORMAL, option->rect);
+ mask->region = d->region(themeData);
+ }
+ }
+ break;
+ case SH_Table_GridLineColor:
+ if (option)
+ ret = option->palette.color(QPalette::Base).darker(118).rgb();
+ 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) {
+#ifndef QT_NO_COMBOBOX
+ case CC_ComboBox:
+ if (const QStyleOptionComboBox *cb = qstyleoption_cast<const QStyleOptionComboBox *>(option)) {
+ int x = cb->rect.x(),
+ y = cb->rect.y(),
+ wi = cb->rect.width(),
+ he = cb->rect.height();
+ int xpos = x;
+ int margin = cb->frame ? 3 : 0;
+ int bmarg = cb->frame ? 2 : 0;
+ int arrowButtonWidth = bmarg + 16;
+ xpos += 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 - 16, he - 2 * margin);
+ break;
+ case SC_ComboBoxListBoxPopup:
+ rect = cb->rect;
+ break;
+ default:
+ break;
+ }
+ rect = visualRect(cb->direction, cb->rect, rect);
+ return rect;
+ }
+#endif // QT_NO_COMBOBOX
+ 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 buttonWidth =
+ qRound(qreal(GetSystemMetrics(SM_CXSIZE)) * QWindowsStylePrivate::nativeMetricScaleFactor(widget)
+ - QStyleHelper::dpiScaled(4));
+
+ 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, -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, 4);
+ }
+ 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);
+ }
+ rect.translate(0, 2);
+ rect = visualRect(option->direction, option->rect, rect);
+ break;
+ case SC_TitleBarSysMenu:
+ {
+ const int controlTop = 6;
+ const int controlHeight = height - controlTop - 3;
+ int iconExtent = proxy()->pixelMetric(PM_SmallIconSize);
+ 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, 3);
+ 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));
+
+ return QWindowsXPStyle::pixelMetric(metric, option, widget);
+}
+
+/*!
+ \internal
+ */
+QPalette QWindowsVistaStyle::standardPalette() const
+{
+ return QWindowsXPStyle::standardPalette();
+}
+
+/*!
+ \internal
+ */
+void QWindowsVistaStyle::polish(QApplication *app)
+{
+ QWindowsXPStyle::polish(app);
+}
+
+/*!
+ \internal
+ */
+void QWindowsVistaStyle::polish(QWidget *widget)
+{
+ QWindowsXPStyle::polish(widget);
+#ifndef QT_NO_LINEEDIT
+ if (qobject_cast<QLineEdit*>(widget))
+ widget->setAttribute(Qt::WA_Hover);
+ else
+#endif // QT_NO_LINEEDIT
+ if (qobject_cast<QGroupBox*>(widget))
+ widget->setAttribute(Qt::WA_Hover);
+ else if (qobject_cast<QCommandLinkButton*>(widget)) {
+ QFont buttonFont = widget->font();
+ buttonFont.setFamily(QLatin1String("Segoe UI"));
+ widget->setFont(buttonFont);
+ }
+ 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) : 0, 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
+ }
+#ifndef QT_NO_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_NO_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);
+
+#ifndef QT_NO_LINEEDIT
+ if (qobject_cast<QLineEdit*>(widget))
+ widget->setAttribute(Qt::WA_Hover, false);
+ else
+#endif // QT_NO_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
+ }
+#ifndef QT_NO_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_NO_INPUTDIALOG
+ else if (QTreeView *tree = qobject_cast<QTreeView *> (widget)) {
+ tree->viewport()->setAttribute(Qt::WA_Hover, false);
+ } 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);
+ }
+}
+
+
+/*!
+ \internal
+ */
+void QWindowsVistaStyle::unpolish(QApplication *app)
+{
+ QWindowsXPStyle::unpolish(app);
+}
+
+/*!
+ \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);
+}
+
+QWindowsVistaStylePrivate::QWindowsVistaStylePrivate() :
+ QWindowsXPStylePrivate()
+{
+}
+
+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(0, 0,
+ QWindowsXPStylePrivate::ButtonTheme,
+ BP_COMMANDLINKGLYPH, CMDLGS_NORMAL);
+ if (theme.isValid()) {
+ const QSize size = (theme.size() * QWindowsStylePrivate::nativeMetricScaleFactor(widget)).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.h b/src/plugins/styles/windowsvista/qwindowsvistastyle_p.h
new file mode 100644
index 0000000000..5ffcbc6aa9
--- /dev/null
+++ b/src/plugins/styles/windowsvista/qwindowsvistastyle_p.h
@@ -0,0 +1,106 @@
+/****************************************************************************
+**
+** 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_H
+#define QWINDOWSVISTASTYLE_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 "qwindowsxpstyle_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QWindowsVistaStylePrivate;
+class QWindowsVistaStyle : public QWindowsXPStyle
+{
+ Q_OBJECT
+public:
+ QWindowsVistaStyle();
+ ~QWindowsVistaStyle();
+
+ void drawPrimitive(PrimitiveElement element, const QStyleOption *option,
+ QPainter *painter, const QWidget *widget = 0) const;
+ void drawControl(ControlElement element, const QStyleOption *option,
+ QPainter *painter, const QWidget *widget) const;
+ void drawComplexControl(ComplexControl control, const QStyleOptionComplex *option,
+ QPainter *painter, const QWidget *widget) const;
+ QSize sizeFromContents(ContentsType type, const QStyleOption *option,
+ const QSize &size, const QWidget *widget) const;
+
+ QRect subElementRect(SubElement element, const QStyleOption *option, const QWidget *widget) const;
+ QRect subControlRect(ComplexControl cc, const QStyleOptionComplex *opt,
+ SubControl sc, const QWidget *widget) const;
+
+ SubControl hitTestComplexControl(ComplexControl control, const QStyleOptionComplex *option,
+ const QPoint &pos, const QWidget *widget = 0) const;
+
+ QIcon standardIcon(StandardPixmap standardIcon, const QStyleOption *option = 0,
+ const QWidget *widget = 0) const;
+ QPixmap standardPixmap(StandardPixmap standardPixmap, const QStyleOption *opt,
+ const QWidget *widget = 0) const;
+ int pixelMetric(PixelMetric metric, const QStyleOption *option = 0, const QWidget *widget = 0) const;
+ int styleHint(StyleHint hint, const QStyleOption *opt = 0, const QWidget *widget = 0,
+ QStyleHintReturn *returnData = 0) const;
+
+ void polish(QWidget *widget);
+ void unpolish(QWidget *widget);
+ void polish(QPalette &pal);
+ void polish(QApplication *app);
+ void unpolish(QApplication *app);
+ QPalette standardPalette() const;
+
+private:
+ Q_DISABLE_COPY(QWindowsVistaStyle)
+ Q_DECLARE_PRIVATE(QWindowsVistaStyle)
+ friend class QStyleFactory;
+};
+
+QT_END_NAMESPACE
+
+#endif // QWINDOWSVISTASTYLE_P_H
diff --git a/src/plugins/styles/windowsvista/qwindowsvistastyle_p_p.h b/src/plugins/styles/windowsvista/qwindowsvistastyle_p_p.h
new file mode 100644
index 0000000000..ee3a12c641
--- /dev/null
+++ b/src/plugins/styles/windowsvista/qwindowsvistastyle_p_p.h
@@ -0,0 +1,180 @@
+/****************************************************************************
+**
+** 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>
+#include <qlineedit.h>
+#include <qgroupbox.h>
+#include <qtoolbutton.h>
+#include <qspinbox.h>
+#include <qtoolbar.h>
+#include <qcombobox.h>
+#include <qscrollbar.h>
+#include <qprogressbar.h>
+#include <qdockwidget.h>
+#include <qlistview.h>
+#include <qtreeview.h>
+#include <qtextedit.h>
+#include <qmessagebox.h>
+#if QT_CONFIG(dialogbuttonbox)
+#include <qdialogbuttonbox.h>
+#endif
+#include <qinputdialog.h>
+#include <qtableview.h>
+#include <qdatetime.h>
+#include <qcommandlinkbutton.h>
+
+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) { }
+
+ virtual bool isUpdateNeeded() const;
+ 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:
+ QWindowsVistaStylePrivate();
+
+ 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
new file mode 100644
index 0000000000..3f59886f1d
--- /dev/null
+++ b/src/plugins/styles/windowsvista/qwindowsxpstyle.cpp
@@ -0,0 +1,4223 @@
+/****************************************************************************
+**
+** 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 <qpa/qplatformnativeinterface.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/qplatformnativeinterface.h>
+
+#include <qdesktopwidget.h>
+#include <qtoolbutton.h>
+#include <qtabbar.h>
+#include <qcombobox.h>
+#include <qscrollbar.h>
+#include <qheaderview.h>
+#include <qspinbox.h>
+#include <qlistview.h>
+#include <qstackedwidget.h>
+#if QT_CONFIG(pushbutton)
+#include <qpushbutton.h>
+#endif
+#include <qtoolbar.h>
+#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
+
+// External function calls
+extern Q_WIDGETS_EXPORT HDC qt_win_display_dc();
+extern QRegion qt_region_from_HRGN(HRGN rgn);
+
+// 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 0;
+}
+
+static inline HDC hdcForWidgetBackingStore(const QWidget *widget)
+{
+ if (QBackingStore *backingStore = backingStoreForWidget(widget)) {
+ QPlatformNativeInterface *nativeInterface = QGuiApplication::platformNativeInterface();
+ return static_cast<HDC>(nativeInterface->nativeResourceForBackingStore(QByteArrayLiteral("getDC"), backingStore));
+ }
+ return 0;
+}
+
+// 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 0;
+
+ 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 0;
+
+ HRGN hrgn;
+ HDC dc = 0;
+ if (widget)
+ dc = hdcForWidgetBackingStore(widget);
+ RECT nativeRect = toRECT(rect);
+ GetThemeBackgroundRegion(handle(), dc, partId, stateId, &nativeRect, &hrgn);
+ return hrgn;
+}
+
+// QWindowsXPStylePrivate -------------------------------------------------------------------------
+// Static initializations
+QPixmap *QWindowsXPStylePrivate::tabbody = 0;
+HWND QWindowsXPStylePrivate::m_vistaTreeViewHelper = 0;
+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 &region)
+{
+ 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.
+*/
+bool QWindowsXPStylePrivate::useXP(bool update)
+{
+ if (!update)
+ return use_xp;
+ return use_xp = IsThemeActive() && (IsAppThemed() || !QApplication::instance());
+}
+
+/* \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, HTHEME(0));
+}
+
+/* \internal
+ Cleans up all static data.
+*/
+void QWindowsXPStylePrivate::cleanup(bool force)
+{
+ if(bufferBitmap) {
+ if (bufferDC && nullBitmap)
+ SelectObject(bufferDC, nullBitmap);
+ DeleteObject(bufferBitmap);
+ bufferBitmap = 0;
+ }
+
+ if(bufferDC)
+ DeleteDC(bufferDC);
+ bufferDC = 0;
+
+ if (ref.deref() && !force)
+ return;
+ if (!force) // -1 based atomic refcounting
+ ref.deref();
+
+ use_xp = false;
+ cleanupHandleMap();
+ delete tabbody;
+ tabbody = 0;
+}
+
+/* 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()
+{
+ if (QPlatformNativeInterface *ni = QGuiApplication::platformNativeInterface()) {
+ void *hwnd = 0;
+ void *wndProc = reinterpret_cast<void *>(DefWindowProc);
+ if (QMetaObject::invokeMethod(ni, "createMessageWindow", Qt::DirectConnection,
+ Q_RETURN_ARG(void *, hwnd),
+ Q_ARG(QString, QStringLiteral("QTreeViewThemeHelperWindowClass")),
+ Q_ARG(QString, QStringLiteral("QTreeViewThemeHelperWindow")),
+ Q_ARG(void *, wndProc)) && hwnd) {
+ return reinterpret_cast<HWND>(hwnd);
+ }
+ }
+ return 0;
+}
+
+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", NULL))) {
+ qErrnoWarning("SetWindowTheme() failed.");
+ cleanupVistaTreeViewTheming();
+ return false;
+ }
+ return true;
+}
+
+void QWindowsXPStylePrivate::cleanupVistaTreeViewTheming()
+{
+ if (m_vistaTreeViewHelper) {
+ DestroyWindow(m_vistaTreeViewHelper);
+ m_vistaTreeViewHelper = 0;
+ }
+}
+
+/* \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 (int i = 0; i < NThemes; ++i)
+ if (m_themes[i]) {
+ CloseThemeData(m_themes[i]);
+ m_themes[i] = 0;
+ }
+ 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 0;
+ }
+ 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.resolve();
+ 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.
+#ifndef QT_NO_SPINBOX
+ if (const QAbstractSpinBox *spinbox = qobject_cast<QAbstractSpinBox*>(widget->parentWidget()))
+ resolveMask |= spinbox->palette().resolve();
+#endif // QT_NO_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 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 topLevels = QApplication::topLevelWidgets();
+ for (const QWidget *toplevel : topLevels) {
+ if (toplevel->windowHandle() && toplevel->windowHandle()->handle())
+ if (const HWND topLevelHwnd = QApplicationPrivate::getHWNDForWidget(toplevel))
+ return topLevelHwnd;
+ }
+
+ return GetDesktopWindow();
+}
+
+/*! \internal
+ Returns the pointer to a tab widgets body pixmap, scaled to the
+ height of the screen. This way the theme engine doesn't need to
+ scale the body for every time we ask for it. (Speed optimization)
+*/
+const QPixmap *QWindowsXPStylePrivate::tabBody(QWidget *widget)
+{
+ if (!tabbody) {
+ XPThemeData theme(0, 0, QWindowsXPStylePrivate::TabTheme, TABP_BODY);
+ const QSize size = (theme.size() * QWindowsStylePrivate::nativeMetricScaleFactor(widget)).toSize();
+
+ tabbody = new QPixmap(size.width(), QApplication::desktop()->screenGeometry().height());
+ QPainter painter(tabbody);
+ theme.rect = QRect(QPoint(0, 0), size);
+ drawBackground(theme);
+ // We fill with the last line of the themedata, that
+ // way we don't get a tiled pixmap inside big tabs
+ QPixmap temp(size.width(), 1);
+ painter.drawPixmap(0, 0, temp, 0, size.height() - 1, -1, -1);
+ painter.drawTiledPixmap(0, size.height(), size.width(), tabbody->height() - size.height(), temp);
+ }
+ return tabbody;
+}
+
+/*! \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 = 0;
+ }
+
+ w = qMax(bufferW, w);
+ h = qMax(bufferH, h);
+
+ if (!bufferDC) {
+ HDC displayDC = GetDC(0);
+ bufferDC = CreateCompatibleDC(displayDC);
+ ReleaseDC(0, 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 = 0;
+ bufferBitmap = CreateDIBSection(bufferDC, &bmi, DIB_RGB_COLORS, (void **) &bufferPixels, 0, 0);
+ GdiFlush();
+ nullBitmap = (HBITMAP)SelectObject(bufferDC, bufferBitmap);
+
+ if (Q_UNLIKELY(!bufferBitmap)) {
+ qErrnoWarning("QWindowsXPStylePrivate::buffer(%dx%d), CreateDIBSection() failed.", w, h);
+ bufferW = 0;
+ bufferH = 0;
+ return 0;
+ }
+ if (Q_UNLIKELY(!bufferPixels)) {
+ qErrnoWarning("QWindowsXPStylePrivate::buffer(%dx%d), CreateDIBSection() did not allocate pixel data.", w, h);
+ bufferW = 0;
+ bufferH = 0;
+ return 0;
+ }
+ 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 = 0;
+ RECT rect = themeData.toRECT(themeData.rect);
+ 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, 0, RGN_COPY) != ERROR;
+
+ QRegion region;
+
+ if (success) {
+ int numBytes = GetRegionData(dest, 0, 0);
+ if (numBytes == 0)
+ return QRegion();
+
+ char *buf = new char[numBytes];
+ if (buf == 0)
+ 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(r->left, r->top, r->right - 1, r->bottom - 1);
+ ++r;
+ region |= rect;
+ }
+
+ delete [] buf;
+ }
+
+ DeleteObject(hRgn);
+ DeleteObject(dest);
+
+ return region;
+}
+
+/*! \internal
+ Sets the parts region on a window.
+*/
+void QWindowsXPStylePrivate::setTransparency(QWidget *widget, XPThemeData &themeData)
+{
+ HRGN hrgn = themeData.mask(widget);
+ if (hrgn && widget)
+ SetWindowRgn(winId(widget), hrgn, true);
+}
+
+/*! \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) {
+ DWORD *buffer = (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) {
+ DWORD *buffer = (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) {
+ DWORD *buffer = (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;
+}
+
+/*! \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)
+{
+ if (themeData.rect.isEmpty())
+ return true;
+
+ QPainter *painter = themeData.painter;
+ Q_ASSERT_X(painter != 0, "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->devicePixelRatioF() : 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
+ && 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) : HDC(0);
+ const bool result = dc
+ ? drawBackgroundDirectly(dc, themeData, aditionalDevicePixelRatio)
+ : drawBackgroundThruNativeBuffer(themeData, aditionalDevicePixelRatio);
+ 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 &region, qreal factor)
+{
+ if (region.isEmpty() || qFuzzyCompare(factor, qreal(1)))
+ return region;
+ if (region.rectCount() == 1)
+ return QRegion(scaleRect(QRectF(region.boundingRect()), factor).toRect());
+ QRegion result;
+ foreach (const QRect &rect, region.rects())
+ 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 QPointF redirectionDelta(painter->deviceMatrix().dx(), painter->deviceMatrix().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, 0);
+ 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).
+*/
+bool QWindowsXPStylePrivate::drawBackgroundThruNativeBuffer(XPThemeData &themeData,
+ qreal additionalDevicePixelRatio)
+{
+ 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);
+ QRect rect = rectF.toRect();
+ 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'));
+
+ 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 = rect;
+ 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);
+
+ // 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(w, h)) // 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 * h * 4);
+ }
+
+ // Difference between area and rect
+ int dx = area.x() - rect.x();
+ int dy = area.y() - rect.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(&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(w, h);
+#endif
+ }
+
+ // Fix alpha values, if needed
+ if (potentialInvalidAlpha)
+ wasAlphaFixed = fixAlphaChannel(rect);
+
+ 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);
+ 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(extraClip, 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) {
+ QMatrix 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(&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
+*/
+
+/*!
+ Constructs a QWindowsStyle
+*/
+QWindowsXPStyle::QWindowsXPStyle()
+ : QWindowsStyle(*new QWindowsXPStylePrivate)
+{
+}
+
+/*!
+ Destroys the style.
+*/
+QWindowsXPStyle::~QWindowsXPStyle()
+{
+}
+
+/*! \reimp */
+void QWindowsXPStyle::unpolish(QApplication *app)
+{
+ QWindowsStyle::unpolish(app);
+}
+
+/*! \reimp */
+void QWindowsXPStyle::polish(QApplication *app)
+{
+ QWindowsStyle::polish(app);
+ if (!QWindowsXPStylePrivate::useXP())
+ return;
+}
+
+/*! \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)
+#ifndef QT_NO_COMBOBOX
+ || qobject_cast<QComboBox*>(widget)
+#endif // QT_NO_COMBOBOX
+ || qobject_cast<QScrollBar*>(widget)
+ || qobject_cast<QSlider*>(widget)
+ || qobject_cast<QHeaderView*>(widget)
+#ifndef QT_NO_SPINBOX
+ || qobject_cast<QAbstractSpinBox*>(widget)
+ || qobject_cast<QSpinBox*>(widget)
+#endif // QT_NO_SPINBOX
+ ) {
+ widget->setAttribute(Qt::WA_Hover);
+ }
+
+#ifndef QT_NO_RUBBERBAND
+ if (qobject_cast<QRubberBand*>(widget)) {
+ widget->setWindowOpacity(0.6);
+ }
+#endif
+ if (qobject_cast<QStackedWidget*>(widget) &&
+ qobject_cast<QTabWidget*>(widget->parent()))
+ widget->parentWidget()->setAttribute(Qt::WA_ContentsPropagated);
+
+ Q_D(QWindowsXPStyle);
+ if (!d->hasInitColors) {
+ // Get text color for group box labels
+ COLORREF cref;
+ XPThemeData theme(widget, 0, 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)
+{
+#ifndef QT_NO_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)
+#ifndef QT_NO_COMBOBOX
+ || qobject_cast<QComboBox*>(widget)
+#endif // QT_NO_COMBOBOX
+ || qobject_cast<QScrollBar*>(widget)
+ || qobject_cast<QSlider*>(widget)
+ || qobject_cast<QHeaderView*>(widget)
+#ifndef QT_NO_SPINBOX
+ || qobject_cast<QAbstractSpinBox*>(widget)
+ || qobject_cast<QSpinBox*>(widget)
+#endif // QT_NO_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, 0, 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, NULL, BP_PUSHBUTTON, stateId, TMT_CONTENTMARGINS, NULL, &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, 0, themeNumber, partId, 0);
+
+ 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(), 1));
+ p->drawRect(option->rect.adjusted(1, 1, -2, -2));
+ // Outer dark border
+ p->setPen(QPen(bordercolor, 1));
+ p->drawRect(option->rect.adjusted(0, 0, -1, -1));
+ p->setPen(oldPen);
+ return;
+ } else if (fillType == BT_NONE) {
+ return;
+ } else {
+ 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;
+ } else 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(0, 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, NULL, 0) == S_OK) {
+ wchar_t *offset = 0;
+ if ((offset = wcsrchr(themeFileName, QChar(QLatin1Char('\\')).unicode())) != NULL) {
+ 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->orientation;
+ 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 = frm->lineWidth + frm->midLineWidth;
+
+ XPThemeData theme(0, 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(0, 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(0, p, themeNumber, partId, 0);
+ 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, 0,
+ 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 = Qt::Horizontal;
+ if (const QStyleOptionProgressBar *pb = qstyleoption_cast<const QStyleOptionProgressBar *>(option))
+ orient = pb->orientation;
+ 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->tabWidth;
+ 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;
+#ifndef QT_NO_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 != 0 && 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_NO_DOCKWIDGET
+#ifndef QT_NO_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;
+ }
+#endif // QT_NO_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();
+}
+
+/*!
+ \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) {
+#ifndef QT_NO_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_NO_SPINBOX
+#ifndef QT_NO_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_NO_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;
+
+#ifndef QT_NO_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.size() > 0) {
+ 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
+#ifndef QT_NO_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, 0);
+ 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_NO_TOOLBUTTON
+
+ case CC_TitleBar:
+ {
+ if (const QStyleOptionTitleBar *tb = qstyleoption_cast<const QStyleOptionTitleBar *>(option))
+ {
+ 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(ir.x() + 3, ir.y() + 2, ir.width() - 1, 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(ir.x() + 2, ir.y() + 1, ir.width() - 2, 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)) {
+ theme.rect = proxy()->subControlRect(CC_TitleBar, option, SC_TitleBarMinButton, widget);
+ partId = WP_MINBUTTON;
+ if (widget && !widget->isEnabled())
+ stateId = MINBS_DISABLED;
+ else if (option->activeSubControls == SC_TitleBarMinButton && (option->state & State_Sunken))
+ stateId = MINBS_PUSHED;
+ else if (option->activeSubControls == SC_TitleBarMinButton && (option->state & State_MouseOver))
+ stateId = MINBS_HOT;
+ else if (!isActive)
+ stateId = MINBS_INACTIVE;
+ else
+ stateId = MINBS_NORMAL;
+ theme.partId = partId;
+ theme.stateId = stateId;
+ d->drawBackground(theme);
+ }
+ if (sub & SC_TitleBarMaxButton && tb->titleBarFlags & Qt::WindowMaximizeButtonHint
+ && !(tb->titleBarState & Qt::WindowMaximized)) {
+ theme.rect = proxy()->subControlRect(CC_TitleBar, option, SC_TitleBarMaxButton, widget);
+ partId = WP_MAXBUTTON;
+ if (widget && !widget->isEnabled())
+ stateId = MAXBS_DISABLED;
+ else if (option->activeSubControls == SC_TitleBarMaxButton && (option->state & State_Sunken))
+ stateId = MAXBS_PUSHED;
+ else if (option->activeSubControls == SC_TitleBarMaxButton && (option->state & State_MouseOver))
+ stateId = MAXBS_HOT;
+ else if (!isActive)
+ stateId = MAXBS_INACTIVE;
+ else
+ stateId = MAXBS_NORMAL;
+ theme.partId = partId;
+ theme.stateId = stateId;
+ d->drawBackground(theme);
+ }
+ if (sub & SC_TitleBarContextHelpButton
+ && tb->titleBarFlags & Qt::WindowContextHelpButtonHint) {
+ theme.rect = proxy()->subControlRect(CC_TitleBar, option, SC_TitleBarContextHelpButton, widget);
+ partId = WP_HELPBUTTON;
+ if (widget && !widget->isEnabled())
+ stateId = MINBS_DISABLED;
+ else if (option->activeSubControls == SC_TitleBarContextHelpButton && (option->state & State_Sunken))
+ stateId = MINBS_PUSHED;
+ else if (option->activeSubControls == SC_TitleBarContextHelpButton && (option->state & State_MouseOver))
+ stateId = MINBS_HOT;
+ else if (!isActive)
+ stateId = MINBS_INACTIVE;
+ else
+ stateId = MINBS_NORMAL;
+ theme.partId = partId;
+ theme.stateId = stateId;
+ 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) {
+ theme.rect = proxy()->subControlRect(CC_TitleBar, option, SC_TitleBarNormalButton, widget);
+ partId = WP_RESTOREBUTTON;
+ if (widget && !widget->isEnabled())
+ stateId = RBS_DISABLED;
+ else if (option->activeSubControls == SC_TitleBarNormalButton && (option->state & State_Sunken))
+ stateId = RBS_PUSHED;
+ else if (option->activeSubControls == SC_TitleBarNormalButton && (option->state & State_MouseOver))
+ stateId = RBS_HOT;
+ else if (!isActive)
+ stateId = RBS_INACTIVE;
+ else
+ stateId = RBS_NORMAL;
+ theme.partId = partId;
+ theme.stateId = stateId;
+ d->drawBackground(theme);
+ }
+ if (sub & SC_TitleBarShadeButton && tb->titleBarFlags & Qt::WindowShadeButtonHint
+ && !(tb->titleBarState & Qt::WindowMinimized)) {
+ theme.rect = proxy()->subControlRect(CC_TitleBar, option, SC_TitleBarShadeButton, widget);
+ partId = WP_MINBUTTON;
+ if (widget && !widget->isEnabled())
+ stateId = MINBS_DISABLED;
+ else if (option->activeSubControls == SC_TitleBarShadeButton && (option->state & State_Sunken))
+ stateId = MINBS_PUSHED;
+ else if (option->activeSubControls == SC_TitleBarShadeButton && (option->state & State_MouseOver))
+ stateId = MINBS_HOT;
+ else if (!isActive)
+ stateId = MINBS_INACTIVE;
+ else
+ stateId = MINBS_NORMAL;
+ theme.partId = partId;
+ theme.stateId = stateId;
+ d->drawBackground(theme);
+ }
+ if (sub & SC_TitleBarUnshadeButton && tb->titleBarFlags & Qt::WindowShadeButtonHint
+ && tb->titleBarState & Qt::WindowMinimized) {
+ theme.rect = proxy()->subControlRect(CC_TitleBar, option, SC_TitleBarUnshadeButton, widget);
+ partId = WP_RESTOREBUTTON;
+ if (widget && !widget->isEnabled())
+ stateId = RBS_DISABLED;
+ else if (option->activeSubControls == SC_TitleBarUnshadeButton && (option->state & State_Sunken))
+ stateId = RBS_PUSHED;
+ else if (option->activeSubControls == SC_TitleBarUnshadeButton && (option->state & State_MouseOver))
+ stateId = RBS_HOT;
+ else if (!isActive)
+ stateId = RBS_INACTIVE;
+ else
+ stateId = RBS_NORMAL;
+ theme.partId = partId;
+ theme.stateId = stateId;
+ d->drawBackground(theme);
+ }
+ if (sub & SC_TitleBarCloseButton && tb->titleBarFlags & Qt::WindowSystemMenuHint) {
+ theme.rect = proxy()->subControlRect(CC_TitleBar, option, SC_TitleBarCloseButton, widget);
+ //partId = titlebar->testWFlags(Qt::WA_WState_Tool) ? WP_SMALLCLOSEBUTTON : WP_CLOSEBUTTON;
+ partId = WP_CLOSEBUTTON;
+ if (widget && !widget->isEnabled())
+ stateId = CBS_DISABLED;
+ else if (option->activeSubControls == SC_TitleBarCloseButton && (option->state & State_Sunken))
+ stateId = CBS_PUSHED;
+ else if (option->activeSubControls == SC_TitleBarCloseButton && (option->state & State_MouseOver))
+ stateId = CBS_HOT;
+ else if (!isActive)
+ stateId = CBS_INACTIVE;
+ else
+ stateId = CBS_NORMAL;
+ theme.partId = partId;
+ theme.stateId = stateId;
+ d->drawBackground(theme);
+ }
+ }
+ }
+ break;
+
+#ifndef QT_NO_MDIAREA
+ case CC_MdiControls:
+ {
+ QRect buttonRect;
+ XPThemeData theme(widget, p, QWindowsXPStylePrivate::WindowTheme, WP_MDICLOSEBUTTON, CBS_NORMAL);
+
+ if (option->subControls & SC_MdiCloseButton) {
+ buttonRect = proxy()->subControlRect(CC_MdiControls, option, SC_MdiCloseButton, widget);
+ if (theme.isValid()) {
+ theme.partId = WP_MDICLOSEBUTTON;
+ theme.rect = buttonRect;
+ if (!(flags & State_Enabled))
+ theme.stateId = CBS_INACTIVE;
+ else if (flags & State_Sunken && (option->activeSubControls & SC_MdiCloseButton))
+ theme.stateId = CBS_PUSHED;
+ else if (flags & State_MouseOver && (option->activeSubControls & SC_MdiCloseButton))
+ theme.stateId = CBS_HOT;
+ else
+ theme.stateId = CBS_NORMAL;
+ d->drawBackground(theme);
+ }
+ }
+ if (option->subControls & SC_MdiNormalButton) {
+ buttonRect = proxy()->subControlRect(CC_MdiControls, option, SC_MdiNormalButton, widget);
+ if (theme.isValid()) {
+ theme.partId = WP_MDIRESTOREBUTTON;
+ theme.rect = buttonRect;
+ if (!(flags & State_Enabled))
+ theme.stateId = CBS_INACTIVE;
+ else if (flags & State_Sunken && (option->activeSubControls & SC_MdiNormalButton))
+ theme.stateId = CBS_PUSHED;
+ else if (flags & State_MouseOver && (option->activeSubControls & SC_MdiNormalButton))
+ theme.stateId = CBS_HOT;
+ else
+ theme.stateId = CBS_NORMAL;
+ d->drawBackground(theme);
+ }
+ }
+ if (option->subControls & QStyle::SC_MdiMinButton) {
+ buttonRect = proxy()->subControlRect(CC_MdiControls, option, SC_MdiMinButton, widget);
+ if (theme.isValid()) {
+ theme.partId = WP_MDIMINBUTTON;
+ theme.rect = buttonRect;
+ if (!(flags & State_Enabled))
+ theme.stateId = CBS_INACTIVE;
+ else if (flags & State_Sunken && (option->activeSubControls & SC_MdiMinButton))
+ theme.stateId = CBS_PUSHED;
+ else if (flags & State_MouseOver && (option->activeSubControls & SC_MdiMinButton))
+ theme.stateId = CBS_HOT;
+ else
+ theme.stateId = CBS_NORMAL;
+ d->drawBackground(theme);
+ }
+ }
+ }
+ break;
+#endif //QT_NO_MDIAREA
+#ifndef QT_NO_DIAL
+ case CC_Dial:
+ if (const QStyleOptionSlider *dial = qstyleoption_cast<const QStyleOptionSlider *>(option))
+ QStyleHelper::drawDial(dial, p);
+ break;
+#endif // QT_NO_DIAL
+ default:
+ QWindowsStyle::drawComplexControl(cc, option, p, widget);
+ break;
+ }
+}
+
+static inline Qt::Orientation progressBarOrientation(const QStyleOption *option = 0)
+{
+ if (const QStyleOptionProgressBar *pb = qstyleoption_cast<const QStyleOptionProgressBar *>(option))
+ return pb->orientation;
+ return Qt::Horizontal;
+}
+
+int QWindowsXPStylePrivate::pixelMetricFromSystemDp(QStyle::PixelMetric pm, const QStyleOption *option, const QWidget *widget)
+{
+ switch (pm) {
+ case QStyle::PM_IndicatorWidth:
+ return XPThemeData::themeSize(widget, 0, QWindowsXPStylePrivate::ButtonTheme, BP_CHECKBOX, CBS_UNCHECKEDNORMAL).width();
+ case QStyle::PM_IndicatorHeight:
+ return XPThemeData::themeSize(widget, 0, QWindowsXPStylePrivate::ButtonTheme, BP_CHECKBOX, CBS_UNCHECKEDNORMAL).height();
+ case QStyle::PM_ExclusiveIndicatorWidth:
+ return XPThemeData::themeSize(widget, 0, QWindowsXPStylePrivate::ButtonTheme, BP_RADIOBUTTON, RBS_UNCHECKEDNORMAL).width();
+ case QStyle::PM_ExclusiveIndicatorHeight:
+ return XPThemeData::themeSize(widget, 0, QWindowsXPStylePrivate::ButtonTheme, BP_RADIOBUTTON, RBS_UNCHECKEDNORMAL).height();
+ case QStyle::PM_ProgressBarChunkWidth:
+ return progressBarOrientation(option) == Qt::Horizontal
+ ? XPThemeData::themeSize(widget, 0, QWindowsXPStylePrivate::ProgressTheme, PP_CHUNK).width()
+ : XPThemeData::themeSize(widget, 0, QWindowsXPStylePrivate::ProgressTheme, PP_CHUNKVERT).height();
+ case QStyle::PM_SliderThickness:
+ return XPThemeData::themeSize(widget, 0, 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, 0, QWindowsXPStylePrivate::WindowTheme, WP_FRAMELEFT, FS_ACTIVE).width();
+ case QStyle::PM_DockWidgetFrameWidth:
+ return XPThemeData::themeSize(widget, 0, 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 = qMax(int(QStyleHelper::dpiScaled(5.)), QApplication::globalStrut().width());
+ break;
+
+ case PM_MdiSubWindowMinimizedWidth:
+ res = 160;
+ break;
+
+#ifndef QT_NO_TOOLBAR
+ case PM_ToolBarHandleExtent:
+ res = int(QStyleHelper::dpiScaled(8.));
+ break;
+
+#endif // QT_NO_TOOLBAR
+ case PM_DockWidgetSeparatorExtent:
+ case PM_DockWidgetTitleMargin:
+ res = int(QStyleHelper::dpiScaled(4.));
+ 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));
+ 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);
+ 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)) {
+ int x = cmb->rect.x(), y = cmb->rect.y(), wi = cmb->rect.width(), he = cmb->rect.height();
+ int xpos = x;
+ xpos += wi - 1 - 16;
+
+ switch (subControl) {
+ case SC_ComboBoxFrame:
+ rect = cmb->rect;
+ break;
+
+ case SC_ComboBoxArrow:
+ rect = QRect(xpos, y+1, 16, he-2);
+ break;
+
+ case SC_ComboBoxEditField:
+ rect = QRect(x+2, y+2, wi-3-16, he-4);
+ break;
+
+ case SC_ComboBoxListBoxPopup:
+ rect = cmb->rect;
+ break;
+
+ default:
+ break;
+ }
+ }
+ break;
+#ifndef QT_NO_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_NO_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, 0, 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) + 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;
+#ifndef QT_NO_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:
+ if (const QStyleOptionComplex *styleOpt = qstyleoption_cast<const QStyleOptionComplex *>(option)) {
+ int 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 = QSize(width, 19);
+ } else {
+ sz = QSize(54, 19);
+ }
+ 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, 0,
+ QWindowsXPStylePrivate::WindowTheme,
+ WP_MINCAPTION, CS_ACTIVE, titleBarRect);
+ } else
+ themeData = XPThemeData(widget, 0,
+ QWindowsXPStylePrivate::WindowTheme,
+ WP_CAPTION, CS_ACTIVE, titleBarRect);
+ mask->region = d->region(themeData) +
+ QRect(0, tbHeight, option->rect.width(), option->rect.height() - tbHeight);
+ }
+ }
+ break;
+#ifndef QT_NO_RUBBERBAND
+ case SH_RubberBand_Mask:
+ if (qstyleoption_cast<const QStyleOptionRubberBand *>(option)) {
+ res = 0;
+ break;
+ }
+#endif // QT_NO_RUBBERBAND
+
+ case SH_ItemView_DrawDelegateFrame:
+ res = 1;
+ break;
+
+ default:
+ res =QWindowsStyle::styleHint(hint, option, widget, returnData);
+ }
+
+ return res;
+}
+
+/*! \reimp */
+QPalette QWindowsXPStyle::standardPalette() const
+{
+ if (QWindowsXPStylePrivate::useXP() && QApplicationPrivate::sys_pal)
+ return *QApplicationPrivate::sys_pal;
+ else
+ return 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, 0, 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(0, 0, QWindowsXPStylePrivate::WindowTheme,
+ WP_SMALLCLOSEBUTTON, CBS_NORMAL);
+ XPThemeData theme(0, 0, 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(0, 0, 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(0, 0, QWindowsXPStylePrivate::WindowTheme,
+ WP_SMALLCLOSEBUTTON, CBS_NORMAL);
+ XPThemeData theme(0, 0, 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);
+}
+
+/*!
+ \internal
+
+ Constructs a QWindowsXPStyle object.
+*/
+QWindowsXPStyle::QWindowsXPStyle(QWindowsXPStylePrivate &dd) : QWindowsStyle(dd)
+{
+}
+
+
+// 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
new file mode 100644
index 0000000000..d00620eefa
--- /dev/null
+++ b/src/plugins/styles/windowsvista/qwindowsxpstyle_p.h
@@ -0,0 +1,104 @@
+/****************************************************************************
+**
+** 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();
+ QWindowsXPStyle(QWindowsXPStylePrivate &dd);
+ ~QWindowsXPStyle();
+
+ void unpolish(QApplication*);
+ void polish(QApplication*);
+ void polish(QWidget*);
+ void polish(QPalette&);
+ void unpolish(QWidget*);
+
+ void drawPrimitive(PrimitiveElement pe, const QStyleOption *option, QPainter *p,
+ const QWidget *widget = 0) const;
+ void drawControl(ControlElement element, const QStyleOption *option, QPainter *p,
+ const QWidget *wwidget = 0) const;
+ QRect subElementRect(SubElement r, const QStyleOption *option, const QWidget *widget = 0) const;
+ QRect subControlRect(ComplexControl cc, const QStyleOptionComplex *option, SubControl sc,
+ const QWidget *widget = 0) const;
+ void drawComplexControl(ComplexControl cc, const QStyleOptionComplex *option, QPainter *p,
+ const QWidget *widget = 0) const;
+ QSize sizeFromContents(ContentsType ct, const QStyleOption *option, const QSize &contentsSize,
+ const QWidget *widget = 0) const;
+ int pixelMetric(PixelMetric pm, const QStyleOption *option = 0,
+ const QWidget *widget = 0) const;
+ int styleHint(StyleHint hint, const QStyleOption *option = 0, const QWidget *widget = 0,
+ QStyleHintReturn *returnData = 0) const;
+
+ QPalette standardPalette() const;
+ QPixmap standardPixmap(StandardPixmap standardIcon, const QStyleOption *option,
+ const QWidget *widget = 0) const;
+ QIcon standardIcon(StandardPixmap standardIcon, const QStyleOption *option = 0,
+ const QWidget *widget = 0) const;
+
+private:
+ Q_DISABLE_COPY(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
new file mode 100644
index 0000000000..721a734829
--- /dev/null
+++ b/src/plugins/styles/windowsvista/qwindowsxpstyle_p_p.h
@@ -0,0 +1,341 @@
+/****************************************************************************
+**
+** 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
+
+// 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 = 0, QPainter *p = 0, int themeIn = -1,
+ int part = 0, int state = 0, const QRect &r = QRect())
+ : widget(w), painter(p), theme(themeIn), htheme(0), partId(part), stateId(state),
+ mirrorHorizontally(false), mirrorVertically(false), noBorder(false),
+ noContent(false), rotate(0), 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 = 0, QPainter *p = 0, int themeIn = -1, int part = 0, int state = 0);
+ static QMarginsF themeMargins(const QRect &rect, const QWidget *w = 0, QPainter *p = 0, int themeIn = -1,
+ int part = 0, int state = 0, int propId = TMT_CONTENTMARGINS);
+ static QMarginsF themeMargins(const QWidget *w = 0, QPainter *p = 0, int themeIn = -1,
+ int part = 0, int state = 0, int propId = TMT_CONTENTMARGINS);
+
+ const QWidget *widget;
+ QPainter *painter;
+
+ int theme;
+ HTHEME htheme;
+ int partId;
+ int stateId;
+
+ uint mirrorHorizontally : 1;
+ uint mirrorVertically : 1;
+ uint noBorder : 1;
+ uint noContent : 1;
+ uint rotate;
+ QRect rect;
+};
+
+struct ThemeMapKey {
+ int theme;
+ int partId;
+ int stateId;
+ bool noBorder;
+ bool noContent;
+
+ ThemeMapKey() : partId(-1), stateId(-1) {}
+ ThemeMapKey(const XPThemeData &data)
+ : theme(data.theme), partId(data.partId), stateId(data.stateId),
+ noBorder(data.noBorder), noContent(data.noContent) {}
+
+};
+
+inline uint 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; // 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) {}
+};
+
+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()
+ : QWindowsStylePrivate(), hasInitColors(false), bufferDC(0), bufferBitmap(0), nullBitmap(0),
+ bufferPixels(0), bufferW(0), bufferH(0)
+ { init(); }
+
+ ~QWindowsXPStylePrivate()
+ { cleanup(); }
+
+ static int pixelMetricFromSystemDp(QStyle::PixelMetric pm, const QStyleOption *option = 0, const QWidget *widget = 0);
+ static int fixedPixelMetric(QStyle::PixelMetric pm, const QStyleOption *option = 0, const QWidget *widget = 0);
+
+ static HWND winId(const QWidget *widget);
+
+ void init(bool force = false);
+ void cleanup(bool force = false);
+ void cleanupHandleMap();
+ const QPixmap *tabBody(QWidget *widget);
+
+ 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);
+
+ void setTransparency(QWidget *widget, XPThemeData &themeData);
+ bool drawBackground(XPThemeData &themeData);
+ bool drawBackgroundThruNativeBuffer(XPThemeData &themeData, qreal aditionalDevicePixelRatio);
+ 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;
+ QRgb groupBoxTextColorDisabled;
+ QRgb sliderTickColor;
+ bool hasInitColors;
+
+ 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;
+ static QPixmap *tabbody;
+
+ QHash<ThemeMapKey, ThemeMapData> alphaCache;
+ HDC bufferDC;
+ HBITMAP bufferBitmap;
+ HBITMAP nullBitmap;
+ uchar *bufferPixels;
+ int bufferW, bufferH;
+
+ 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(), 0, partId, stateId, 0, 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(), 0, 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(), 0, partId, stateId, propId, NULL, &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
new file mode 100644
index 0000000000..f82bcfc91b
--- /dev/null
+++ b/src/plugins/styles/windowsvista/windowsvista.pro
@@ -0,0 +1,22 @@
+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
+
+LIBS_PRIVATE += -lgdi32 -luser32
+
+# DEFINES/LIBS needed for qwizard_win.cpp and the styles
+include(../../../widgets/kernel/win.pri)
+
+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
new file mode 100644
index 0000000000..771aa0c600
--- /dev/null
+++ b/src/plugins/styles/windowsvista/windowsvistastyle.json
@@ -0,0 +1,3 @@
+{
+ "Keys": [ "windowsvista" ]
+}