diff options
152 files changed, 37422 insertions, 5 deletions
diff --git a/examples/quickcontrols2/desktopgallery/.gitignore b/examples/quickcontrols2/desktopgallery/.gitignore new file mode 100644 index 00000000..fab7372d --- /dev/null +++ b/examples/quickcontrols2/desktopgallery/.gitignore @@ -0,0 +1,73 @@ +# This file is used to ignore files which are generated +# ---------------------------------------------------------------------------- + +*~ +*.autosave +*.a +*.core +*.moc +*.o +*.obj +*.orig +*.rej +*.so +*.so.* +*_pch.h.cpp +*_resource.rc +*.qm +.#* +*.*# +core +!core/ +tags +.DS_Store +.directory +*.debug +Makefile* +*.prl +*.app +moc_*.cpp +ui_*.h +qrc_*.cpp +Thumbs.db +*.res +*.rc +/.qmake.cache +/.qmake.stash + +# qtcreator generated files +*.pro.user* + +# xemacs temporary files +*.flc + +# Vim temporary files +.*.swp + +# Visual Studio generated files +*.ib_pdb_index +*.idb +*.ilk +*.pdb +*.sln +*.suo +*.vcproj +*vcproj.*.*.user +*.ncb +*.sdf +*.opensdf +*.vcxproj +*vcxproj.* + +# MinGW generated files +*.Debug +*.Release + +# Python byte code +*.pyc + +# Binaries +# -------- +*.dll +*.exe + diff --git a/examples/quickcontrols2/desktopgallery/Buttons.qml b/examples/quickcontrols2/desktopgallery/Buttons.qml new file mode 100644 index 00000000..2a11dea3 --- /dev/null +++ b/examples/quickcontrols2/desktopgallery/Buttons.qml @@ -0,0 +1,100 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Layouts 2.15 + +ControlContainer { + id: container + title: "Buttons" + + Row { + spacing: container.rowSpacing + + Button { + text: "Default" + } + + Button { + text: "Disabled" + enabled: false + } + + Button { + text: "Small" + property bool qqc2_style_small + } + + Button { + text: "Mini" + property bool qqc2_style_mini + } + } + + Row { + spacing: container.rowSpacing + + Button { + text: "Explicit height" + height: 50 + } + + Button { + text: "Explicit width" + width: 200 + } + } + + Row { + spacing: container.rowSpacing + + Button { + text: "Highlighted" + highlighted: true + } + + Button { + text: "Flat" + flat: true + } + + Button { + text: "Checkable" + checkable: true + } + } +} diff --git a/examples/quickcontrols2/desktopgallery/CMakeLists.txt b/examples/quickcontrols2/desktopgallery/CMakeLists.txt new file mode 100644 index 00000000..e4712a6e --- /dev/null +++ b/examples/quickcontrols2/desktopgallery/CMakeLists.txt @@ -0,0 +1,80 @@ +# Generated from desktopgallery.pro. + +cmake_minimum_required(VERSION 3.14) +project(desktopgallery LANGUAGES CXX) + +set(CMAKE_INCLUDE_CURRENT_DIR ON) + +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTORCC ON) +set(CMAKE_AUTOUIC ON) + +if(NOT DEFINED INSTALL_EXAMPLESDIR) + set(INSTALL_EXAMPLESDIR "examples") +endif() + +set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/quickcontrols2/desktopgallery") + +find_package(Qt6 COMPONENTS Core) +find_package(Qt6 COMPONENTS Gui) +find_package(Qt6 COMPONENTS Quick) +find_package(Qt6 COMPONENTS QuickControls2) + +add_qt_gui_executable(desktopgallery + main.cpp +) +target_link_libraries(desktopgallery PUBLIC + Qt::Core + Qt::Gui + Qt::Quick + Qt::QuickControls2 +) + + +# Resources: +set(qmake_immediate_resource_files + "Buttons.qml" + "CheckBoxes.qml" + "ComboBoxes.qml" + "ControlContainer.qml" + "CustomButtons.qml" + "CustomCheckBoxes.qml" + "CustomComboBoxes.qml" + "CustomDials.qml" + "CustomFrames.qml" + "CustomProgressBars.qml" + "CustomRadioButtons.qml" + "CustomScrollBars.qml" + "CustomSliders.qml" + "CustomSpinBoxes.qml" + "CustomTextAreas.qml" + "CustomTextFields.qml" + "Dials.qml" + "Frames.qml" + "ProgressBars.qml" + "RadioButtons.qml" + "ScrollBars.qml" + "Sliders.qml" + "SlidersMini.qml" + "SlidersSmall.qml" + "SpinBoxes.qml" + "TextAreas.qml" + "TextFields.qml" + "checkbox-icon.png" + "checkbox-icon16.png" + "checkbox-icon@2x.png" + "main.qml" +) + +qt6_add_resources(desktopgallery "qmake_immediate" + PREFIX + "/" + FILES + ${qmake_immediate_resource_files} +) + +install(TARGETS desktopgallery + RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}" + BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}" + LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}" +) diff --git a/examples/quickcontrols2/desktopgallery/CheckBoxes.qml b/examples/quickcontrols2/desktopgallery/CheckBoxes.qml new file mode 100644 index 00000000..50b81ec4 --- /dev/null +++ b/examples/quickcontrols2/desktopgallery/CheckBoxes.qml @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Layouts 2.15 +import QtQuick.Controls.impl 2.12 + +ControlContainer { + id: container + title: "CheckBoxes" + + Row { + spacing: container.rowSpacing + + CheckBox { + text: "Default" + checked: true + } + + CheckBox { + text: "Disabled" + enabled: false + } + + CheckBox { + text: "Tri-state" + tristate: true + checkState: Qt.PartiallyChecked + } + + CheckBox { + text: "Small" + property bool qqc2_style_small + } + + CheckBox { + text: "Mini" + property bool qqc2_style_mini + checked: true + } + } +} diff --git a/examples/quickcontrols2/desktopgallery/ComboBoxes.qml b/examples/quickcontrols2/desktopgallery/ComboBoxes.qml new file mode 100644 index 00000000..37c6982c --- /dev/null +++ b/examples/quickcontrols2/desktopgallery/ComboBoxes.qml @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Layouts 2.15 + +ControlContainer { + id: container + title: "ComboBoxes" + + Row { + spacing: container.rowSpacing + + ComboBox { + model: [ "Default", "Banana", "Apple", "Coconut" ] + } + + ComboBox { + model: [ "Disabled", "Banana", "Apple", "Coconut" ] + enabled: false + } + + ComboBox { + model: [ "Small", "Banana", "Apple", "Coconut" ] + property bool qqc2_style_small + } + + ComboBox { + model: [ "Mini", "Banana", "Apple", "Coconut" ] + property bool qqc2_style_mini + } + } + + Row { + spacing: container.rowSpacing + + ComboBox { + model: [ "Default", "Banana", "Apple", "Coconut" ] + editable: true + } + + ComboBox { + model: [ "Disabled", "Banana", "Apple", "Coconut" ] + enabled: false + editable: true + } + + ComboBox { + model: [ "Small", "Banana", "Apple", "Coconut" ] + editable: true + property bool qqc2_style_small + } + + ComboBox { + model: [ "Mini", "Banana", "Apple", "Coconut" ] + editable: true + property bool qqc2_style_mini + } + } +} diff --git a/examples/quickcontrols2/desktopgallery/ControlContainer.qml b/examples/quickcontrols2/desktopgallery/ControlContainer.qml new file mode 100644 index 00000000..30022540 --- /dev/null +++ b/examples/quickcontrols2/desktopgallery/ControlContainer.qml @@ -0,0 +1,62 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Layouts 2.15 + +Item { + anchors.left: parent.left + anchors.right: parent.right + anchors.rightMargin: 20 // make room for scrollbar + implicitHeight: groupBox.height + + property alias title: groupBox.title + property real rowSpacing: 20 + + default property alias data: layout.data + + GroupBox { + id: groupBox + anchors.left: parent.left + anchors.right: parent.right + + ColumnLayout { + id: layout + spacing: 15 + } + } +} diff --git a/examples/quickcontrols2/desktopgallery/CustomButtons.qml b/examples/quickcontrols2/desktopgallery/CustomButtons.qml new file mode 100644 index 00000000..edcdb086 --- /dev/null +++ b/examples/quickcontrols2/desktopgallery/CustomButtons.qml @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Layouts 2.15 + +ControlContainer { + id: container + title: "Buttons" + + Row { + spacing: container.rowSpacing + + Button { + id: buttonWithCustomContentItem + text: "Custom contentItem" + contentItem: Rectangle { + implicitWidth: 120 + implicitHeight: il.implicitHeight + color: buttonWithCustomContentItem.pressed ? "green" : "lightGreen" + Text { + id: il + text: buttonWithCustomContentItem.text + anchors.centerIn: parent + } + } + } + + Button { + id: cb + text: "Custom background" + background: Rectangle { + implicitWidth: 200 + implicitHeight: 30 + radius: 5 + color: cb.pressed ? "LightGray" : "gray" + } + } + + Button { + id: cb2 + text: "All custom" + background: Rectangle { + implicitWidth: 200 + implicitHeight: 30 + radius: 5 + color: cb2.pressed ? "LightGray" : "gray" + } + contentItem: Rectangle { + implicitWidth: il2.implicitWidth + implicitHeight: il2.implicitHeight + radius: 3 + color: "lightgray" + Text { + id: il2 + text: cb2.text + anchors.centerIn: parent + } + } + } + } +} diff --git a/examples/quickcontrols2/desktopgallery/CustomCheckBoxes.qml b/examples/quickcontrols2/desktopgallery/CustomCheckBoxes.qml new file mode 100644 index 00000000..fcec932f --- /dev/null +++ b/examples/quickcontrols2/desktopgallery/CustomCheckBoxes.qml @@ -0,0 +1,137 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Layouts 2.15 +import QtQuick.Controls.impl 2.12 + +ControlContainer { + id: container + title: "CheckBoxes" + + Row { + spacing: container.rowSpacing + + CheckBox { + id: customContentItem + text: "Custom content item" + contentItem: Text { + text: customContentItem.text + color: "green" + leftPadding: customContentItem.indicator.width + customContentItem.spacing + } + } + + CheckBox { + id: customIndicator + text: "Custom indicator" + indicator: Rectangle { + implicitWidth: 15 + implicitHeight: 15 + + x: customIndicator.text ? customIndicator.leftPadding : customIndicator.leftPadding + (customIndicator.availableWidth - width) / 2 + y: customIndicator.topPadding + (customIndicator.availableHeight - height) / 2 + + color: customIndicator.down ? customIndicator.palette.light : customIndicator.palette.base + border.color: "green" + border.width: 2 + + ColorImage { + x: (parent.width - width) / 2 + y: (parent.height - height) / 2 + defaultColor: "#353637" + scale: 0.5 + color: "green" + source: "qrc:/qt-project.org/imports/QtQuick/Controls.2/images/check.png" + visible: customIndicator.checkState === Qt.Checked + } + + Rectangle { + x: (parent.width - width) / 2 + y: (parent.height - height) / 2 + width: 16 + height: 3 + color: customIndicator.palette.text + visible: customIndicator.checkState === Qt.PartiallyChecked + } + } + } + + CheckBox { + id: allCustom + text: "All custom" + + contentItem: Text { + text: allCustom.text + color: "green" + leftPadding: allCustom.indicator.width + allCustom.spacing + rightPadding: allCustom.indicator.width + allCustom.spacing + } + + indicator: Rectangle { + implicitWidth: 15 + implicitHeight: 15 + + x: allCustom.text ? allCustom.leftPadding : allCustom.leftPadding + (allCustom.availableWidth - width) / 2 + y: allCustom.topPadding + (allCustom.availableHeight - height) / 2 + + color: allCustom.down ? allCustom.palette.light : allCustom.palette.base + border.color: "green" + border.width: 2 + + ColorImage { + x: (parent.width - width) / 2 + y: (parent.height - height) / 2 + defaultColor: "#353637" + scale: 0.5 + color: "green" + source: "qrc:/qt-project.org/imports/QtQuick/Controls.2/images/check.png" + visible: allCustom.checkState === Qt.Checked + } + + Rectangle { + x: (parent.width - width) / 2 + y: (parent.height - height) / 2 + width: 16 + height: 3 + color: "green" + visible: allCustom.checkState === Qt.PartiallyChecked + } + } + } + } +} diff --git a/examples/quickcontrols2/desktopgallery/CustomComboBoxes.qml b/examples/quickcontrols2/desktopgallery/CustomComboBoxes.qml new file mode 100644 index 00000000..58793182 --- /dev/null +++ b/examples/quickcontrols2/desktopgallery/CustomComboBoxes.qml @@ -0,0 +1,122 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Layouts 2.15 +import QtQuick.Controls.impl 2.15 + +ControlContainer { + id: container + title: "ComboBoxes" + + Row { + spacing: container.rowSpacing + + ComboBox { + id: control + model: [ "Custom background", "Banana", "Apple", "Coconut" ] + background: Rectangle { + implicitWidth: 200 + implicitHeight: 20 + color: control.down ? control.palette.mid : control.palette.button + border.color: "green" + border.width: 1 + } + indicator: ColorImage { + x: control.mirrored ? control.padding : control.width - width - control.padding + y: control.topPadding + (control.availableHeight - height) / 2 + color: control.palette.dark + defaultColor: "#353637" + source: "qrc:/qt-project.org/imports/QtQuick/Controls.2/images/double-arrow.png" + opacity: enabled ? 1 : 0.3 + } + } + + ComboBox { + model: [ "Banana", "Apple", "Coconut" ] + contentItem: Rectangle { + implicitWidth: text.implicitWidth + color: "lightGreen" + Text { + id: text + text: "Custom content item" + anchors.centerIn: parent + } + } + } + + } + + Row { + spacing: container.rowSpacing + + ComboBox { + id: control2 + model: [ "Custom background", "Banana", "Apple", "Coconut" ] + editable: true + background: Rectangle { + implicitWidth: 200 + implicitHeight: 20 + color: control2.down ? control2.palette.mid : control2.palette.button + border.color: "green" + border.width: 1 + } + indicator: ColorImage { + x: control2.mirrored ? control2.padding : control2.width - width - control2.padding + y: control2.topPadding + (control2.availableHeight - height) / 2 + color: control2.palette.dark + defaultColor: "#353637" + source: "qrc:/qt-project.org/imports/QtQuick/Controls.2/images/double-arrow.png" + opacity: enabled ? 1 : 0.3 + } + } + + ComboBox { + model: [ "Banana", "Apple", "Coconut" ] + editable: true + contentItem: Rectangle { + implicitWidth: text2.implicitWidth + color: "lightGreen" + TextEdit { + id: text2 + text: "Custom content item" + anchors.centerIn: parent + } + } + } + } +} diff --git a/examples/quickcontrols2/desktopgallery/CustomDials.qml b/examples/quickcontrols2/desktopgallery/CustomDials.qml new file mode 100644 index 00000000..c1a0ac4e --- /dev/null +++ b/examples/quickcontrols2/desktopgallery/CustomDials.qml @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick dial1s 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Controls.impl 2.12 +import QtQuick.Layouts 2.15 + +ControlContainer { + id: container + title: "Dials" + + Row { + spacing: container.rowSpacing + + Dial { + id: dial1 + width: 50 + height: 50 + from: 0 + to: 10 + value: 5 + + background: DialImpl { + implicitWidth: 184 + implicitHeight: 184 + color: "darkgray" + progress: dial1.position + opacity: dial1.enabled ? 1 : 0.3 + } + + handle: ColorImage { + x: dial1.background.x + dial1.background.width / 2 - width / 2 + y: dial1.background.y + dial1.background.height / 2 - height / 2 + width: 14 + height: 10 + color: "green" + source: "qrc:/qt-project.org/imports/QtQuick/Controls.2/images/dial-indicator.png" + antialiasing: true + opacity: dial1.enabled ? 1 : 0.3 + transform: [ + Translate { + y: -Math.min(dial1.background.width, dial1.background.height) * 0.4 + dial1.handle.height / 2 + }, + Rotation { + angle: dial1.angle + origin.x: dial1.handle.width / 2 + origin.y: dial1.handle.height / 2 + } + ] + } + } + } +} diff --git a/examples/quickcontrols2/desktopgallery/CustomFrames.qml b/examples/quickcontrols2/desktopgallery/CustomFrames.qml new file mode 100644 index 00000000..0b74b220 --- /dev/null +++ b/examples/quickcontrols2/desktopgallery/CustomFrames.qml @@ -0,0 +1,62 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Layouts 2.15 + +ControlContainer { + id: container + title: "Frames" + + Row { + spacing: container.rowSpacing + + Frame { + width: 200 + height: 50 + background: Rectangle { + border.width: 1 + border.color: "green" + Text { + anchors.centerIn: parent + color: "green" + text: "Custom background" + } + } + } + } +} diff --git a/examples/quickcontrols2/desktopgallery/CustomProgressBars.qml b/examples/quickcontrols2/desktopgallery/CustomProgressBars.qml new file mode 100644 index 00000000..93932a5e --- /dev/null +++ b/examples/quickcontrols2/desktopgallery/CustomProgressBars.qml @@ -0,0 +1,115 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Layouts 2.15 +import QtQuick.Controls.impl 2.12 + +ControlContainer { + id: container + title: "ProgressBars" + + property int time: 0 + Timer { + interval: 1000 + running: true + repeat: true + onTriggered: { + time++ + if (time > 10) + time = 0 + } + } + + Row { + spacing: container.rowSpacing + + ProgressBar { + id: c1 + width: 100 + from: 0 + to: 10 + value: time + indeterminate: false + padding: 5 + background: Rectangle { + implicitWidth: 100 + implicitHeight: 6 + color: "darkgray" + } + } + + ProgressBar { + id: c2 + width: 100 + from: 0 + to: 10 + value: time + indeterminate: false + padding: 5 + contentItem: ProgressBarImpl { + implicitHeight: 6 + implicitWidth: 100 + progress: c2.position + indeterminate: false + color: "lightgreen" + } + } + + ProgressBar { + id: c3 + width: 100 + from: 0 + to: 10 + value: time + indeterminate: false + padding: 5 + background: Rectangle { + implicitWidth: 100 + implicitHeight: 6 + color: "darkgray" + } + contentItem: ProgressBarImpl { + implicitHeight: 6 + implicitWidth: 100 + progress: c3.position + indeterminate: false + color: "lightgreen" + } + } + } +} diff --git a/examples/quickcontrols2/desktopgallery/CustomRadioButtons.qml b/examples/quickcontrols2/desktopgallery/CustomRadioButtons.qml new file mode 100644 index 00000000..9418b689 --- /dev/null +++ b/examples/quickcontrols2/desktopgallery/CustomRadioButtons.qml @@ -0,0 +1,121 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Layouts 2.15 +import QtQuick.Controls.impl 2.12 + +ControlContainer { + id: container + title: "RadioButtons" + + Row { + spacing: container.rowSpacing + + RadioButton { + id: customContentItem + text: "Custom content item" + contentItem: Text { + text: customContentItem.text + color: "green" + leftPadding: customContentItem.indicator.width + customContentItem.spacing + } + } + + RadioButton { + id: customIndicator + text: "Custom indicator" + indicator: Rectangle { + implicitWidth: 14 + implicitHeight: 14 + + x: customIndicator.text ? customIndicator.leftPadding : customIndicator.leftPadding + (customIndicator.availableWidth - width) / 2 + y: customIndicator.topPadding + (customIndicator.availableHeight - height) / 2 + + radius: width / 2 + color: customIndicator.down ? customIndicator.palette.light : customIndicator.palette.base + border.width: 2 + border.color: "green" + + Rectangle { + x: (parent.width - width) / 2 + y: (parent.height - height) / 2 + width: 5 + height: 5 + radius: width / 2 + color: "green" + visible: customIndicator.checked + } + } + } + + RadioButton { + id: allCustom + text: "All custom" + + contentItem: Text { + text: allCustom.text + color: "green" + leftPadding: allCustom.indicator.width + allCustom.spacing + } + + indicator: Rectangle { + implicitWidth: 14 + implicitHeight: 14 + + x: allCustom.text ? allCustom.leftPadding : allCustom.leftPadding + (allCustom.availableWidth - width) / 2 + y: allCustom.topPadding + (allCustom.availableHeight - height) / 2 + + radius: width / 2 + color: allCustom.down ? allCustom.palette.light : allCustom.palette.base + border.width: 2 + border.color: "green" + + Rectangle { + x: (parent.width - width) / 2 + y: (parent.height - height) / 2 + width: 5 + height: 5 + radius: width / 2 + color: "green" + visible: allCustom.checked + } + } + } + } + +} diff --git a/examples/quickcontrols2/desktopgallery/CustomScrollBars.qml b/examples/quickcontrols2/desktopgallery/CustomScrollBars.qml new file mode 100644 index 00000000..fcac2eaf --- /dev/null +++ b/examples/quickcontrols2/desktopgallery/CustomScrollBars.qml @@ -0,0 +1,137 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Layouts 2.15 + +ControlContainer { + id: container + title: "ScrollBars" + + Row { + spacing: container.rowSpacing + + ScrollBar { + height: 200 + orientation: Qt.Vertical + size: 0.2 + policy: ScrollBar.AlwaysOn + background: Rectangle { + color: "lightgray" + border.color: "gray" + border.width: 1 + } + } + + ScrollBar { + height: 200 + orientation: Qt.Vertical + size: 0.2 + policy: ScrollBar.AlwaysOn + contentItem: Rectangle { + color: "lightgreen" + border.color: "green" + border.width: 1 + } + } + + ScrollBar { + height: 200 + orientation: Qt.Vertical + size: 0.2 + policy: ScrollBar.AlwaysOn + background: Rectangle { + color: "lightgray" + border.color: "gray" + border.width: 1 + } + contentItem: Rectangle { + implicitWidth: 15 + color: "lightgreen" + border.color: "green" + border.width: 1 + } + } + + Column { + spacing: container.rowSpacing + + ScrollBar { + width: 300 + orientation: Qt.Horizontal + size: 0.2 + policy: ScrollBar.AlwaysOn + background: Rectangle { + color: "lightgray" + border.color: "gray" + border.width: 1 + } + } + + ScrollBar { + width: 300 + orientation: Qt.Horizontal + size: 0.2 + policy: ScrollBar.AlwaysOn + contentItem: Rectangle { + color: "lightgray" + border.color: "gray" + border.width: 1 + } + } + + ScrollBar { + width: 300 + orientation: Qt.Horizontal + size: 0.2 + policy: ScrollBar.AlwaysOn + background: Rectangle { + color: "lightgray" + border.color: "gray" + border.width: 1 + } + contentItem: Rectangle { + implicitHeight: 15 + color: "lightgreen" + border.color: "green" + border.width: 1 + } + } + } + } + +} diff --git a/examples/quickcontrols2/desktopgallery/CustomSliders.qml b/examples/quickcontrols2/desktopgallery/CustomSliders.qml new file mode 100644 index 00000000..38cdf0a2 --- /dev/null +++ b/examples/quickcontrols2/desktopgallery/CustomSliders.qml @@ -0,0 +1,167 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Layouts 2.15 + +ControlContainer { + id: container + title: "Sliders" + property int sliderWidth: 300 + property int sliderHeight: 140 + + Row { + spacing: 40 + + Column { + spacing: 15 + + Slider { + id: customHandle + width: sliderWidth + height: 20 + from: 0 + to: 10 + value: 5 + handle: Rectangle { + id: handle + width: 12 + height: customHandle.height + color: "white" + border.width: 2 + + x: customHandle.visualPosition * (customHandle.availableWidth - width) + y: (customHandle.availableHeight - height) / 2 + } + } + + Slider { + id: customBackground + width: sliderWidth + from: 0 + to: 10 + background: Rectangle { + implicitHeight: 5 + color: "lightgray" + border.width: 1 + } + } + + Slider { + id: customAll + width: sliderWidth + height: 20 + from: 0 + to: 10 + background: Rectangle { + implicitHeight: customAll.height + color: "lightgray" + border.width: 1 + } + handle: Rectangle { + width: 12 + height: customAll.height + color: "white" + border.width: 2 + + x: customAll.visualPosition * (customAll.availableWidth - width) + y: (customAll.availableHeight - height) / 2 + } + } + } + + Row { + spacing: 20 + + Slider { + id: customVHandle + width: 20 + height: sliderHeight + orientation: Qt.Vertical + from: 0 + to: 10 + value: 5 + handle: Rectangle { + height: 12 + width: customVHandle.width + color: "white" + border.width: 2 + + x: (customVHandle.availableWidth - width) / 2 + y: customVHandle.visualPosition * (customVHandle.availableHeight - height) + } + } + + Slider { + id: customVBackground + height: sliderHeight + orientation: Qt.Vertical + from: 0 + to: 10 + background: Rectangle { + implicitWidth: 5 + color: "lightgray" + border.width: 1 + } + } + + Slider { + id: customVAll + width: 20 + height: sliderHeight + orientation: Qt.Vertical + from: 0 + to: 10 + value: 5 + handle: Rectangle { + height: 12 + width: customVHandle.width + color: "white" + border.width: 2 + + x: (customVAll.availableWidth - width) / 2 + y: customVAll.visualPosition * (customVAll.availableHeight - height) + } + background: Rectangle { + implicitWidth: 5 + color: "lightgray" + border.width: 1 + } + } + } + } +} diff --git a/examples/quickcontrols2/desktopgallery/CustomSpinBoxes.qml b/examples/quickcontrols2/desktopgallery/CustomSpinBoxes.qml new file mode 100644 index 00000000..b632f311 --- /dev/null +++ b/examples/quickcontrols2/desktopgallery/CustomSpinBoxes.qml @@ -0,0 +1,171 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Layouts 2.15 + +ControlContainer { + id: container + title: "SpinBoxes" + + Row { + spacing: container.rowSpacing + + SpinBox { + id: custombg + value: 1000 + to: 2000 + background: Rectangle { + border.color: "green" + implicitWidth: 50 + } + } + + SpinBox { + id: customIndicator + value: 500 + to: 2000 + + rightPadding: 17 + spacing: 0 + implicitWidth: 60 + implicitHeight: 25 + + up.indicator: Rectangle { + x: customIndicator.width - width - 4 + y: 4 + implicitWidth: customIndicator.rightPadding - 4 + implicitHeight: 8 + border.width: 1 + border.color: "green" + color: customIndicator.up.pressed ? "gray" : "transparent" + Text { + text: "+" + font.pixelSize: 8 + font.bold: true + anchors.centerIn: parent + } + } + + down.indicator: Rectangle { + x: customIndicator.width - width - 4 + y: height + 6 + implicitWidth: customIndicator.rightPadding - 4 + implicitHeight: 8 + border.width: 1 + border.color: "green" + color: customIndicator.down.pressed ? "gray" : "transparent" + Text { + text: "-" + font.pixelSize: 10 + font.bold: true + anchors.centerIn: parent + } + } + } + + SpinBox { + id: allCustom + value: 500 + to: 2000 + + rightPadding: 17 + spacing: 0 + implicitWidth: 60 + implicitHeight: 25 + + background: Rectangle { + border.color: "green" + implicitWidth: 50 + } + + up.indicator: Rectangle { + x: allCustom.width - width - 4 + y: 4 + implicitWidth: allCustom.rightPadding - 4 + implicitHeight: 8 + border.width: 1 + border.color: "green" + color: allCustom.up.pressed ? "gray" : "transparent" + Text { + text: "+" + font.pixelSize: 8 + font.bold: true + anchors.centerIn: parent + } + } + + down.indicator: Rectangle { + x: allCustom.width - width - 4 + y: height + 6 + implicitWidth: allCustom.rightPadding - 4 + implicitHeight: 8 + border.width: 1 + border.color: "green" + color: allCustom.down.pressed ? "gray" : "transparent" + Text { + text: "-" + font.pixelSize: 10 + font.bold: true + anchors.centerIn: parent + } + } + + contentItem: TextInput { + text: allCustom.displayText + font: allCustom.font + color: "green" + selectionColor: allCustom.palette.highlight + selectedTextColor: allCustom.palette.highlightedText + horizontalAlignment: Qt.AlignLeft + verticalAlignment: Qt.AlignVCenter + + topPadding: 2 + bottomPadding: 2 + leftPadding: 10 + rightPadding: 10 + + readOnly: !allCustom.editable + validator: allCustom.validator + inputMethodHints: allCustom.inputMethodHints + } + + } + + } + +} diff --git a/examples/quickcontrols2/desktopgallery/CustomTextAreas.qml b/examples/quickcontrols2/desktopgallery/CustomTextAreas.qml new file mode 100644 index 00000000..a729ea10 --- /dev/null +++ b/examples/quickcontrols2/desktopgallery/CustomTextAreas.qml @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Layouts 2.15 + +ControlContainer { + id: container + title: "TextFields" + + Row { + spacing: container.rowSpacing + + TextArea { + id: customBackground + width: 200 + wrapMode: TextEdit.WordWrap + text: "Custom background - Lorem ipsum dolor sit amet, consectetur adipiscing elit, " + + "sed do eiusmod tempor incididunt ut labore et dolore magna aliqua." + background: Rectangle { + implicitWidth: customBackground.contentWidth + implicitHeight: customBackground.contentHeight + border.width: customBackground.activeFocus ? 2 : 1 + color: control.palette.base + border.color: "green" + } + } + + TextArea { + width: 200 + placeholderText: "Large font" + font.pixelSize: 20 + wrapMode: TextEdit.WordWrap + text: "Large font - Lorem ipsum dolor sit amet, consectetur adipiscing elit" + } + } +} diff --git a/examples/quickcontrols2/desktopgallery/CustomTextFields.qml b/examples/quickcontrols2/desktopgallery/CustomTextFields.qml new file mode 100644 index 00000000..f781eb19 --- /dev/null +++ b/examples/quickcontrols2/desktopgallery/CustomTextFields.qml @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Layouts 2.15 + +ControlContainer { + id: container + title: "TextFields" + + Row { + spacing: container.rowSpacing + + TextField { + placeholderText: "Custom background" + background: Rectangle { + implicitWidth: 130 + implicitHeight: 20 + border.width: control.activeFocus ? 2 : 1 + color: control.palette.base + border.color: "green" + } + } + TextField { + placeholderText: "Large font" + font.pixelSize: 20 + } + } +} diff --git a/examples/quickcontrols2/desktopgallery/Dials.qml b/examples/quickcontrols2/desktopgallery/Dials.qml new file mode 100644 index 00000000..56dff2a4 --- /dev/null +++ b/examples/quickcontrols2/desktopgallery/Dials.qml @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Layouts 2.15 + +ControlContainer { + id: container + title: "Dials" + + Row { + spacing: container.rowSpacing + + Dial { + width: 50 + height: 50 + from: 0 + to: 10 + value: 5 + } + + Dial { + width: 50 + height: 50 + from: 0 + to: 10 + value: 5 + stepSize: 1 + property int qqc2_style_tickPosition: 1 + } + } +} diff --git a/examples/quickcontrols2/desktopgallery/Frames.qml b/examples/quickcontrols2/desktopgallery/Frames.qml new file mode 100644 index 00000000..91cbec6d --- /dev/null +++ b/examples/quickcontrols2/desktopgallery/Frames.qml @@ -0,0 +1,65 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Layouts 2.15 + +ControlContainer { + id: container + title: "Frames" + + Row { + spacing: container.rowSpacing + + Frame { + width: 100 + height: 50 + } + + Frame { + Rectangle { + implicitWidth: label.width + 50 + implicitHeight: 45 + Label { + id: label + anchors.centerIn: parent + text: "Frame with contents" + } + } + } + } +} diff --git a/examples/quickcontrols2/desktopgallery/ProgressBars.qml b/examples/quickcontrols2/desktopgallery/ProgressBars.qml new file mode 100644 index 00000000..f19c05bd --- /dev/null +++ b/examples/quickcontrols2/desktopgallery/ProgressBars.qml @@ -0,0 +1,102 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Layouts 2.15 + +ControlContainer { + id: container + title: "ProgressBars" + + property int time: 0 + Timer { + interval: 1000 + running: true + repeat: true + onTriggered: { + time++ + if (time > 10) + time = 0 + } + } + + Row { + spacing: container.rowSpacing + + ProgressBar { + width: 100 + from: 0 + to: 10 + value: time + indeterminate: false + } + + ProgressBar { + width: 100 + from: 0 + to: 10 + value: time + enabled: false + indeterminate: false + } + + ProgressBar { + width: 100 + from: 0 + to: 10 + indeterminate: true + } + + ProgressBar { + width: 80 + from: 0 + to: 10 + value: time + indeterminate: false + property bool qqc2_style_small + } + + ProgressBar { + width: 60 + from: 0 + to: 10 + value: time + indeterminate: false + property bool qqc2_style_mini + } + } +} diff --git a/examples/quickcontrols2/desktopgallery/RadioButtons.qml b/examples/quickcontrols2/desktopgallery/RadioButtons.qml new file mode 100644 index 00000000..b9c49e7d --- /dev/null +++ b/examples/quickcontrols2/desktopgallery/RadioButtons.qml @@ -0,0 +1,111 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Layouts 2.15 +import QtQuick.Controls.impl 2.12 + +ControlContainer { + id: container + title: "RadioButtons" + + Row { + spacing: container.rowSpacing + + Column { + RadioButton { + text: "Default" + checked: true + } + RadioButton { + text: "Default" + } + RadioButton { + text: "Default" + } + } + + Column { + RadioButton { + text: "Disabled" + enabled: false + } + RadioButton { + text: "Disabled" + enabled: false + } + RadioButton { + text: "Disabled" + enabled: false + checked: true + } + } + + Column { + RadioButton { + text: "Small" + property bool qqc2_style_small + } + RadioButton { + text: "Small" + checked: true + property bool qqc2_style_small + } + RadioButton { + text: "Small" + property bool qqc2_style_small + } + } + + Column { + RadioButton { + text: "Mini" + property bool qqc2_style_mini + } + RadioButton { + text: "Mini" + property bool qqc2_style_mini + } + RadioButton { + text: "Mini" + checked: true + property bool qqc2_style_mini + } + } + } + +} diff --git a/examples/quickcontrols2/desktopgallery/ScrollBars.qml b/examples/quickcontrols2/desktopgallery/ScrollBars.qml new file mode 100644 index 00000000..f7b78f86 --- /dev/null +++ b/examples/quickcontrols2/desktopgallery/ScrollBars.qml @@ -0,0 +1,99 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Layouts 2.15 + +ControlContainer { + id: container + title: "ScrollBars" + + Row { + spacing: container.rowSpacing + + ScrollBar { + height: 200 + orientation: Qt.Vertical + size: 0.2 + policy: ScrollBar.AlwaysOn + } + + ScrollBar { + height: 150 + orientation: Qt.Vertical + size: 0.2 + policy: ScrollBar.AlwaysOn + property bool qqc2_style_small + } + + ScrollBar { + height: 100 + orientation: Qt.Vertical + size: 0.2 + policy: ScrollBar.AlwaysOn + property bool qqc2_style_mini + } + + Column { + spacing: container.rowSpacing + + ScrollBar { + width: 300 + orientation: Qt.Horizontal + size: 0.2 + policy: ScrollBar.AlwaysOn + } + + ScrollBar { + width: 200 + orientation: Qt.Horizontal + size: 0.2 + policy: ScrollBar.AlwaysOn + property bool qqc2_style_small + } + + ScrollBar { + width: 100 + orientation: Qt.Horizontal + size: 0.2 + policy: ScrollBar.AlwaysOn + property bool qqc2_style_mini + } + } + } + +} diff --git a/examples/quickcontrols2/desktopgallery/Sliders.qml b/examples/quickcontrols2/desktopgallery/Sliders.qml new file mode 100644 index 00000000..dba0794b --- /dev/null +++ b/examples/quickcontrols2/desktopgallery/Sliders.qml @@ -0,0 +1,132 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Layouts 2.15 + +ControlContainer { + id: container + title: "Sliders normal" + property int sliderWidth: 300 + property int sliderHeight: 140 + + Row { + spacing: 40 + + Column { + spacing: 15 + + Slider { + width: sliderWidth + from: 0 + to: 10 + value: 5 + } + + Slider { + width: sliderWidth + from: 0 + to: 10 + value: 5 + enabled: false + } + + Slider { + width: sliderWidth + from: 0 + to: 100 + value: 20 + stepSize: 20 + + property int qqc2_style_tickPosition: 1 + } + + Slider { + width: sliderWidth + from: 0 + to: 100 + stepSize: 5 + value: 65 + + property int qqc2_style_tickPosition: 2 + } + } + + Row { + spacing: 20 + + Slider { + height: sliderHeight + orientation: Qt.Vertical + from: 0 + to: 10 + value: 5 + } + + Slider { + height: sliderHeight + orientation: Qt.Vertical + from: 0 + to: 10 + value: 5 + enabled: false + } + + Slider { + height: sliderHeight + orientation: Qt.Vertical + from: 0 + to: 100 + value: 20 + stepSize: 20 + + property int qqc2_style_tickPosition: 1 + } + + Slider { + height: sliderHeight + orientation: Qt.Vertical + from: 0 + to: 100 + stepSize: 5 + value: 65 + + property int qqc2_style_tickPosition: 2 + } + } + } +} diff --git a/examples/quickcontrols2/desktopgallery/SlidersMini.qml b/examples/quickcontrols2/desktopgallery/SlidersMini.qml new file mode 100644 index 00000000..542719e9 --- /dev/null +++ b/examples/quickcontrols2/desktopgallery/SlidersMini.qml @@ -0,0 +1,140 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Layouts 2.15 + +ControlContainer { + id: container + title: "Sliders mini" + property int sliderWidth: 100 + property int sliderHeight: 90 + + Row { + spacing: 40 + + Column { + spacing: 15 + + Slider { + width: sliderWidth + from: 0 + to: 10 + value: 5 + property bool qqc2_style_mini + } + + Slider { + width: sliderWidth + from: 0 + to: 10 + value: 5 + enabled: false + property bool qqc2_style_mini + } + + Slider { + width: sliderWidth + from: 0 + to: 100 + value: 20 + stepSize: 20 + + property bool qqc2_style_mini + property int qqc2_style_tickPosition: 1 + } + + Slider { + width: sliderWidth + from: 0 + to: 100 + stepSize: 5 + value: 65 + + property bool qqc2_style_mini + property int qqc2_style_tickPosition: 2 + } + } + + Row { + spacing: 20 + + Slider { + height: sliderHeight + orientation: Qt.Vertical + from: 0 + to: 10 + value: 5 + property bool qqc2_style_mini + } + + Slider { + height: sliderHeight + orientation: Qt.Vertical + from: 0 + to: 10 + value: 5 + enabled: false + property bool qqc2_style_mini + } + + Slider { + height: sliderHeight + orientation: Qt.Vertical + from: 0 + to: 100 + value: 20 + stepSize: 20 + + property bool qqc2_style_mini + property int qqc2_style_tickPosition: 1 + } + + Slider { + height: sliderHeight + orientation: Qt.Vertical + from: 0 + to: 100 + stepSize: 5 + value: 65 + + property bool qqc2_style_mini + property int qqc2_style_tickPosition: 2 + } + } + } +} diff --git a/examples/quickcontrols2/desktopgallery/SlidersSmall.qml b/examples/quickcontrols2/desktopgallery/SlidersSmall.qml new file mode 100644 index 00000000..bd1f85a6 --- /dev/null +++ b/examples/quickcontrols2/desktopgallery/SlidersSmall.qml @@ -0,0 +1,140 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Layouts 2.15 + +ControlContainer { + id: container + title: "Sliders small" + property int sliderWidth: 200 + property int sliderHeight: 100 + + Row { + spacing: 40 + + Column { + spacing: 15 + + Slider { + width: sliderWidth + from: 0 + to: 10 + value: 5 + property bool qqc2_style_small + } + + Slider { + width: sliderWidth + from: 0 + to: 10 + value: 5 + enabled: false + property bool qqc2_style_small + } + + Slider { + width: sliderWidth + from: 0 + to: 100 + value: 20 + stepSize: 20 + + property bool qqc2_style_small + property int qqc2_style_tickPosition: 1 + } + + Slider { + width: sliderWidth + from: 0 + to: 100 + stepSize: 5 + value: 65 + + property bool qqc2_style_small + property int qqc2_style_tickPosition: 2 + } + } + + Row { + spacing: 20 + + Slider { + height: sliderHeight + orientation: Qt.Vertical + from: 0 + to: 10 + value: 5 + property bool qqc2_style_small + } + + Slider { + height: sliderHeight + orientation: Qt.Vertical + from: 0 + to: 10 + value: 5 + enabled: false + property bool qqc2_style_small + } + + Slider { + height: sliderHeight + orientation: Qt.Vertical + from: 0 + to: 100 + value: 20 + stepSize: 20 + + property bool qqc2_style_small + property int qqc2_style_tickPosition: 1 + } + + Slider { + height: sliderHeight + orientation: Qt.Vertical + from: 0 + to: 100 + stepSize: 5 + value: 65 + + property bool qqc2_style_small + property int qqc2_style_tickPosition: 2 + } + } + } +} diff --git a/examples/quickcontrols2/desktopgallery/SpinBoxes.qml b/examples/quickcontrols2/desktopgallery/SpinBoxes.qml new file mode 100644 index 00000000..0b66e977 --- /dev/null +++ b/examples/quickcontrols2/desktopgallery/SpinBoxes.qml @@ -0,0 +1,73 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Layouts 2.15 + +ControlContainer { + id: container + title: "SpinBoxes" + + Row { + spacing: container.rowSpacing + + SpinBox { + value: 500 + to: 2000 + } + + SpinBox { + value: 0 + enabled: false + } + + SpinBox { + value: 5 + from: 0 + to: 9 + property bool qqc2_style_small + } + + SpinBox { + value: 0 + from: -9 + to: 9 + property bool qqc2_style_mini + } + } + +} diff --git a/examples/quickcontrols2/desktopgallery/TextAreas.qml b/examples/quickcontrols2/desktopgallery/TextAreas.qml new file mode 100644 index 00000000..be777ac5 --- /dev/null +++ b/examples/quickcontrols2/desktopgallery/TextAreas.qml @@ -0,0 +1,107 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Layouts 2.15 + +ControlContainer { + id: container + title: "TextAreas" + + Row { + spacing: container.rowSpacing + + TextArea { + id: defaultTextArea + width: 200 + wrapMode: TextEdit.WordWrap + text: "Default - Lorem ipsum dolor sit amet, consectetur adipiscing elit, " + + "sed do eiusmod tempor incididunt ut labore et dolore magna aliqua." + } + + TextArea { + enabled: false + width: 200 + wrapMode: TextEdit.WordWrap + text: "Disabled - Lorem ipsum dolor sit amet, consectetur adipiscing elit, " + + "sed do eiusmod tempor incididunt ut labore et dolore magna aliqua." + } + + Flickable { + width: 200 + height: defaultTextArea.height + clip: true + + TextArea.flickable: TextArea { + text: "Inside flickable - Lorem ipsum dolor sit amet, consectetur adipiscing elit, " + + "sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. " + + "Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi " + + "ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit " + + "in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur " + + "sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt " + + "mollit anim id est laborum." + wrapMode: TextEdit.WordWrap + } + + ScrollBar.vertical: ScrollBar { policy: ScrollBar.AlwaysOn } + } + } + + Row { + spacing: container.rowSpacing + + TextArea { + width: 200 + wrapMode: TextEdit.WordWrap + text: "Small - Lorem ipsum dolor sit amet, consectetur adipiscing elit, " + + "sed do eiusmod tempor incididunt ut labore et dolore magna aliqua." + property bool qqc2_style_small + } + + TextArea { + width: 200 + wrapMode: TextEdit.WordWrap + text: "Mini - Lorem ipsum dolor sit amet, consectetur adipiscing elit, " + + "sed do eiusmod tempor incididunt ut labore et dolore magna aliqua." + property bool qqc2_style_mini + } + + TextArea { + placeholderText: "Placeholder text" + } + } +} diff --git a/examples/quickcontrols2/desktopgallery/TextFields.qml b/examples/quickcontrols2/desktopgallery/TextFields.qml new file mode 100644 index 00000000..75f2b720 --- /dev/null +++ b/examples/quickcontrols2/desktopgallery/TextFields.qml @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Layouts 2.15 + +ControlContainer { + id: container + title: "TextFields" + + Row { + spacing: container.rowSpacing + + TextField { + text: "Default" + } + + TextField { + enabled: false + text: "Disabled" + } + + TextField { + placeholderText: "Placeholder text" + } + + TextField { + text: "Small" + property bool qqc2_style_small + } + + TextField { + text: "Mini" + property bool qqc2_style_mini + } + } +} diff --git a/examples/quickcontrols2/desktopgallery/checkbox-icon.png b/examples/quickcontrols2/desktopgallery/checkbox-icon.png Binary files differnew file mode 100644 index 00000000..ee669b3a --- /dev/null +++ b/examples/quickcontrols2/desktopgallery/checkbox-icon.png diff --git a/examples/quickcontrols2/desktopgallery/checkbox-icon16.png b/examples/quickcontrols2/desktopgallery/checkbox-icon16.png Binary files differnew file mode 100644 index 00000000..8d89eab8 --- /dev/null +++ b/examples/quickcontrols2/desktopgallery/checkbox-icon16.png diff --git a/examples/quickcontrols2/desktopgallery/checkbox-icon@2x.png b/examples/quickcontrols2/desktopgallery/checkbox-icon@2x.png Binary files differnew file mode 100644 index 00000000..51c5601d --- /dev/null +++ b/examples/quickcontrols2/desktopgallery/checkbox-icon@2x.png diff --git a/examples/quickcontrols2/desktopgallery/desktopgallery.pro b/examples/quickcontrols2/desktopgallery/desktopgallery.pro new file mode 100644 index 00000000..6c6112db --- /dev/null +++ b/examples/quickcontrols2/desktopgallery/desktopgallery.pro @@ -0,0 +1,41 @@ +QT += quick quickcontrols2 + +SOURCES += \ + main.cpp + +QML_FILES = \ + main.qml \ + ControlContainer.qml \ + Buttons.qml \ + CustomButtons.qml \ + Sliders.qml \ + SlidersSmall.qml \ + SlidersMini.qml \ + CustomSliders.qml \ + CheckBoxes.qml \ + CustomCheckBoxes.qml \ + RadioButtons.qml \ + CustomRadioButtons.qml \ + SpinBoxes.qml \ + CustomSpinBoxes.qml \ + TextFields.qml \ + CustomTextFields.qml \ + Frames.qml \ + CustomFrames.qml \ + TextAreas.qml \ + CustomTextAreas.qml \ + ComboBoxes.qml \ + CustomComboBoxes.qml \ + ScrollBars.qml \ + CustomScrollBars.qml \ + ProgressBars.qml \ + CustomProgressBars.qml \ + Dials.qml \ + CustomDials.qml \ + +OTHER_FILES += $$QML_FILES +RESOURCES += $$QML_FILES +RESOURCES += checkbox-icon.png checkbox-icon16.png checkbox-icon@2x.png + +target.path = $$[QT_INSTALL_EXAMPLES]/quickcontrols2/desktopgallery +INSTALLS += target diff --git a/examples/quickcontrols2/desktopgallery/main.cpp b/examples/quickcontrols2/desktopgallery/main.cpp new file mode 100644 index 00000000..aee43242 --- /dev/null +++ b/examples/quickcontrols2/desktopgallery/main.cpp @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QGuiApplication> +#include <QQmlApplicationEngine> +#include <QtQuickControls2/qquickstyle.h> + +int main(int argc, char *argv[]) +{ + QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); + QGuiApplication app(argc, argv); + + // Todo: rename the "default" style to e.g "simple" + // and use "default" as a phony style name instad + // to mean "get me the default style on the current + // platform". + const QString style = qEnvironmentVariable("DESKTOPGALLERY_STYLE"); + if (!style.isEmpty()) + QQuickStyle::setStyle(style); + else +#if defined(Q_OS_MACOS) + QQuickStyle::setStyle("macOS"); +#elif defined(Q_OS_WINDOWS) + QQuickStyle::setStyle("Windows"); +#else + QQuickStyle::setStyle("FusionDesktop"); +#endif + + QQmlApplicationEngine engine; + const QUrl url(QStringLiteral("qrc:/main.qml")); + QObject::connect(&engine, &QQmlApplicationEngine::objectCreated, + &app, [url](QObject *obj, const QUrl &objUrl) { + if (!obj && url == objUrl) + QCoreApplication::exit(-1); + }, Qt::QueuedConnection); + engine.load(url); + + return app.exec(); +} diff --git a/examples/quickcontrols2/desktopgallery/main.qml b/examples/quickcontrols2/desktopgallery/main.qml new file mode 100644 index 00000000..6cf7a646 --- /dev/null +++ b/examples/quickcontrols2/desktopgallery/main.qml @@ -0,0 +1,125 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.15 +import QtQuick.Window 2.13 +import QtQuick.Controls 2.15 +import QtQuick.Layouts 2.15 + +ApplicationWindow { + visible: true + width: 800 + height: 600 + title: qsTr("Desktop Gallery") + + TabBar { + id: bar + anchors.left: parent.left + anchors.right: parent.right + anchors.margins: 20 + anchors.rightMargin: 40 + + TabButton { + text: qsTr("Default controls") + } + + TabButton { + text: qsTr("Customized controls") + } + } + + StackLayout { + currentIndex: bar.currentIndex + anchors.top: bar.bottom + anchors.bottom: parent.bottom + anchors.left: parent.left + anchors.right: parent.right + anchors.margins: 20 + + ScrollView { + contentWidth: width + clip: true + ScrollBar.vertical.policy: ScrollBar.AlwaysOn + + Column { + anchors.left: parent.left + anchors.right: parent.right + spacing: 20 + + Buttons { } + CheckBoxes { } + RadioButtons { } + SpinBoxes { } + TextFields { } + TextAreas { } + ComboBoxes { } + Dials { } + Frames { } + ProgressBars { } + ScrollBars { } + Sliders { } + SlidersSmall { } + SlidersMini { } + } + } + + ScrollView { + contentWidth: width + clip: true + ScrollBar.vertical.policy: ScrollBar.AlwaysOn + + Column { + anchors.left: parent.left + anchors.right: parent.right + spacing: 20 + + CustomButtons { } + CustomCheckBoxes { } + CustomRadioButtons { } + CustomSpinBoxes { } + CustomTextFields { } + CustomTextAreas { } + CustomComboBoxes { } + CustomDials { } + CustomFrames { } + CustomProgressBars { } + CustomScrollBars { } + CustomSliders { } + } + } + } + +} diff --git a/examples/quickcontrols2/desktopgallery/qml.qrc b/examples/quickcontrols2/desktopgallery/qml.qrc new file mode 100644 index 00000000..fba642e5 --- /dev/null +++ b/examples/quickcontrols2/desktopgallery/qml.qrc @@ -0,0 +1,7 @@ +<RCC> + <qresource prefix="/"> + <file>main.qml</file> + <file>ButtonsTab.qml</file> + <file>SlidersTab.qml</file> + </qresource> +</RCC> diff --git a/src/imports/CMakeLists.txt b/src/imports/CMakeLists.txt index 46b803f7..eb5ec874 100644 --- a/src/imports/CMakeLists.txt +++ b/src/imports/CMakeLists.txt @@ -3,7 +3,14 @@ add_subdirectory(controls) add_subdirectory(platform) add_subdirectory(templates) +add_subdirectory(nativestyle) add_subdirectory(controls/fusion) add_subdirectory(controls/imagine) add_subdirectory(controls/material) add_subdirectory(controls/universal) +if(MACOS) + add_subdirectory(controls/macos) +endif() +if(WIN32) + add_subdirectory(controls/windows) +endif() diff --git a/src/imports/controls/macos/Button.qml b/src/imports/controls/macos/Button.qml new file mode 100644 index 00000000..7db1f9ed --- /dev/null +++ b/src/imports/controls/macos/Button.qml @@ -0,0 +1,43 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.12 +import QtQuick.Controls 2.12 +import QtQuick.Controls.impl 2.12 +import QtQuick.NativeStyle 6.0 as NativeStyle + +NativeStyle.DefaultButton { +} diff --git a/src/imports/controls/macos/CMakeLists.txt b/src/imports/controls/macos/CMakeLists.txt new file mode 100644 index 00000000..211d6871 --- /dev/null +++ b/src/imports/controls/macos/CMakeLists.txt @@ -0,0 +1,102 @@ +# Generated from macos.pro. + +##################################################################### +## qtquickcontrols2macosstyleplugin Plugin: +##################################################################### + +qt_add_qml_module(qtquickcontrols2macosstyleplugin + TARGET_PATH "QtQuick/Controls.2/macOS" + URI "QtQuick.Controls.macOS" + VERSION "2.0" + CLASSNAME QtQuickControls2MacOSStylePlugin + DEPENDENCIES + QtQuick.Controls/2.5 + SKIP_TYPE_REGISTRATION + GENERATE_QMLTYPES + SOURCES + qtquickcontrols2macosstyleplugin.cpp + DEFINES + QT_NO_CAST_FROM_ASCII + QT_NO_CAST_TO_ASCII + LIBRARIES + Qt::CorePrivate + Qt::GuiPrivate + Qt::QmlPrivate + Qt::QuickControls2Private + Qt::QuickPrivate + Qt::QuickTemplates2Private + PUBLIC_LIBRARIES + Qt::Core + Qt::Gui + Qt::Qml + Qt::Quick + Qt::QuickControls2 + Qt::QuickTemplates2 +) + +#### Keys ignored in scope 1:.:.:macos.pro:<TRUE>: +# OTHER_FILES = "qmldir" "$$QML_FILES" +# QML_IMPORT_MAJOR_VERSION = "2" +# QML_IMPORT_NAME = "QtQuick.Controls.macOS" +# TARGETPATH = "QtQuick/Controls.2/macOS" +# _REQUIREMENTS = "qtConfig(quickcontrols2-macos)" + +set(qml_files + "Button.qml" + "Slider.qml" + "GroupBox.qml" + "CheckBox.qml" + "RadioButton.qml" + "SpinBox.qml" + "TextField.qml" + "Frame.qml" + "TextArea.qml" + "ComboBox.qml" + "ScrollBar.qml" + "ProgressBar.qml" + "Dial.qml" +) +set_source_files_properties(Button.qml PROPERTIES + QT_QML_SKIP_QMLDIR_ENTRY TRUE +) +set_source_files_properties(Slider.qml PROPERTIES + QT_QML_SKIP_QMLDIR_ENTRY TRUE +) +set_source_files_properties(GroupBox.qml PROPERTIES + QT_QML_SKIP_QMLDIR_ENTRY TRUE +) +set_source_files_properties(CheckBox.qml PROPERTIES + QT_QML_SKIP_QMLDIR_ENTRY TRUE +) +set_source_files_properties(RadioButton.qml PROPERTIES + QT_QML_SKIP_QMLDIR_ENTRY TRUE +) +set_source_files_properties(SpinBox.qml PROPERTIES + QT_QML_SKIP_QMLDIR_ENTRY TRUE +) +set_source_files_properties(TextField.qml PROPERTIES + QT_QML_SKIP_QMLDIR_ENTRY TRUE +) +set_source_files_properties(Frame.qml PROPERTIES + QT_QML_SKIP_QMLDIR_ENTRY TRUE +) +set_source_files_properties(TextArea.qml PROPERTIES + QT_QML_SKIP_QMLDIR_ENTRY TRUE +) +set_source_files_properties(ComboBox.qml PROPERTIES + QT_QML_SKIP_QMLDIR_ENTRY TRUE +) +set_source_files_properties(ScrollBar.qml PROPERTIES + QT_QML_SKIP_QMLDIR_ENTRY TRUE +) +set_source_files_properties(ProgressBar.qml PROPERTIES + QT_QML_SKIP_QMLDIR_ENTRY TRUE +) +set_source_files_properties(Dial.qml PROPERTIES + QT_QML_SKIP_QMLDIR_ENTRY TRUE +) + +qt6_target_qml_files(qtquickcontrols2macosstyleplugin + FILES + ${qml_files} +) diff --git a/src/imports/controls/macos/CheckBox.qml b/src/imports/controls/macos/CheckBox.qml new file mode 100644 index 00000000..00ab05d6 --- /dev/null +++ b/src/imports/controls/macos/CheckBox.qml @@ -0,0 +1,43 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.12 +import QtQuick.Controls 2.12 +import QtQuick.Controls.impl 2.12 +import QtQuick.NativeStyle 6.0 as NativeStyle + +NativeStyle.DefaultCheckBox { +} diff --git a/src/imports/controls/macos/ComboBox.qml b/src/imports/controls/macos/ComboBox.qml new file mode 100644 index 00000000..e331c401 --- /dev/null +++ b/src/imports/controls/macos/ComboBox.qml @@ -0,0 +1,43 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.12 +import QtQuick.Controls 2.12 +import QtQuick.Controls.impl 2.12 +import QtQuick.NativeStyle 6.0 as NativeStyle + +NativeStyle.DefaultComboBox { +} diff --git a/src/imports/controls/macos/Dial.qml b/src/imports/controls/macos/Dial.qml new file mode 100644 index 00000000..d21bfbed --- /dev/null +++ b/src/imports/controls/macos/Dial.qml @@ -0,0 +1,43 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.12 +import QtQuick.Controls 2.12 +import QtQuick.Controls.impl 2.12 +import QtQuick.NativeStyle 6.0 as NativeStyle + +NativeStyle.DefaultDial { +} diff --git a/src/imports/controls/macos/Frame.qml b/src/imports/controls/macos/Frame.qml new file mode 100644 index 00000000..024e31a2 --- /dev/null +++ b/src/imports/controls/macos/Frame.qml @@ -0,0 +1,43 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.12 +import QtQuick.Controls 2.12 +import QtQuick.Controls.impl 2.12 +import QtQuick.NativeStyle 6.0 as NativeStyle + +NativeStyle.DefaultFrame { +} diff --git a/src/imports/controls/macos/GroupBox.qml b/src/imports/controls/macos/GroupBox.qml new file mode 100644 index 00000000..45911ba1 --- /dev/null +++ b/src/imports/controls/macos/GroupBox.qml @@ -0,0 +1,43 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.12 +import QtQuick.Controls 2.12 +import QtQuick.Controls.impl 2.12 +import QtQuick.NativeStyle 6.0 as NativeStyle + +NativeStyle.DefaultGroupBox { +} diff --git a/src/imports/controls/macos/ProgressBar.qml b/src/imports/controls/macos/ProgressBar.qml new file mode 100644 index 00000000..6c4c9c22 --- /dev/null +++ b/src/imports/controls/macos/ProgressBar.qml @@ -0,0 +1,60 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.12 +import QtQuick.Controls 2.12 +import QtQuick.Controls.impl 2.12 +import QtQuick.Templates 2.12 as T +import QtQuick.NativeStyle 6.0 as NativeStyle + +T.ProgressBar { + id: control + + implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset, + implicitContentWidth + leftPadding + rightPadding) + implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset, + implicitContentHeight + topPadding + bottomPadding) + + font.pixelSize: nativeIndicator ? indicator.styleFont(control).pixelSize : undefined + + background: NativeStyle.ProgressBar { + control: control + // Note: on macOS, the groove will paint both the background and the contents + subControl: NativeStyle.ProgressBar.Groove + useNinePatchImage: false + } + +} diff --git a/src/imports/controls/macos/RadioButton.qml b/src/imports/controls/macos/RadioButton.qml new file mode 100644 index 00000000..6f1bdb04 --- /dev/null +++ b/src/imports/controls/macos/RadioButton.qml @@ -0,0 +1,43 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.12 +import QtQuick.Controls 2.12 +import QtQuick.Controls.impl 2.12 +import QtQuick.NativeStyle 6.0 as NativeStyle + +NativeStyle.DefaultRadioButton { +} diff --git a/src/imports/controls/macos/ScrollBar.qml b/src/imports/controls/macos/ScrollBar.qml new file mode 100644 index 00000000..cfd5735e --- /dev/null +++ b/src/imports/controls/macos/ScrollBar.qml @@ -0,0 +1,43 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.12 +import QtQuick.Controls 2.12 +import QtQuick.Controls.impl 2.12 +import QtQuick.NativeStyle 6.0 as NativeStyle + +NativeStyle.DefaultScrollBar { +} diff --git a/src/imports/controls/macos/Slider.qml b/src/imports/controls/macos/Slider.qml new file mode 100644 index 00000000..3aed431f --- /dev/null +++ b/src/imports/controls/macos/Slider.qml @@ -0,0 +1,43 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.12 +import QtQuick.Controls 2.12 +import QtQuick.Controls.impl 2.12 +import QtQuick.NativeStyle 6.0 as NativeStyle + +NativeStyle.DefaultSlider { +} diff --git a/src/imports/controls/macos/SpinBox.qml b/src/imports/controls/macos/SpinBox.qml new file mode 100644 index 00000000..57b7669d --- /dev/null +++ b/src/imports/controls/macos/SpinBox.qml @@ -0,0 +1,129 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.12 +import QtQuick.Controls 2.12 +import QtQuick.Controls.impl 2.12 +import QtQuick.Templates 2.12 as T +import QtQuick.NativeStyle 6.0 as NativeStyle + +T.SpinBox { + id: control + + property bool nativeBackground: background instanceof NativeStyle.StyleItem + property bool nativeIndicators: true + + Component.onCompleted: { + // Work-around: up.indicator will not emit changed, so we + // need to evaluate nativeIndicators after completed instead. + nativeIndicators = up.indicator.hasOwnProperty("_qt_default") + && down.indicator.hasOwnProperty("_qt_default") + } + + font.pixelSize: nativeBackground ? background.styleFont(control).pixelSize : undefined + + implicitWidth: implicitBackgroundWidth + leftInset + rightInset + implicitHeight: Math.max(implicitBackgroundHeight, up.implicitIndicatorHeight + down.implicitIndicatorHeight) + + topInset + bottomInset + + spacing: 2 + + // Push the background right to make room for the indicators + rightInset: nativeIndicators ? up.implicitIndicatorWidth + spacing : 0 + + leftPadding: nativeBackground ? background.contentPadding.left: 0 + topPadding: nativeBackground ? background.contentPadding.top: 0 + rightPadding: (nativeBackground ? background.contentPadding.right : 0) + rightInset + bottomPadding: nativeBackground ? background.contentPadding.bottom: 0 + + validator: IntValidator { + locale: control.locale.name + bottom: Math.min(control.from, control.to) + top: Math.max(control.from, control.to) + } + + contentItem: TextInput { + text: control.displayText + font: control.font + color: control.palette.text + selectionColor: control.palette.highlight + selectedTextColor: control.palette.highlightedText + horizontalAlignment: Qt.AlignLeft + verticalAlignment: Qt.AlignVCenter + + topPadding: 2 + bottomPadding: 2 + leftPadding: 10 + rightPadding: 10 + + readOnly: !control.editable + validator: control.validator + inputMethodHints: control.inputMethodHints + } + + NativeStyle.SpinBox { + id: upAndDown + control: control + subControl: NativeStyle.SpinBox.Up + visible: nativeIndicators + x: up.indicator.x + y: up.indicator.y + useNinePatchImage: false + } + + up.indicator: Item { + x: parent.width - width + y: (parent.height / 2) - height + implicitWidth: upAndDown.width + implicitHeight: upAndDown.height / 2 + property bool _qt_default + } + + down.indicator: Item { + x: parent.width - width + y: up.indicator.y + upAndDown.height / 2 + implicitWidth: upAndDown.width + implicitHeight: upAndDown.height / 2 + property bool _qt_default + } + + background: NativeStyle.SpinBox { + control: control + subControl: NativeStyle.SpinBox.Frame + contentWidth: contentItem.implicitWidth + contentHeight: contentItem.implicitHeight + } +} diff --git a/src/imports/controls/macos/TextArea.qml b/src/imports/controls/macos/TextArea.qml new file mode 100644 index 00000000..6845c13e --- /dev/null +++ b/src/imports/controls/macos/TextArea.qml @@ -0,0 +1,43 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.12 +import QtQuick.Controls 2.12 +import QtQuick.Controls.impl 2.12 +import QtQuick.NativeStyle 6.0 as NativeStyle + +NativeStyle.DefaultTextArea { +} diff --git a/src/imports/controls/macos/TextField.qml b/src/imports/controls/macos/TextField.qml new file mode 100644 index 00000000..5728689b --- /dev/null +++ b/src/imports/controls/macos/TextField.qml @@ -0,0 +1,43 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.12 +import QtQuick.Controls 2.12 +import QtQuick.Controls.impl 2.12 +import QtQuick.NativeStyle 6.0 as NativeStyle + +NativeStyle.DefaultTextField { +} diff --git a/src/imports/controls/macos/macos.pri b/src/imports/controls/macos/macos.pri new file mode 100644 index 00000000..98474a92 --- /dev/null +++ b/src/imports/controls/macos/macos.pri @@ -0,0 +1,14 @@ +QML_FILES += \ + $$PWD/Button.qml \ + $$PWD/Slider.qml \ + $$PWD/GroupBox.qml \ + $$PWD/CheckBox.qml \ + $$PWD/RadioButton.qml \ + $$PWD/SpinBox.qml \ + $$PWD/TextField.qml \ + $$PWD/Frame.qml \ + $$PWD/TextArea.qml \ + $$PWD/ComboBox.qml \ + $$PWD/ScrollBar.qml \ + $$PWD/ProgressBar.qml \ + $$PWD/Dial.qml \ diff --git a/src/imports/controls/macos/macos.pro b/src/imports/controls/macos/macos.pro new file mode 100644 index 00000000..b8b2c19c --- /dev/null +++ b/src/imports/controls/macos/macos.pro @@ -0,0 +1,28 @@ +TARGET = qtquickcontrols2macosstyleplugin +TARGETPATH = QtQuick/Controls.2/macOS + +QML_IMPORT_NAME = QtQuick.Controls.macOS +QML_IMPORT_MAJOR_VERSION = 2 + +QT += qml quick quickcontrols2 quicktemplates2 +QT_PRIVATE += core-private gui-private qml-private quick-private quicktemplates2-private quickcontrols2-private + +DEFINES += QT_NO_CAST_TO_ASCII QT_NO_CAST_FROM_ASCII + +include(macos.pri) + +OTHER_FILES += \ + qmldir \ + $$QML_FILES + +SOURCES += \ + $$PWD/qtquickcontrols2macosstyleplugin.cpp + +CONFIG += no_cxx_module install_qml_files builtin_resources qtquickcompiler +CONFIG += qmltypes install_qmltypes + +load(qml_plugin) + +requires(qtConfig(quickcontrols2-macos)) + +HEADERS += diff --git a/src/imports/controls/macos/qmldir b/src/imports/controls/macos/qmldir new file mode 100644 index 00000000..acc73818 --- /dev/null +++ b/src/imports/controls/macos/qmldir @@ -0,0 +1,4 @@ +module QtQuick.Controls.macOS +plugin qtquickcontrols2macosstyleplugin +classname QtQuickControls2MacOSStylePlugin +depends QtQuick.Controls 2.5 diff --git a/src/imports/controls/macos/qtquickcontrols2macosstyleplugin.cpp b/src/imports/controls/macos/qtquickcontrols2macosstyleplugin.cpp new file mode 100644 index 00000000..268eb026 --- /dev/null +++ b/src/imports/controls/macos/qtquickcontrols2macosstyleplugin.cpp @@ -0,0 +1,65 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtQml/qqml.h> +#include <QtQuickControls2/private/qquickstyleplugin_p.h> +#include <QtQuickControls2/qquickstyle.h> + +QT_BEGIN_NAMESPACE + +class QtQuickControls2MacOSStylePlugin : public QQuickStylePlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID QQmlExtensionInterface_iid) + +public: + void registerTypes(const char *uri) override; + QString name() const override; +}; + +QString QtQuickControls2MacOSStylePlugin::name() const +{ + return QStringLiteral("macOS"); +} + +void QtQuickControls2MacOSStylePlugin::registerTypes(const char *uri) +{ + Q_UNUSED(uri); +} + +QT_END_NAMESPACE + +#include "qtquickcontrols2macosstyleplugin.moc" diff --git a/src/imports/controls/windows/Button.qml b/src/imports/controls/windows/Button.qml new file mode 100644 index 00000000..a00a3ea4 --- /dev/null +++ b/src/imports/controls/windows/Button.qml @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.12 +import QtQuick.Controls 2.12 +import QtQuick.Controls.impl 2.12 +import QtQuick.Templates 2.12 as T +import QtQuick.NativeStyle 6.0 as NativeStyle + +T.Button { + id: control + + readonly property bool nativeBackground: background instanceof NativeStyle.StyleItem + + // Since QQuickControl will subtract the insets from the control size to + // figure out the background size, we need to reverse that here, otherwise + // the control ends up too big. + implicitWidth: implicitBackgroundWidth + leftInset + rightInset + implicitHeight: implicitBackgroundHeight + topInset + bottomInset + + font.pixelSize: nativeBackground ? background.styleFont(control).pixelSize : undefined + + leftPadding: nativeBackground ? background.contentPadding.left : 5 + rightPadding: nativeBackground ? background.contentPadding.right : 5 + topPadding: nativeBackground ? background.contentPadding.top : 5 + bottomPadding: nativeBackground ? background.contentPadding.bottom : 5 + + background: NativeStyle.Button { + control: control + contentWidth: contentItem.implicitWidth + contentHeight: contentItem.implicitHeight + useNinePatchImage: false + + } + + icon.width: 24 + icon.height: 24 + icon.color: control.checked || control.highlighted ? control.palette.brightText : + control.flat && !control.down ? (control.visualFocus ? control.palette.highlight : control.palette.windowText) : control.palette.buttonText + + contentItem: IconLabel { + spacing: control.spacing + mirrored: control.mirrored + display: control.display + + icon: control.icon + text: control.text + font: control.font + color: control.checked || control.highlighted ? control.palette.brightText : + control.flat && !control.down ? (control.visualFocus ? control.palette.highlight : control.palette.windowText) : control.palette.buttonText + } +} diff --git a/src/imports/controls/windows/CMakeLists.txt b/src/imports/controls/windows/CMakeLists.txt new file mode 100644 index 00000000..0470f370 --- /dev/null +++ b/src/imports/controls/windows/CMakeLists.txt @@ -0,0 +1,90 @@ +# Generated from windows.pro. + +##################################################################### +## qtquickcontrols2windowsstyleplugin Plugin: +##################################################################### + +qt_add_qml_module(qtquickcontrols2windowsstyleplugin + TARGET_PATH "QtQuick/Controls.2/Windows" + URI "QtQuick.Controls.Windows" + VERSION "6.0" + CLASSNAME QtQuickControls2WindowsStylePlugin + DEPENDENCIES + QtQuick.Controls/2.5 + SKIP_TYPE_REGISTRATION + GENERATE_QMLTYPES + SOURCES + qtquickcontrols2windowsstyleplugin.cpp + DEFINES + QT_NO_CAST_FROM_ASCII + QT_NO_CAST_TO_ASCII + LIBRARIES + Qt::CorePrivate + Qt::GuiPrivate + Qt::QmlPrivate + Qt::QuickControls2Private + Qt::QuickPrivate + Qt::QuickTemplates2Private + PUBLIC_LIBRARIES + Qt::Core + Qt::Gui + Qt::Qml + Qt::Quick + Qt::QuickControls2 + Qt::QuickTemplates2 +) + +#### Keys ignored in scope 1:.:.:windows.pro:<TRUE>: +# OTHER_FILES = "qmldir" "$$QML_FILES" +# QML_IMPORT_MAJOR_VERSION = "6" +# QML_IMPORT_NAME = "QtQuick.Controls.Windows" +# TARGETPATH = "QtQuick/Controls.2/Windows" +# _REQUIREMENTS = "qtConfig(quickcontrols2-windows)" + +set(qml_files + "Button.qml" + "CheckBox.qml" + "ComboBox.qml" + "Frame.qml" + "GroupBox.qml" + "RadioButton.qml" + "Slider.qml" + "SpinBox.qml" + "TextArea.qml" + "TextField.qml" +) +set_source_files_properties(Button.qml PROPERTIES + QT_QML_SKIP_QMLDIR_ENTRY TRUE +) +set_source_files_properties(CheckBox.qml PROPERTIES + QT_QML_SKIP_QMLDIR_ENTRY TRUE +) +set_source_files_properties(ComboBox.qml PROPERTIES + QT_QML_SKIP_QMLDIR_ENTRY TRUE +) +set_source_files_properties(Frame.qml PROPERTIES + QT_QML_SKIP_QMLDIR_ENTRY TRUE +) +set_source_files_properties(GroupBox.qml PROPERTIES + QT_QML_SKIP_QMLDIR_ENTRY TRUE +) +set_source_files_properties(RadioButton.qml PROPERTIES + QT_QML_SKIP_QMLDIR_ENTRY TRUE +) +set_source_files_properties(Slider.qml PROPERTIES + QT_QML_SKIP_QMLDIR_ENTRY TRUE +) +set_source_files_properties(SpinBox.qml PROPERTIES + QT_QML_SKIP_QMLDIR_ENTRY TRUE +) +set_source_files_properties(TextArea.qml PROPERTIES + QT_QML_SKIP_QMLDIR_ENTRY TRUE +) +set_source_files_properties(TextField.qml PROPERTIES + QT_QML_SKIP_QMLDIR_ENTRY TRUE +) + +qt6_target_qml_files(qtquickcontrols2windowsstyleplugin + FILES + ${qml_files} +) diff --git a/src/imports/controls/windows/CheckBox.qml b/src/imports/controls/windows/CheckBox.qml new file mode 100644 index 00000000..00ab05d6 --- /dev/null +++ b/src/imports/controls/windows/CheckBox.qml @@ -0,0 +1,43 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.12 +import QtQuick.Controls 2.12 +import QtQuick.Controls.impl 2.12 +import QtQuick.NativeStyle 6.0 as NativeStyle + +NativeStyle.DefaultCheckBox { +} diff --git a/src/imports/controls/windows/ComboBox.qml b/src/imports/controls/windows/ComboBox.qml new file mode 100644 index 00000000..e331c401 --- /dev/null +++ b/src/imports/controls/windows/ComboBox.qml @@ -0,0 +1,43 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.12 +import QtQuick.Controls 2.12 +import QtQuick.Controls.impl 2.12 +import QtQuick.NativeStyle 6.0 as NativeStyle + +NativeStyle.DefaultComboBox { +} diff --git a/src/imports/controls/windows/Frame.qml b/src/imports/controls/windows/Frame.qml new file mode 100644 index 00000000..024e31a2 --- /dev/null +++ b/src/imports/controls/windows/Frame.qml @@ -0,0 +1,43 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.12 +import QtQuick.Controls 2.12 +import QtQuick.Controls.impl 2.12 +import QtQuick.NativeStyle 6.0 as NativeStyle + +NativeStyle.DefaultFrame { +} diff --git a/src/imports/controls/windows/GroupBox.qml b/src/imports/controls/windows/GroupBox.qml new file mode 100644 index 00000000..45911ba1 --- /dev/null +++ b/src/imports/controls/windows/GroupBox.qml @@ -0,0 +1,43 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.12 +import QtQuick.Controls 2.12 +import QtQuick.Controls.impl 2.12 +import QtQuick.NativeStyle 6.0 as NativeStyle + +NativeStyle.DefaultGroupBox { +} diff --git a/src/imports/controls/windows/RadioButton.qml b/src/imports/controls/windows/RadioButton.qml new file mode 100644 index 00000000..6f1bdb04 --- /dev/null +++ b/src/imports/controls/windows/RadioButton.qml @@ -0,0 +1,43 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.12 +import QtQuick.Controls 2.12 +import QtQuick.Controls.impl 2.12 +import QtQuick.NativeStyle 6.0 as NativeStyle + +NativeStyle.DefaultRadioButton { +} diff --git a/src/imports/controls/windows/Slider.qml b/src/imports/controls/windows/Slider.qml new file mode 100644 index 00000000..3aed431f --- /dev/null +++ b/src/imports/controls/windows/Slider.qml @@ -0,0 +1,43 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.12 +import QtQuick.Controls 2.12 +import QtQuick.Controls.impl 2.12 +import QtQuick.NativeStyle 6.0 as NativeStyle + +NativeStyle.DefaultSlider { +} diff --git a/src/imports/controls/windows/SpinBox.qml b/src/imports/controls/windows/SpinBox.qml new file mode 100644 index 00000000..57b7669d --- /dev/null +++ b/src/imports/controls/windows/SpinBox.qml @@ -0,0 +1,129 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.12 +import QtQuick.Controls 2.12 +import QtQuick.Controls.impl 2.12 +import QtQuick.Templates 2.12 as T +import QtQuick.NativeStyle 6.0 as NativeStyle + +T.SpinBox { + id: control + + property bool nativeBackground: background instanceof NativeStyle.StyleItem + property bool nativeIndicators: true + + Component.onCompleted: { + // Work-around: up.indicator will not emit changed, so we + // need to evaluate nativeIndicators after completed instead. + nativeIndicators = up.indicator.hasOwnProperty("_qt_default") + && down.indicator.hasOwnProperty("_qt_default") + } + + font.pixelSize: nativeBackground ? background.styleFont(control).pixelSize : undefined + + implicitWidth: implicitBackgroundWidth + leftInset + rightInset + implicitHeight: Math.max(implicitBackgroundHeight, up.implicitIndicatorHeight + down.implicitIndicatorHeight) + + topInset + bottomInset + + spacing: 2 + + // Push the background right to make room for the indicators + rightInset: nativeIndicators ? up.implicitIndicatorWidth + spacing : 0 + + leftPadding: nativeBackground ? background.contentPadding.left: 0 + topPadding: nativeBackground ? background.contentPadding.top: 0 + rightPadding: (nativeBackground ? background.contentPadding.right : 0) + rightInset + bottomPadding: nativeBackground ? background.contentPadding.bottom: 0 + + validator: IntValidator { + locale: control.locale.name + bottom: Math.min(control.from, control.to) + top: Math.max(control.from, control.to) + } + + contentItem: TextInput { + text: control.displayText + font: control.font + color: control.palette.text + selectionColor: control.palette.highlight + selectedTextColor: control.palette.highlightedText + horizontalAlignment: Qt.AlignLeft + verticalAlignment: Qt.AlignVCenter + + topPadding: 2 + bottomPadding: 2 + leftPadding: 10 + rightPadding: 10 + + readOnly: !control.editable + validator: control.validator + inputMethodHints: control.inputMethodHints + } + + NativeStyle.SpinBox { + id: upAndDown + control: control + subControl: NativeStyle.SpinBox.Up + visible: nativeIndicators + x: up.indicator.x + y: up.indicator.y + useNinePatchImage: false + } + + up.indicator: Item { + x: parent.width - width + y: (parent.height / 2) - height + implicitWidth: upAndDown.width + implicitHeight: upAndDown.height / 2 + property bool _qt_default + } + + down.indicator: Item { + x: parent.width - width + y: up.indicator.y + upAndDown.height / 2 + implicitWidth: upAndDown.width + implicitHeight: upAndDown.height / 2 + property bool _qt_default + } + + background: NativeStyle.SpinBox { + control: control + subControl: NativeStyle.SpinBox.Frame + contentWidth: contentItem.implicitWidth + contentHeight: contentItem.implicitHeight + } +} diff --git a/src/imports/controls/windows/TextArea.qml b/src/imports/controls/windows/TextArea.qml new file mode 100644 index 00000000..6845c13e --- /dev/null +++ b/src/imports/controls/windows/TextArea.qml @@ -0,0 +1,43 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.12 +import QtQuick.Controls 2.12 +import QtQuick.Controls.impl 2.12 +import QtQuick.NativeStyle 6.0 as NativeStyle + +NativeStyle.DefaultTextArea { +} diff --git a/src/imports/controls/windows/TextField.qml b/src/imports/controls/windows/TextField.qml new file mode 100644 index 00000000..5728689b --- /dev/null +++ b/src/imports/controls/windows/TextField.qml @@ -0,0 +1,43 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.12 +import QtQuick.Controls 2.12 +import QtQuick.Controls.impl 2.12 +import QtQuick.NativeStyle 6.0 as NativeStyle + +NativeStyle.DefaultTextField { +} diff --git a/src/imports/controls/windows/qmldir b/src/imports/controls/windows/qmldir new file mode 100644 index 00000000..92601cb6 --- /dev/null +++ b/src/imports/controls/windows/qmldir @@ -0,0 +1,4 @@ +module QtQuick.Controls.Windows +plugin qtquickcontrols2windowsstyleplugin +classname QtQuickControls2WindowsStylePlugin +depends QtQuick.Controls 2.5 diff --git a/src/imports/controls/windows/qtquickcontrols2windowsstyleplugin.cpp b/src/imports/controls/windows/qtquickcontrols2windowsstyleplugin.cpp new file mode 100644 index 00000000..6d95e7f6 --- /dev/null +++ b/src/imports/controls/windows/qtquickcontrols2windowsstyleplugin.cpp @@ -0,0 +1,65 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtQml/qqml.h> +#include <QtQuickControls2/private/qquickstyleplugin_p.h> +#include <QtQuickControls2/qquickstyle.h> + +QT_BEGIN_NAMESPACE + +class QtQuickControls2WindowsStylePlugin : public QQuickStylePlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID QQmlExtensionInterface_iid) + +public: + void registerTypes(const char *uri) override; + QString name() const override; +}; + +QString QtQuickControls2WindowsStylePlugin::name() const +{ + return QStringLiteral("Windows"); +} + +void QtQuickControls2WindowsStylePlugin::registerTypes(const char *uri) +{ + Q_UNUSED(uri); +} + +QT_END_NAMESPACE + +#include "qtquickcontrols2windowsstyleplugin.moc" diff --git a/src/imports/controls/windows/windows.pri b/src/imports/controls/windows/windows.pri new file mode 100644 index 00000000..f60f9e03 --- /dev/null +++ b/src/imports/controls/windows/windows.pri @@ -0,0 +1,11 @@ +QML_FILES += \ + $$PWD/Button.qml \ + $$PWD/CheckBox.qml \ + $$PWD/ComboBox.qml \ + $$PWD/Frame.qml \ + $$PWD/GroupBox.qml \ + $$PWD/RadioButton.qml \ + $$PWD/Slider.qml \ + $$PWD/SpinBox.qml \ + $$PWD/TextArea.qml \ + $$PWD/TextField.qml \ diff --git a/src/imports/controls/windows/windows.pro b/src/imports/controls/windows/windows.pro new file mode 100644 index 00000000..d78690ec --- /dev/null +++ b/src/imports/controls/windows/windows.pro @@ -0,0 +1,28 @@ +TARGET = qtquickcontrols2windowsstyleplugin +TARGETPATH = QtQuick/Controls.2/Windows + +QML_IMPORT_NAME = QtQuick.Controls.Windows +QML_IMPORT_MAJOR_VERSION = 6 + +QT += qml quick quickcontrols2 quicktemplates2 +QT_PRIVATE += core-private gui-private qml-private quick-private quicktemplates2-private quickcontrols2-private + +DEFINES += QT_NO_CAST_TO_ASCII QT_NO_CAST_FROM_ASCII + +include(windows.pri) + +OTHER_FILES += \ + qmldir \ + $$QML_FILES + +SOURCES += \ + $$PWD/qtquickcontrols2windowsstyleplugin.cpp + +CONFIG += no_cxx_module install_qml_files builtin_resources qtquickcompiler +CONFIG += qmltypes install_qmltypes + +load(qml_plugin) + +requires(qtConfig(quickcontrols2-windows)) + +HEADERS += diff --git a/src/imports/imports.pro b/src/imports/imports.pro index 2345616e..40787b8d 100644 --- a/src/imports/imports.pro +++ b/src/imports/imports.pro @@ -2,10 +2,15 @@ TEMPLATE = subdirs SUBDIRS += \ controls \ platform \ - templates + templates \ + nativestyle SUBDIRS += \ controls/fusion/fusion.pro \ controls/imagine/imagine.pro \ controls/material/material.pro \ controls/universal/universal.pro + +macos: SUBDIRS += controls/macos/macos.pro +win32: SUBDIRS += controls/windows/windows.pro + diff --git a/src/imports/nativestyle/CMakeLists.txt b/src/imports/nativestyle/CMakeLists.txt new file mode 100644 index 00000000..ee972a3b --- /dev/null +++ b/src/imports/nativestyle/CMakeLists.txt @@ -0,0 +1,149 @@ +# Generated from nativestyle.pro. + +##################################################################### +## qtquickcontrols2nativestyleplugin Plugin: +##################################################################### + +qt_add_qml_module(qtquickcontrols2nativestyleplugin + URI "QtQuick.NativeStyle" + VERSION "6.0" + CLASSNAME QtQuickControls2NativeStylePlugin + DEPENDENCIES + QtQuick.Controls/2.5 + GENERATE_QMLTYPES + SOURCES + items/qquickstyleitem.cpp items/qquickstyleitem.h + items/qquickstyleitembutton.cpp items/qquickstyleitembutton.h + items/qquickstyleitemcheckbox.cpp items/qquickstyleitemcheckbox.h + items/qquickstyleitemcombobox.cpp items/qquickstyleitemcombobox.h + items/qquickstyleitemdial.cpp items/qquickstyleitemdial.h + items/qquickstyleitemframe.cpp items/qquickstyleitemframe.h + items/qquickstyleitemgroupbox.cpp items/qquickstyleitemgroupbox.h + items/qquickstyleitemprogressbar.cpp items/qquickstyleitemprogressbar.h + items/qquickstyleitemradiobutton.cpp items/qquickstyleitemradiobutton.h + items/qquickstyleitemscrollbar.cpp items/qquickstyleitemscrollbar.h + items/qquickstyleitemslider.cpp items/qquickstyleitemslider.h + items/qquickstyleitemspinbox.cpp items/qquickstyleitemspinbox.h + items/qquickstyleitemtextarea.cpp items/qquickstyleitemtextarea.h + items/qquickstyleitemtextfield.cpp items/qquickstyleitemtextfield.h + qstyle/qquickcommonstyle.cpp qstyle/qquickcommonstyle.h qstyle/qquickcommonstyle_p.h + qstyle/qquickcommonstylepixmaps_p.h + qstyle/qquickdrawutil.cpp qstyle/qquickdrawutil.h + qstyle/qquicknativestyle.cpp qstyle/qquicknativestyle.h + qstyle/qquickstyle.cpp qstyle/qquickstyle.h qstyle/qquickstyle_p.h + qstyle/qquickstylehelper.cpp qstyle/qquickstylehelper_p.h + qstyle/qquickstyleoption.cpp qstyle/qquickstyleoption.h + qtquickcontrols2nativestyleplugin.cpp + DEFINES + QT_NO_CAST_FROM_ASCII + QT_NO_CAST_TO_ASCII + INCLUDE_DIRECTORIES + items + qstyle + LIBRARIES + Qt::CorePrivate + Qt::GuiPrivate + Qt::QmlPrivate + Qt::QuickControls2Private + Qt::QuickPrivate + Qt::QuickTemplates2Private + PUBLIC_LIBRARIES + Qt::Core + Qt::Gui + Qt::Qml + Qt::Quick + Qt::QuickControls2 + Qt::QuickTemplates2 +) + +#### Keys ignored in scope 1:.:.:nativestyle.pro:<TRUE>: +# OTHER_FILES = "qmldir" "$$QML_FILES" +# QML_IMPORT_MAJOR_VERSION = "6" +# QML_IMPORT_NAME = "QtQuick.NativeStyle" +# TARGETPATH = "QtQuick/NativeStyle" +# _REQUIREMENTS = "qtConfig(quickcontrols2-macos)|qtConfig(quickcontrols2-windows)" + +## Scopes: +##################################################################### + +qt_extend_target(qtquickcontrols2nativestyleplugin CONDITION MACOS + SOURCES + qstyle/mac/qquickmacstyle_mac.mm qstyle/mac/qquickmacstyle_mac_p.h + qstyle/mac/qquickmacstyle_mac_p_p.h + INCLUDE_DIRECTORIES + qstyle/mac + LIBRARIES + ${FWAppKit} +) + +qt_extend_target(qtquickcontrols2nativestyleplugin CONDITION WIN32 + SOURCES + qstyle/windows/qquickwindowsstyle.cpp qstyle/windows/qquickwindowsstyle_p.h + qstyle/windows/qquickwindowsstyle_p_p.h + INCLUDE_DIRECTORIES + qstyle/windows + LIBRARIES + gdi32 + user32 + uxtheme +) + +set(qml_files + "controls/DefaultButton.qml" + "controls/DefaultSlider.qml" + "controls/DefaultGroupBox.qml" + "controls/DefaultCheckBox.qml" + "controls/DefaultRadioButton.qml" + "controls/DefaultSpinBox.qml" + "controls/DefaultTextField.qml" + "controls/DefaultFrame.qml" + "controls/DefaultTextArea.qml" + "controls/DefaultComboBox.qml" + "controls/DefaultScrollBar.qml" + "controls/DefaultProgressBar.qml" + "controls/DefaultDial.qml" +) +set_source_files_properties(controls/DefaultButton.qml PROPERTIES + QT_QML_SOURCE_VERSION "6.0" +) +set_source_files_properties(controls/DefaultSlider.qml PROPERTIES + QT_QML_SOURCE_VERSION "6.0" +) +set_source_files_properties(controls/DefaultGroupBox.qml PROPERTIES + QT_QML_SOURCE_VERSION "6.0" +) +set_source_files_properties(controls/DefaultCheckBox.qml PROPERTIES + QT_QML_SOURCE_VERSION "6.0" +) +set_source_files_properties(controls/DefaultRadioButton.qml PROPERTIES + QT_QML_SOURCE_VERSION "6.0" +) +set_source_files_properties(controls/DefaultSpinBox.qml PROPERTIES + QT_QML_SOURCE_VERSION "6.0" +) +set_source_files_properties(controls/DefaultTextField.qml PROPERTIES + QT_QML_SOURCE_VERSION "6.0" +) +set_source_files_properties(controls/DefaultFrame.qml PROPERTIES + QT_QML_SOURCE_VERSION "6.0" +) +set_source_files_properties(controls/DefaultTextArea.qml PROPERTIES + QT_QML_SOURCE_VERSION "6.0" +) +set_source_files_properties(controls/DefaultComboBox.qml PROPERTIES + QT_QML_SOURCE_VERSION "6.0" +) +set_source_files_properties(controls/DefaultScrollBar.qml PROPERTIES + QT_QML_SOURCE_VERSION "6.0" +) +set_source_files_properties(controls/DefaultProgressBar.qml PROPERTIES + QT_QML_SOURCE_VERSION "6.0" +) +set_source_files_properties(controls/DefaultDial.qml PROPERTIES + QT_QML_SOURCE_VERSION "6.0" +) + +qt6_target_qml_files(qtquickcontrols2nativestyleplugin + FILES + ${qml_files} +) diff --git a/src/imports/nativestyle/controls/DefaultButton.qml b/src/imports/nativestyle/controls/DefaultButton.qml new file mode 100644 index 00000000..d98916ca --- /dev/null +++ b/src/imports/nativestyle/controls/DefaultButton.qml @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.12 +import QtQuick.Controls 2.12 +import QtQuick.Controls.impl 2.12 +import QtQuick.Templates 2.12 as T +import QtQuick.NativeStyle 6.0 as NativeStyle + +T.Button { + id: control + + readonly property bool nativeBackground: background instanceof NativeStyle.StyleItem + + // Since QQuickControl will subtract the insets from the control size to + // figure out the background size, we need to reverse that here, otherwise + // the control ends up too big. + implicitWidth: implicitBackgroundWidth + leftInset + rightInset + implicitHeight: implicitBackgroundHeight + topInset + bottomInset + + font.pixelSize: nativeBackground ? background.styleFont(control).pixelSize : undefined + + leftPadding: nativeBackground ? background.contentPadding.left : 5 + rightPadding: nativeBackground ? background.contentPadding.right : 5 + topPadding: nativeBackground ? background.contentPadding.top : 5 + bottomPadding: nativeBackground ? background.contentPadding.bottom : 5 + + background: NativeStyle.Button { + control: control + contentWidth: contentItem.implicitWidth + contentHeight: contentItem.implicitHeight + } + + icon.width: 24 + icon.height: 24 + icon.color: control.checked || control.highlighted ? control.palette.brightText : + control.flat && !control.down ? (control.visualFocus ? control.palette.highlight : control.palette.windowText) : control.palette.buttonText + + contentItem: IconLabel { + spacing: control.spacing + mirrored: control.mirrored + display: control.display + + icon: control.icon + text: control.text + font: control.font + color: control.checked || control.highlighted ? control.palette.brightText : + control.flat && !control.down ? (control.visualFocus ? control.palette.highlight : control.palette.windowText) : control.palette.buttonText + } +} diff --git a/src/imports/nativestyle/controls/DefaultCheckBox.qml b/src/imports/nativestyle/controls/DefaultCheckBox.qml new file mode 100644 index 00000000..c75e755e --- /dev/null +++ b/src/imports/nativestyle/controls/DefaultCheckBox.qml @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.12 +import QtQuick.Templates 2.12 as T +import QtQuick.Controls 2.12 +import QtQuick.Controls.impl 2.12 +import QtQuick.NativeStyle 6.0 as NativeStyle + +T.CheckBox { + id: control + + readonly property bool nativeIndicator: indicator instanceof NativeStyle.StyleItem + + implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset, + implicitContentWidth + leftPadding + rightPadding) + implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset, + implicitContentHeight + topPadding + bottomPadding, + implicitIndicatorHeight + topPadding + bottomPadding) + + font.pixelSize: nativeIndicator ? indicator.styleFont(control).pixelSize : undefined + + spacing: nativeIndicator ? 0 : 6 + padding: nativeIndicator ? 0 : 6 + + indicator: NativeStyle.CheckBox { + control: control + y: control.topPadding + (control.availableHeight - height) >> 1 + contentWidth: contentItem.implicitWidth + contentHeight: contentItem.implicitHeight + useNinePatchImage: false + } + + contentItem: CheckLabel { + text: control.text + font: control.font + color: control.palette.windowText + + // For some reason, the other styles set padding here (in the delegate), instead of in + // the control above. And they also adjust the indicator position by setting x and y + // explicitly (instead of using insets). So we follow the same pattern to ensure that + // setting a custom contentItem delegate from the app will end up looking the same for + // all styles. But this should probably be fixed for all styles (to make them work the + // same way as e.g Buttons). + leftPadding: { + if (nativeIndicator) + indicator.contentPadding.left + else + indicator && !mirrored ? indicator.width + spacing : 0 + } + + topPadding: nativeIndicator ? indicator.contentPadding.top : 0 + rightPadding: { + if (nativeIndicator) + indicator.contentPadding.right + else + indicator && mirrored ? indicator.width + spacing : 0 + } + } +} diff --git a/src/imports/nativestyle/controls/DefaultComboBox.qml b/src/imports/nativestyle/controls/DefaultComboBox.qml new file mode 100644 index 00000000..ec6fe7c8 --- /dev/null +++ b/src/imports/nativestyle/controls/DefaultComboBox.qml @@ -0,0 +1,135 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.15 +import QtQuick.Window 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Controls.impl 2.15 +import QtQuick.Templates 2.15 as T +import QtQuick.NativeStyle 6.0 as NativeStyle + +T.ComboBox { + id: control + + readonly property bool nativeBackground: background instanceof NativeStyle.StyleItem + + implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset, + implicitContentWidth + leftPadding + rightPadding) + implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset, + implicitContentHeight + topPadding + bottomPadding, + implicitIndicatorHeight + topPadding + bottomPadding) + + font.pixelSize: nativeBackground ? background.styleFont(control).pixelSize : undefined + + leftPadding: nativeBackground ? background.contentPadding.left : 5 + rightPadding: nativeBackground ? background.contentPadding.right : 5 + topPadding: nativeBackground ? background.contentPadding.top : 5 + bottomPadding: nativeBackground ? background.contentPadding.bottom : 5 + + topInset: nativeBackground ? background.insets.top : 0 + bottomInset: nativeBackground ? background.insets.bottom : 0 + leftInset: nativeBackground ? background.insets.left : 0 + rightInset: nativeBackground ? background.insets.right : 0 + + contentItem: T.TextField { + id: textField + + implicitWidth: textField.contentWidth + implicitHeight: textField.contentHeight + text: control.editable ? control.editText : control.displayText + + enabled: control.editable + autoScroll: control.editable + readOnly: control.down + inputMethodHints: control.inputMethodHints + validator: control.validator + selectByMouse: control.selectTextByMouse + + font: control.font + color: control.editable ? control.palette.text : control.palette.buttonText + selectionColor: control.palette.highlight + selectedTextColor: control.palette.highlightedText + verticalAlignment: Text.AlignVCenter + } + + background: NativeStyle.ComboBox { + control: control + contentWidth: contentItem.implicitWidth + contentHeight: contentItem.implicitHeight + } + + delegate: ItemDelegate { + width: ListView.view.width + text: control.textRole ? (Array.isArray(control.model) ? modelData[control.textRole] : model[control.textRole]) : modelData + palette.text: control.palette.text + palette.highlightedText: control.palette.highlightedText + font.weight: control.currentIndex === index ? Font.DemiBold : Font.Normal + highlighted: control.highlightedIndex === index + hoverEnabled: control.hoverEnabled + } + + popup: T.Popup { + readonly property var layoutMargins: control.nativeBackground ? control.background.layoutMargins : null + x: layoutMargins ? layoutMargins.left : 0 + y: control.height - (layoutMargins ? layoutMargins.bottom : 0) + width: control.width - (layoutMargins ? layoutMargins.left + layoutMargins.right : 0) + height: Math.min(contentItem.implicitHeight, control.Window.height - topMargin - bottomMargin) + topMargin: 6 + bottomMargin: 6 + + contentItem: ListView { + clip: true + implicitHeight: contentHeight + model: control.delegateModel + currentIndex: control.highlightedIndex + highlightMoveDuration: 0 + + Rectangle { + z: 10 + width: parent.width + height: parent.height + color: "transparent" + border.color: control.palette.mid + } + + T.ScrollIndicator.vertical: ScrollIndicator { } + } + + background: Rectangle { + color: control.palette.window + } + } +} diff --git a/src/imports/nativestyle/controls/DefaultDial.qml b/src/imports/nativestyle/controls/DefaultDial.qml new file mode 100644 index 00000000..d4fc3b98 --- /dev/null +++ b/src/imports/nativestyle/controls/DefaultDial.qml @@ -0,0 +1,59 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.12 +import QtQuick.Controls 2.12 +import QtQuick.Controls.impl 2.12 +import QtQuick.Templates 2.12 as T +import QtQuick.NativeStyle 6.0 as NativeStyle + +T.Dial { + id: control + + readonly property bool nativeBackground: background instanceof NativeStyle.StyleItem + + implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset, + implicitContentWidth + leftPadding + rightPadding) + implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset, + implicitContentHeight + topPadding + bottomPadding) + + font.pixelSize: nativeBackground ? background.styleFont(control).pixelSize : undefined + + background: NativeStyle.Dial { + control: control + useNinePatchImage: false + } +} diff --git a/src/imports/nativestyle/controls/DefaultFrame.qml b/src/imports/nativestyle/controls/DefaultFrame.qml new file mode 100644 index 00000000..3dffd4d1 --- /dev/null +++ b/src/imports/nativestyle/controls/DefaultFrame.qml @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.12 +import QtQuick.Controls 2.12 +import QtQuick.Controls.impl 2.12 +import QtQuick.Templates 2.12 as T +import QtQuick.NativeStyle 6.0 as NativeStyle + +T.Frame { + id: control + + readonly property bool nativeBackground: background instanceof NativeStyle.StyleItem + + implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset, + contentWidth + leftPadding + rightPadding) + implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset, + contentHeight + topPadding + bottomPadding) + + leftPadding: nativeBackground ? background.contentPadding.left : 12 + rightPadding: nativeBackground ? background.contentPadding.right : 12 + topPadding: nativeBackground ? background.contentPadding.top : 12 + bottomPadding: nativeBackground ? background.contentPadding.bottom : 12 + + background: NativeStyle.Frame { + control: control + contentWidth: control.contentWidth + contentHeight: control.contentHeight + } +} diff --git a/src/imports/nativestyle/controls/DefaultGroupBox.qml b/src/imports/nativestyle/controls/DefaultGroupBox.qml new file mode 100644 index 00000000..0d29fd7d --- /dev/null +++ b/src/imports/nativestyle/controls/DefaultGroupBox.qml @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.12 +import QtQuick.Controls 2.12 +import QtQuick.Controls.impl 2.12 +import QtQuick.Templates 2.12 as T +import QtQuick.NativeStyle 6.0 as NativeStyle + +T.GroupBox { + id: control + + readonly property bool nativeBackground: background instanceof NativeStyle.StyleItem + + implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset, + contentWidth + leftPadding + rightPadding, + implicitLabelWidth + leftPadding + rightPadding) + implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset, + contentHeight + topPadding + bottomPadding) + + font.pixelSize: nativeBackground ? background.styleFont(control).pixelSize : undefined + + label: Rectangle { + color: control.palette.window + property point labelPos : control.nativeBackground + ? background.labelPos + : Qt.point(0,0) + x: labelPos.x + background.x + y: labelPos.y + background.y - groupBoxPadding.top + width: txt.implicitWidth + height: txt.implicitHeight + Text { + id: txt + width: parent.width + height: parent.height + text: control.title + font: control.font + color: control.palette.windowText + elide: Text.ElideRight + verticalAlignment: Text.AlignVCenter + } + } + + leftPadding: nativeBackground ? background.contentPadding.left : 0 + rightPadding: nativeBackground ? background.contentPadding.right : 0 + topPadding: nativeBackground ? background.contentPadding.top : 0 + bottomPadding: nativeBackground ? background.contentPadding.bottom : 0 + + background: NativeStyle.GroupBox { + control: control + + x: groupBoxPadding.left + y: groupBoxPadding.top + width: contentItem.width + control.leftPadding + control.rightPadding - groupBoxPadding.left - groupBoxPadding.right + height: contentItem.height + control.topPadding + control.bottomPadding - groupBoxPadding.top - groupBoxPadding.bottom + + contentWidth: contentItem.implicitWidth + contentHeight: contentItem.implicitHeight + } +} diff --git a/src/imports/nativestyle/controls/DefaultProgressBar.qml b/src/imports/nativestyle/controls/DefaultProgressBar.qml new file mode 100644 index 00000000..e86f9410 --- /dev/null +++ b/src/imports/nativestyle/controls/DefaultProgressBar.qml @@ -0,0 +1,65 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.12 +import QtQuick.Templates 2.12 as T +import QtQuick.Controls 2.12 +import QtQuick.Controls.impl 2.12 +import QtQuick.NativeStyle 6.0 as NativeStyle + +T.ProgressBar { + id: control + + readonly property bool nativeBackground: background instanceof NativeStyle.StyleItem + + implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset, + implicitContentWidth + leftPadding + rightPadding) + implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset, + implicitContentHeight + topPadding + bottomPadding) + + font.pixelSize: nativeIndicator ? indicator.styleFont(control).pixelSize : undefined + + background: NativeStyle.ProgressBar { + control: control + subControl: NativeStyle.ProgressBar.Groove + } + + contentItem: NativeStyle.ProgressBar { + control: control + subControl: NativeStyle.ProgressBar.Contents + useNinePatchImage: false + } +} diff --git a/src/imports/nativestyle/controls/DefaultRadioButton.qml b/src/imports/nativestyle/controls/DefaultRadioButton.qml new file mode 100644 index 00000000..e22e2f2f --- /dev/null +++ b/src/imports/nativestyle/controls/DefaultRadioButton.qml @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.12 +import QtQuick.Templates 2.12 as T +import QtQuick.Controls 2.12 +import QtQuick.Controls.impl 2.12 +import QtQuick.NativeStyle 6.0 as NativeStyle + +T.RadioButton { + id: control + + readonly property bool nativeIndicator: indicator instanceof NativeStyle.StyleItem + + implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset, + implicitContentWidth + leftPadding + rightPadding) + implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset, + implicitContentHeight + topPadding + bottomPadding, + implicitIndicatorHeight + topPadding + bottomPadding) + + font.pixelSize: nativeIndicator ? indicator.styleFont(control).pixelSize : undefined + + spacing: nativeIndicator ? 0 : 6 + padding: nativeIndicator ? 0 : 6 + + indicator: NativeStyle.RadioButton { + control: control + contentWidth: contentItem.implicitWidth + contentHeight: contentItem.implicitHeight + useNinePatchImage: false +// Component.onCompleted: { +// var f = indicator.font(control) +// control.font.pixelSize = f.pixelSize +// print(f) +// } + } + + contentItem: CheckLabel { + text: control.text + font: control.font + color: control.palette.windowText + + // For some reason, the other styles set padding here (in the delegate), instead of in + // the control above. And they also adjust the indicator position by setting x and y + // explicitly (instead of using insets). So we follow the same pattern to ensure that + // setting a custom contentItem delegate from the app will end up looking the same for + // all styles. But this should probably be fixed for all styles (to make them work the + // same way as e.g Buttons). + leftPadding: { + if (nativeIndicator) + indicator.contentPadding.left + else + indicator && !mirrored ? indicator.width + spacing : 0 + } + + rightPadding: { + if (nativeIndicator) + indicator.contentPadding.right + else + indicator && mirrored ? indicator.width + spacing : 0 + } + } +} diff --git a/src/imports/nativestyle/controls/DefaultScrollBar.qml b/src/imports/nativestyle/controls/DefaultScrollBar.qml new file mode 100644 index 00000000..1e563bbc --- /dev/null +++ b/src/imports/nativestyle/controls/DefaultScrollBar.qml @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.12 +import QtQuick.Controls 2.12 +import QtQuick.Controls.impl 2.12 +import QtQuick.Templates 2.12 as T +import QtQuick.NativeStyle 6.0 as NativeStyle + +T.ScrollBar { + id: control + + readonly property bool nativeBackground: background instanceof NativeStyle.StyleItem + + implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset, + implicitContentWidth + leftPadding + rightPadding) + implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset, + implicitContentHeight + topPadding + bottomPadding) + + font.pixelSize: nativeBackground ? background.styleFont(control).pixelSize : undefined + + visible: policy === T.ScrollBar.AlwaysOn || (active && size < 1.0) + + background: NativeStyle.ScrollBar { + control: control + subControl: NativeStyle.ScrollBar.Groove + } + + contentItem: NativeStyle.ScrollBar { + control: control + subControl: NativeStyle.ScrollBar.Handle + width: control.width + } +} diff --git a/src/imports/nativestyle/controls/DefaultSlider.qml b/src/imports/nativestyle/controls/DefaultSlider.qml new file mode 100644 index 00000000..00069137 --- /dev/null +++ b/src/imports/nativestyle/controls/DefaultSlider.qml @@ -0,0 +1,73 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.12 +import QtQuick.Controls 2.12 +import QtQuick.Controls.impl 2.12 +import QtQuick.Templates 2.12 as T +import QtQuick.NativeStyle 6.0 as NativeStyle + +T.Slider { + id: control + + readonly property bool nativeBackground: background instanceof NativeStyle.StyleItem + + implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset, + implicitHandleWidth + leftPadding + rightPadding) + implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset, + implicitHandleHeight + topPadding + bottomPadding) + + font.pixelSize: nativeBackground ? background.styleFont(control).pixelSize : undefined + + background: NativeStyle.Slider { + control: control + subControl: NativeStyle.Slider.Groove + // We normally cannot use a nine patch image for the + // groove if we draw tickmarks (since then the scaling + // would scale the tickmarks too). The groove might + // also use a different background color before, and + // after, the handle. + useNinePatchImage: false + } + + handle: NativeStyle.Slider { + control: control + subControl: NativeStyle.Slider.Handle + x: control.leftPadding + (control.horizontal ? control.visualPosition * (control.availableWidth - width) : (control.availableWidth - width) / 2) + y: control.topPadding + (control.horizontal ? (control.availableHeight - height) / 2 : control.visualPosition * (control.availableHeight - height)) + useNinePatchImage: false + } +} diff --git a/src/imports/nativestyle/controls/DefaultSpinBox.qml b/src/imports/nativestyle/controls/DefaultSpinBox.qml new file mode 100644 index 00000000..11cf345d --- /dev/null +++ b/src/imports/nativestyle/controls/DefaultSpinBox.qml @@ -0,0 +1,109 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.12 +import QtQuick.Controls 2.12 +import QtQuick.Controls.impl 2.12 +import QtQuick.Templates 2.12 as T +import QtQuick.NativeStyle 6.0 as NativeStyle + +T.SpinBox { + id: control + + readonly property bool nativeBackground: background instanceof NativeStyle.StyleItem + + implicitWidth: implicitBackgroundWidth + spacing + up.implicitIndicatorWidth + + leftInset + rightInset + implicitHeight: Math.max(implicitBackgroundHeight, up.implicitIndicatorHeight + down.implicitIndicatorHeight + + (spacing * 3)) + topInset + bottomInset + + font.pixelSize: nativeBackground ? background.styleFont(control).pixelSize : undefined + + spacing: 2 + + leftPadding: (nativeBackground ? background.contentPadding.left: 0) + topPadding: (nativeBackground ? background.contentPadding.top: 0) + rightPadding: (nativeBackground ? background.contentPadding.right : 0) + up.implicitIndicatorWidth + spacing + bottomPadding: (nativeBackground ? background.contentPadding.bottom: 0) + spacing + + validator: IntValidator { + locale: control.locale.name + bottom: Math.min(control.from, control.to) + top: Math.max(control.from, control.to) + } + + contentItem: TextInput { + text: control.displayText + font: font.font + color: control.palette.text + selectionColor: control.palette.highlight + selectedTextColor: control.palette.highlightedText + horizontalAlignment: Qt.AlignLeft + verticalAlignment: Qt.AlignVCenter + + topPadding: 2 + bottomPadding: 2 + leftPadding: 10 + rightPadding: 10 + + readOnly: !control.editable + validator: control.validator + inputMethodHints: control.inputMethodHints + } + + up.indicator: NativeStyle.SpinBox { + control: control + subControl: NativeStyle.SpinBox.Up + x: parent.width - width - spacing + y: (parent.height / 2) - height + useNinePatchImage: false + } + + down.indicator: NativeStyle.SpinBox { + control: control + subControl: NativeStyle.SpinBox.Down + x: up.indicator.x + y: up.indicator.y + up.indicator.height + useNinePatchImage: false + } + + background: NativeStyle.SpinBox { + control: control + subControl: NativeStyle.SpinBox.Frame + contentWidth: contentItem.implicitWidth + contentHeight: contentItem.implicitHeight + } +} diff --git a/src/imports/nativestyle/controls/DefaultTextArea.qml b/src/imports/nativestyle/controls/DefaultTextArea.qml new file mode 100644 index 00000000..29277414 --- /dev/null +++ b/src/imports/nativestyle/controls/DefaultTextArea.qml @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.12 +import QtQuick.Controls 2.12 +import QtQuick.Controls.impl 2.12 +import QtQuick.Templates 2.12 as T +import QtQuick.NativeStyle 6.0 as NativeStyle + +T.TextArea { + id: control + + readonly property bool nativeBackground: background instanceof NativeStyle.StyleItem + + implicitWidth: Math.max(contentWidth + leftPadding + rightPadding, + implicitBackgroundWidth + leftInset + rightInset, + placeholder.implicitWidth + leftPadding + rightPadding) + implicitHeight: Math.max(contentHeight + topPadding + bottomPadding, + implicitBackgroundHeight + topInset + bottomInset, + placeholder.implicitHeight + topPadding + bottomPadding) + + font.pixelSize: nativeBackground ? background.styleFont(control).pixelSize : undefined + + leftPadding: nativeBackground ? background.contentPadding.left : 7 + rightPadding: nativeBackground ? background.contentPadding.right : 7 + topPadding: nativeBackground ? background.contentPadding.top : 3 + bottomPadding: nativeBackground ? background.contentPadding.bottom + 5: 3 + + color: control.palette.text + selectionColor: control.palette.highlight + selectedTextColor: control.palette.highlightedText + placeholderTextColor: Color.transparent(control.color, 0.5) + verticalAlignment: TextInput.AlignTop + + PlaceholderText { + id: placeholder + height: control.height + topPadding: control.topPadding + bottomPadding: control.bottomPadding + leftPadding: control.leftPadding + rightPadding: control.rightPadding + text: control.placeholderText + font: control.font + color: control.placeholderTextColor + verticalAlignment: control.verticalAlignment + visible: !control.length && !control.preeditText && (!control.activeFocus || control.horizontalAlignment !== Qt.AlignHCenter) + elide: Text.ElideRight + renderType: control.renderType + } + + background: NativeStyle.TextArea { + control: control + contentWidth: Math.max(control.contentWidth, placeholder.implicitWidth) + contentHeight: control.contentHeight + } +} diff --git a/src/imports/nativestyle/controls/DefaultTextField.qml b/src/imports/nativestyle/controls/DefaultTextField.qml new file mode 100644 index 00000000..8df069d8 --- /dev/null +++ b/src/imports/nativestyle/controls/DefaultTextField.qml @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.12 +import QtQuick.Controls 2.12 +import QtQuick.Controls.impl 2.12 +import QtQuick.Templates 2.12 as T +import QtQuick.NativeStyle 6.0 as NativeStyle + +T.TextField { + id: control + + readonly property bool nativeBackground: background instanceof NativeStyle.StyleItem + + implicitWidth: implicitBackgroundWidth + leftInset + rightInset + || Math.max(contentWidth, placeholder.implicitWidth) + leftPadding + rightPadding + implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset, + contentHeight + topPadding + bottomPadding, + placeholder.implicitHeight + topPadding + bottomPadding) + + font.pixelSize: nativeBackground ? background.styleFont(control).pixelSize : undefined + + leftPadding: nativeBackground ? background.contentPadding.left: 7 + rightPadding: nativeBackground ? background.contentPadding.right: 7 + topPadding: nativeBackground ? background.contentPadding.top: 3 + bottomPadding: nativeBackground ? background.contentPadding.bottom: 3 + + color: control.palette.text + selectionColor: control.palette.highlight + selectedTextColor: control.palette.highlightedText + placeholderTextColor: Color.transparent(control.color, 0.5) + verticalAlignment: TextInput.AlignTop + + PlaceholderText { + id: placeholder + height: control.height + topPadding: control.topPadding + bottomPadding: control.bottomPadding + leftPadding: control.leftPadding + rightPadding: control.rightPadding + text: control.placeholderText + font: control.font + color: control.placeholderTextColor + verticalAlignment: control.verticalAlignment + visible: !control.length && !control.preeditText && (!control.activeFocus || control.horizontalAlignment !== Qt.AlignHCenter) + elide: Text.ElideRight + renderType: control.renderType + } + + background: NativeStyle.TextField { + control: control + contentWidth: Math.max(control.contentWidth, placeholder.implicitWidth) + contentHeight: control.contentHeight + } +} diff --git a/src/imports/nativestyle/controls/controls.pri b/src/imports/nativestyle/controls/controls.pri new file mode 100644 index 00000000..8675989e --- /dev/null +++ b/src/imports/nativestyle/controls/controls.pri @@ -0,0 +1,14 @@ +QML_FILES += \ + $$PWD/DefaultButton.qml \ + $$PWD/DefaultSlider.qml \ + $$PWD/DefaultGroupBox.qml \ + $$PWD/DefaultCheckBox.qml \ + $$PWD/DefaultRadioButton.qml \ + $$PWD/DefaultSpinBox.qml \ + $$PWD/DefaultTextField.qml \ + $$PWD/DefaultFrame.qml \ + $$PWD/DefaultTextArea.qml \ + $$PWD/DefaultComboBox.qml \ + $$PWD/DefaultScrollBar.qml \ + $$PWD/DefaultProgressBar.qml \ + $$PWD/DefaultDial.qml \ diff --git a/src/imports/nativestyle/items/items.pri b/src/imports/nativestyle/items/items.pri new file mode 100644 index 00000000..f9caca1c --- /dev/null +++ b/src/imports/nativestyle/items/items.pri @@ -0,0 +1,33 @@ +INCLUDEPATH += $$PWD + +HEADERS += \ + $$PWD/qquickstyleitem.h \ + $$PWD/qquickstyleitembutton.h \ + $$PWD/qquickstyleitemgroupbox.h \ + $$PWD/qquickstyleitemcheckbox.h \ + $$PWD/qquickstyleitemradiobutton.h \ + $$PWD/qquickstyleitemslider.h \ + $$PWD/qquickstyleitemspinbox.h \ + $$PWD/qquickstyleitemtextfield.h \ + $$PWD/qquickstyleitemframe.h \ + $$PWD/qquickstyleitemtextarea.h \ + $$PWD/qquickstyleitemcombobox.h \ + $$PWD/qquickstyleitemscrollbar.h \ + $$PWD/qquickstyleitemprogressbar.h \ + $$PWD/qquickstyleitemdial.h \ + +SOURCES += \ + $$PWD/qquickstyleitem.cpp \ + $$PWD/qquickstyleitembutton.cpp \ + $$PWD/qquickstyleitemgroupbox.cpp \ + $$PWD/qquickstyleitemcheckbox.cpp \ + $$PWD/qquickstyleitemradiobutton.cpp \ + $$PWD/qquickstyleitemslider.cpp \ + $$PWD/qquickstyleitemspinbox.cpp \ + $$PWD/qquickstyleitemtextfield.cpp \ + $$PWD/qquickstyleitemframe.cpp \ + $$PWD/qquickstyleitemtextarea.cpp \ + $$PWD/qquickstyleitemcombobox.cpp \ + $$PWD/qquickstyleitemscrollbar.cpp \ + $$PWD/qquickstyleitemprogressbar.cpp \ + $$PWD/qquickstyleitemdial.cpp \ diff --git a/src/imports/nativestyle/items/qquickstyleitem.cpp b/src/imports/nativestyle/items/qquickstyleitem.cpp new file mode 100644 index 00000000..1fd933ea --- /dev/null +++ b/src/imports/nativestyle/items/qquickstyleitem.cpp @@ -0,0 +1,431 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickstyleitem.h" + +#include <QtCore/qscopedvaluerollback.h> + +#include <QtQuick/qsgninepatchnode.h> +#include <QtQuick/private/qquickwindow_p.h> +#include <QtQuick/qquickwindow.h> + +#include <QtQuickTemplates2/private/qquickcontrol_p.h> +#include <QtQuickTemplates2/private/qquickbutton_p.h> + +#include <QtQml/qqml.h> + +#include "qquickstyleitembutton.h" +#include "qquickstylehelper_p.h" + +QT_BEGIN_NAMESPACE + +QDebug operator<<(QDebug debug, const QQuickStyleMargins &padding) +{ + QDebugStateSaver saver(debug); + debug.nospace(); + debug << "StyleMargins("; + debug << padding.left() << ", "; + debug << padding.top() << ", "; + debug << padding.right() << ", "; + debug << padding.bottom(); + debug << ')'; + return debug; +} + +QDebug operator<<(QDebug debug, const StyleItemGeometry &cg) +{ + QDebugStateSaver saver(debug); + debug.nospace(); + debug << "StyleItemGeometry("; + debug << "implicitSize:" << cg.implicitSize << ", "; + debug << "contentRect:" << cg.contentRect << ", "; + debug << "layoutRect:" << cg.layoutRect << ", "; + debug << "minimumSize:" << cg.minimumSize << ", "; + debug << "9patchMargins:" << cg.ninePatchMargins; + debug << ')'; + return debug; +} + +QQuickStyleItem::QQuickStyleItem() +{ + setFlag(QQuickItem::ItemHasContents); +} + +QQuickStyleItem::~QQuickStyleItem() +{ +} + +void QQuickStyleItem::connectToControl() +{ + connect(m_control, &QQuickStyleItem::enabledChanged, this, &QQuickStyleItem::markImageDirty); + connect(m_control, &QQuickItem::activeFocusChanged, this, &QQuickStyleItem::markImageDirty); + connect(m_control, &QQuickStyleItem::windowChanged, this, &QQuickStyleItem::markImageDirty); + connect(window(), &QQuickWindow::activeChanged, this, &QQuickStyleItem::markImageDirty); +} + +void QQuickStyleItem::markImageDirty() +{ + m_dirty.setFlag(DirtyFlag::Image); + polish(); +} + +void QQuickStyleItem::markGeometryDirty() +{ + m_dirty.setFlag(DirtyFlag::Geometry); + polish(); +} + +QSGNode *QQuickStyleItem::updatePaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *) +{ + QSGNinePatchNode *node = static_cast<QSGNinePatchNode *>(oldNode); + if (!node) + node = window()->createNinePatchNode(); + + auto texture = window()->createTextureFromImage(m_paintedImage, QQuickWindow::TextureCanUseAtlas); + + QRectF bounds = boundingRect(); + const qreal scale = window()->devicePixelRatio(); + const QSizeF ninePatchImageSize = m_paintedImage.rect().size() / scale; +#ifdef QT_DEBUG + if (m_debugFlags.testFlag(Unscaled)) { + bounds = QRectF(QPointF(), ninePatchImageSize); + qqc2Debug() << "Setting paint node bounds to size of image:" << bounds; + } +#endif + + QMargins padding = m_useNinePatchImage ? m_styleItemGeometry.ninePatchMargins : QMargins(0, 0, 0, 0); + if (padding.right() == -1) { + // Special case: a right padding of -1 means that + // the image should not scale horizontally. + bounds.setWidth(ninePatchImageSize.width()); + padding.setLeft(0); + padding.setRight(0); + } else if (boundingRect().width() < m_styleItemGeometry.minimumSize.width()) { + // If the item size is smaller that the image, using nine-patch scaling + // ends up wrapping it. In that case we scale the whole image instead. + padding.setLeft(0); + padding.setRight(0); + } + if (padding.bottom() == -1) { + bounds.setHeight(ninePatchImageSize.height()); + padding.setTop(0); + padding.setBottom(0); + } else if (boundingRect().height() < m_styleItemGeometry.minimumSize.height()) { + padding.setTop(0); + padding.setBottom(0); + } + + node->setBounds(bounds); + node->setTexture(texture); + node->setDevicePixelRatio(window()->devicePixelRatio()); + node->setPadding(padding.left(), padding.top(), padding.right(), padding.bottom()); + node->update(); + + return node; +} + +QStyle::State QQuickStyleItem::controlSize(QQuickItem *item) +{ + // TODO: add proper API for small and mini + if (item->metaObject()->indexOfProperty("qqc2_style_small") != -1) + return QStyle::State_Small; + if (item->metaObject()->indexOfProperty("qqc2_style_mini") != -1) + return QStyle::State_Mini; + return QStyle::State_None; +} + +void QQuickStyleItem::initStyleOptionBase(QStyleOption &styleOption) +{ + Q_ASSERT(m_control); + + styleOption.control = const_cast<QQuickItem *>(control<QQuickItem>()); + styleOption.window = window(); + styleOption.palette = QQuickItemPrivate::get(m_control)->palette()->toQPalette(); + styleOption.rect = QRect(QPoint(0, 0), m_styleItemGeometry.minimumSize); + + styleOption.state = QStyle::State_None; + styleOption.state |= controlSize(styleOption.control); + + // Note: not all controls inherit from QQuickControl (e.g QQuickTextField) + if (const auto quickControl = dynamic_cast<QQuickControl *>(m_control.data())) + styleOption.direction = quickControl->isMirrored() ? Qt::RightToLeft : Qt::LeftToRight; + + if (window()) { + if (styleOption.window->isActive()) + styleOption.state |= QStyle::State_Active; + if (m_control->isEnabled()) + styleOption.state |= QStyle::State_Enabled; + if (m_control->hasActiveFocus()) + styleOption.state |= QStyle::State_HasFocus; + if (m_control->isUnderMouse()) + styleOption.state |= QStyle::State_MouseOver; + // Should this depend on the focusReason (e.g. only TabFocus) ? + styleOption.state |= QStyle::State_KeyboardFocusChange; + } + + qqc2Debug() << styleOption; +} + +void QQuickStyleItem::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry) +{ + QQuickItem::geometryChange(newGeometry, oldGeometry); + + // Ensure that we only schedule a new geometry update + // and polish if this geometry change was caused by + // something else than us already updating geometry. + if (!m_polishing) + markGeometryDirty(); +} + +void QQuickStyleItem::updateGeometry() +{ + qqc2DebugHeading("GEOMETRY"); + m_dirty.setFlag(DirtyFlag::Geometry, false); + + const QQuickStyleMargins oldContentPadding = contentPadding(); + const QQuickStyleMargins oldLayoutMargins = layoutMargins(); + + m_styleItemGeometry = calculateGeometry(); + +#ifdef QT_DEBUG + if (m_styleItemGeometry.minimumSize.isEmpty()) + qmlWarning(this) << "minimumSize is empty!"; +#endif + + if (m_styleItemGeometry.implicitSize.isEmpty()) { + // If the item has no contents (or its size is + // empty), we just use the minimum size as implicit size. + m_styleItemGeometry.implicitSize = m_styleItemGeometry.minimumSize; + qqc2Debug() << "implicitSize is empty, using minimumSize instead"; + } + + if (contentPadding() != oldContentPadding) + emit contentPaddingChanged(); + if (layoutMargins() != oldLayoutMargins) + emit layoutMarginsChanged(); + + setImplicitSize(m_styleItemGeometry.implicitSize.width(), m_styleItemGeometry.implicitSize.height()); + + if (!m_useNinePatchImage) + m_styleItemGeometry.minimumSize = size().toSize(); + + qqc2Debug() << m_styleItemGeometry + << "bounding rect:" << boundingRect() + << "layout margins:" << layoutMargins() + << "content padding:" << contentPadding() + << "input content size:" << m_contentSize; +} + +void QQuickStyleItem::paintControlToImage() +{ + qqc2DebugHeading("PAINT"); + if (m_styleItemGeometry.minimumSize.isEmpty()) + return; + + m_dirty.setFlag(DirtyFlag::Image, false); + const qreal scale = window()->devicePixelRatio(); + m_paintedImage = QImage(m_styleItemGeometry.minimumSize * scale, QImage::Format_ARGB32_Premultiplied); + m_paintedImage.setDevicePixelRatio(scale); + m_paintedImage.fill(Qt::transparent); + + QPainter painter(&m_paintedImage); + paintEvent(&painter); + +#ifdef QT_DEBUG + if (m_debugFlags != NoDebug) { + painter.setPen(QColor(255, 0, 0, 255)); + if (m_debugFlags.testFlag(ImageRect)) + painter.drawRect(QRect(QPoint(0, 0), m_paintedImage.size() / scale)); + if (m_debugFlags.testFlag(LayoutRect)) { + const auto m = layoutMargins(); + QRect rect = QRect(QPoint(0, 0), m_paintedImage.size() / scale); + rect.adjust(m.left(), m.top(), -m.right(), -m.bottom()); + painter.drawRect(rect); + } + if (m_debugFlags.testFlag(ContentRect)) { + const auto p = contentPadding(); + QRect rect = QRect(QPoint(0, 0), m_paintedImage.size() / scale); + rect.adjust(p.left(), p.top(), -p.right(), -p.bottom()); + painter.drawRect(rect); + } + if (m_debugFlags.testFlag(InputContentSize)) { + const int offset = 2; + const QPoint p = m_styleItemGeometry.contentRect.topLeft(); + painter.drawLine(p.x() - offset, p.y() - offset, p.x() + m_contentSize.width(), p.y() - offset); + painter.drawLine(p.x() - offset, p.y() - offset, p.x() - offset, p.y() + m_contentSize.height()); + } + if (m_debugFlags.testFlag(NinePatchMargins)) { + const QMargins m = m_styleItemGeometry.ninePatchMargins; + const int w = int(m_paintedImage.rect().width() / scale); + const int h = int(m_paintedImage.rect().height() / scale); + if (m.right() != -1) { + painter.drawLine(m.left(), 0, m.left(), h); + painter.drawLine(w - m.right(), 0, w - m.right(), h); + } + if (m.bottom() != -1) { + painter.drawLine(0, m.top(), w, m.top()); + painter.drawLine(0, h - m.bottom(), w, h - m.bottom()); + } + } + } +#endif + + update(); +} + +void QQuickStyleItem::updatePolish() +{ + QScopedValueRollback<bool> guard(m_polishing, true); + + const bool dirtyGeometry = m_dirty & DirtyFlag::Geometry; + const bool dirtyImage = (m_dirty & DirtyFlag::Image) || (!m_useNinePatchImage && dirtyGeometry); + + if (dirtyGeometry) + updateGeometry(); + if (dirtyImage) + paintControlToImage(); +} + +void QQuickStyleItem::componentComplete() +{ + Q_ASSERT_X(m_control, Q_FUNC_INFO, "You need to assign a value to property 'control'"); + +#ifdef QT_DEBUG + if (!qEnvironmentVariable("QQC2_NATIVESTYLE_DEBUG").isEmpty()) { + // Set objectName to "debug" pluss one or more options separated + // by space to show extra information about this item. + +#define QQC2_DEBUG_FLAG(FLAG) \ + if (name.contains(QString(QLatin1String(#FLAG)).toLower())) m_debugFlags |= FLAG + + const QString name = m_control->objectName().toLower(); + if (name.startsWith(QString(QLatin1String("debug")).toLower())) { + QQC2_DEBUG_FLAG(Output); + QQC2_DEBUG_FLAG(ImageRect); + QQC2_DEBUG_FLAG(ContentRect); + QQC2_DEBUG_FLAG(LayoutRect); + QQC2_DEBUG_FLAG(InputContentSize); + QQC2_DEBUG_FLAG(DontUseNinePatchImage); + QQC2_DEBUG_FLAG(NinePatchMargins); + QQC2_DEBUG_FLAG(Unscaled); + + if (m_debugFlags & (DontUseNinePatchImage + | InputContentSize + | ContentRect + | LayoutRect + | NinePatchMargins)) { + // Some rects will not fit inside the drawn image unless + // we switch off (nine patch) image scaling. + m_debugFlags |= DontUseNinePatchImage; + m_useNinePatchImage = false; + } + + if (m_debugFlags != NoDebug) + qDebug() << "debug options set:" << m_debugFlags; + else + qDebug() << "available debug options:" << DebugFlags(0xFFFF); + } + } +#endif + + QQuickItem::componentComplete(); + connectToControl(); + polish(); +} + +qreal QQuickStyleItem::contentWidth() +{ + return m_contentSize.width(); +} + +void QQuickStyleItem::setContentWidth(qreal contentWidth) +{ + if (qFuzzyCompare(m_contentSize.width(), contentWidth)) + return; + + m_contentSize.setWidth(contentWidth); + markGeometryDirty(); +} + +qreal QQuickStyleItem::contentHeight() +{ + return m_contentSize.height(); +} + +void QQuickStyleItem::setContentHeight(qreal contentHeight) +{ + if (qFuzzyCompare(m_contentSize.height(), contentHeight)) + return; + + m_contentSize.setHeight(contentHeight); + markGeometryDirty(); +} + +QQuickStyleMargins QQuickStyleItem::contentPadding() const +{ + const QRect outerRect(QPoint(0, 0), m_styleItemGeometry.implicitSize); + return QQuickStyleMargins(outerRect, m_styleItemGeometry.contentRect); +} + +QQuickStyleMargins QQuickStyleItem::layoutMargins() const +{ + // ### TODO: layoutRect is currently not being used for anything. But + // eventually this information will be needed by layouts to align the controls + // correctly. This because the images drawn by QStyle are usually a bit bigger + // than the control(frame) itself, to e.g make room for shadow effects + // or focus rects/glow. And this will differ from control to control. The + // layoutRect will then inform where the frame of the control is. + const QRect outerRect(QPoint(0, 0), m_styleItemGeometry.implicitSize); + return QQuickStyleMargins(outerRect, m_styleItemGeometry.layoutRect); +} + +QFont QQuickStyleItem::styleFont(QQuickItem *control) +{ + Q_ASSERT(control); + // Note: This function should be treated as if it was static + // (meaning, don't assume anything in this object to be initialized). + // Resolving the font/font size should be done early on from QML, before we get + // around to calculate geometry and paint. Otherwise we typically need to do it + // all over again when/if the font changes. In practice this means that other + // items in QML that uses a style font, and at the same time, affects our input + // contentSize, cannot wait for this item to be fully constructed before it + // gets the font. So we need to resolve it here and now, even if this + // object might be in a half initialized state (hence also the control + // argument, instead of relying on m_control to be set). + return QGuiApplication::font(); +} + +QT_END_NAMESPACE diff --git a/src/imports/nativestyle/items/qquickstyleitem.h b/src/imports/nativestyle/items/qquickstyleitem.h new file mode 100644 index 00000000..37e9dc3d --- /dev/null +++ b/src/imports/nativestyle/items/qquickstyleitem.h @@ -0,0 +1,273 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKSTYLEITEM_H +#define QQUICKSTYLEITEM_H + +#include <QtCore/qdebug.h> +#include <QtQml/qqml.h> +#include <QtQml/qqmlinfo.h> +#include <QtQuick/private/qquickitem_p.h> +#include <QtQuickTemplates2/private/qquickcontrol_p.h> + +#include "qquicknativestyle.h" +#include "qquickstyle.h" +#include "qquickstyleoption.h" + +// Work-around for now, to avoid creator getting confused +// about missing macros. Should eventually be defined +// in qt declarative somewhere I assume. +#ifndef QML_NAMED_ELEMENT +#define QML_NAMED_ELEMENT(NAME) +#define QML_UNCREATABLE(NAME) +#endif + +#ifdef QT_DEBUG +#define qqc2Debug() if (m_debugFlags.testFlag(Output)) qDebug() << __FUNCTION__ << ":" +#define qqc2DebugHeading(HEADING) if (m_debugFlags.testFlag(Output)) qDebug() << "--------" << HEADING << "--------" +#else +#define qqc2Debug() if (false) qDebug() +#define qqc2DebugHeading(HEADING) if (false) qDebug() +#endif + +QT_BEGIN_NAMESPACE + +using namespace QQC2; + +class QQuickStyleMargins +{ + Q_GADGET + + Q_PROPERTY(int left READ left()) + Q_PROPERTY(int top READ top()) + Q_PROPERTY(int right READ right()) + Q_PROPERTY(int bottom READ bottom()) + + QML_NAMED_ELEMENT(StyleMargins) + +public: + QQuickStyleMargins() {} + QQuickStyleMargins(const QQuickStyleMargins &other) : m_margins(other.m_margins) {} + QQuickStyleMargins(const QMargins &margins) : m_margins(margins) {} + QQuickStyleMargins(const QRect &outer, const QRect &inner) + { + const int left = inner.left() - outer.left(); + const int top = inner.top() - outer.top(); + const int right = outer.right() - inner.right(); + const int bottom = outer.bottom() - inner.bottom(); + m_margins = QMargins(left, top, right, bottom); + } + + inline void operator=(const QQuickStyleMargins &other) { m_margins = other.m_margins; } + inline bool operator==(const QQuickStyleMargins &other) const { return other.m_margins == m_margins; } + inline bool operator!=(const QQuickStyleMargins &other) const { return other.m_margins != m_margins; } + + inline int left() const { return m_margins.left(); } + inline int right() const { return m_margins.right(); } + inline int top() const { return m_margins.top(); } + inline int bottom() const { return m_margins.bottom(); } + + QMargins m_margins; +}; + +QDebug operator<<(QDebug debug, const QQuickStyleMargins &padding); + +struct StyleItemGeometry +{ + /* + A QQuickItem is responsible for drawing a control, or a part of it. + + 'minimumSize' should be the minimum possible size that the item can + have _without_ taking content size into consideration (and still render + correctly). This will also be the size of the image that the item is drawn + to, unless QQuickStyleItem::useNinePatchImage is set to false. In that + case, the size of the image will be set to the size of the item instead + (which is set from QML, and will typically be the same as the size of the control). + The default way to calculate minimumSize is to call style()->sizeFromContents() + with an empty content size. This is not always well supported by the legacy QStyle + implementations, which means that you might e.g get an empty size in return). + For those cases, the correct solution is to go into the specific platform style + and change it so that it returns a valid size also for this special case. + + 'implicitSize' should reflect the preferred size of the item, taking the + given content size (as set from QML) into account. But not all controls + have contents (slider), and for many controls, the content/label is instead + placed outside the item/background image (radiobutton). In both cases, the + size of the item will not include the content size, and implicitSize can + usually be set equal to minimumSize instead. + + 'contentRect' should be the free space where the contents can be placed. Note that + this rect doesn't need to have the same size as the contentSize provided as input + to the style item. Instead, QStyle can typically calculate a rect that is bigger, to + e.g center the contents inside the control. + + 'layoutRect' can be set to shift the position of the whole control so + that aligns correctly with the other controls. This is important for + controls that draws e.g shadows or focus rings. Such adornments should + be painted, but not be included when aligning the controls. + */ + + QSize minimumSize; + QSize implicitSize; + QRect contentRect; + QRect layoutRect; + QMargins ninePatchMargins; +}; + +QDebug operator<<(QDebug debug, const StyleItemGeometry &cg); + +class QQuickStyleItem : public QQuickItem +{ + Q_OBJECT + + // Input + Q_PROPERTY(QQuickItem *control MEMBER m_control) + Q_PROPERTY(qreal contentWidth READ contentWidth WRITE setContentWidth) + Q_PROPERTY(qreal contentHeight READ contentHeight WRITE setContentHeight) + Q_PROPERTY(bool useNinePatchImage MEMBER m_useNinePatchImage) + + // Output + Q_PROPERTY(QQuickStyleMargins contentPadding READ contentPadding() NOTIFY contentPaddingChanged) + Q_PROPERTY(QQuickStyleMargins layoutMargins READ layoutMargins() NOTIFY layoutMarginsChanged) + + QML_NAMED_ELEMENT(StyleItem) + QML_UNCREATABLE("StyleItem is an abstract base class.") + +public: + enum DirtyFlag { + Nothing = 0, + Geometry, + Image, + Everything = 255 + }; + Q_DECLARE_FLAGS(DirtyFlags, DirtyFlag) + +#ifdef QT_DEBUG + enum DebugFlag { + NoDebug = 0x00, + Output = 0x01, + ImageRect = 0x02, + ContentRect = 0x04, + LayoutRect = 0x08, + Unscaled = 0x10, + InputContentSize = 0x20, + DontUseNinePatchImage = 0x40, + NinePatchMargins = 0x80 + }; + Q_FLAG(DebugFlag) + Q_DECLARE_FLAGS(DebugFlags, DebugFlag) +#endif + + QQuickStyleItem(); + ~QQuickStyleItem() override; + + qreal contentWidth(); + void setContentWidth(qreal contentWidth); + qreal contentHeight(); + void setContentHeight(qreal contentHeight); + + QQuickStyleMargins contentPadding() const; + QQuickStyleMargins layoutMargins() const; + + Q_INVOKABLE virtual QFont styleFont(QQuickItem *control); + + void markGeometryDirty(); + void markImageDirty(); + +signals: + void contentPaddingChanged(); + void layoutMarginsChanged(); + void fontChanged(); + +protected: + void componentComplete() override; + QSGNode *updatePaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *updatePaintNodeData) override; + void geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry) override; + void updatePolish() override; + + virtual void connectToControl(); + virtual void paintEvent(QPainter *painter) = 0; + virtual StyleItemGeometry calculateGeometry() = 0; + + static QStyle::State controlSize(QQuickItem *item); + void initStyleOptionBase(QStyleOption &styleOption); + + inline QSize contentSize() { return m_contentSize.toSize(); } + inline static QStyle *style() { return QQuickNativeStyle::style(); } + + template <class T> inline const T* control() const { +#ifdef QT_DEBUG + if (!dynamic_cast<T *>(m_control.data())) { + qmlWarning(this) << "control property is not of correct type"; + Q_UNREACHABLE(); + } +#endif + return static_cast<T *>(m_control.data()); + } + +#ifdef QT_DEBUG + DebugFlags m_debugFlags = NoDebug; +#endif + +private: + inline void updateGeometry(); + inline void paintControlToImage(); + +private: + QPointer<QQuickItem> m_control; + QImage m_paintedImage; + StyleItemGeometry m_styleItemGeometry; + QSizeF m_contentSize; + + DirtyFlags m_dirty = Everything; + bool m_useNinePatchImage = true; + bool m_polishing = false; + +private: + friend class QtQuickControls2MacOSStylePlugin; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QQuickStyleItem::DirtyFlags) + +#ifdef QT_DEBUG +Q_DECLARE_OPERATORS_FOR_FLAGS(QQuickStyleItem::DebugFlags) +#endif + +QML_DECLARE_TYPE(QQuickStyleItem) + +QT_END_NAMESPACE + +#endif // QQUICKSTYLEITEM_H diff --git a/src/imports/nativestyle/items/qquickstyleitembutton.cpp b/src/imports/nativestyle/items/qquickstyleitembutton.cpp new file mode 100644 index 00000000..8e45cc69 --- /dev/null +++ b/src/imports/nativestyle/items/qquickstyleitembutton.cpp @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickstyleitembutton.h" + +QFont QQuickStyleItemButton::styleFont(QQuickItem *control) +{ + return style()->font(QStyle::CE_PushButtonLabel, controlSize(control)); +} + +void QQuickStyleItemButton::connectToControl() +{ + QQuickStyleItem::connectToControl(); + auto button = control<QQuickButton>(); + connect(button, &QQuickButton::downChanged, this, &QQuickStyleItem::markImageDirty); +} + +StyleItemGeometry QQuickStyleItemButton::calculateGeometry() +{ + QStyleOptionButton styleOption; + initStyleOption(styleOption); + StyleItemGeometry geometry; + + geometry.minimumSize = style()->sizeFromContents(QStyle::CT_PushButton, &styleOption, QSize(0, 0)); + geometry.implicitSize = style()->sizeFromContents(QStyle::CT_PushButton, &styleOption, contentSize()); + styleOption.rect = QRect(QPoint(0, 0), geometry.implicitSize); + geometry.contentRect = style()->subElementRect(QStyle::SE_PushButtonContents, &styleOption); + geometry.layoutRect = style()->subElementRect(QStyle::SE_PushButtonLayoutItem, &styleOption); + geometry.ninePatchMargins = style()->ninePatchMargins(QStyle::CE_PushButtonBevel, &styleOption, geometry.minimumSize); + + return geometry; +} + +void QQuickStyleItemButton::paintEvent(QPainter *painter) +{ + QStyleOptionButton styleOption; + initStyleOption(styleOption); + style()->drawControl(QStyle::CE_PushButtonBevel, &styleOption, painter); +} + +void QQuickStyleItemButton::initStyleOption(QStyleOptionButton &styleOption) +{ + initStyleOptionBase(styleOption); + auto button = control<QQuickButton>(); + + if (button->isDown()) + styleOption.state |= QStyle::State_Sunken; + if (!button->isFlat() && !button->isDown()) + styleOption.state |= QStyle::State_Raised; + if (button->isHighlighted() || button->isChecked()) + styleOption.state |= QStyle::State_On; + if (button->isFlat()) + styleOption.features |= QStyleOptionButton::Flat; +} diff --git a/src/imports/nativestyle/items/qquickstyleitembutton.h b/src/imports/nativestyle/items/qquickstyleitembutton.h new file mode 100644 index 00000000..e23601a7 --- /dev/null +++ b/src/imports/nativestyle/items/qquickstyleitembutton.h @@ -0,0 +1,60 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKSTYLEITEMBUTTON_H +#define QQUICKSTYLEITEMBUTTON_H + +#include "qquickstyleitem.h" +#include <QtQuickTemplates2/private/qquickbutton_p.h> + +class QQuickStyleItemButton : public QQuickStyleItem +{ + Q_OBJECT + QML_NAMED_ELEMENT(Button) + +public: + QFont styleFont(QQuickItem *control) override; + +protected: + void connectToControl() override; + void paintEvent(QPainter *painter) override; + StyleItemGeometry calculateGeometry() override; + +private: + void initStyleOption(QStyleOptionButton &styleOption); +}; + +#endif // QQUICKSTYLEITEMBUTTON_H diff --git a/src/imports/nativestyle/items/qquickstyleitemcheckbox.cpp b/src/imports/nativestyle/items/qquickstyleitemcheckbox.cpp new file mode 100644 index 00000000..e5ee9133 --- /dev/null +++ b/src/imports/nativestyle/items/qquickstyleitemcheckbox.cpp @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickstyleitemcheckbox.h" + +QFont QQuickStyleItemCheckBox::styleFont(QQuickItem *control) +{ + return style()->font(QStyle::CE_RadioButtonLabel, controlSize(control)); +} + +void QQuickStyleItemCheckBox::connectToControl() +{ + QQuickStyleItem::connectToControl(); + auto checkbox = control<QQuickCheckBox>(); + connect(checkbox, &QQuickCheckBox::downChanged, this, &QQuickStyleItem::markImageDirty); + connect(checkbox, &QQuickCheckBox::checkStateChanged, this, &QQuickStyleItem::markImageDirty); +} + +StyleItemGeometry QQuickStyleItemCheckBox::calculateGeometry() +{ + QStyleOptionButton styleOption; + initStyleOption(styleOption); + StyleItemGeometry geometry; + + geometry.minimumSize = style()->sizeFromContents(QStyle::CT_CheckBox, &styleOption, QSize(0, 0)); + geometry.implicitSize = geometry.minimumSize; + styleOption.rect = QRect(QPoint(0, 0), geometry.implicitSize); + geometry.contentRect = style()->subElementRect(QStyle::SE_CheckBoxContents, &styleOption); + geometry.layoutRect = style()->subElementRect(QStyle::SE_CheckBoxLayoutItem, &styleOption); + geometry.ninePatchMargins = style()->ninePatchMargins(QStyle::CE_CheckBox, &styleOption, geometry.minimumSize); + + // Spacing seems to already be baked into SE_CheckBoxContents, so ignore until needed + //const int space = style()->pixelMetric(QStyle::PM_CheckBoxLabelSpacing, &styleOption); + + return geometry; +} + +void QQuickStyleItemCheckBox::paintEvent(QPainter *painter) +{ + QStyleOptionButton styleOption; + initStyleOption(styleOption); + style()->drawControl(QStyle::CE_CheckBox, &styleOption, painter); +} + +void QQuickStyleItemCheckBox::initStyleOption(QStyleOptionButton &styleOption) +{ + initStyleOptionBase(styleOption); + auto checkbox = control<QQuickCheckBox>(); + + styleOption.state |= checkbox->isDown() ? QStyle::State_Sunken : QStyle::State_Raised; + if (checkbox->isTristate() && checkbox->checkState() == Qt::PartiallyChecked) + styleOption.state |= QStyle::State_NoChange; + else + styleOption.state |= checkbox->isChecked() ? QStyle::State_On : QStyle::State_Off; +} diff --git a/src/imports/nativestyle/items/qquickstyleitemcheckbox.h b/src/imports/nativestyle/items/qquickstyleitemcheckbox.h new file mode 100644 index 00000000..a8ccda2c --- /dev/null +++ b/src/imports/nativestyle/items/qquickstyleitemcheckbox.h @@ -0,0 +1,60 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKSTYLEITEMCHECKBOX_H +#define QQUICKSTYLEITEMCHECKBOX_H + +#include "qquickstyleitem.h" +#include <QtQuickTemplates2/private/qquickcheckbox_p.h> + +class QQuickStyleItemCheckBox : public QQuickStyleItem +{ + Q_OBJECT + QML_NAMED_ELEMENT(CheckBox) + +public: + QFont styleFont(QQuickItem *control) override; + +protected: + void connectToControl() override; + void paintEvent(QPainter *painter) override; + StyleItemGeometry calculateGeometry() override; + +private: + void initStyleOption(QStyleOptionButton &styleOption); +}; + +#endif // QQUICKSTYLEITEMCHECKBOX_H diff --git a/src/imports/nativestyle/items/qquickstyleitemcombobox.cpp b/src/imports/nativestyle/items/qquickstyleitemcombobox.cpp new file mode 100644 index 00000000..c9b7fca1 --- /dev/null +++ b/src/imports/nativestyle/items/qquickstyleitemcombobox.cpp @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickstyleitemcombobox.h" + +QFont QQuickStyleItemComboBox::styleFont(QQuickItem *control) +{ + return style()->font(QStyle::CE_PushButtonLabel, controlSize(control)); +} + +void QQuickStyleItemComboBox::connectToControl() +{ + QQuickStyleItem::connectToControl(); + auto comboBox = control<QQuickComboBox>(); + connect(comboBox, &QQuickComboBox::downChanged, this, &QQuickStyleItem::markImageDirty); +} + +StyleItemGeometry QQuickStyleItemComboBox::calculateGeometry() +{ + QStyleOptionComboBox styleOption; + initStyleOption(styleOption); + StyleItemGeometry geometry; + + geometry.minimumSize = style()->sizeFromContents(QStyle::CT_ComboBox, &styleOption, QSize(0, 0)); + geometry.implicitSize = style()->sizeFromContents(QStyle::CT_ComboBox, &styleOption, contentSize()); + styleOption.rect = QRect(QPoint(0, 0), geometry.implicitSize); + geometry.contentRect = style()->subControlRect(QStyle::CC_ComboBox, &styleOption, QStyle::SC_ComboBoxEditField); + geometry.layoutRect = style()->subElementRect(QStyle::SE_ComboBoxLayoutItem, &styleOption); + geometry.ninePatchMargins = style()->ninePatchMargins(QStyle::CC_ComboBox, &styleOption, geometry.minimumSize); + + return geometry; +} + +void QQuickStyleItemComboBox::paintEvent(QPainter *painter) +{ + QStyleOptionComboBox styleOption; + initStyleOption(styleOption); + style()->drawComplexControl(QStyle::CC_ComboBox, &styleOption, painter); +} + +void QQuickStyleItemComboBox::initStyleOption(QStyleOptionComboBox &styleOption) +{ + initStyleOptionBase(styleOption); + auto comboBox = control<QQuickComboBox>(); + + styleOption.subControls = QStyle::SC_ComboBoxFrame | QStyle::SC_ComboBoxArrow;// | QStyle::SC_ComboBoxEditField | QStyle::SC_ComboBoxArrow; + styleOption.frame = true; + styleOption.state |= QStyle::State_Selected; + styleOption.editable = comboBox->isEditable(); + + if (comboBox->isDown()) + styleOption.state |= QStyle::State_Sunken; + if (!comboBox->isFlat() && !comboBox->isDown()) + styleOption.state |= QStyle::State_Raised; +} diff --git a/src/imports/nativestyle/items/qquickstyleitemcombobox.h b/src/imports/nativestyle/items/qquickstyleitemcombobox.h new file mode 100644 index 00000000..f446b191 --- /dev/null +++ b/src/imports/nativestyle/items/qquickstyleitemcombobox.h @@ -0,0 +1,60 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKSTYLEITEMCOMBOBOX_H +#define QQUICKSTYLEITEMCOMBOBOX_H + +#include "qquickstyleitem.h" +#include <QtQuickTemplates2/private/qquickcombobox_p.h> + +class QQuickStyleItemComboBox : public QQuickStyleItem +{ + Q_OBJECT + QML_NAMED_ELEMENT(ComboBox) + +public: + QFont styleFont(QQuickItem *control) override; + +protected: + void connectToControl() override; + void paintEvent(QPainter *painter) override; + StyleItemGeometry calculateGeometry() override; + +private: + void initStyleOption(QStyleOptionComboBox &styleOption); +}; + +#endif // QQUICKSTYLEITEMCOMBOBOX_H diff --git a/src/imports/nativestyle/items/qquickstyleitemdial.cpp b/src/imports/nativestyle/items/qquickstyleitemdial.cpp new file mode 100644 index 00000000..f214fb2e --- /dev/null +++ b/src/imports/nativestyle/items/qquickstyleitemdial.cpp @@ -0,0 +1,110 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickstyleitemdial.h" + +QFont QQuickStyleItemDial::styleFont(QQuickItem *control) +{ + return style()->font(QStyle::CE_ProgressBarLabel, controlSize(control)); +} + +void QQuickStyleItemDial::connectToControl() +{ + QQuickStyleItem::connectToControl(); + auto dial = control<QQuickDial>(); + connect(dial, &QQuickDial::fromChanged, this, &QQuickStyleItem::markImageDirty); + connect(dial, &QQuickDial::toChanged, this, &QQuickStyleItem::markImageDirty); + connect(dial, &QQuickDial::positionChanged, this, &QQuickStyleItem::markImageDirty); + connect(dial, &QQuickDial::valueChanged, this, &QQuickStyleItem::markImageDirty); + connect(dial, &QQuickDial::stepSizeChanged, this, &QQuickStyleItem::markImageDirty); + connect(dial, &QQuickDial::pressedChanged, this, &QQuickStyleItem::markImageDirty); +} + +StyleItemGeometry QQuickStyleItemDial::calculateGeometry() +{ + QStyleOptionSlider styleOption; + initStyleOption(styleOption); + + StyleItemGeometry geometry; + geometry.minimumSize = style()->sizeFromContents(QStyle::CT_Dial, &styleOption, QSize(0, 0)); + geometry.implicitSize = geometry.minimumSize; + geometry.layoutRect = style()->subElementRect(QStyle::SE_SliderLayoutItem, &styleOption); + geometry.ninePatchMargins = style()->ninePatchMargins(QStyle::CC_Dial, &styleOption, geometry.minimumSize); + + return geometry; +} + +void QQuickStyleItemDial::paintEvent(QPainter *painter) +{ + QStyleOptionSlider styleOption; + initStyleOption(styleOption); + style()->drawComplexControl(QStyle::CC_Dial, &styleOption, painter); +} + +void QQuickStyleItemDial::initStyleOption(QStyleOptionSlider &styleOption) +{ + initStyleOptionBase(styleOption); + auto dial = control<QQuickDial>(); + + styleOption.subControls = QStyle::SC_SliderGroove | QStyle::SC_SliderHandle; + styleOption.activeSubControls = QStyle::SC_None; + styleOption.tickInterval = dial->stepSize(); + styleOption.dialWrapping = dial->wrap(); + styleOption.upsideDown = true; + + if (dial->isPressed()) + styleOption.state |= QStyle::State_Sunken; + + if (dial->stepSize() == 0) { + styleOption.minimum = 0; + styleOption.maximum = 10000; + styleOption.sliderPosition = dial->position() * styleOption.maximum; + } else { + styleOption.minimum = dial->from(); + styleOption.maximum = dial->to(); + styleOption.sliderPosition = dial->value(); + } + + // TODO: add proper API for tickmarks + const int index = dial->metaObject()->indexOfProperty("qqc2_style_tickPosition"); + if (index != -1) { + const int tickPosition = dial->metaObject()->property(index).read(dial).toInt(); + styleOption.tickPosition = QStyleOptionSlider::TickPosition(tickPosition); + if (styleOption.tickPosition != QStyleOptionSlider::NoTicks) + styleOption.subControls |= QStyle::SC_DialTickmarks; + } + +} diff --git a/src/imports/nativestyle/items/qquickstyleitemdial.h b/src/imports/nativestyle/items/qquickstyleitemdial.h new file mode 100644 index 00000000..3eefa734 --- /dev/null +++ b/src/imports/nativestyle/items/qquickstyleitemdial.h @@ -0,0 +1,60 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKSTYLEITEMDIAL_H +#define QQUICKSTYLEITEMDIAL_H + +#include "qquickstyleitem.h" +#include <QtQuickTemplates2/private/qquickdial_p.h> + +class QQuickStyleItemDial : public QQuickStyleItem +{ + Q_OBJECT + QML_NAMED_ELEMENT(Dial) + +public: + QFont styleFont(QQuickItem *control) override; + +protected: + void connectToControl() override; + void paintEvent(QPainter *painter) override; + StyleItemGeometry calculateGeometry() override; + +private: + void initStyleOption(QStyleOptionSlider &styleOption); +}; + +#endif // QQUICKSTYLEITEMDIAL_H diff --git a/src/imports/nativestyle/items/qquickstyleitemframe.cpp b/src/imports/nativestyle/items/qquickstyleitemframe.cpp new file mode 100644 index 00000000..42a6c231 --- /dev/null +++ b/src/imports/nativestyle/items/qquickstyleitemframe.cpp @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickstyleitemframe.h" + +StyleItemGeometry QQuickStyleItemFrame::calculateGeometry() +{ + QStyleOptionFrame styleOption; + initStyleOption(styleOption); + StyleItemGeometry geometry; + + geometry.minimumSize = style()->sizeFromContents(QStyle::CT_Frame, &styleOption, QSize(0, 0)); + geometry.implicitSize = contentSize(); + styleOption.rect = QRect(QPoint(0, 0), geometry.implicitSize); + geometry.contentRect = style()->subElementRect(QStyle::SE_FrameContents, &styleOption); + geometry.ninePatchMargins = style()->ninePatchMargins(QStyle::CE_ShapedFrame, &styleOption, geometry.minimumSize); + + return geometry; +} + +void QQuickStyleItemFrame::paintEvent(QPainter *painter) +{ + QStyleOptionFrame styleOption; + initStyleOption(styleOption); + style()->drawControl(QStyle::CE_ShapedFrame, &styleOption, painter); +} + +void QQuickStyleItemFrame::initStyleOption(QStyleOptionFrame &styleOption) +{ + initStyleOptionBase(styleOption); + styleOption.lineWidth = 1; + styleOption.frameShape = QStyleOptionFrame::StyledPanel; + styleOption.features = QStyleOptionFrame::Flat; +} diff --git a/src/imports/nativestyle/items/qquickstyleitemframe.h b/src/imports/nativestyle/items/qquickstyleitemframe.h new file mode 100644 index 00000000..70063ed6 --- /dev/null +++ b/src/imports/nativestyle/items/qquickstyleitemframe.h @@ -0,0 +1,56 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKSTYLEITEMFRAME_H +#define QQUICKSTYLEITEMFRAME_H + +#include "qquickstyleitem.h" +#include <QtQuickTemplates2/private/qquickframe_p.h> + +class QQuickStyleItemFrame : public QQuickStyleItem +{ + Q_OBJECT + QML_NAMED_ELEMENT(Frame) + +protected: + void paintEvent(QPainter *painter) override; + StyleItemGeometry calculateGeometry() override; + +private: + void initStyleOption(QStyleOptionFrame &styleOption); +}; + +#endif // QQUICKSTYLEITEMFRAME_H diff --git a/src/imports/nativestyle/items/qquickstyleitemgroupbox.cpp b/src/imports/nativestyle/items/qquickstyleitemgroupbox.cpp new file mode 100644 index 00000000..d62b6f23 --- /dev/null +++ b/src/imports/nativestyle/items/qquickstyleitemgroupbox.cpp @@ -0,0 +1,100 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickstyleitemgroupbox.h" + +QFont QQuickStyleItemGroupBox::styleFont(QQuickItem *control) +{ + return style()->font(QStyle::CE_HeaderLabel, controlSize(control)); +} + +StyleItemGeometry QQuickStyleItemGroupBox::calculateGeometry() +{ + QStyleOptionGroupBox styleOption; + initStyleOption(styleOption); + + StyleItemGeometry geometry; + geometry.minimumSize = style()->sizeFromContents(QStyle::CT_GroupBox, &styleOption, QSize(0, 0)); + + if (!control<QQuickGroupBox>()->title().isEmpty()) { + // We don't draw the title, but we need to take + // it into calculation for the control size + styleOption.text = QStringLiteral(" "); + styleOption.subControls |= QStyle::SC_GroupBoxLabel; + } + + geometry.implicitSize = style()->sizeFromContents(QStyle::CT_GroupBox, &styleOption, contentSize()); + styleOption.rect.setSize(geometry.implicitSize); + geometry.contentRect = style()->subControlRect(QStyle::CC_GroupBox, &styleOption, QStyle::SC_GroupBoxContents); + geometry.layoutRect = style()->subElementRect(QStyle::SE_GroupBoxLayoutItem, &styleOption); + geometry.ninePatchMargins = style()->ninePatchMargins(QStyle::CC_GroupBox, &styleOption, geometry.minimumSize); + + const QQuickStyleMargins oldGroupBoxPadding = m_groupBoxPadding; + const QRect frame = style()->subControlRect(QStyle::CC_GroupBox, &styleOption, QStyle::SC_GroupBoxFrame); + m_groupBoxPadding = QQuickStyleMargins(QRect(QPoint(), geometry.implicitSize), frame); + if (m_groupBoxPadding != oldGroupBoxPadding) + emit groupBoxPaddingChanged(); + + const QPointF oldLabelPos = m_labelPos; + m_labelPos = style()->subControlRect(QStyle::CC_GroupBox, &styleOption, QStyle::SC_GroupBoxLabel).topLeft(); + if (m_labelPos != oldLabelPos) + emit labelPosChanged(); + return geometry; +} + +void QQuickStyleItemGroupBox::paintEvent(QPainter *painter) +{ + QStyleOptionGroupBox styleOption; + initStyleOption(styleOption); + style()->drawComplexControl(QStyle::CC_GroupBox, &styleOption, painter); +} + +void QQuickStyleItemGroupBox::initStyleOption(QStyleOptionGroupBox &styleOption) +{ + initStyleOptionBase(styleOption); + styleOption.subControls = QStyle::SC_GroupBoxFrame; + styleOption.lineWidth = 1; +} + +QQuickStyleMargins QQuickStyleItemGroupBox::groupBoxPadding() const +{ + return m_groupBoxPadding; +} + +QPointF QQuickStyleItemGroupBox::labelPos() const +{ + return m_labelPos; +} diff --git a/src/imports/nativestyle/items/qquickstyleitemgroupbox.h b/src/imports/nativestyle/items/qquickstyleitemgroupbox.h new file mode 100644 index 00000000..efb6d4f6 --- /dev/null +++ b/src/imports/nativestyle/items/qquickstyleitemgroupbox.h @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKSTYLEITEMGROUPBOX_H +#define QQUICKSTYLEITEMGROUPBOX_H + +#include "qquickstyleitem.h" +#include <QtQuickTemplates2/private/qquickgroupbox_p.h> + +class QQuickStyleItemGroupBox : public QQuickStyleItem +{ + Q_OBJECT + Q_PROPERTY(QQuickStyleMargins groupBoxPadding READ groupBoxPadding NOTIFY groupBoxPaddingChanged) + Q_PROPERTY(QPointF labelPos READ labelPos NOTIFY labelPosChanged) + QML_NAMED_ELEMENT(GroupBox) + +public: + QQuickStyleMargins groupBoxPadding() const; + QPointF labelPos() const; + QFont styleFont(QQuickItem *control) override; + +signals: + void groupBoxPaddingChanged(); + void labelPosChanged(); + +protected: + void paintEvent(QPainter *painter) override; + StyleItemGeometry calculateGeometry() override; + +private: + QQuickStyleMargins m_groupBoxPadding; + QPointF m_labelPos; + + void initStyleOption(QStyleOptionGroupBox &styleOption); +}; + +#endif // QQUICKSTYLEITEMGROUPBOX_H diff --git a/src/imports/nativestyle/items/qquickstyleitemprogressbar.cpp b/src/imports/nativestyle/items/qquickstyleitemprogressbar.cpp new file mode 100644 index 00000000..3429b5ac --- /dev/null +++ b/src/imports/nativestyle/items/qquickstyleitemprogressbar.cpp @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickstyleitemprogressbar.h" + +QFont QQuickStyleItemProgressBar::styleFont(QQuickItem *control) +{ + return style()->font(QStyle::CE_ProgressBarLabel, controlSize(control)); +} + +void QQuickStyleItemProgressBar::connectToControl() +{ + QQuickStyleItem::connectToControl(); + auto progressBar = control<QQuickProgressBar>(); + connect(progressBar, &QQuickProgressBar::fromChanged, this, &QQuickStyleItem::markImageDirty); + connect(progressBar, &QQuickProgressBar::toChanged, this, &QQuickStyleItem::markImageDirty); + connect(progressBar, &QQuickProgressBar::positionChanged, this, &QQuickStyleItem::markImageDirty); +} + +StyleItemGeometry QQuickStyleItemProgressBar::calculateGeometry() +{ + QStyleOptionProgressBar styleOption; + initStyleOption(styleOption); + + StyleItemGeometry geometry; + geometry.minimumSize = style()->sizeFromContents(QStyle::CT_ProgressBar, &styleOption, QSize(0, 0)); + geometry.implicitSize = geometry.minimumSize; + styleOption.rect = QRect(QPoint(0, 0), geometry.implicitSize); + geometry.contentRect = style()->subElementRect(QStyle::SE_ProgressBarContents, &styleOption); + geometry.layoutRect = style()->subElementRect(QStyle::SE_ProgressBarLayoutItem, &styleOption); + geometry.ninePatchMargins = style()->ninePatchMargins(QStyle::CE_ProgressBar, &styleOption, geometry.minimumSize); + + return geometry; +} + +void QQuickStyleItemProgressBar::paintEvent(QPainter *painter) +{ + QStyleOptionProgressBar styleOption; + initStyleOption(styleOption); + if (m_subControl == Groove) { + styleOption.rect = style()->subElementRect(QStyle::SE_ProgressBarGroove, &styleOption); + style()->drawControl(QStyle::CE_ProgressBarGroove, &styleOption, painter); + } else { + styleOption.rect = style()->subElementRect(QStyle::SE_ProgressBarContents, &styleOption); + style()->drawControl(QStyle::CE_ProgressBarContents, &styleOption, painter); + } +} + +void QQuickStyleItemProgressBar::initStyleOption(QStyleOptionProgressBar &styleOption) +{ + initStyleOptionBase(styleOption); + auto progressBar = control<QQuickProgressBar>(); + + styleOption.state = QStyle::State_Horizontal; + + if (progressBar->isIndeterminate()) { + styleOption.minimum = 0; + styleOption.maximum = 0; + } else { + styleOption.minimum = progressBar->from(); + styleOption.maximum = progressBar->to(); + styleOption.progress = progressBar->value(); + } +} diff --git a/src/imports/nativestyle/items/qquickstyleitemprogressbar.h b/src/imports/nativestyle/items/qquickstyleitemprogressbar.h new file mode 100644 index 00000000..f6281f0f --- /dev/null +++ b/src/imports/nativestyle/items/qquickstyleitemprogressbar.h @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKSTYLEITEMPROGRESSBAR_H +#define QQUICKSTYLEITEMPROGRESSBAR_H + +#include "qquickstyleitem.h" +#include <QtQuickTemplates2/private/qquickprogressbar_p.h> + +class QQuickStyleItemProgressBar : public QQuickStyleItem +{ + Q_OBJECT + + Q_PROPERTY(SubControl subControl MEMBER m_subControl) + + QML_NAMED_ELEMENT(ProgressBar) + +public: + enum SubControl { + Groove = 1, + Contents, + }; + Q_ENUM(SubControl) + + QFont styleFont(QQuickItem *control) override; + +protected: + void connectToControl() override; + void paintEvent(QPainter *painter) override; + StyleItemGeometry calculateGeometry() override; + +private: + void initStyleOption(QStyleOptionProgressBar &styleOption); + +private: + SubControl m_subControl = Groove; +}; + +#endif // QQUICKSTYLEITEMPROGRESSBAR_H diff --git a/src/imports/nativestyle/items/qquickstyleitemradiobutton.cpp b/src/imports/nativestyle/items/qquickstyleitemradiobutton.cpp new file mode 100644 index 00000000..c8bb251f --- /dev/null +++ b/src/imports/nativestyle/items/qquickstyleitemradiobutton.cpp @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickstyleitemradiobutton.h" + +QFont QQuickStyleItemRadioButton::styleFont(QQuickItem *control) +{ + return style()->font(QStyle::CE_RadioButtonLabel, controlSize(control)); +} + +void QQuickStyleItemRadioButton::connectToControl() +{ + QQuickStyleItem::connectToControl(); + auto checkbox = control<QQuickRadioButton>(); + connect(checkbox, &QQuickRadioButton::downChanged, this, &QQuickStyleItem::markImageDirty); + connect(checkbox, &QQuickRadioButton::checkedChanged, this, &QQuickStyleItem::markImageDirty); +} + +StyleItemGeometry QQuickStyleItemRadioButton::calculateGeometry() +{ + QStyleOptionButton styleOption; + initStyleOption(styleOption); + StyleItemGeometry geometry; + + geometry.minimumSize = style()->sizeFromContents(QStyle::CT_RadioButton, &styleOption, QSize(0, 0)); + geometry.implicitSize = geometry.minimumSize; + styleOption.rect = QRect(QPoint(0, 0), geometry.implicitSize); + geometry.contentRect = style()->subElementRect(QStyle::SE_RadioButtonContents, &styleOption); + geometry.layoutRect = style()->subElementRect(QStyle::SE_RadioButtonLayoutItem, &styleOption); + geometry.ninePatchMargins = style()->ninePatchMargins(QStyle::CE_RadioButton, &styleOption, geometry.minimumSize); + + return geometry; +} + +void QQuickStyleItemRadioButton::paintEvent(QPainter *painter) +{ + QStyleOptionButton styleOption; + initStyleOption(styleOption); + style()->drawControl(QStyle::CE_RadioButton, &styleOption, painter); +} + +void QQuickStyleItemRadioButton::initStyleOption(QStyleOptionButton &styleOption) +{ + initStyleOptionBase(styleOption); + auto checkbox = control<QQuickRadioButton>(); + + styleOption.state |= checkbox->isDown() ? QStyle::State_Sunken : QStyle::State_Raised; + styleOption.state |= checkbox->isChecked() ? QStyle::State_On : QStyle::State_Off; +} diff --git a/src/imports/nativestyle/items/qquickstyleitemradiobutton.h b/src/imports/nativestyle/items/qquickstyleitemradiobutton.h new file mode 100644 index 00000000..5ee9097b --- /dev/null +++ b/src/imports/nativestyle/items/qquickstyleitemradiobutton.h @@ -0,0 +1,60 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKSTYLEITEMRADIOBUTTON_H +#define QQUICKSTYLEITEMRADIOBUTTON_H + +#include "qquickstyleitem.h" +#include <QtQuickTemplates2/private/qquickradiobutton_p.h> + +class QQuickStyleItemRadioButton : public QQuickStyleItem +{ + Q_OBJECT + QML_NAMED_ELEMENT(RadioButton) + +public: + QFont styleFont(QQuickItem *control) override; + +protected: + void connectToControl() override; + void paintEvent(QPainter *painter) override; + StyleItemGeometry calculateGeometry() override; + +private: + void initStyleOption(QStyleOptionButton &styleOption); +}; + +#endif // QQUICKSTYLEITEMRADIOBUTTON_H diff --git a/src/imports/nativestyle/items/qquickstyleitemscrollbar.cpp b/src/imports/nativestyle/items/qquickstyleitemscrollbar.cpp new file mode 100644 index 00000000..7a462082 --- /dev/null +++ b/src/imports/nativestyle/items/qquickstyleitemscrollbar.cpp @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickstyleitemscrollbar.h" + +QFont QQuickStyleItemScrollBar::styleFont(QQuickItem *control) +{ + return style()->font(QStyle::CE_ProgressBarLabel, controlSize(control)); +} + +void QQuickStyleItemScrollBar::connectToControl() +{ + QQuickStyleItem::connectToControl(); + auto scrollBar = control<QQuickScrollBar>(); + connect(scrollBar, &QQuickScrollBar::orientationChanged, this, &QQuickStyleItem::markImageDirty); + connect(scrollBar, &QQuickScrollBar::pressedChanged, this, &QQuickStyleItem::markImageDirty); +} + +StyleItemGeometry QQuickStyleItemScrollBar::calculateGeometry() +{ + QStyleOptionSlider styleOption; + initStyleOption(styleOption); + + StyleItemGeometry geometry; + geometry.minimumSize = style()->sizeFromContents(QStyle::CT_ScrollBar, &styleOption, QSize(0, 0)); + geometry.implicitSize = geometry.minimumSize; + styleOption.rect = QRect(QPoint(0, 0), geometry.implicitSize); + geometry.layoutRect = style()->subElementRect(QStyle::SE_ScrollBarLayoutItem, &styleOption); + geometry.ninePatchMargins = style()->ninePatchMargins(QStyle::CC_ScrollBar, &styleOption, geometry.minimumSize); + + return geometry; +} + +void QQuickStyleItemScrollBar::paintEvent(QPainter *painter) +{ + QStyleOptionSlider styleOption; + initStyleOption(styleOption); + style()->drawComplexControl(QStyle::CC_ScrollBar, &styleOption, painter); +} + +void QQuickStyleItemScrollBar::initStyleOption(QStyleOptionSlider &styleOption) +{ + initStyleOptionBase(styleOption); + auto scrollBar = control<QQuickScrollBar>(); + + styleOption.subControls = m_subControl == Groove ? QStyle::SC_ScrollBarGroove : QStyle::SC_ScrollBarSlider; + styleOption.activeSubControls = QStyle::SC_None; + styleOption.orientation = scrollBar->orientation(); + + if (scrollBar->isPressed()) + styleOption.state |= QStyle::State_Sunken; + + // The following values will let the handle fill 100% of the + // groove / imageSize. But when the handle is resized by + // QQuickScrollBar, it will end up with the correct size visually. + styleOption.pageStep = 1000; + styleOption.minimum = 0; + styleOption.maximum = 1; + styleOption.sliderValue = 0; +} diff --git a/src/imports/nativestyle/items/qquickstyleitemscrollbar.h b/src/imports/nativestyle/items/qquickstyleitemscrollbar.h new file mode 100644 index 00000000..e181d4ec --- /dev/null +++ b/src/imports/nativestyle/items/qquickstyleitemscrollbar.h @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKSTYLEITEMSCROLLBAR_H +#define QQUICKSTYLEITEMSCROLLBAR_H + +#include "qquickstyleitem.h" +#include <QtQuickTemplates2/private/qquickscrollbar_p.h> + +class QQuickStyleItemScrollBar : public QQuickStyleItem +{ + Q_OBJECT + + Q_PROPERTY(SubControl subControl MEMBER m_subControl) + + QML_NAMED_ELEMENT(ScrollBar) + +public: + enum SubControl { + Groove = 1, + Handle, + }; + Q_ENUM(SubControl) + + QFont styleFont(QQuickItem *control) override; + +protected: + void connectToControl() override; + void paintEvent(QPainter *painter) override; + StyleItemGeometry calculateGeometry() override; + +private: + void initStyleOption(QStyleOptionSlider &styleOption); + +private: + SubControl m_subControl = Groove; +}; + +#endif // QQUICKSTYLEITEMSCROLLBAR_H diff --git a/src/imports/nativestyle/items/qquickstyleitemslider.cpp b/src/imports/nativestyle/items/qquickstyleitemslider.cpp new file mode 100644 index 00000000..5ca282cc --- /dev/null +++ b/src/imports/nativestyle/items/qquickstyleitemslider.cpp @@ -0,0 +1,109 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickstyleitemslider.h" + +QFont QQuickStyleItemSlider::styleFont(QQuickItem *control) +{ + return style()->font(QStyle::CE_ProgressBarLabel, controlSize(control)); +} + +void QQuickStyleItemSlider::connectToControl() +{ + QQuickStyleItem::connectToControl(); + auto slider = control<QQuickSlider>(); + connect(slider, &QQuickSlider::fromChanged, this, &QQuickStyleItem::markImageDirty); + connect(slider, &QQuickSlider::toChanged, this, &QQuickStyleItem::markImageDirty); + connect(slider, &QQuickSlider::positionChanged, this, &QQuickStyleItem::markImageDirty); + connect(slider, &QQuickSlider::valueChanged, this, &QQuickStyleItem::markImageDirty); + connect(slider, &QQuickSlider::stepSizeChanged, this, &QQuickStyleItem::markImageDirty); + connect(slider, &QQuickSlider::pressedChanged, this, &QQuickStyleItem::markImageDirty); + connect(slider, &QQuickSlider::orientationChanged, this, &QQuickStyleItem::markImageDirty); +} + +StyleItemGeometry QQuickStyleItemSlider::calculateGeometry() +{ + QStyleOptionSlider styleOption; + initStyleOption(styleOption); + + StyleItemGeometry geometry; + geometry.minimumSize = style()->sizeFromContents(QStyle::CT_Slider, &styleOption, QSize(0, 0)); + geometry.implicitSize = geometry.minimumSize; + geometry.layoutRect = style()->subElementRect(QStyle::SE_SliderLayoutItem, &styleOption); + geometry.ninePatchMargins = style()->ninePatchMargins(QStyle::CC_Slider, &styleOption, geometry.minimumSize); + + return geometry; +} + +void QQuickStyleItemSlider::paintEvent(QPainter *painter) +{ + QStyleOptionSlider styleOption; + initStyleOption(styleOption); + style()->drawComplexControl(QStyle::CC_Slider, &styleOption, painter); +} + +void QQuickStyleItemSlider::initStyleOption(QStyleOptionSlider &styleOption) +{ + initStyleOptionBase(styleOption); + auto slider = control<QQuickSlider>(); + + styleOption.subControls = m_subControl == Groove ? QStyle::SC_SliderGroove : QStyle::SC_SliderHandle; + styleOption.activeSubControls = QStyle::SC_None; + styleOption.orientation = slider->orientation(); + styleOption.tickInterval = slider->stepSize(); + + if (slider->isPressed()) + styleOption.state |= QStyle::State_Sunken; + + if (slider->stepSize() == 0) { + styleOption.minimum = 0; + styleOption.maximum = 10000; + styleOption.sliderPosition = slider->position() * styleOption.maximum; + } else { + styleOption.minimum = slider->from(); + styleOption.maximum = slider->to(); + styleOption.sliderPosition = slider->value(); + + // TODO: add proper API for tickmarks + const int index = slider->metaObject()->indexOfProperty("qqc2_style_tickPosition"); + if (index != -1) { + const int tickPosition = slider->metaObject()->property(index).read(slider).toInt(); + styleOption.tickPosition = QStyleOptionSlider::TickPosition(tickPosition); + if (styleOption.tickPosition != QStyleOptionSlider::NoTicks) + styleOption.subControls |= QStyle::SC_SliderTickmarks; + } + } +} diff --git a/src/imports/nativestyle/items/qquickstyleitemslider.h b/src/imports/nativestyle/items/qquickstyleitemslider.h new file mode 100644 index 00000000..1108930b --- /dev/null +++ b/src/imports/nativestyle/items/qquickstyleitemslider.h @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKSTYLEITEMSLIDER_H +#define QQUICKSTYLEITEMSLIDER_H + +#include "qquickstyleitem.h" +#include <QtQuickTemplates2/private/qquickslider_p.h> + +class QQuickStyleItemSlider : public QQuickStyleItem +{ + Q_OBJECT + + Q_PROPERTY(SubControl subControl MEMBER m_subControl) + + QML_NAMED_ELEMENT(Slider) + +public: + enum SubControl { + Groove = 1, + Handle, + }; + Q_ENUM(SubControl) + + QFont styleFont(QQuickItem *control) override; + +protected: + void connectToControl() override; + void paintEvent(QPainter *painter) override; + StyleItemGeometry calculateGeometry() override; + +private: + void initStyleOption(QStyleOptionSlider &styleOption); + +private: + SubControl m_subControl = Groove; +}; + +#endif // QQUICKSTYLEITEMSLIDER_H diff --git a/src/imports/nativestyle/items/qquickstyleitemspinbox.cpp b/src/imports/nativestyle/items/qquickstyleitemspinbox.cpp new file mode 100644 index 00000000..a2c4f1d1 --- /dev/null +++ b/src/imports/nativestyle/items/qquickstyleitemspinbox.cpp @@ -0,0 +1,108 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickstyleitemspinbox.h" + +QFont QQuickStyleItemSpinBox::styleFont(QQuickItem *control) +{ + return style()->font(QStyle::CE_ComboBoxLabel, controlSize(control)); +} + +void QQuickStyleItemSpinBox::connectToControl() +{ + QQuickStyleItem::connectToControl(); + auto spinbox = control<QQuickSpinBox>(); + connect(spinbox->up(), &QQuickSpinButton::pressedChanged, this, &QQuickStyleItem::markImageDirty); + connect(spinbox->down(), &QQuickSpinButton::pressedChanged, this, &QQuickStyleItem::markImageDirty); +} + +StyleItemGeometry QQuickStyleItemSpinBox::calculateGeometry() +{ + QStyleOptionSpinBox styleOption; + initStyleOption(styleOption); + StyleItemGeometry geometry; + + geometry.minimumSize = style()->sizeFromContents(QStyle::CT_SpinBox, &styleOption, QSize(0, 0)); + + if (styleOption.subControls == QStyle::SC_SpinBoxFrame) { + geometry.implicitSize = style()->sizeFromContents(QStyle::CT_SpinBox, &styleOption, contentSize()); + styleOption.rect = QRect(QPoint(0, 0), geometry.implicitSize); + geometry.contentRect = style()->subControlRect(QStyle::CC_SpinBox, &styleOption, QStyle::SC_SpinBoxEditField); + geometry.layoutRect = style()->subElementRect(QStyle::SE_SpinBoxLayoutItem, &styleOption); + geometry.ninePatchMargins = style()->ninePatchMargins(QStyle::CC_SpinBox, &styleOption, geometry.minimumSize); + } else { + geometry.implicitSize = geometry.minimumSize; + } + + return geometry; +} + +void QQuickStyleItemSpinBox::paintEvent(QPainter *painter) +{ + QStyleOptionSpinBox styleOption; + initStyleOption(styleOption); + style()->drawComplexControl(QStyle::CC_SpinBox, &styleOption, painter); +} + +void QQuickStyleItemSpinBox::initStyleOption(QStyleOptionSpinBox &styleOption) +{ + initStyleOptionBase(styleOption); + auto spinbox = control<QQuickSpinBox>(); + + switch (m_subControl) { + case Frame: + styleOption.subControls = QStyle::SC_SpinBoxFrame; + styleOption.frame = true; + break; + case Up: + styleOption.subControls = QStyle::SC_SpinBoxUp; + break; + case Down: + styleOption.subControls = QStyle::SC_SpinBoxDown; + break; + } + + if (spinbox->up()->isPressed()) { + styleOption.activeSubControls = QStyle::SC_SpinBoxUp; + styleOption.state |= QStyle::State_Sunken; + } else if (spinbox->down()->isPressed()) { + styleOption.activeSubControls = QStyle::SC_SpinBoxDown; + styleOption.state |= QStyle::State_Sunken; + } + + styleOption.buttonSymbols = QStyleOptionSpinBox::UpDownArrows; + styleOption.stepEnabled = QStyleOptionSpinBox::StepEnabled; +} diff --git a/src/imports/nativestyle/items/qquickstyleitemspinbox.h b/src/imports/nativestyle/items/qquickstyleitemspinbox.h new file mode 100644 index 00000000..101d4b2a --- /dev/null +++ b/src/imports/nativestyle/items/qquickstyleitemspinbox.h @@ -0,0 +1,73 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKSTYLEITEMSPINBOX_H +#define QQUICKSTYLEITEMSPINBOX_H + +#include "qquickstyleitem.h" +#include <QtQuickTemplates2/private/qquickspinbox_p.h> + +class QQuickStyleItemSpinBox : public QQuickStyleItem +{ + Q_OBJECT + + Q_PROPERTY(SubControl subControl MEMBER m_subControl) + + QML_NAMED_ELEMENT(SpinBox) + +public: + enum SubControl { + Frame = 1, + Up, + Down, + }; + Q_ENUM(SubControl) + + QFont styleFont(QQuickItem *control) override; + +protected: + void connectToControl() override; + void paintEvent(QPainter *painter) override; + StyleItemGeometry calculateGeometry() override; + +private: + void initStyleOption(QStyleOptionSpinBox &styleOption); + +private: + SubControl m_subControl = Frame; +}; + +#endif // QQUICKSTYLEITEMSPINBOX_H diff --git a/src/imports/nativestyle/items/qquickstyleitemtextarea.cpp b/src/imports/nativestyle/items/qquickstyleitemtextarea.cpp new file mode 100644 index 00000000..c149444b --- /dev/null +++ b/src/imports/nativestyle/items/qquickstyleitemtextarea.cpp @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickstyleitemtextarea.h" + +QFont QQuickStyleItemTextArea::styleFont(QQuickItem *control) +{ + return style()->font(QStyle::CE_ComboBoxLabel, controlSize(control)); +} + +void QQuickStyleItemTextArea::connectToControl() +{ + QQuickStyleItem::connectToControl(); + auto textArea = control<QQuickTextArea>(); + connect(textArea, &QQuickTextArea::readOnlyChanged, this, &QQuickStyleItem::markImageDirty); + connect(textArea, &QQuickTextArea::focusChanged, this, &QQuickStyleItem::markImageDirty); +} + +StyleItemGeometry QQuickStyleItemTextArea::calculateGeometry() +{ + QStyleOptionFrame styleOption; + initStyleOption(styleOption); + StyleItemGeometry geometry; + + // There is no CT_TextEdit in QStyle, so we "borrow" CT_LineEdit for now + geometry.minimumSize = style()->sizeFromContents(QStyle::CT_LineEdit, &styleOption, QSize(0, 0)); + geometry.implicitSize = style()->sizeFromContents(QStyle::CT_LineEdit, &styleOption, contentSize()); + styleOption.rect = QRect(QPoint(0, 0), geometry.implicitSize); + geometry.contentRect = style()->subElementRect(QStyle::SE_LineEditContents, &styleOption); + geometry.ninePatchMargins = style()->ninePatchMargins(QStyle::CE_ShapedFrame, &styleOption, geometry.minimumSize); + + return geometry; +} + +void QQuickStyleItemTextArea::paintEvent(QPainter *painter) +{ + QStyleOptionFrame styleOption; + initStyleOption(styleOption); + style()->drawPrimitive(QStyle::PE_PanelLineEdit, &styleOption, painter); +} + +void QQuickStyleItemTextArea::initStyleOption(QStyleOptionFrame &styleOption) +{ + initStyleOptionBase(styleOption); + auto textArea = control<QQuickTextArea>(); + + styleOption.lineWidth = 1; + styleOption.midLineWidth = 0; + styleOption.state |= QStyle::State_Sunken; + if (textArea->isReadOnly()) + styleOption.state |= QStyle::State_ReadOnly; +} diff --git a/src/imports/nativestyle/items/qquickstyleitemtextarea.h b/src/imports/nativestyle/items/qquickstyleitemtextarea.h new file mode 100644 index 00000000..90d83ad7 --- /dev/null +++ b/src/imports/nativestyle/items/qquickstyleitemtextarea.h @@ -0,0 +1,60 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKSTYLEITEMTEXTAREA_H +#define QQUICKSTYLEITEMTEXTAREA_H + +#include "qquickstyleitem.h" +#include <QtQuickTemplates2/private/qquicktextarea_p.h> + +class QQuickStyleItemTextArea : public QQuickStyleItem +{ + Q_OBJECT + QML_NAMED_ELEMENT(TextArea) + +public: + QFont styleFont(QQuickItem *control) override; + +protected: + void connectToControl() override; + void paintEvent(QPainter *painter) override; + StyleItemGeometry calculateGeometry() override; + +private: + void initStyleOption(QStyleOptionFrame &styleOption); +}; + +#endif // QQUICKSTYLEITEMTEXTAREA_H diff --git a/src/imports/nativestyle/items/qquickstyleitemtextfield.cpp b/src/imports/nativestyle/items/qquickstyleitemtextfield.cpp new file mode 100644 index 00000000..1a262033 --- /dev/null +++ b/src/imports/nativestyle/items/qquickstyleitemtextfield.cpp @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickstyleitemtextfield.h" + +QFont QQuickStyleItemTextField::styleFont(QQuickItem *control) +{ + return style()->font(QStyle::CE_ComboBoxLabel, controlSize(control)); +} + +void QQuickStyleItemTextField::connectToControl() +{ + QQuickStyleItem::connectToControl(); + auto textField = control<QQuickTextField>(); + connect(textField, &QQuickTextField::readOnlyChanged, this, &QQuickStyleItem::markImageDirty); + connect(textField, &QQuickTextField::focusChanged, this, &QQuickStyleItem::markImageDirty); +} + +StyleItemGeometry QQuickStyleItemTextField::calculateGeometry() +{ + QStyleOptionFrame styleOption; + initStyleOption(styleOption); + StyleItemGeometry geometry; + + geometry.minimumSize = style()->sizeFromContents(QStyle::CT_LineEdit, &styleOption, QSize(0, 0)); + geometry.implicitSize = style()->sizeFromContents(QStyle::CT_LineEdit, &styleOption, contentSize()); + styleOption.rect = QRect(QPoint(0, 0), geometry.implicitSize); + geometry.contentRect = style()->subElementRect(QStyle::SE_LineEditContents, &styleOption); + geometry.ninePatchMargins = style()->ninePatchMargins(QStyle::CE_ShapedFrame, &styleOption, geometry.minimumSize); + + return geometry; +} + +void QQuickStyleItemTextField::paintEvent(QPainter *painter) +{ + QStyleOptionFrame styleOption; + initStyleOption(styleOption); + style()->drawPrimitive(QStyle::PE_PanelLineEdit, &styleOption, painter); +} + +void QQuickStyleItemTextField::initStyleOption(QStyleOptionFrame &styleOption) +{ + initStyleOptionBase(styleOption); + auto textField = control<QQuickTextField>(); + + styleOption.lineWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth, &styleOption); + styleOption.midLineWidth = 0; + styleOption.state |= QStyle::State_Sunken; + if (textField->isReadOnly()) + styleOption.state |= QStyle::State_ReadOnly; +} diff --git a/src/imports/nativestyle/items/qquickstyleitemtextfield.h b/src/imports/nativestyle/items/qquickstyleitemtextfield.h new file mode 100644 index 00000000..57aa8885 --- /dev/null +++ b/src/imports/nativestyle/items/qquickstyleitemtextfield.h @@ -0,0 +1,60 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKSTYLEITEMTEXTFIELD_H +#define QQUICKSTYLEITEMTEXTFIELD_H + +#include "qquickstyleitem.h" +#include <QtQuickTemplates2/private/qquicktextfield_p.h> + +class QQuickStyleItemTextField : public QQuickStyleItem +{ + Q_OBJECT + QML_NAMED_ELEMENT(TextField) + +public: + QFont styleFont(QQuickItem *control) override; + +protected: + void connectToControl() override; + void paintEvent(QPainter *painter) override; + StyleItemGeometry calculateGeometry() override; + +private: + void initStyleOption(QStyleOptionFrame &styleOption); +}; + +#endif // QQUICKSTYLEITEMTEXTFIELD_H diff --git a/src/imports/nativestyle/nativestyle.pro b/src/imports/nativestyle/nativestyle.pro new file mode 100644 index 00000000..b06e5daf --- /dev/null +++ b/src/imports/nativestyle/nativestyle.pro @@ -0,0 +1,30 @@ +TARGET = qtquickcontrols2nativestyleplugin +TARGETPATH = QtQuick/NativeStyle + +QML_IMPORT_NAME = QtQuick.NativeStyle +QML_IMPORT_MAJOR_VERSION = 6 + +QT += qml quick quickcontrols2 quicktemplates2 +QT_PRIVATE += core-private gui-private qml-private quick-private quicktemplates2-private quickcontrols2-private + +DEFINES += QT_NO_CAST_TO_ASCII QT_NO_CAST_FROM_ASCII + +include(items/items.pri) +include(qstyle/qstyle.pri) +include(controls/controls.pri) + +OTHER_FILES += \ + qmldir \ + $$QML_FILES + +SOURCES += \ + $$PWD/qtquickcontrols2nativestyleplugin.cpp + +CONFIG += no_cxx_module install_qml_files builtin_resources qtquickcompiler +CONFIG += qmltypes install_qmltypes + +load(qml_plugin) + +requires(qtConfig(quickcontrols2-macos)|qtConfig(quickcontrols2-windows)) + +HEADERS += diff --git a/src/imports/nativestyle/qmldir b/src/imports/nativestyle/qmldir new file mode 100644 index 00000000..3b6ce6c5 --- /dev/null +++ b/src/imports/nativestyle/qmldir @@ -0,0 +1,18 @@ +module QtQuick.NativeStyle +plugin qtquickcontrols2nativestyleplugin +classname QtQuickControls2NativeStylePlugin +depends QtQuick.Controls 2.5 + +DefaultButton 6.0 controls/DefaultButton.qml +DefaultCheckBox 6.0 controls/DefaultCheckBox.qml +DefaultRadioButton 6.0 controls/DefaultRadioButton.qml +DefaultSlider 6.0 controls/DefaultSlider.qml +DefaultGroupBox 6.0 controls/DefaultGroupBox.qml +DefaultSpinBox 6.0 controls/DefaultSpinBox.qml +DefaultTextField 6.0 controls/DefaultTextField.qml +DefaultFrame 6.0 controls/DefaultFrame.qml +DefaultTextArea 6.0 controls/DefaultTextArea.qml +DefaultComboBox 6.0 controls/DefaultComboBox.qml +DefaultScrollBar 6.0 controls/DefaultScrollBar.qml +DefaultProgressBar 6.0 controls/DefaultProgressBar.qml +DefaultDial 6.0 controls/DefaultDial.qml diff --git a/src/imports/nativestyle/qstyle/mac/mac.pri b/src/imports/nativestyle/qstyle/mac/mac.pri new file mode 100644 index 00000000..2f7ee7d4 --- /dev/null +++ b/src/imports/nativestyle/qstyle/mac/mac.pri @@ -0,0 +1,12 @@ +LIBS_PRIVATE += -framework AppKit + +INCLUDEPATH += $$PWD + +HEADERS += \ + $$PWD/qquickmacstyle_mac_p.h \ + $$PWD/qquickmacstyle_mac_p_p.h + +SOURCES += + +OBJECTIVE_SOURCES += \ + $$PWD/qquickmacstyle_mac.mm diff --git a/src/imports/nativestyle/qstyle/mac/qquickmacstyle_mac.mm b/src/imports/nativestyle/qstyle/mac/qquickmacstyle_mac.mm new file mode 100644 index 00000000..e61bdafa --- /dev/null +++ b/src/imports/nativestyle/qstyle/mac/qquickmacstyle_mac.mm @@ -0,0 +1,6319 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/* + Note: The qdoc comments for QMacStyle are contained in + .../doc/src/qstyles.qdoc. +*/ + +#include <AppKit/AppKit.h> + +#include "qquickmacstyle_mac_p.h" +#include "qquickmacstyle_mac_p_p.h" +#include "qquickstylehelper_p.h" + +#include <QtQuickTemplates2/private/qquickcontrol_p.h> + +#define QMAC_QAQUASTYLE_SIZE_CONSTRAIN +//#define DEBUG_SIZE_CONSTRAINT + +#include <QtCore/qoperatingsystemversion.h> +#include <QtCore/qvariant.h> +#include <QtCore/qvarlengtharray.h> + +#include <QtGui/qpainterpath.h> +#include <QtGui/qpa/qplatformnativeinterface.h> +#include <QtGui/qpa/qplatformfontdatabase.h> +#include <QtGui/qpa/qplatformtheme.h> + +#include <QtCore/private/qcore_mac_p.h> +#include <QtGui/private/qcoregraphics_p.h> +#include <QtGui/private/qguiapplication_p.h> + +#include <cmath> + +QT_USE_NAMESPACE + +@interface QT_MANGLE_NAMESPACE(QIndeterminateProgressIndicator) : NSProgressIndicator + +@property (readonly, nonatomic) NSInteger animators; + +- (instancetype)init; + +- (void)startAnimation; +- (void)stopAnimation; + +- (void)drawWithFrame:(CGRect)rect inView:(NSView *)view; + +@end + +QT_NAMESPACE_ALIAS_OBJC_CLASS(QIndeterminateProgressIndicator); + +@implementation QIndeterminateProgressIndicator + +- (instancetype)init +{ + if ((self = [super init])) { + _animators = 0; + self.indeterminate = YES; + self.usesThreadedAnimation = NO; + self.alphaValue = 0.0; + } + + return self; +} + +- (void)startAnimation +{ + if (_animators == 0) { + self.hidden = NO; + [super startAnimation:self]; + } + ++_animators; +} + +- (void)stopAnimation +{ + --_animators; + if (_animators == 0) { + [super stopAnimation:self]; + self.hidden = YES; + [self removeFromSuperviewWithoutNeedingDisplay]; + } +} + +- (void)drawWithFrame:(CGRect)rect inView:(NSView *)view +{ + // The alphaValue change is not strictly necessary, but feels safer. + self.alphaValue = 1.0; + if (self.superview != view) + [view addSubview:self]; + if (!CGRectEqualToRect(self.frame, rect)) + self.frame = rect; + [self drawRect:rect]; + self.alphaValue = 0.0; +} + +@end + +@interface QT_MANGLE_NAMESPACE(QVerticalSplitView) : NSSplitView +- (BOOL)isVertical; +@end + +QT_NAMESPACE_ALIAS_OBJC_CLASS(QVerticalSplitView); + +@implementation QVerticalSplitView +- (BOOL)isVertical +{ + return YES; +} +@end + +// See render code in drawPrimitive(PE_FrameTabWidget) +@interface QT_MANGLE_NAMESPACE(QDarkNSBox) : NSBox +@end + +QT_NAMESPACE_ALIAS_OBJC_CLASS(QDarkNSBox); + +@implementation QDarkNSBox +- (instancetype)init +{ + if ((self = [super init])) { + self.title = @""; + self.titlePosition = NSNoTitle; + self.boxType = NSBoxCustom; + self.cornerRadius = 3; + self.borderColor = [NSColor.controlColor colorWithAlphaComponent:0.1]; + self.fillColor = [NSColor.darkGrayColor colorWithAlphaComponent:0.2]; + } + + return self; +} + +- (void)drawRect:(NSRect)rect +{ + [super drawRect:rect]; +} +@end + +QT_BEGIN_NAMESPACE + +namespace QQC2 { + +// 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::PushButtonRightOffset = 12; +const int QMacStylePrivate::PushButtonContentPadding = 6; + +QVector<QPointer<QObject> > QMacStylePrivate::scrollBars; + +// Title bar gradient colors for Lion were determined by inspecting PSDs exported +// using CoreUI's CoreThemeDocument; there is no public API to retrieve them + +static QLinearGradient titlebarGradientActive() +{ + static QLinearGradient darkGradient = [](){ + QLinearGradient gradient; + // FIXME: colors are chosen somewhat arbitrarily and could be fine-tuned, + // or ideally determined by calling a native API. + gradient.setColorAt(0, QColor(47, 47, 47)); + return gradient; + }(); + static QLinearGradient lightGradient = [](){ + QLinearGradient gradient; + gradient.setColorAt(0, QColor(235, 235, 235)); + gradient.setColorAt(0.5, QColor(210, 210, 210)); + gradient.setColorAt(0.75, QColor(195, 195, 195)); + gradient.setColorAt(1, QColor(180, 180, 180)); + return gradient; + }(); + return qt_mac_applicationIsInDarkMode() ? darkGradient : lightGradient; +} + +static QLinearGradient titlebarGradientInactive() +{ + static QLinearGradient darkGradient = [](){ + QLinearGradient gradient; + gradient.setColorAt(1, QColor(42, 42, 42)); + return gradient; + }(); + static QLinearGradient lightGradient = [](){ + QLinearGradient gradient; + gradient.setColorAt(0, QColor(250, 250, 250)); + gradient.setColorAt(1, QColor(225, 225, 225)); + return gradient; + }(); + return qt_mac_applicationIsInDarkMode() ? darkGradient : lightGradient; +} + +/* + Since macOS 10.14 AppKit is using transparency more extensively, especially for the + dark theme. Inactive buttons, for example, are semi-transparent. And we use them to + draw tab widget's tab bar. The combination of NSBox (also a part of tab widget) + and these transparent buttons gives us an undesired side-effect: an outline of + NSBox is visible through transparent buttons. To avoid this, we have this hack below: + we clip the area where the line would be visible through the buttons. The area we + want to clip away can be described as an intersection of the option's rect and + the tab widget's tab bar rect. But some adjustments are required, since those rects + are anyway adjusted during the rendering and they are not exactly what you'll see on + the screen. Thus this switch-statement inside. +*/ +static void clipTabBarFrame(const QStyleOption *option, const QMacStyle *style, CGContextRef ctx) +{ + Q_ASSERT(option); + Q_ASSERT(style); + Q_ASSERT(ctx); + + if (qt_mac_applicationIsInDarkMode()) { +// QTabWidget *tabWidget = qobject_cast<QTabWidget *>(option->styleObject); +// Q_ASSERT(tabWidget); + +// QRect tabBarRect = style->subElementRect(QStyle::SE_TabWidgetTabBar, option, tabWidget).adjusted(2, 0, -3, 0); +// switch (tabWidget->tabPosition()) { +// case QTabWidget::South: +// tabBarRect.setY(tabBarRect.y() + tabBarRect.height() / 2); +// break; +// case QTabWidget::North: +// case QTabWidget::West: +// tabBarRect = tabBarRect.adjusted(0, 2, 0, -2); +// break; +// case QTabWidget::East: +// tabBarRect = tabBarRect.adjusted(tabBarRect.width() / 2, 2, tabBarRect.width() / 2, -2); +// } + +// const QRegion clipPath = QRegion(option->rect) - tabBarRect; +// QVarLengthArray<CGRect, 3> cgRects; +// for (const QRect &qtRect : clipPath) +// cgRects.push_back(qtRect.toCGRect()); +// if (cgRects.size()) +// CGContextClipToRects(ctx, &cgRects[0], size_t(cgRects.size())); + } +} + +static const QColor titlebarSeparatorLineActive(111, 111, 111); +static const QColor titlebarSeparatorLineInactive(131, 131, 131); +static const QColor darkModeSeparatorLine(88, 88, 88); + +// Gradient colors used for the dock widget title bar and +// non-unifed tool bar background. +static const QColor lightMainWindowGradientBegin(240, 240, 240); +static const QColor lightMainWindowGradientEnd(200, 200, 200); +static const QColor darkMainWindowGradientBegin(47, 47, 47); +static const QColor darkMainWindowGradientEnd(47, 47, 47); + +static const int DisclosureOffset = 4; + +static const qreal titleBarIconTitleSpacing = 5; +static const qreal titleBarTitleRightMargin = 12; +static const qreal titleBarButtonSpacing = 8; + +// Tab bar colors +// active: window is active +// selected: tab is selected +// hovered: tab is hovered +bool isDarkMode() { return qt_mac_applicationIsInDarkMode(); } + +static const QColor lightTabBarTabBackgroundActive(190, 190, 190); +static const QColor darkTabBarTabBackgroundActive(38, 38, 38); +static const QColor tabBarTabBackgroundActive() { return isDarkMode() ? darkTabBarTabBackgroundActive : lightTabBarTabBackgroundActive; } + +static const QColor lightTabBarTabBackgroundActiveHovered(178, 178, 178); +static const QColor darkTabBarTabBackgroundActiveHovered(32, 32, 32); +static const QColor tabBarTabBackgroundActiveHovered() { return isDarkMode() ? darkTabBarTabBackgroundActiveHovered : lightTabBarTabBackgroundActiveHovered; } + +static const QColor lightTabBarTabBackgroundActiveSelected(211, 211, 211); +static const QColor darkTabBarTabBackgroundActiveSelected(52, 52, 52); +static const QColor tabBarTabBackgroundActiveSelected() { return isDarkMode() ? darkTabBarTabBackgroundActiveSelected : lightTabBarTabBackgroundActiveSelected; } + +static const QColor lightTabBarTabBackground(227, 227, 227); +static const QColor darkTabBarTabBackground(38, 38, 38); +static const QColor tabBarTabBackground() { return isDarkMode() ? darkTabBarTabBackground : lightTabBarTabBackground; } + +static const QColor lightTabBarTabBackgroundSelected(246, 246, 246); +static const QColor darkTabBarTabBackgroundSelected(52, 52, 52); +static const QColor tabBarTabBackgroundSelected() { return isDarkMode() ? darkTabBarTabBackgroundSelected : lightTabBarTabBackgroundSelected; } + +static const QColor lightTabBarTabLineActive(160, 160, 160); +static const QColor darkTabBarTabLineActive(90, 90, 90); +static const QColor tabBarTabLineActive() { return isDarkMode() ? darkTabBarTabLineActive : lightTabBarTabLineActive; } + +static const QColor lightTabBarTabLineActiveHovered(150, 150, 150); +static const QColor darkTabBarTabLineActiveHovered(90, 90, 90); +static const QColor tabBarTabLineActiveHovered() { return isDarkMode() ? darkTabBarTabLineActiveHovered : lightTabBarTabLineActiveHovered; } + +static const QColor lightTabBarTabLine(210, 210, 210); +static const QColor darkTabBarTabLine(90, 90, 90); +static const QColor tabBarTabLine() { return isDarkMode() ? darkTabBarTabLine : lightTabBarTabLine; } + +static const QColor lightTabBarTabLineSelected(189, 189, 189); +static const QColor darkTabBarTabLineSelected(90, 90, 90); +static const QColor tabBarTabLineSelected() { return isDarkMode() ? darkTabBarTabLineSelected : lightTabBarTabLineSelected; } + +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; + +#ifndef QT_NO_ACCESSIBILITY // This ifdef to avoid "unused function" warning. +QBrush brushForToolButton(bool isOnKeyWindow) +{ + // When a toolbutton in a toolbar is in the 'ON' state, we draw a + // partially transparent background. The colors must be different + // for 'Aqua' and 'DarkAqua' appearances though. + if (isDarkMode()) + return isOnKeyWindow ? QColor(73, 73, 73, 100) : QColor(56, 56, 56, 100); + + return isOnKeyWindow ? QColor(0, 0, 0, 28) : QColor(0, 0, 0, 21); +} +#endif // QT_NO_ACCESSIBILITY + +static const int headerSectionArrowHeight = 6; +static const int headerSectionSeparatorInset = 2; + +// One for each of QStyleHelper::WidgetSizePolicy +static const QMarginsF comboBoxFocusRingMargins[3] = { + { 0.5, 2, 3.5, 4 }, + { 0.5, 1, 2.5, 4 }, + { 0.5, 1.5, 2.5, 3.5 } +}; + +static const QMarginsF pullDownButtonShadowMargins[3] = { + { 0.5, -1, 0.5, 2 }, + { 0.5, -1.5, 0.5, 2.5 }, + { 0.5, 0, 0.5, 1 } +}; + +static const QMarginsF pushButtonShadowMargins[3] = { + { 1.5, -1.5, 1.5, 4.5 }, + { 1.5, -1, 1.5, 4 }, + { 1.5, 0.5, 1.5, 2.5 } +}; + +// These are frame heights as reported by Xcode 9's Interface Builder. +// Alignemnet rectangle's heights match for push and popup buttons +// with respective values 21, 18 and 15. + +static const qreal comboBoxDefaultHeight[3] = { + 26, 22, 19 +}; + +static const qreal pushButtonDefaultHeight[3] = { + 32, 28, 16 +}; + +static const qreal popupButtonDefaultHeight[3] = { + 26, 22, 15 +}; + +static const int toolButtonArrowSize = 7; +static const int toolButtonArrowMargin = 2; + +static const qreal focusRingWidth = 3.5; + +// An application can force 'Aqua' theme while the system theme is one of +// the 'Dark' variants. Since in Qt we sometimes use NSControls and even +// NSCells directly without attaching them to any view hierarchy, we have +// to set NSAppearance.currentAppearance to 'Aqua' manually, to make sure +// the correct rendering path is triggered. Apple recommends us to un-set +// the current appearance back after we finished with drawing. This is what +// AppearanceSync is for. + +class AppearanceSync { +public: + AppearanceSync() + { +#if QT_MACOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_14) + if (QOperatingSystemVersion::current() >= QOperatingSystemVersion::MacOSMojave + && !qt_mac_applicationIsInDarkMode()) { + auto requiredAppearanceName = NSApplication.sharedApplication.effectiveAppearance.name; + if (![NSAppearance.currentAppearance.name isEqualToString:requiredAppearanceName]) { + previous = NSAppearance.currentAppearance; + NSAppearance.currentAppearance = [NSAppearance appearanceNamed:requiredAppearanceName]; + } + } +#endif // QT_MACOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_14) + } + + ~AppearanceSync() + { + if (previous) + NSAppearance.currentAppearance = previous; + } + +private: + NSAppearance *previous = nil; + + Q_DISABLE_COPY(AppearanceSync) +}; + +static bool setupScroller(NSScroller *scroller, const QStyleOptionSlider *sb) +{ + const qreal length = sb->maximum - sb->minimum + sb->pageStep; + if (qFuzzyIsNull(length)) + return false; + const qreal proportion = sb->pageStep / length; + const qreal range = qreal(sb->maximum - sb->minimum); + qreal value = range ? qreal(sb->sliderValue - sb->minimum) / range : 0; + if (sb->orientation == Qt::Horizontal && sb->direction == Qt::RightToLeft) + value = 1.0 - value; + + scroller.frame = sb->rect.toCGRect(); + scroller.floatValue = value; + scroller.knobProportion = proportion; + return true; +} + +static bool setupSlider(NSSlider *slider, const QStyleOptionSlider *sl) +{ + if (sl->minimum >= sl->maximum) + return false; + + slider.frame = sl->rect.toCGRect(); + slider.minValue = sl->minimum; + slider.maxValue = sl->maximum; + slider.intValue = sl->sliderPosition; + slider.enabled = sl->state & QStyle::State_Enabled; + if (sl->tickPosition != QStyleOptionSlider::NoTicks) { + // Set numberOfTickMarks, but TicksBothSides will be treated differently + int interval = sl->tickInterval; + if (interval == 0) { + interval = sl->pageStep; + if (interval == 0) + interval = sl->singleStep; + if (interval == 0) + interval = 1; // return false? + } + slider.numberOfTickMarks = 1 + ((sl->maximum - sl->minimum) / interval); + + const bool ticksAbove = sl->tickPosition == QStyleOptionSlider::TicksAbove; + if (sl->orientation == Qt::Horizontal) + slider.tickMarkPosition = ticksAbove ? NSTickMarkPositionAbove : NSTickMarkPositionBelow; + else + slider.tickMarkPosition = ticksAbove ? NSTickMarkPositionLeading : NSTickMarkPositionTrailing; + } else { + slider.numberOfTickMarks = 0; + } + + return true; +} + +static void fixStaleGeometry(NSSlider *slider) +{ + // If it's later fixed in AppKit, this function is not needed. + // On macOS Mojave we suddenly have NSSliderCell with a cached + // (and stale) geometry, thus its -drawKnob, -drawBarInside:flipped:, + // -drawTickMarks fail to render the slider properly. Setting the number + // of tickmarks triggers an update in geometry. + + Q_ASSERT(slider); + + if (QOperatingSystemVersion::current() < QOperatingSystemVersion::MacOSMojave) + return; + + NSSliderCell *cell = slider.cell; + const NSRect barRect = [cell barRectFlipped:NO]; + const NSSize sliderSize = slider.frame.size; + CGFloat difference = 0.; + if (slider.vertical) + difference = std::abs(sliderSize.height - barRect.size.height); + else + difference = std::abs(sliderSize.width - barRect.size.width); + + if (difference > 6.) { + // Stale ... + const auto nOfTicks = slider.numberOfTickMarks; + // Non-zero, different from nOfTicks to force update + slider.numberOfTickMarks = nOfTicks + 10; + slider.numberOfTickMarks = nOfTicks; + } +} + +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); +} + + +static void drawTabCloseButton(QPainter *p, bool hover, bool selected, bool pressed, bool documentMode) +{ + 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) { + if (documentMode) + background = pressed ? tabBarCloseButtonBackgroundSelectedPressed : tabBarCloseButtonBackgroundSelectedHovered; + else + background = QColor(255, 255, 255, pressed ? 150 : 100); // Translucent white + } else { + background = pressed ? tabBarCloseButtonBackgroundPressed : tabBarCloseButtonBackgroundHovered; + if (!documentMode) + background = background.lighter(pressed ? 135 : 140); // Lighter tab background, lighter color + } + + p->setPen(Qt::transparent); + p->setBrush(background); + p->drawRoundedRect(rect, closeButtonCornerRadius, closeButtonCornerRadius); + } + + // draw cross + const int margin = 3; + QPen crossPen; + crossPen.setColor(selected ? (documentMode ? tabBarCloseButtonCrossSelected : Qt::white) : 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); +} + +QRect rotateTabPainter(QPainter *p, QStyleOptionTab::Shape shape, QRect tabRect) +{ + const auto tabDirection = QMacStylePrivate::tabDirection(shape); + if (QMacStylePrivate::verticalTabs(tabDirection)) { + int newX, newY, newRot; + if (tabDirection == QMacStylePrivate::East) { + 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()); + QTransform transform; + transform.translate(newX, newY); + transform.rotate(newRot); + p->setTransform(transform, true); + } + return tabRect; +} + +void drawTabShape(QPainter *p, const QStyleOptionTab *tabOpt, bool isUnified, int tabOverlap) +{ + QRect rect = tabOpt->rect; + if (QMacStylePrivate::verticalTabs(QMacStylePrivate::tabDirection(tabOpt->shape))) + rect = rect.adjusted(-tabOverlap, 0, 0, 0); + else + rect = rect.adjusted(0, -tabOverlap, 0, 0); + + 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, 2, width - 2, height - 3); + 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, isDarkMode() ? QColor(Qt::black) : 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) +{ + QRect r = tbb->rect; +// if (QMacStylePrivate::verticalTabs(QMacStylePrivate::tabDirection(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); + bool isDocument = false; +// if (const QTabBar *tabBar = qobject_cast<const QTabBar*>(w)) +// isDocument = tabBar->documentMode(); + const QColor bottomLineColor = isDocument && isDarkMode() ? QColor(Qt::black) : active ? tabBarTabLineActive() : tabBarTabLine(); + p->fillRect(bottomLineRect, bottomLineColor); +} + +static QStyleHelper::WidgetSizePolicy getControlSize(const QStyleOption *option) +{ + const auto wsp = QStyleHelper::widgetSizePolicy(option); + if (wsp == QStyleHelper::SizeDefault) + return QStyleHelper::SizeLarge; + + return wsp; +} + +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('&')) { + ++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; +} + +static bool qt_macWindowMainWindow(const QWindow *window) +{ + if (window->handle()) { + if (NSWindow *nswindow = static_cast<NSWindow*>( + QGuiApplication::platformNativeInterface()-> + nativeResourceForWindow(QByteArrayLiteral("nswindow"), + const_cast<QWindow *>(window)))) { + return [nswindow isMainWindow]; + } + } + return false; +} + +/***************************************************************************** + QMacCGStyle globals + *****************************************************************************/ +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 + +/***************************************************************************** + QMacCGStyle utility functions + *****************************************************************************/ + +enum QAquaMetric { + // Prepend kThemeMetric to get the HIToolBox constant. + // Represents the values already used in QMacStyle. + CheckBoxHeight = 0, + CheckBoxWidth, + EditTextFrameOutset, + FocusRectOutset, + HSliderHeight, + HSliderTickHeight, + LargeProgressBarThickness, + ListHeaderHeight, + MenuSeparatorHeight, // GetThemeMenuSeparatorHeight + MiniCheckBoxHeight, + MiniCheckBoxWidth, + MiniHSliderHeight, + MiniHSliderTickHeight, + MiniPopupButtonHeight, + MiniPushButtonHeight, + MiniRadioButtonHeight, + MiniRadioButtonWidth, + MiniVSliderTickWidth, + MiniVSliderWidth, + NormalProgressBarThickness, + PopupButtonHeight, + ProgressBarShadowOutset, + PushButtonHeight, + RadioButtonHeight, + RadioButtonWidth, + SeparatorSize, + SmallCheckBoxHeight, + SmallCheckBoxWidth, + SmallHSliderHeight, + SmallHSliderTickHeight, + SmallPopupButtonHeight, + SmallProgressBarShadowOutset, + SmallPushButtonHeight, + SmallRadioButtonHeight, + SmallRadioButtonWidth, + SmallVSliderTickWidth, + SmallVSliderWidth, + VSliderTickWidth, + VSliderWidth +}; + +static const int qt_mac_aqua_metrics[] = { + // Values as of macOS 10.12.4 and Xcode 8.3.1 + 18 /* CheckBoxHeight */, + 18 /* CheckBoxWidth */, + 1 /* EditTextFrameOutset */, + 4 /* FocusRectOutset */, + 22 /* HSliderHeight */, + 5 /* HSliderTickHeight */, + 16 /* LargeProgressBarThickness */, + 17 /* ListHeaderHeight */, + 12 /* MenuSeparatorHeight, aka GetThemeMenuSeparatorHeight */, + 11 /* MiniCheckBoxHeight */, + 11 /* MiniCheckBoxWidth */, + 12 /* MiniHSliderHeight */, + 4 /* MiniHSliderTickHeight */, + 15 /* MiniPopupButtonHeight */, + 16 /* MiniPushButtonHeight */, + 11 /* MiniRadioButtonHeight */, + 11 /* MiniRadioButtonWidth */, + 4 /* MiniVSliderTickWidth */, + 12 /* MiniVSliderWidth */, + 12 /* NormalProgressBarThickness */, + 20 /* PopupButtonHeight */, + 4 /* ProgressBarShadowOutset */, + 20 /* PushButtonHeight */, + 18 /* RadioButtonHeight */, + 18 /* RadioButtonWidth */, + 1 /* SeparatorSize */, + 16 /* SmallCheckBoxHeight */, + 14 /* SmallCheckBoxWidth */, + 15 /* SmallHSliderHeight */, + 4 /* SmallHSliderTickHeight */, + 17 /* SmallPopupButtonHeight */, + 2 /* SmallProgressBarShadowOutset */, + 17 /* SmallPushButtonHeight */, + 15 /* SmallRadioButtonHeight */, + 15 /* SmallRadioButtonWidth */, + 4 /* SmallVSliderTickWidth */, + 15 /* SmallVSliderWidth */, + 5 /* VSliderTickWidth */, + 22 /* VSliderWidth */ +}; + +static inline int qt_mac_aqua_get_metric(QAquaMetric m) +{ + return qt_mac_aqua_metrics[m]; +} + +static QSize qt_aqua_get_known_size(QStyle::ContentsType ct, const QStyleOption *opt, + QSize szHint, QStyleHelper::WidgetSizePolicy sz) +{ + QSize ret(-1, -1); + if (sz != QStyleHelper::SizeSmall && sz != QStyleHelper::SizeLarge && sz != QStyleHelper::SizeMini) { + qDebug("Not sure how to return this..."); + return ret; + } +// if ((widget && widget->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(widget->metaObject()->className(), QFont()); +// if (widget->font().pointSize() > font.pointSize()) +// return ret; +// } + + // TODO: investigate how this function is used. 'ct' can/should be + // filled out correctly in the styleoption already from the styleitem? + Q_ASSERT(ct != QStyle::CT_CustomBase); +// if (ct == QStyle::CT_CustomBase && widget) { +//#if QT_CONFIG(pushbutton) +// if (qobject_cast<const QPushButton *>(widg)) +// ct = QStyle::CT_PushButton; +//#endif +// else if (qobject_cast<const QRadioButton *>(widget)) +// ct = QStyle::CT_RadioButton; +//#if QT_CONFIG(checkbox) +// else if (qobject_cast<const QCheckBox *>(widg)) +// ct = QStyle::CT_CheckBox; +//#endif +//#if QT_CONFIG(combobox) +// else if (qobject_cast<const QComboBox *>(widg)) +// ct = QStyle::CT_ComboBox; +//#endif +//#if QT_CONFIG(toolbutton) +// else if (qobject_cast<const QToolButton *>(widg)) +// ct = QStyle::CT_ToolButton; +//#endif +// else if (qobject_cast<const QSlider *>(widget)) +// ct = QStyle::CT_Slider; +//#if QT_CONFIG(progressbar) +// else if (qobject_cast<const QProgressBar *>(widg)) +// ct = QStyle::CT_ProgressBar; +//#endif +//#if QT_CONFIG(lineedit) +// else if (qobject_cast<const QLineEdit *>(widg)) +// ct = QStyle::CT_LineEdit; +//#endif +//#if QT_CONFIG(itemviews) +// else if (qobject_cast<const QHeaderView *>(widg)) +// ct = QStyle::CT_HeaderSection; +//#endif +//#if QT_CONFIG(menubar) +// else if (qobject_cast<const QMenuBar *>(widg)) +// ct = QStyle::CT_MenuBar; +//#endif +//#if QT_CONFIG(sizegrip) +// else if (qobject_cast<const QSizeGrip *>(widg)) +// ct = QStyle::CT_SizeGrip; +//#endif +// else +// return ret; +// } + + switch (ct) { + case QStyle::CT_PushButton: { + const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(opt); + if (btn) { + QString buttonText = qt_mac_removeMnemonics(btn->text); + if (buttonText.contains(QLatin1Char('\n'))) + ret = QSize(-1, -1); + else if (sz == QStyleHelper::SizeLarge) + ret = QSize(-1, qt_mac_aqua_get_metric(PushButtonHeight)); + else if (sz == QStyleHelper::SizeSmall) + ret = QSize(-1, qt_mac_aqua_get_metric(SmallPushButtonHeight)); + else if (sz == QStyleHelper::SizeMini) + ret = QSize(-1, qt_mac_aqua_get_metric(MiniPushButtonHeight)); + + if (!btn->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() < btn->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. + // FIXME This is not exactly true. Out of context, OK buttons have their + // implicit size calculated the same way as any other button. Inside a + // QDialogButtonBox, their size should be calculated such that the action + // or accept button (i.e., rightmost) and cancel button have the same width. + ret.setWidth(69); + } + } else { + // The only sensible thing to do is to return whatever the style suggests... + if (sz == QStyleHelper::SizeLarge) + ret = QSize(-1, qt_mac_aqua_get_metric(PushButtonHeight)); + else if (sz == QStyleHelper::SizeSmall) + ret = QSize(-1, qt_mac_aqua_get_metric(SmallPushButtonHeight)); + else if (sz == QStyleHelper::SizeMini) + ret = QSize(-1, qt_mac_aqua_get_metric(MiniPushButtonHeight)); + else + // Since there's no default size we return the large size... + ret = QSize(-1, qt_mac_aqua_get_metric(PushButtonHeight)); + } + break; } + case QStyle::CT_SizeGrip: + // Not HIG kosher: mimic what we were doing earlier until we support 4-edge resizing in MDI subwindows + if (sz == QStyleHelper::SizeLarge || sz == QStyleHelper::SizeSmall) { + int s = sz == QStyleHelper::SizeSmall ? 16 : 22; // large: pixel measured from HITheme, small: from my hat + int width = 0; +//#if QT_CONFIG(mdiarea) +// if (widg && qobject_cast<QMdiSubWindow *>(widg->parentWidget())) +// width = s; +//#endif + ret = QSize(width, s); + } + break; + case QStyle::CT_ComboBox: + switch (sz) { + case QStyleHelper::SizeLarge: + ret = QSize(-1, qt_mac_aqua_get_metric(PopupButtonHeight)); + break; + case QStyleHelper::SizeSmall: + ret = QSize(-1, qt_mac_aqua_get_metric(SmallPopupButtonHeight)); + break; + case QStyleHelper::SizeMini: + ret = QSize(-1, qt_mac_aqua_get_metric(MiniPopupButtonHeight)); + break; + default: + break; + } + break; + case QStyle::CT_ToolButton: + if (sz == QStyleHelper::SizeSmall) { + int width = 0, height = 0; + if (szHint == QSize(-1, -1)) { //just 'guess'.. +//#if QT_CONFIG(toolbutton) +// const QStyleOptionToolButton *bt = qstyleoption_cast<const QStyleOptionToolButton *>(opt); +// // If this conversion fails then the widget was not what it claimed to be. +// if(bt) { +// if (!bt->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.horizontalAdvance(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 QStyleOptionSlider *sld = qstyleoption_cast<const QStyleOptionSlider *>(opt); + // If this conversion fails then the widget was not what it claimed to be. + if(sld) { + if (sz == QStyleHelper::SizeLarge) { + if (sld->orientation == Qt::Horizontal) { + w = qt_mac_aqua_get_metric(HSliderHeight); + if (sld->tickPosition != QStyleOptionSlider::NoTicks) + w += qt_mac_aqua_get_metric(HSliderTickHeight); + } else { + w = qt_mac_aqua_get_metric(VSliderWidth); + if (sld->tickPosition != QStyleOptionSlider::NoTicks) + w += qt_mac_aqua_get_metric(VSliderTickWidth); + } + } else if (sz == QStyleHelper::SizeSmall) { + if (sld->orientation == Qt::Horizontal) { + w = qt_mac_aqua_get_metric(SmallHSliderHeight); + if (sld->tickPosition != QStyleOptionSlider::NoTicks) + w += qt_mac_aqua_get_metric(SmallHSliderTickHeight); + } else { + w = qt_mac_aqua_get_metric(SmallVSliderWidth); + if (sld->tickPosition != QStyleOptionSlider::NoTicks) + w += qt_mac_aqua_get_metric(SmallVSliderTickWidth); + } + } else if (sz == QStyleHelper::SizeMini) { + if (sld->orientation == Qt::Horizontal) { + w = qt_mac_aqua_get_metric(MiniHSliderHeight); + if (sld->tickPosition != QStyleOptionSlider::NoTicks) + w += qt_mac_aqua_get_metric(MiniHSliderTickHeight); + } else { + w = qt_mac_aqua_get_metric(MiniVSliderWidth); + if (sld->tickPosition != QStyleOptionSlider::NoTicks) + w += qt_mac_aqua_get_metric(MiniVSliderTickWidth); + } + } + } 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(HSliderHeight); + w += qt_mac_aqua_get_metric(HSliderTickHeight); + } + if (sld->orientation == Qt::Horizontal) + ret.setHeight(w); + else + ret.setWidth(w); + break; + } +//#if QT_CONFIG(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 == QStyleHelper::SizeLarge) +// finalValue = qt_mac_aqua_get_metric(LargeProgressBarThickness) +// + qt_mac_aqua_get_metric(ProgressBarShadowOutset); +// else +// finalValue = qt_mac_aqua_get_metric(NormalProgressBarThickness) +// + qt_mac_aqua_get_metric(SmallProgressBarShadowOutset); +// if (orient == Qt::Horizontal) +// ret.setHeight(finalValue); +// else +// ret.setWidth(finalValue); +// break; +// } +//#endif +//#if QT_CONFIG(combobox) +// case QStyle::CT_LineEdit: +// if (!widg || !qobject_cast<QComboBox *>(widg->parentWidget())) { +// //should I take into account the font dimentions of the lineedit? -Sam +// if (sz == QStyleHelper::SizeLarge) +// ret = QSize(-1, 21); +// else +// ret = QSize(-1, 19); +// } +// break; +//#endif + case QStyle::CT_HeaderSection: +//#if QT_CONFIG(treeview) +// if (isTreeView(widg)) +// ret = QSize(-1, qt_mac_aqua_get_metric(ListHeaderHeight)); +//#endif + break; + case QStyle::CT_MenuBar: + if (sz == QStyleHelper::SizeLarge) { + 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 QStyleHelper::WidgetSizePolicy qt_aqua_guess_size( + const QSize &large, + const QSize &small, + const QSize &mini) +{ + if (large == QSize(-1, -1)) { + if (small != QSize(-1, -1)) + return QStyleHelper::SizeSmall; + if (mini != QSize(-1, -1)) + return QStyleHelper::SizeMini; + return QStyleHelper::SizeDefault; + } else if (small == QSize(-1, -1)) { + if (mini != QSize(-1, -1)) + return QStyleHelper::SizeMini; + return QStyleHelper::SizeLarge; + } else if (mini == QSize(-1, -1)) { + return QStyleHelper::SizeLarge; + } + + if (qEnvironmentVariableIsSet("QWIDGET_ALL_SMALL")) + return QStyleHelper::SizeSmall; + else if (qEnvironmentVariableIsSet("QWIDGET_ALL_MINI")) + return QStyleHelper::SizeMini; + + return QStyleHelper::SizeLarge; +} +#endif + +void QMacStylePrivate::drawFocusRing(QPainter *p, const QRectF &targetRect, int hMargin, int vMargin, const CocoaControl &cw) const +{ + QPainterPath focusRingPath; + focusRingPath.setFillRule(Qt::OddEvenFill); + + qreal hOffset = 0.0; + qreal vOffset = 0.0; + switch (cw.type) { + case Box: + case Button_SquareButton: + case SegmentedControl_Middle: + case TextField: { + auto innerRect = targetRect; + if (cw.type == TextField) + innerRect = innerRect.adjusted(hMargin, vMargin, -hMargin, -vMargin).adjusted(0.5, 0.5, -0.5, -0.5); + const auto outerRect = innerRect.adjusted(-focusRingWidth, -focusRingWidth, focusRingWidth, focusRingWidth); + const auto outerRadius = focusRingWidth; + focusRingPath.addRect(innerRect); + focusRingPath.addRoundedRect(outerRect, outerRadius, outerRadius); + break; + } + case Button_CheckBox: { + const auto cbInnerRadius = (cw.size == QStyleHelper::SizeMini ? 2.0 : 3.0); + const auto cbSize = cw.size == QStyleHelper::SizeLarge ? 13 : + cw.size == QStyleHelper::SizeSmall ? 11 : 9; // As measured + hOffset = hMargin + (cw.size == QStyleHelper::SizeLarge ? 2.5 : + cw.size == QStyleHelper::SizeSmall ? 2.0 : 1.0); // As measured + vOffset = 0.5 * qreal(targetRect.height() - cbSize); + const auto cbInnerRect = QRectF(0, 0, cbSize, cbSize); + const auto cbOuterRadius = cbInnerRadius + focusRingWidth; + const auto cbOuterRect = cbInnerRect.adjusted(-focusRingWidth, -focusRingWidth, focusRingWidth, focusRingWidth); + focusRingPath.addRoundedRect(cbOuterRect, cbOuterRadius, cbOuterRadius); + focusRingPath.addRoundedRect(cbInnerRect, cbInnerRadius, cbInnerRadius); + break; + } + case Button_RadioButton: { + const auto rbSize = cw.size == QStyleHelper::SizeLarge ? 15 : + cw.size == QStyleHelper::SizeSmall ? 13 : 9; // As measured + hOffset = hMargin + (cw.size == QStyleHelper::SizeLarge ? 1.5 : + cw.size == QStyleHelper::SizeSmall ? 1.0 : 1.0); // As measured + vOffset = 0.5 * qreal(targetRect.height() - rbSize); + const auto rbInnerRect = QRectF(0, 0, rbSize, rbSize); + const auto rbOuterRect = rbInnerRect.adjusted(-focusRingWidth, -focusRingWidth, focusRingWidth, focusRingWidth); + focusRingPath.addEllipse(rbInnerRect); + focusRingPath.addEllipse(rbOuterRect); + break; + } + case Button_PopupButton: + case Button_PullDown: + case Button_PushButton: + case SegmentedControl_Single: { + const qreal innerRadius = cw.type == Button_PushButton ? 3 : 4; + const qreal outerRadius = innerRadius + focusRingWidth; + hOffset = targetRect.left(); + vOffset = targetRect.top(); + const auto innerRect = targetRect.translated(-targetRect.topLeft()); + const auto outerRect = innerRect.adjusted(-hMargin, -vMargin, hMargin, vMargin); + focusRingPath.addRoundedRect(innerRect, innerRadius, innerRadius); + focusRingPath.addRoundedRect(outerRect, outerRadius, outerRadius); + break; + } + case ComboBox: + case SegmentedControl_First: + case SegmentedControl_Last: { + hOffset = targetRect.left(); + vOffset = targetRect.top(); + const qreal innerRadius = 8; + const qreal outerRadius = innerRadius + focusRingWidth; + const auto innerRect = targetRect.translated(-targetRect.topLeft()); + const auto outerRect = innerRect.adjusted(-hMargin, -vMargin, hMargin, vMargin); + + const auto cbFocusFramePath = [](const QRectF &rect, qreal tRadius, qreal bRadius) { + QPainterPath path; + + if (tRadius > 0) { + const auto topLeftCorner = QRectF(rect.topLeft(), QSizeF(tRadius, tRadius)); + path.arcMoveTo(topLeftCorner, 180); + path.arcTo(topLeftCorner, 180, -90); + } else { + path.moveTo(rect.topLeft()); + } + const auto rightEdge = rect.right() - bRadius; + path.arcTo(rightEdge, rect.top(), bRadius, bRadius, 90, -90); + path.arcTo(rightEdge, rect.bottom() - bRadius, bRadius, bRadius, 0, -90); + if (tRadius > 0) + path.arcTo(rect.left(), rect.bottom() - tRadius, tRadius, tRadius, 270, -90); + else + path.lineTo(rect.bottomLeft()); + path.closeSubpath(); + + return path; + }; + + const auto innerPath = cbFocusFramePath(innerRect, 0, innerRadius); + focusRingPath.addPath(innerPath); + const auto outerPath = cbFocusFramePath(outerRect, 2 * focusRingWidth, outerRadius); + focusRingPath.addPath(outerPath); + break; + } + default: + Q_UNREACHABLE(); + } + + auto focusRingColor = qt_mac_toQColor(NSColor.keyboardFocusIndicatorColor.CGColor); + if (!qt_mac_applicationIsInDarkMode()) { + // This color already has alpha ~ 0.25, this value is too small - the ring is + // very pale and nothing like the native one. 0.39 makes it better (not ideal + // anyway). The color seems to be correct in dark more without any modification. + focusRingColor.setAlphaF(0.39); + } + + p->save(); + p->setRenderHint(QPainter::Antialiasing); + + if (cw.type == SegmentedControl_First) { + // TODO Flip left-right + } + p->translate(hOffset, vOffset); + p->fillPath(focusRingPath, focusRingColor); + p->restore(); +} + +QPainterPath QMacStylePrivate::windowPanelPath(const QRectF &r) const +{ + static const qreal CornerPointOffset = 5.5; + static const qreal CornerControlOffset = 2.1; + + QPainterPath path; + // Top-left corner + path.moveTo(r.left(), r.top() + CornerPointOffset); + path.cubicTo(r.left(), r.top() + CornerControlOffset, + r.left() + CornerControlOffset, r.top(), + r.left() + CornerPointOffset, r.top()); + // Top-right corner + path.lineTo(r.right() - CornerPointOffset, r.top()); + path.cubicTo(r.right() - CornerControlOffset, r.top(), + r.right(), r.top() + CornerControlOffset, + r.right(), r.top() + CornerPointOffset); + // Bottom-right corner + path.lineTo(r.right(), r.bottom() - CornerPointOffset); + path.cubicTo(r.right(), r.bottom() - CornerControlOffset, + r.right() - CornerControlOffset, r.bottom(), + r.right() - CornerPointOffset, r.bottom()); + // Bottom-right corner + path.lineTo(r.left() + CornerPointOffset, r.bottom()); + path.cubicTo(r.left() + CornerControlOffset, r.bottom(), + r.left(), r.bottom() - CornerControlOffset, + r.left(), r.bottom() - CornerPointOffset); + path.lineTo(r.left(), r.top() + CornerPointOffset); + + return path; +} + +QMacStylePrivate::CocoaControlType QMacStylePrivate::windowButtonCocoaControl(QStyle::SubControl sc) const +{ + struct WindowButtons { + QStyle::SubControl sc; + QMacStylePrivate::CocoaControlType ct; + }; + + static const WindowButtons buttons[] = { + { QStyle::SC_TitleBarCloseButton, QMacStylePrivate::Button_WindowClose }, + { QStyle::SC_TitleBarMinButton, QMacStylePrivate::Button_WindowMiniaturize }, + { QStyle::SC_TitleBarMaxButton, QMacStylePrivate::Button_WindowZoom } + }; + + for (const auto &wb : buttons) + if (wb.sc == sc) + return wb.ct; + + return NoControl; +} + + +void QMacStylePrivate::tabLayout(const QStyleOptionTab *opt, QRect *textRect, QRect *iconRect) const +{ + Q_ASSERT(textRect); + Q_ASSERT(iconRect); + QRect tr = opt->rect; + const bool verticalTabs = opt->shape == QStyleOptionTab::RoundedEast + || opt->shape == QStyleOptionTab::RoundedWest + || opt->shape == QStyleOptionTab::TriangularEast + || opt->shape == QStyleOptionTab::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); + int horizontalShift = proxyStyle->pixelMetric(QStyle::PM_TabBarTabShiftHorizontal, opt); + const int hpadding = 4; + const int vpadding = proxyStyle->pixelMetric(QStyle::PM_TabBarTabVSpace, opt) / 2; + if (opt->shape == QStyleOptionTab::RoundedSouth || opt->shape == QStyleOptionTab::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); + } + + // icon + if (!opt->icon.isNull()) { + QSize iconSize = opt->iconSize; + if (!iconSize.isValid()) { + int iconExtent = proxyStyle->pixelMetric(QStyle::PM_SmallIconSize); + iconSize = QSize(iconExtent, iconExtent); + } + QSize tabIconSize = opt->icon.actualSize(iconSize, + (opt->state & QStyle::State_Enabled) ? QIcon::Normal : QIcon::Disabled, + (opt->state & QStyle::State_Selected) ? QIcon::On : QIcon::Off); + // High-dpi icons do not need adjustment; make sure tabIconSize is not larger than iconSize + tabIconSize = QSize(qMin(tabIconSize.width(), iconSize.width()), qMin(tabIconSize.height(), iconSize.height())); + + const int stylePadding = proxyStyle->pixelMetric(QStyle::PM_TabBarTabHSpace, opt) / 2 - hpadding; + + if (opt->documentMode) { + // documents show the icon as part of the the text + const int textWidth = + opt->fontMetrics.boundingRect(tr, Qt::AlignCenter | Qt::TextShowMnemonic, opt->text).width(); + *iconRect = QRect(tr.center().x() - textWidth / 2 - stylePadding - tabIconSize.width(), + tr.center().y() - tabIconSize.height() / 2, + tabIconSize.width(), tabIconSize.height()); + } else { + *iconRect = QRect(tr.left() + stylePadding, tr.center().y() - tabIconSize.height() / 2, + tabIconSize.width(), tabIconSize.height()); + } + if (!verticalTabs) + *iconRect = proxyStyle->visualRect(opt->direction, opt->rect, *iconRect); + + tr.setLeft(tr.left() + stylePadding + tabIconSize.width() + 4); + tr.setRight(tr.right() - stylePadding - tabIconSize.width() - 4); + } + + if (!verticalTabs) + tr = proxyStyle->visualRect(opt->direction, opt->rect, tr); + + *textRect = tr; +} + +QMacStylePrivate::Direction QMacStylePrivate::tabDirection(QStyleOptionTab::Shape shape) +{ + switch (shape) { + case QStyleOptionTab::RoundedSouth: + case QStyleOptionTab::TriangularSouth: + return South; + case QStyleOptionTab::RoundedNorth: + case QStyleOptionTab::TriangularNorth: + return North; + case QStyleOptionTab::RoundedWest: + case QStyleOptionTab::TriangularWest: + return West; + case QStyleOptionTab::RoundedEast: + case QStyleOptionTab::TriangularEast: + return East; + } +} + +bool QMacStylePrivate::verticalTabs(QMacStylePrivate::Direction direction) +{ + return (direction == QMacStylePrivate::East + || direction == QMacStylePrivate::West); +} + +QStyleHelper::WidgetSizePolicy QMacStylePrivate::effectiveAquaSizeConstrain(const QStyleOption *option, + QStyle::ContentsType ct, + QSize szHint, QSize *insz) const +{ + QStyleHelper::WidgetSizePolicy sz = aquaSizeConstrain(option, ct, szHint, insz); + if (sz == QStyleHelper::SizeDefault) + return QStyleHelper::SizeLarge; + return sz; +} + +QStyleHelper::WidgetSizePolicy QMacStylePrivate::aquaSizeConstrain(const QStyleOption *option, + QStyle::ContentsType ct, QSize szHint, QSize *insz) const +{ + if (!option) + return QStyleHelper::SizeLarge; + + if (option->state & QStyle::State_Small) + return QStyleHelper::SizeSmall; + if (option->state & QStyle::State_Mini) + return QStyleHelper::SizeMini; + + return QStyleHelper::SizeLarge; + +} + +uint qHash(const QMacStylePrivate::CocoaControl &cw, uint seed = 0) +{ + return ((cw.type << 2) | cw.size) ^ seed; +} + +QMacStylePrivate::CocoaControl::CocoaControl() + : type(NoControl), size(QStyleHelper::SizeDefault) +{ +} + +QMacStylePrivate::CocoaControl::CocoaControl(CocoaControlType t, QStyleHelper::WidgetSizePolicy s) + : type(t), size(s) +{ +} + +bool QMacStylePrivate::CocoaControl::operator==(const CocoaControl &other) const +{ + return other.type == type && other.size == size; +} + +QSizeF QMacStylePrivate::CocoaControl::defaultFrameSize() const +{ + // We need this because things like NSView.alignmentRectInsets + // or -[NSCell titleRectForBounds:] won't work unless the control + // has a reasonable frame set. IOW, it's a chicken and egg problem. + // These values are as observed in Xcode 9's Interface Builder. + + if (type == Button_PushButton) + return QSizeF(-1, pushButtonDefaultHeight[size]); + + if (type == Button_PopupButton + || type == Button_PullDown) + return QSizeF(-1, popupButtonDefaultHeight[size]); + + if (type == ComboBox) + return QSizeF(-1, comboBoxDefaultHeight[size]); + + return QSizeF(); +} + +QRectF QMacStylePrivate::CocoaControl::adjustedControlFrame(const QRectF &rect) const +{ + QRectF frameRect; + const auto frameSize = defaultFrameSize(); + if (type == QMacStylePrivate::Button_SquareButton) { + frameRect = rect.adjusted(3, 1, -3, -1) + .adjusted(focusRingWidth, focusRingWidth, -focusRingWidth, -focusRingWidth); + } else if (type == QMacStylePrivate::Button_PushButton) { + // Start from the style option's top-left corner. + frameRect = QRectF(rect.topLeft(), + QSizeF(rect.width(), frameSize.height())); + if (size == QStyleHelper::SizeSmall) + frameRect = frameRect.translated(0, 1.5); + else if (size == QStyleHelper::SizeMini) + frameRect = frameRect.adjusted(0, 0, -8, 0).translated(4, 4); + } else { + // Center in the style option's rect. + frameRect = QRectF(QPointF(0, (rect.height() - frameSize.height()) / 2.0), + QSizeF(rect.width(), frameSize.height())); + frameRect = frameRect.translated(rect.topLeft()); + if (type == QMacStylePrivate::Button_PullDown || type == QMacStylePrivate::Button_PopupButton) { + if (size == QStyleHelper::SizeLarge) + frameRect = frameRect.adjusted(0, 0, -6, 0).translated(3, 0); + else if (size == QStyleHelper::SizeSmall) + frameRect = frameRect.adjusted(0, 0, -4, 0).translated(2, 1); + else if (size == QStyleHelper::SizeMini) + frameRect = frameRect.adjusted(0, 0, -9, 0).translated(5, 0); + } else if (type == QMacStylePrivate::ComboBox) { + frameRect = frameRect.adjusted(0, 0, -6, 0).translated(4, 0); + } + } + + return frameRect; +} + +QMarginsF QMacStylePrivate::CocoaControl::titleMargins() const +{ + if (type == QMacStylePrivate::Button_PushButton) { + if (size == QStyleHelper::SizeLarge) + return QMarginsF(12, 5, 12, 9); + if (size == QStyleHelper::SizeSmall) + return QMarginsF(12, 4, 12, 9); + if (size == QStyleHelper::SizeMini) + return QMarginsF(10, 1, 10, 2); + } + + if (type == QMacStylePrivate::Button_PullDown) { + if (size == QStyleHelper::SizeLarge) + return QMarginsF(7.5, 2.5, 22.5, 5.5); + if (size == QStyleHelper::SizeSmall) + return QMarginsF(7.5, 2, 20.5, 4); + if (size == QStyleHelper::SizeMini) + return QMarginsF(4.5, 0, 16.5, 2); + } + + if (type == QMacStylePrivate::Button_SquareButton) + return QMarginsF(6, 1, 6, 2); + + return QMarginsF(); +} + +bool QMacStylePrivate::CocoaControl::getCocoaButtonTypeAndBezelStyle(NSButtonType *buttonType, NSBezelStyle *bezelStyle) const +{ + switch (type) { + case Button_CheckBox: + *buttonType = NSSwitchButton; + *bezelStyle = NSRegularSquareBezelStyle; + break; + case Button_Disclosure: + *buttonType = NSOnOffButton; + *bezelStyle = NSDisclosureBezelStyle; + break; + case Button_RadioButton: + *buttonType = NSRadioButton; + *bezelStyle = NSRegularSquareBezelStyle; + break; + case Button_SquareButton: + *buttonType = NSPushOnPushOffButton; + *bezelStyle = NSShadowlessSquareBezelStyle; + break; + case Button_PushButton: + *buttonType = NSPushOnPushOffButton; + *bezelStyle = NSRoundedBezelStyle; + break; + default: + return false; + } + + return true; +} + +QMacStylePrivate::CocoaControlType cocoaControlType(const QStyleOption *opt) +{ + if (const auto *btn = qstyleoption_cast<const QStyleOptionButton *>(opt)) { + const bool hasMenu = btn->features & QStyleOptionButton::HasMenu; + // When the contents won't fit in a large sized button, + // and WA_MacNormalSize is not set, make the button square. + // Threshold used to be at 34, not 32. + const auto maxNonSquareHeight = pushButtonDefaultHeight[QStyleHelper::SizeLarge]; + const bool isSquare = (btn->features & QStyleOptionButton::Flat) + || (btn->rect.height() > maxNonSquareHeight); +// && !(w && w->testAttribute(Qt::WA_MacNormalSize))); + return (isSquare? QMacStylePrivate::Button_SquareButton : + hasMenu ? QMacStylePrivate::Button_PullDown : + QMacStylePrivate::Button_PushButton); + } + + if (const auto *combo = qstyleoption_cast<const QStyleOptionComboBox *>(opt)) { + if (combo->editable) + return QMacStylePrivate::ComboBox; + // TODO Me may support square, non-editable combo boxes, but not more than that + return QMacStylePrivate::Button_PopupButton; + } + + return QMacStylePrivate::NoControl; +} + +/** + 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. +*/ +CGRect QMacStylePrivate::comboboxInnerBounds(const CGRect &outerBounds, const CocoaControl &cocoaWidget) +{ + CGRect innerBounds = outerBounds; + // Carbon draw parts of the view outside the rect. + // So make the rect a bit smaller to compensate + // (I wish HIThemeGetButtonBackgroundBounds worked) + if (cocoaWidget.type == Button_PopupButton) { + switch (cocoaWidget.size) { + case QStyleHelper::SizeSmall: + innerBounds.origin.x += 3; + innerBounds.origin.y += 3; + innerBounds.size.width -= 6; + innerBounds.size.height -= 7; + break; + case QStyleHelper::SizeMini: + innerBounds.origin.x += 2; + innerBounds.origin.y += 2; + innerBounds.size.width -= 5; + innerBounds.size.height -= 6; + break; + case QStyleHelper::SizeLarge: + case QStyleHelper::SizeDefault: + innerBounds.origin.x += 2; + innerBounds.origin.y += 2; + innerBounds.size.width -= 5; + innerBounds.size.height -= 6; + } + } else if (cocoaWidget.type == ComboBox) { + switch (cocoaWidget.size) { + case QStyleHelper::SizeSmall: + innerBounds.origin.x += 3; + innerBounds.origin.y += 3; + innerBounds.size.width -= 7; + innerBounds.size.height -= 8; + break; + case QStyleHelper::SizeMini: + innerBounds.origin.x += 3; + innerBounds.origin.y += 3; + innerBounds.size.width -= 4; + innerBounds.size.height -= 8; + break; + case QStyleHelper::SizeLarge: + case QStyleHelper::SizeDefault: + innerBounds.origin.x += 3; + innerBounds.origin.y += 2; + innerBounds.size.width -= 6; + innerBounds.size.height -= 8; + } + } + + 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. +*/ +QRectF QMacStylePrivate::comboboxEditBounds(const QRectF &outerBounds, const CocoaControl &cw) +{ + QRectF ret = outerBounds; + if (cw.type == ComboBox) { + switch (cw.size) { + case QStyleHelper::SizeLarge: + ret = ret.adjusted(0, 0, -25, 0).translated(2, 4.5); + ret.setHeight(16); + break; + case QStyleHelper::SizeSmall: + ret = ret.adjusted(0, 0, -22, 0).translated(2, 3); + ret.setHeight(14); + break; + case QStyleHelper::SizeMini: + ret = ret.adjusted(0, 0, -19, 0).translated(2, 2.5); + ret.setHeight(10.5); + break; + default: + break; + } + } else if (cw.type == Button_PopupButton) { + switch (cw.size) { + case QStyleHelper::SizeLarge: + ret.adjust(10, 1, -23, -4); + break; + case QStyleHelper::SizeSmall: + ret.adjust(10, 4, -20, -3); + break; + case QStyleHelper::SizeMini: + ret.adjust(9, 0, -19, 0); + ret.setHeight(13); + break; + default: + break; + } + } + return ret; +} + +QMacStylePrivate::QMacStylePrivate() + : backingStoreNSView(nil) +{ + if (auto *ssf = QGuiApplicationPrivate::platformTheme()->font(QPlatformTheme::SmallFont)) + smallSystemFont = *ssf; + if (auto *msf = QGuiApplicationPrivate::platformTheme()->font(QPlatformTheme::MiniFont)) + miniSystemFont = *msf; +} + +QMacStylePrivate::~QMacStylePrivate() +{ + QMacAutoReleasePool pool; + for (NSView *b : cocoaControls) + [b release]; + for (NSCell *cell : cocoaCells) + [cell release]; +} + +NSView *QMacStylePrivate::cocoaControl(CocoaControl cocoaControl) const +{ + if (cocoaControl.type == QMacStylePrivate::NoControl + || cocoaControl.size == QStyleHelper::SizeDefault) + return nil; + + if (cocoaControl.type == Box) { + if (__builtin_available(macOS 10.14, *)) { + if (qt_mac_applicationIsInDarkMode()) { + // See render code in drawPrimitive(PE_FrameTabWidget) + cocoaControl.type = Box_Dark; + } + } + } + + NSView *bv = cocoaControls.value(cocoaControl, nil); + if (!bv) { + switch (cocoaControl.type) { + case Box: { + NSBox *box = [[NSBox alloc] init]; + bv = box; + box.title = @""; + box.titlePosition = NSNoTitle; + break; + } + case Box_Dark: + bv = [[QDarkNSBox alloc] init]; + break; + case Button_CheckBox: + case Button_Disclosure: + case Button_PushButton: + case Button_RadioButton: + case Button_SquareButton: { + NSButton *bc = [[NSButton alloc] init]; + bc.title = @""; + // See below for style and bezel setting. + bv = bc; + break; + } + case Button_PopupButton: + case Button_PullDown: { + NSPopUpButton *bc = [[NSPopUpButton alloc] init]; + bc.title = @""; + if (cocoaControl.type == Button_PullDown) + bc.pullsDown = YES; + bv = bc; + break; + } + case Button_WindowClose: + case Button_WindowMiniaturize: + case Button_WindowZoom: { + const NSWindowButton button = [=] { + switch (cocoaControl.type) { + case Button_WindowClose: + return NSWindowCloseButton; + case Button_WindowMiniaturize: + return NSWindowMiniaturizeButton; + case Button_WindowZoom: + return NSWindowZoomButton; + default: + break; + } + Q_UNREACHABLE(); + } (); + const auto styleMask = NSWindowStyleMaskTitled + | NSWindowStyleMaskClosable + | NSWindowStyleMaskMiniaturizable + | NSWindowStyleMaskResizable; + bv = [NSWindow standardWindowButton:button forStyleMask:styleMask]; + [bv retain]; + break; + } + case ComboBox: + bv = [[NSComboBox alloc] init]; + break; + case ProgressIndicator_Determinate: + bv = [[NSProgressIndicator alloc] init]; + break; + case ProgressIndicator_Indeterminate: + bv = [[QIndeterminateProgressIndicator alloc] init]; + break; + case Scroller_Horizontal: + bv = [[NSScroller alloc] initWithFrame:NSMakeRect(0, 0, 200, 20)]; + break; + case Scroller_Vertical: + // Cocoa sets the orientation from the view's frame + // at construction time, and it cannot be changed later. + bv = [[NSScroller alloc] initWithFrame:NSMakeRect(0, 0, 20, 200)]; + break; + case Slider_Horizontal: + bv = [[NSSlider alloc] initWithFrame:NSMakeRect(0, 0, 200, 20)]; + break; + case Slider_Vertical: + // 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, 20, 200)]; + break; + case SplitView_Horizontal: + bv = [[NSSplitView alloc] init]; + break; + case SplitView_Vertical: + bv = [[QVerticalSplitView alloc] init]; + break; + case TextField: + bv = [[NSTextField alloc] init]; + break; + default: + break; + } + + if ([bv isKindOfClass:[NSControl class]]) { + auto *ctrl = static_cast<NSControl *>(bv); + switch (cocoaControl.size) { + case QStyleHelper::SizeSmall: + ctrl.controlSize = NSControlSizeSmall; + break; + case QStyleHelper::SizeMini: + ctrl.controlSize = NSControlSizeMini; + break; + default: + break; + } + } else if (cocoaControl.type == ProgressIndicator_Determinate || + cocoaControl.type == ProgressIndicator_Indeterminate) { + auto *pi = static_cast<NSProgressIndicator *>(bv); + pi.indeterminate = (cocoaControl.type == ProgressIndicator_Indeterminate); + switch (cocoaControl.size) { + case QStyleHelper::SizeSmall: + pi.controlSize = NSControlSizeSmall; + break; + case QStyleHelper::SizeMini: + pi.controlSize = NSControlSizeMini; + break; + default: + break; + } + } + + cocoaControls.insert(cocoaControl, bv); + } + + NSButtonType buttonType; + NSBezelStyle bezelStyle; + if (cocoaControl.getCocoaButtonTypeAndBezelStyle(&buttonType, &bezelStyle)) { + // FIXME We need to reset the button's type and + // bezel style properties, even when cached. + auto *button = static_cast<NSButton *>(bv); + button.buttonType = buttonType; + button.bezelStyle = bezelStyle; + if (cocoaControl.type == Button_CheckBox) + button.allowsMixedState = YES; + } + + return bv; +} + +NSCell *QMacStylePrivate::cocoaCell(CocoaControl cocoaControl) const +{ + NSCell *cell = cocoaCells[cocoaControl]; + if (!cell) { + switch (cocoaControl.type) { + case Stepper: + cell = [[NSStepperCell alloc] init]; + break; + case Button_Disclosure: { + NSButtonCell *bc = [[NSButtonCell alloc] init]; + bc.buttonType = NSOnOffButton; + bc.bezelStyle = NSDisclosureBezelStyle; + cell = bc; + break; + } + default: + break; + } + + switch (cocoaControl.size) { + case QStyleHelper::SizeSmall: + cell.controlSize = NSControlSizeSmall; + break; + case QStyleHelper::SizeMini: + cell.controlSize = NSControlSizeMini; + break; + default: + break; + } + + cocoaCells.insert(cocoaControl, cell); + } + + return cell; +} + +void QMacStylePrivate::drawNSViewInRect(NSView *view, const QRectF &rect, QPainter *p, DrawRectBlock drawRectBlock) const +{ + QMacAutoReleasePool pool; + QMacCGContext ctx(p); + setupNSGraphicsContext(ctx, YES); + + // FIXME: The rect that we get in is relative to the widget that we're drawing + // style on behalf of, and doesn't take into account the offset of that widget + // to the widget that owns the backingstore, which we are placing the native + // view into below. This means most of the views are placed in the upper left + // corner of backingStoreNSView, which does not map to where the actual widget + // is, and which may cause problems such as triggering a setNeedsDisplay of the + // backingStoreNSView for the wrong rect. We work around this by making the view + // layer-backed, which prevents triggering display of the backingStoreNSView, but + // but there may be other issues lurking here due to the wrong position. QTBUG-68023 + view.wantsLayer = YES; + + // FIXME: We are also setting the frame of the incoming view a lot at the call + // sites of this function, making it unclear who's actually responsible for + // maintaining the size and position of the view. In theory the call sites + // should ensure the _size_ of the view is correct, and then let this code + // take care of _positioning_ the view at the right place inside backingStoreNSView. + // For now we pass on the rect as is, to prevent any regressions until this + // can be investigated properly. + view.frame = rect.toCGRect(); + + [backingStoreNSView addSubview:view]; + + // FIXME: Based on the code below, this method isn't drawing an NSView into + // a rect, it's drawing _part of the NSView_, defined by the incoming clip + // or dirty rect, into the current graphics context. We're doing some manual + // translations at the call sites that would indicate that this relationship + // is a bit fuzzy. + const CGRect dirtyRect = rect.toCGRect(); + + if (drawRectBlock) + drawRectBlock(ctx, dirtyRect); + else + [view drawRect:dirtyRect]; + + [view removeFromSuperviewWithoutNeedingDisplay]; + + restoreNSGraphicsContext(ctx); +} + +void QMacStylePrivate::resolveCurrentNSView(QWindow *window) const +{ + backingStoreNSView = window ? (NSView *)window->winId() : nil; +} + +QMacStyle::QMacStyle() + : QCommonStyle(*new QMacStylePrivate) +{ + QMacAutoReleasePool pool; + + static QMacNotificationObserver scrollbarStyleObserver(nil, + NSPreferredScrollerStyleDidChangeNotification, []() { + // Purge destroyed scroll bars + QMacStylePrivate::scrollBars.removeAll(QPointer<QObject>()); + + QEvent event(QEvent::StyleChange); + for (const auto &o : QMacStylePrivate::scrollBars) + QCoreApplication::sendEvent(o, &event); + }); + +#if QT_MACOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_14) + Q_D(QMacStyle); + // FIXME: Tie this logic into theme change, or even polish/unpolish + if (QOperatingSystemVersion::current() >= QOperatingSystemVersion::MacOSMojave) { + d->appearanceObserver = QMacKeyValueObserver(NSApp, @"effectiveAppearance", [this] { + Q_D(QMacStyle); + for (NSView *b : d->cocoaControls) + [b release]; + d->cocoaControls.clear(); + }); + } +#endif +} + +QMacStyle::~QMacStyle() +{ +} + +int QMacStyle::pixelMetric(PixelMetric metric, const QStyleOption *opt) const +{ + Q_D(const QMacStyle); + const int controlSize = getControlSize(opt); + int 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: + ret = qt_mac_aqua_get_metric(FocusRectOutset); + break; + case PM_DialogButtonsSeparator: + ret = -5; + break; + case PM_DialogButtonsButtonHeight: { + QSize sz; + ret = d->aquaSizeConstrain(opt, 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, 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 PM_MenuButtonIndicator: + ret = toolButtonArrowSize; + break; + + case QStyle::PM_MenuDesktopFrameWidth: + ret = 5; + break; + + case PM_CheckBoxLabelSpacing: + case PM_RadioButtonLabelSpacing: + ret = [=] { + if (opt) { + if (opt->state & State_Mini) + return 4; + if (opt->state & State_Small) + return 3; + } + return 2; + } (); + break; + case PM_MenuScrollerHeight: + ret = 15; // I hate having magic numbers in here... + break; + case PM_DefaultFrameWidth: +//#if QT_CONFIG(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: + ret = qt_mac_aqua_get_metric(EditTextFrameOutset); + 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 & QStyleOptionSlider::TicksAbove) + ++n; + if (ticks & QStyleOptionSlider::TicksBelow) + ++n; + if (!n) { + ret = space; + break; + } + + int thick = 6; // Magic constant to get 5 + 16 + 5 + if (ticks != QStyleOptionSlider::TicksBothSides && ticks != QStyleOptionSlider::NoTicks) + thick += proxy()->pixelMetric(PM_SliderLength, sl) / 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., opt)); + break; + + case PM_LargeIconSize: + ret = int(QStyleHelper::dpiScaled(32., opt)); + break; + + case PM_IconViewIconSize: + ret = proxy()->pixelMetric(PM_LargeIconSize, opt); + break; + + case PM_ButtonDefaultIndicator: + ret = 0; + break; + case PM_TitleBarHeight: { + NSUInteger style = NSWindowStyleMaskTitled; +// if (widget && ((widget->windowFlags() & Qt::Tool) == Qt::Tool)) +// style |= NSWindowStyleMaskUtilityWindow; + ret = int([NSWindow frameRectForContentRect:NSZeroRect + styleMask:style].size.height); + break; } + case QStyle::PM_TabBarTabHSpace: + switch (d->aquaSizeConstrain(opt)) { + case QStyleHelper::SizeLarge: + ret = QCommonStyle::pixelMetric(metric, opt); + break; + case QStyleHelper::SizeSmall: + ret = 20; + break; + case QStyleHelper::SizeMini: + ret = 16; + break; + case QStyleHelper::SizeDefault: + const QStyleOptionTab *tb = qstyleoption_cast<const QStyleOptionTab *>(opt); + if (tb && tb->documentMode) + ret = 30; + else + ret = QCommonStyle::pixelMetric(metric, opt); + break; + } + break; + case PM_TabBarTabVSpace: + ret = 4; + break; + case PM_TabBarTabShiftHorizontal: + case PM_TabBarTabShiftVertical: + ret = 0; + break; + case PM_TabBarBaseHeight: + ret = 0; + break; + case PM_TabBarTabOverlap: + ret = 1; + break; + case PM_TabBarBaseOverlap: + switch (d->aquaSizeConstrain(opt)) { + case QStyleHelper::SizeDefault: + case QStyleHelper::SizeLarge: + ret = 11; + break; + case QStyleHelper::SizeSmall: + ret = 8; + break; + case QStyleHelper::SizeMini: + ret = 7; + break; + } + break; + case PM_ScrollBarExtent: { + const QStyleHelper::WidgetSizePolicy size = d->effectiveAquaSizeConstrain(opt); + ret = static_cast<int>([NSScroller + scrollerWidthForControlSize:static_cast<NSControlSize>(size) + scrollerStyle:[NSScroller preferredScrollerStyle]]); + break; } + case PM_IndicatorHeight: { + switch (d->aquaSizeConstrain(opt)) { + case QStyleHelper::SizeDefault: + case QStyleHelper::SizeLarge: + ret = qt_mac_aqua_get_metric(CheckBoxHeight); + break; + case QStyleHelper::SizeMini: + ret = qt_mac_aqua_get_metric(MiniCheckBoxHeight); + break; + case QStyleHelper::SizeSmall: + ret = qt_mac_aqua_get_metric(SmallCheckBoxHeight); + break; + } + break; } + case PM_IndicatorWidth: { + switch (d->aquaSizeConstrain(opt)) { + case QStyleHelper::SizeDefault: + case QStyleHelper::SizeLarge: + ret = qt_mac_aqua_get_metric(CheckBoxWidth); + break; + case QStyleHelper::SizeMini: + ret = qt_mac_aqua_get_metric(MiniCheckBoxWidth); + break; + case QStyleHelper::SizeSmall: + ret = qt_mac_aqua_get_metric(SmallCheckBoxWidth); + break; + } + ++ret; + break; } + case PM_ExclusiveIndicatorHeight: { + switch (d->aquaSizeConstrain(opt)) { + case QStyleHelper::SizeDefault: + case QStyleHelper::SizeLarge: + ret = qt_mac_aqua_get_metric(RadioButtonHeight); + break; + case QStyleHelper::SizeMini: + ret = qt_mac_aqua_get_metric(MiniRadioButtonHeight); + break; + case QStyleHelper::SizeSmall: + ret = qt_mac_aqua_get_metric(SmallRadioButtonHeight); + break; + } + break; } + case PM_ExclusiveIndicatorWidth: { + switch (d->aquaSizeConstrain(opt)) { + case QStyleHelper::SizeDefault: + case QStyleHelper::SizeLarge: + ret = qt_mac_aqua_get_metric(RadioButtonWidth); + break; + case QStyleHelper::SizeMini: + ret = qt_mac_aqua_get_metric(MiniRadioButtonWidth); + break; + case QStyleHelper::SizeSmall: + ret = qt_mac_aqua_get_metric(SmallRadioButtonWidth); + break; + } + ++ret; + break; } + case PM_MenuVMargin: + ret = 4; + break; + case PM_MenuPanelWidth: + ret = 0; + break; + case PM_ToolTipLabelFrameWidth: + ret = 0; + break; + case PM_SizeGripSize: { + QStyleHelper::WidgetSizePolicy aSize; +// if (widget && widget->window()->windowType() == Qt::Tool) +// aSize = QStyleHelper::SizeSmall; +// else + aSize = QStyleHelper::SizeLarge; + const QSize size = qt_aqua_get_known_size(CT_SizeGrip, opt, 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 = 7; + break; + case PM_LayoutLeftMargin: + case PM_LayoutTopMargin: + case PM_LayoutRightMargin: + case PM_LayoutBottomMargin: + { + if (opt->state & State_Window) { + /* + 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) : 0; + break; + default: + ret = QCommonStyle::pixelMetric(metric, opt); + break; + } + return ret; +} + +//QPalette QMacStyle::standardPalette() const +//{ +// auto platformTheme = QGuiApplicationPrivate::platformTheme(); +// auto styleNames = platformTheme->themeHint(QPlatformTheme::StyleNames); +// if (styleNames.toStringList().contains("macintosh")) +// return QPalette(); // Inherit everything from theme +// else +// return QStyle::standardPalette(); +//} + +int QMacStyle::styleHint(StyleHint sh, const QStyleOption *opt, QStyleHintReturn *hret) const +{ + QMacAutoReleasePool pool; + + int 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, 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_Slider_StopMouseOverSlider: + ret = true; + break; + case SH_ListViewExpand_SelectMouseType: + ret = QEvent::MouseButtonRelease; + break; + case SH_TabBar_SelectMouseType: + if (const QStyleOptionTabBarBase *opt2 = qstyleoption_cast<const QStyleOptionTabBarBase *>(opt)) { + ret = opt2->documentMode ? QEvent::MouseButtonPress : QEvent::MouseButtonRelease; + } else { + 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: { +//#if QT_CONFIG(tabwidget) +// if (const QTabWidget *tab = qobject_cast<const QTabWidget*>(w)) { +// if (tab->documentMode()) { +// ret = Qt::AlignLeft; +// break; +// } +// } +//#endif +//#if QT_CONFIG(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); + pix_paint.end(); + img = pix.toImage(); + } + + const QRgb *sptr = (QRgb*)img.bits(), *srow; + const qsizetype 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 qsizetype 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 = false; + break; + case SH_TabBar_ElideMode: + ret = Qt::ElideRight; + break; +// case SH_DialogButtonLayout: +// ret = QDialogButtonBox::MacLayout; +// break; +// 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; +// 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; +// case SH_WizardStyle: +// ret = QWizard::MacStyle; +// break; + case SH_ItemView_ArrowKeysNavigateIntoChildren: + ret = false; + break; + case SH_Menu_FlashTriggeredItem: + ret = true; + break; + case SH_Menu_FadeOutOnHide: + ret = true; + break; + case SH_ItemView_PaintAlternatingRowColorsForEmptyArea: + ret = true; + break; + case SH_TabBar_CloseButtonPosition: + ret = QStyleOptionTabBarBase::LeftSide; + break; + case SH_DockWidget_ButtonsHaveFrame: + ret = false; + break; + case SH_ScrollBar_Transient: + // For the initial version in QQC2, we don't support transient scrollbars. When the + // time comes, consider doing all such animations from QML. + // ret = [NSScroller preferredScrollerStyle] == NSScrollerStyleOverlay; + ret = false; + break; + case SH_TitleBar_ShowToolTipsOnButtons: + // min/max/close buttons on windows don't show tool tips + ret = false; + break; + case SH_ComboBox_AllowWheelScrolling: + ret = false; + break; + case SH_SpinBox_ButtonsInsideFrame: + ret = false; + break; + case SH_Table_GridLineColor: + ret = int(qt_mac_toQColor(NSColor.gridColor).rgba()); + break; + default: + ret = QCommonStyle::styleHint(sh, opt, 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 +{ + // 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); + + recursionGuard = true; + QIcon icon = proxy()->standardIcon(standardPixmap, opt); + 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(opt->window, QSize(size, size)); +} + +void QMacStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, QPainter *p) const +{ + Q_D(const QMacStyle); + + const AppearanceSync appSync; + QMacCGContext cg(p); + d->resolveCurrentNSView(opt->window); + + switch (pe) { + case PE_IndicatorArrowUp: + case PE_IndicatorArrowDown: + case PE_IndicatorArrowRight: + case PE_IndicatorArrowLeft: { + p->save(); + p->setRenderHint(QPainter::Antialiasing); + const int xOffset = 1; // FIXME: opt->direction == Qt::LeftToRight ? 2 : -1; + qreal halfSize = 0.5 * qMin(opt->rect.width(), opt->rect.height()); + const qreal penWidth = qMax(halfSize / 3.0, 1.25); +//#if QT_CONFIG(toolbutton) +// if (const QToolButton *tb = qobject_cast<const QToolButton *>(w)) { +// // When stroking the arrow, make sure it fits in the tool button +// if (tb->arrowType() != Qt::NoArrow +// || tb->popupMode() == QToolButton::MenuButtonPopup) +// halfSize -= penWidth; +// } +//#endif + + QTransform transform; + transform.translate(opt->rect.center().x() + xOffset, opt->rect.center().y() + 2); + QPainterPath path; + switch(pe) { + default: + case PE_IndicatorArrowDown: + break; + case PE_IndicatorArrowUp: + transform.rotate(180); + break; + case PE_IndicatorArrowLeft: + transform.rotate(90); + break; + case PE_IndicatorArrowRight: + transform.rotate(-90); + break; + } + p->setTransform(transform); + + path.moveTo(-halfSize, -halfSize * 0.5); + path.lineTo(0.0, halfSize * 0.5); + path.lineTo(halfSize, -halfSize * 0.5); + + const QPen arrowPen(opt->palette.text(), penWidth, + Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin); + p->strokePath(path, arrowPen); + p->restore(); + break; } + case PE_FrameTabBarBase: + if (const QStyleOptionTabBarBase *tbb + = qstyleoption_cast<const QStyleOptionTabBarBase *>(opt)) { + if (tbb->documentMode) { + p->save(); + drawTabBase(p, tbb); + 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 (QMacStylePrivate::tabDirection(twf.shape)) { + case QMacStylePrivate::North: + twf.rect = twf.rect.adjusted(0, 0, 0, 10); + break; + case QMacStylePrivate::South: + twf.rect = twf.rect.adjusted(0, -10, 0, 0); + break; + case QMacStylePrivate::West: + twf.rect = twf.rect.adjusted(0, 0, 10, 0); + break; + case QMacStylePrivate::East: + twf.rect = twf.rect.adjusted(0, -10, 0, 0); + break; + } + proxy()->drawPrimitive(PE_FrameTabWidget, &twf, p); + p->restore(); + } + break; + case PE_PanelTipLabel: + p->fillRect(opt->rect, opt->palette.brush(QPalette::ToolTipBase)); + break; + case PE_FrameGroupBox: + if (const auto *groupBox = qstyleoption_cast<const QStyleOptionFrame *>(opt)) + if (groupBox->features & QStyleOptionFrame::Flat) { + QCommonStyle::drawPrimitive(pe, groupBox, p); + break; + } + Q_FALLTHROUGH(); + case PE_FrameTabWidget: + { + const auto cw = QMacStylePrivate::CocoaControl(QMacStylePrivate::Box, QStyleHelper::SizeLarge); + auto *box = static_cast<NSBox *>(d->cocoaControl(cw)); + // FIXME Since macOS 10.14, simply calling drawRect: won't display anything anymore. + // The AppKit team is aware of this and has proposed a couple of solutions. + // The first solution was to call displayRectIgnoringOpacity:inContext: instead. + // However, it doesn't seem to work on 10.13. More importantly, dark mode on 10.14 + // is extremely slow. Light mode works fine. + // The second solution is to subclass NSBox and reimplement a trivial drawRect: which + // would only call super. This works without any issue on 10.13, but a double border + // shows on 10.14 in both light and dark modes. + // The code below picks what works on each version and mode. On 10.13 and earlier, we + // simply call drawRect: on a regular NSBox. On 10.14, we call displayRectIgnoringOpacity: + // inContext:, but only in light mode. In dark mode, we use a custom NSBox subclass, + // QDarkNSBox, of type NSBoxCustom. Its appearance is close enough to the real thing so + // we can use this for now. + auto adjustedRect = opt->rect; + bool needTranslation = false; + if (QOperatingSystemVersion::current() >= QOperatingSystemVersion::MacOSMojave + && !qt_mac_applicationIsInDarkMode()) { + // In Aqua theme we have to use the 'default' NSBox (as opposite + // to the 'custom' QDarkNSBox we use in dark theme). Since -drawRect: + // does nothing in default NSBox, we call -displayRectIgnoringOpaticty:. + // Unfortunately, the resulting box is smaller then the actual rect we + // wanted. This can be seen, e.g. because tabs (buttons) are misaligned + // vertically and even worse, if QTabWidget has autoFillBackground + // set, this background overpaints NSBox making it to disappear. + // We trick our NSBox to render in a larger rectangle, so that + // the actuall result (which is again smaller than requested), + // more or less is what we really want. We'll have to adjust CTM + // and translate accordingly. + adjustedRect.adjust(0, 0, 6, 6); + needTranslation = true; + } + d->drawNSViewInRect(box, adjustedRect, p, ^(CGContextRef ctx, const CGRect &rect) { +//#if QT_CONFIG(tabwidget) +// if (QTabWidget *tabWidget = qobject_cast<QTabWidget *>(opt->styleObject)) +// clipTabBarFrame(opt, this, ctx); +//#endif + QMacAutoReleasePool pool; + CGContextTranslateCTM(ctx, 0, rect.origin.y + rect.size.height); + CGContextScaleCTM(ctx, 1, -1); + if (QOperatingSystemVersion::current() < QOperatingSystemVersion::MacOSMojave + || [box isMemberOfClass:QDarkNSBox.class]) { + [box drawRect:rect]; + } else { + if (needTranslation) + CGContextTranslateCTM(ctx, -3.0, 5.0); + [box displayRectIgnoringOpacity:box.bounds inContext:NSGraphicsContext.currentContext]; + } + }); + 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); + const auto dark = qt_mac_applicationIsInDarkMode() ? opt->palette.dark().color().darker() + : QColor(0, 0, 0, 119); + p->fillPath(path, dark); + } + break; + case PE_FrameWindow: + if (const QStyleOptionFrame *frame = qstyleoption_cast<const QStyleOptionFrame *>(opt)) { +// if (w && w->inherits("QMdiSubWindow")) { +// p->save(); +// p->setPen(QPen(frame->palette.dark().color(), frame->lineWidth)); +// p->setBrush(frame->palette.window()); +// p->drawRect(frame->rect); +// p->restore(); +// } + } + 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); + } + break; + case PE_IndicatorMenuCheckMark: { + QColor pc; + if (opt->state & State_On) + 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_IndicatorItemViewItemCheck: + case PE_IndicatorRadioButton: + case PE_IndicatorCheckBox: { + const bool isEnabled = opt->state & State_Enabled; + const bool isPressed = opt->state & State_Sunken; + const bool isRadioButton = (pe == PE_IndicatorRadioButton); + const auto ct = isRadioButton ? QMacStylePrivate::Button_RadioButton : QMacStylePrivate::Button_CheckBox; + const auto cs = d->effectiveAquaSizeConstrain(opt); + const auto cw = QMacStylePrivate::CocoaControl(ct, cs); + auto *tb = static_cast<NSButton *>(d->cocoaControl(cw)); + tb.enabled = isEnabled; + tb.state = (opt->state & State_NoChange) ? NSMixedState : + (opt->state & State_On) ? NSOnState : NSOffState; + [tb highlight:isPressed]; + const auto vOffset = [=] { + // As measured + if (cs == QStyleHelper::SizeMini) + return ct == QMacStylePrivate::Button_CheckBox ? -0.5 : 0.5; + + return cs == QStyleHelper::SizeSmall ? 0.5 : 0.0; + } (); + d->drawNSViewInRect(tb, opt->rect, p, ^(CGContextRef ctx, const CGRect &rect) { + QMacAutoReleasePool pool; + CGContextTranslateCTM(ctx, 0, vOffset); + [tb.cell drawInteriorWithFrame:rect inView:tb]; + }); + break; } + case PE_FrameFocusRect: + // Use the our own focus widget stuff. + break; + case PE_IndicatorBranch: { + if (!(opt->state & State_Children)) + break; + const auto cw = QMacStylePrivate::CocoaControl(QMacStylePrivate::Button_Disclosure, QStyleHelper::SizeLarge); + NSButtonCell *triangleCell = static_cast<NSButtonCell *>(d->cocoaCell(cw)); + [triangleCell setState:(opt->state & State_Open) ? NSOnState : NSOffState]; +// bool viewHasFocus = (w && w->hasFocus()) || (opt->state & State_HasFocus); + bool viewHasFocus = false; + [triangleCell setBackgroundStyle:((opt->state & State_Selected) && viewHasFocus) ? NSBackgroundStyleDark : NSBackgroundStyleLight]; + + d->setupNSGraphicsContext(cg, 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]]; + + d->restoreNSGraphicsContext(cg); + break; } + + case PE_Frame: { + const QPen oldPen = p->pen(); + QPen penCpy = p->pen(); + penCpy.setWidth(2); + penCpy.setColor(opt->palette.dark().color()); + p->setPen(penCpy); + p->drawRect(opt->rect); + p->setPen(oldPen); + break; } + case PE_FrameLineEdit: + if (const QStyleOptionFrame *frame = qstyleoption_cast<const QStyleOptionFrame *>(opt)) { + if (frame->state & State_Sunken) { + const bool isEnabled = opt->state & State_Enabled; + const bool isReadOnly = opt->state & State_ReadOnly; + const bool isRounded = frame->features & QStyleOptionFrame::Rounded; + const auto cs = d->effectiveAquaSizeConstrain(opt, CT_LineEdit); + const auto cw = QMacStylePrivate::CocoaControl(QMacStylePrivate::TextField, cs); + auto *tf = static_cast<NSTextField *>(d->cocoaControl(cw)); + tf.enabled = isEnabled; + tf.editable = !isReadOnly; + tf.bezeled = YES; + static_cast<NSTextFieldCell *>(tf.cell).bezelStyle = isRounded ? NSTextFieldRoundedBezel : NSTextFieldSquareBezel; + tf.frame = opt->rect.toCGRect(); + d->drawNSViewInRect(tf, opt->rect, p, ^(CGContextRef, const CGRect &rect) { + QMacAutoReleasePool pool; + if (!qt_mac_applicationIsInDarkMode()) { + // In 'Dark' mode controls are transparent, so we do not + // over-paint the (potentially custom) color in the background. + // In 'Light' mode we have to care about the correct + // background color. See the comments below for PE_PanelLineEdit. + CGContextRef cgContext = NSGraphicsContext.currentContext.CGContext; + // See QMacCGContext, here we expect bitmap context created with + // color space 'kCGColorSpaceSRGB', if it's something else - we + // give up. + if (cgContext ? bool(CGBitmapContextGetColorSpace(cgContext)) : false) { + tf.drawsBackground = YES; + const QColor bgColor = frame->palette.brush(QPalette::Base).color(); + tf.backgroundColor = [NSColor colorWithSRGBRed:bgColor.redF() + green:bgColor.greenF() + blue:bgColor.blueF() + alpha:bgColor.alphaF()]; + if (bgColor.alpha() != 255) { + // No way we can have it bezeled and transparent ... + tf.bordered = YES; + } + } + } + + [tf.cell drawWithFrame:rect inView:tf]; + }); + } else { + QCommonStyle::drawPrimitive(pe, opt, p); + } + } + break; + case PE_PanelLineEdit: + { + const QStyleOptionFrame *panel = qstyleoption_cast<const QStyleOptionFrame *>(opt); + if (qt_mac_applicationIsInDarkMode() || (panel && panel->lineWidth <= 0)) { + // QCommonStyle::drawPrimitive(PE_PanelLineEdit) fill the background with + // a proper color, defined in opt->palette and then, if lineWidth > 0, it + // calls QMacStyle::drawPrimitive(PE_FrameLineEdit). We use NSTextFieldCell + // to handle PE_FrameLineEdit, which will use system-default background. + // In 'Dark' mode it's transparent and thus it's not over-painted. + QCommonStyle::drawPrimitive(pe, opt, p); + } else { + // In 'Light' mode, if panel->lineWidth > 0, we have to use the correct + // background color when drawing PE_FrameLineEdit, so let's call it + // directly and set the proper color there. + drawPrimitive(PE_FrameLineEdit, opt, p); + } + + // 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 (panel) { +// 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); +// } + } + } + break; + 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; +//#if QT_CONFIG(tabbar) +// case PE_IndicatorTabClose: { +// // Make close button visible only on the hovered tab. +// QTabBar *tabBar = qobject_cast<QTabBar*>(w->parentWidget()); +// const QWidget *closeBtn = w; +// if (!tabBar) { +// // QStyleSheetStyle instead of CloseButton (which has +// // a QTabBar as a parent widget) uses the QTabBar itself: +// tabBar = qobject_cast<QTabBar *>(const_cast<QWidget*>(w)); +// closeBtn = decltype(closeBtn)(property("_q_styleSheetRealCloseButton").value<void *>()); +// } +// if (tabBar) { +// const bool documentMode = tabBar->documentMode(); +// const QTabBarPrivate *tabBarPrivate = static_cast<QTabBarPrivate *>(QObjectPrivate::get(tabBar)); +// const int hoveredTabIndex = tabBarPrivate->hoveredTabIndex(); +// if (!documentMode || +// (hoveredTabIndex != -1 && ((closeBtn == tabBar->tabButton(hoveredTabIndex, QTabBar::LeftSide)) || +// (closeBtn == 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, documentMode); +// } +// } +// } break; +//#endif // QT_CONFIG(tabbar) + case PE_PanelStatusBar: { + // Fill the status bar with the titlebar gradient. + QLinearGradient linearGrad; + const bool isMainWindow = qt_macWindowMainWindow(opt->window); + if (isMainWindow) + 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 (isMainWindow) + p->setPen(titlebarSeparatorLineActive); + else + p->setPen(titlebarSeparatorLineInactive); + p->drawLine(opt->rect.left(), opt->rect.top(), opt->rect.right(), opt->rect.top()); + + break; + } + case PE_PanelMenu: { + p->save(); + p->fillRect(opt->rect, Qt::transparent); + p->setPen(Qt::transparent); + p->setBrush(opt->palette.window()); + p->setRenderHint(QPainter::Antialiasing, true); + const QPainterPath path = d->windowPanelPath(opt->rect); + p->drawPath(path); + p->restore(); + } break; + + default: + QCommonStyle::drawPrimitive(pe, opt, p); + break; + } +} + +static 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 QMacStylePrivate::setupVerticalInvertedXform(CGContextRef cg, bool reverse, bool vertical, const CGRect &rect) const +{ + if (vertical) { + CGContextTranslateCTM(cg, rect.size.height, 0); + CGContextRotateCTM(cg, M_PI_2); + } + if (vertical != reverse) { + CGContextTranslateCTM(cg, rect.size.width, 0); + CGContextScaleCTM(cg, -1, 1); + } +} + +void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter *p) const +{ + Q_D(const QMacStyle); + + const AppearanceSync sync; + const QMacAutoReleasePool pool; + + QMacCGContext cg(p); + d->resolveCurrentNSView(opt->window); + + switch (ce) { + case CE_HeaderSection: + if (const QStyleOptionHeader *header = qstyleoption_cast<const QStyleOptionHeader *>(opt)) { + State flags = header->state; + QRect ir = header->rect; + const bool pressed = (flags & State_Sunken) && !(flags & State_On); + p->fillRect(ir, pressed ? header->palette.dark() : header->palette.button()); + p->setPen(QPen(header->palette.dark(), 1.0)); + if (header->orientation == Qt::Horizontal) + p->drawLine(QLineF(ir.right() + 0.5, ir.top() + headerSectionSeparatorInset, + ir.right() + 0.5, ir.bottom() - headerSectionSeparatorInset)); + else + p->drawLine(QLineF(ir.left() + headerSectionSeparatorInset, ir.bottom(), + ir.right() - headerSectionSeparatorInset, ir.bottom())); + } + + 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(opt->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); + shiftY = proxy()->pixelMetric(PM_ButtonShiftVertical, tb); + } + // 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(opt->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)) + alignment |= Qt::TextHideMnemonic; + if (down) + cr.translate(shiftX, shiftY); + if (tbstyle == Qt::ToolButtonTextOnly + || (tbstyle != Qt::ToolButtonTextOnly && !down)) { + QPen pen = p->pen(); + QColor light = down || isDarkMode() ? 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); + } + } else +#endif // QT_NO_ACCESSIBILITY + { + QCommonStyle::drawControl(ce, &myTb, p); + } + } + break; + case CE_ToolBoxTabShape: + QCommonStyle::drawControl(ce, opt, p); + 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); + break; + } + + const bool hasFocus = btn->state & State_HasFocus; + const bool isActive = btn->state & State_Active; + + // a focused auto-default button within an active window + // takes precedence over a normal default button + if ((btn->features & QStyleOptionButton::AutoDefaultButton) + && isActive && hasFocus) + d->autoDefaultButton = btn->styleObject; + else if (d->autoDefaultButton == btn->styleObject) + d->autoDefaultButton = nullptr; + + const bool isEnabled = btn->state & State_Enabled; + const bool isPressed = btn->state & State_Sunken; + const bool isHighlighted = isActive && + ((btn->state & State_On) + || (btn->features & QStyleOptionButton::DefaultButton) + || (btn->features & QStyleOptionButton::AutoDefaultButton + && d->autoDefaultButton == btn->styleObject)); + const bool hasMenu = btn->features & QStyleOptionButton::HasMenu; + const auto ct = cocoaControlType(btn); + const auto cs = d->effectiveAquaSizeConstrain(btn); + const auto cw = QMacStylePrivate::CocoaControl(ct, cs); + auto *pb = static_cast<NSButton *>(d->cocoaControl(cw)); + // Ensure same size and location as we used to have with HITheme. + // This is more convoluted than we initialy thought. See for example + // differences between plain and menu button frames. + const QRectF frameRect = cw.adjustedControlFrame(btn->rect); + pb.frame = frameRect.toCGRect(); + + pb.enabled = isEnabled; + [pb highlight:isPressed]; + pb.state = isHighlighted && !isPressed ? NSOnState : NSOffState; + d->drawNSViewInRect(pb, frameRect, p, ^(CGContextRef, const CGRect &r) { + QMacAutoReleasePool pool; + [pb.cell drawBezelWithFrame:r inView:pb.superview]; + }); + [pb highlight:NO]; + + if (hasMenu && cw.type == QMacStylePrivate::Button_SquareButton) { + // Using -[NSPopuButtonCell drawWithFrame:inView:] above won't do + // it right because we don't set the text in the native button. + const int mbi = proxy()->pixelMetric(QStyle::PM_MenuButtonIndicator, btn); + const auto ir = frameRect.toRect(); + int arrowYOffset = 0; + const auto ar = visualRect(btn->direction, ir, QRect(ir.right() - mbi - 6, ir.height() / 2 - arrowYOffset, mbi, mbi)); + + QStyleOption arrowOpt = *opt; + arrowOpt.rect = ar; + proxy()->drawPrimitive(PE_IndicatorArrowDown, &arrowOpt, p); + } + + + if (btn->state & State_HasFocus) { + // TODO Remove and use QFocusFrame instead. + const int hMargin = proxy()->pixelMetric(QStyle::PM_FocusFrameHMargin, btn); + const int vMargin = proxy()->pixelMetric(QStyle::PM_FocusFrameVMargin, btn); + QRectF focusRect; + if (cw.type == QMacStylePrivate::Button_SquareButton) { + focusRect = frameRect; + } else { + focusRect = QRectF::fromCGRect([pb alignmentRectForFrame:pb.frame]); + if (cw.type == QMacStylePrivate::Button_PushButton) + focusRect -= pushButtonShadowMargins[cw.size]; + else if (cw.type == QMacStylePrivate::Button_PullDown) + focusRect -= pullDownButtonShadowMargins[cw.size]; + } + d->drawFocusRing(p, focusRect, hMargin, vMargin, cw); + } + } + break; + case CE_PushButtonLabel: + 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. + const bool isEnabled = btn.state & State_Enabled; + const bool hasMenu = btn.features & QStyleOptionButton::HasMenu; + const bool hasIcon = !btn.icon.isNull(); + const bool hasText = !btn.text.isEmpty(); + const bool isActive = btn.state & State_Active; + const bool isPressed = btn.state & State_Sunken; + + const auto ct = cocoaControlType(&btn); + + if (!hasMenu && ct != QMacStylePrivate::Button_SquareButton) { + if (isPressed + || (isActive && isEnabled + && ((btn.state & State_On) + || ((btn.features & QStyleOptionButton::DefaultButton) && !d->autoDefaultButton) + || d->autoDefaultButton == btn.styleObject))) + btn.palette.setColor(QPalette::ButtonText, Qt::white); + } + + if ((!hasIcon && !hasMenu) || (hasIcon && !hasText)) { + QCommonStyle::drawControl(ce, &btn, p); + } else { + QRect freeContentRect = btn.rect; + QRect textRect = itemTextRect( + btn.fontMetrics, freeContentRect, Qt::AlignCenter, isEnabled, btn.text); + if (hasMenu) { + textRect.moveTo(11, textRect.top()); + } + // Draw the icon: + if (hasIcon) { + int contentW = textRect.width(); + if (hasMenu) + contentW += proxy()->pixelMetric(PM_MenuButtonIndicator) + 4; + QIcon::Mode mode = isEnabled ? 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(opt->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, + isEnabled, btn.text, QPalette::ButtonText); + } + } + } + break; + case CE_ComboBoxLabel: + if (const auto *cb = qstyleoption_cast<const QStyleOptionComboBox *>(opt)) { + auto comboCopy = *cb; + comboCopy.direction = Qt::LeftToRight; + // The rectangle will be adjusted to SC_ComboBoxEditField with comboboxEditBounds() + QCommonStyle::drawControl(CE_ComboBoxLabel, &comboCopy, p); + } + break; + case CE_TabBarTabShape: + if (const auto *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); + drawTabShape(p, tabOpt, isUnified, tabOverlap); + + p->restore(); + return; + } + + const bool isActive = tabOpt->state & State_Active; + const bool isEnabled = tabOpt->state & State_Enabled; + const bool isPressed = tabOpt->state & State_Sunken; + const bool isSelected = tabOpt->state & State_Selected; + const auto tabDirection = QMacStylePrivate::tabDirection(tabOpt->shape); + const bool verticalTabs = tabDirection == QMacStylePrivate::East + || tabDirection == QMacStylePrivate::West; + + QStyleOptionTab::TabPosition tp = tabOpt->position; + QStyleOptionTab::SelectedPosition sp = tabOpt->selectedPosition; + if (tabOpt->direction == Qt::RightToLeft && !verticalTabs) { + if (tp == QStyleOptionTab::Beginning) + tp = QStyleOptionTab::End; + else if (tp == QStyleOptionTab::End) + tp = QStyleOptionTab::Beginning; + + if (sp == QStyleOptionTab::NextIsSelected) + sp = QStyleOptionTab::PreviousIsSelected; + else if (sp == QStyleOptionTab::PreviousIsSelected) + sp = QStyleOptionTab::NextIsSelected; + } + + // Alas, NSSegmentedControl and NSSegmentedCell are letting us down. + // We're not able to draw it at will, either calling -[drawSegment: + // inFrame:withView:], -[drawRect:] or anything in between. Besides, + // there's no public API do draw the pressed state, AFAICS. We'll use + // a push NSButton instead and clip the CGContext. + // NOTE/TODO: this is not true. On 10.13 NSSegmentedControl works with + // some (black?) magic/magic dances, on 10.14 it simply works (was + // it fixed in AppKit?). But, indeed, we cannot make a tab 'pressed' + // with NSSegmentedControl (only selected), so we stay with buttons + // (mixing buttons and NSSegmentedControl for such a simple thing + // is too much work). + + const auto cs = d->effectiveAquaSizeConstrain(opt); + // Extra hacks to get the proper pressed appreance when not selected or selected and inactive + const bool needsInactiveHack = (!isActive && isSelected); + const auto ct = !needsInactiveHack && (isSelected || tp == QStyleOptionTab::OnlyOneTab) ? + QMacStylePrivate::Button_PushButton : + QMacStylePrivate::Button_PopupButton; + const bool isPopupButton = ct == QMacStylePrivate::Button_PopupButton; + const auto cw = QMacStylePrivate::CocoaControl(ct, cs); + auto *pb = static_cast<NSButton *>(d->cocoaControl(cw)); + + auto vOffset = isPopupButton ? 1 : 2; + if (tabDirection == QMacStylePrivate::East) + vOffset -= 1; + const auto outerAdjust = isPopupButton ? 1 : 4; + const auto innerAdjust = isPopupButton ? 20 : 10; + QRectF frameRect = tabOpt->rect; + if (verticalTabs) + frameRect = QRectF(frameRect.y(), frameRect.x(), frameRect.height(), frameRect.width()); + // Adjust before clipping + frameRect = frameRect.translated(0, vOffset); + switch (tp) { + case QStyleOptionTab::Beginning: + // Pressed state hack: tweak adjustments in preparation for flip below + if (!isSelected && tabDirection == QMacStylePrivate::West) + frameRect = frameRect.adjusted(-innerAdjust, 0, outerAdjust, 0); + else + frameRect = frameRect.adjusted(-outerAdjust, 0, innerAdjust, 0); + break; + case QStyleOptionTab::Middle: + frameRect = frameRect.adjusted(-innerAdjust, 0, innerAdjust, 0); + break; + case QStyleOptionTab::End: + // Pressed state hack: tweak adjustments in preparation for flip below + if (isSelected || tabDirection == QMacStylePrivate::West) + frameRect = frameRect.adjusted(-innerAdjust, 0, outerAdjust, 0); + else + frameRect = frameRect.adjusted(-outerAdjust, 0, innerAdjust, 0); + break; + case QStyleOptionTab::OnlyOneTab: + frameRect = frameRect.adjusted(-outerAdjust, 0, outerAdjust, 0); + break; + } + pb.frame = frameRect.toCGRect(); + + pb.enabled = isEnabled; + [pb highlight:isPressed]; + // Set off state when inactive. See needsInactiveHack for when it's selected + pb.state = (isActive && isSelected && !isPressed) ? NSOnState : NSOffState; + + const auto drawBezelBlock = ^(CGContextRef ctx, const CGRect &r) { + QMacAutoReleasePool pool; + CGContextClipToRect(ctx, opt->rect.toCGRect()); + if (!isSelected || needsInactiveHack) { + // Final stage of the pressed state hack: flip NSPopupButton rendering + if (!verticalTabs && tp == QStyleOptionTab::End) { + CGContextTranslateCTM(ctx, opt->rect.right(), 0); + CGContextScaleCTM(ctx, -1, 1); + CGContextTranslateCTM(ctx, -frameRect.left(), 0); + } else if (tabDirection == QMacStylePrivate::West && tp == QStyleOptionTab::Beginning) { + CGContextTranslateCTM(ctx, 0, opt->rect.top()); + CGContextScaleCTM(ctx, 1, -1); + CGContextTranslateCTM(ctx, 0, -frameRect.right()); + } else if (tabDirection == QMacStylePrivate::East && tp == QStyleOptionTab::End) { + CGContextTranslateCTM(ctx, 0, opt->rect.bottom()); + CGContextScaleCTM(ctx, 1, -1); + CGContextTranslateCTM(ctx, 0, -frameRect.left()); + } + } + + // Rotate and translate CTM when vertical + // On macOS: positive angle is CW, negative is CCW + if (tabDirection == QMacStylePrivate::West) { + CGContextTranslateCTM(ctx, 0, frameRect.right()); + CGContextRotateCTM(ctx, -M_PI_2); + CGContextTranslateCTM(ctx, -frameRect.left(), 0); + } else if (tabDirection == QMacStylePrivate::East) { + CGContextTranslateCTM(ctx, opt->rect.right(), 0); + CGContextRotateCTM(ctx, M_PI_2); + } + + // Now, if it's a trick with a popup button, it has an arrow + // which makes no sense on tabs. + NSPopUpArrowPosition oldPosition = NSPopUpArrowAtCenter; + NSPopUpButtonCell *pbCell = nil; + if (isPopupButton) { + pbCell = static_cast<NSPopUpButtonCell *>(pb.cell); + oldPosition = pbCell.arrowPosition; + pbCell.arrowPosition = NSPopUpNoArrow; + } + + [pb.cell drawBezelWithFrame:r inView:pb.superview]; + + if (pbCell) // Restore, we may reuse it for a ComboBox. + pbCell.arrowPosition = oldPosition; + }; + + if (needsInactiveHack) { + // First, render tab as non-selected tab on a pixamp + const qreal pixelRatio = p->device()->devicePixelRatioF(); + QImage tabPixmap(opt->rect.size() * pixelRatio, QImage::Format_ARGB32_Premultiplied); + tabPixmap.setDevicePixelRatio(pixelRatio); + tabPixmap.fill(Qt::transparent); + QPainter tabPainter(&tabPixmap); + d->drawNSViewInRect(pb, frameRect, &tabPainter, ^(CGContextRef ctx, const CGRect &r) { + QMacAutoReleasePool pool; + CGContextTranslateCTM(ctx, -opt->rect.left(), -opt->rect.top()); + drawBezelBlock(ctx, r); + }); + tabPainter.end(); + + // Then, darken it with the proper shade of gray + const qreal inactiveGray = 0.898; // As measured + const int inactiveGray8 = qRound(inactiveGray * 255.0); + const QRgb inactiveGrayRGB = qRgb(inactiveGray8, inactiveGray8, inactiveGray8); + for (int l = 0; l < tabPixmap.height(); ++l) { + auto *line = reinterpret_cast<QRgb*>(tabPixmap.scanLine(l)); + for (int i = 0; i < tabPixmap.width(); ++i) { + if (qAlpha(line[i]) == 255) { + line[i] = inactiveGrayRGB; + } else if (qAlpha(line[i]) > 128) { + const int g = qRound(inactiveGray * qRed(line[i])); + line[i] = qRgba(g, g, g, qAlpha(line[i])); + } + } + } + + // Finally, draw the tab pixmap on the current painter + p->drawImage(opt->rect, tabPixmap); + } else { + d->drawNSViewInRect(pb, frameRect, p, drawBezelBlock); + } + + if (!isSelected && sp != QStyleOptionTab::NextIsSelected + && tp != QStyleOptionTab::End + && tp != QStyleOptionTab::OnlyOneTab) { + static const QPen separatorPen(Qt::black, 1.0); + p->save(); + p->setOpacity(isEnabled ? 0.105 : 0.06); // As measured + p->setPen(separatorPen); + if (tabDirection == QMacStylePrivate::West) { + p->drawLine(QLineF(opt->rect.left() + 1.5, opt->rect.bottom(), + opt->rect.right() - 0.5, opt->rect.bottom())); + } else if (tabDirection == QMacStylePrivate::East) { + p->drawLine(QLineF(opt->rect.left(), opt->rect.bottom(), + opt->rect.right() - 0.5, opt->rect.bottom())); + } else { + p->drawLine(QLineF(opt->rect.right(), opt->rect.top() + 1.0, + opt->rect.right(), opt->rect.bottom() - 0.5)); + } + p->restore(); + } + + // TODO Needs size adjustment to fit the focus ring + if (tabOpt->state & State_HasFocus) { + QMacStylePrivate::CocoaControlType focusRingType; + switch (tp) { + case QStyleOptionTab::Beginning: + focusRingType = verticalTabs ? QMacStylePrivate::SegmentedControl_Last + : QMacStylePrivate::SegmentedControl_First; + break; + case QStyleOptionTab::Middle: + focusRingType = QMacStylePrivate::SegmentedControl_Middle; + break; + case QStyleOptionTab::End: + focusRingType = verticalTabs ? QMacStylePrivate::SegmentedControl_First + : QMacStylePrivate::SegmentedControl_Last; + break; + case QStyleOptionTab::OnlyOneTab: + focusRingType = QMacStylePrivate::SegmentedControl_Single; + break; + } + } + } + break; + case CE_TabBarTabLabel: + if (const auto *tab = qstyleoption_cast<const QStyleOptionTab *>(opt)) { + QStyleOptionTab myTab = *tab; + const auto tabDirection = QMacStylePrivate::tabDirection(tab->shape); + const bool verticalTabs = tabDirection == QMacStylePrivate::East + || tabDirection == QMacStylePrivate::West; + + // 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). +// const bool nonDefaultFont = p->font() != qt_app_fonts_hash()->value("QComboMenuItem"); + const bool nonDefaultFont = false; + +// if (!myTab.documentMode && (myTab.state & State_Selected) && (myTab.state & State_Active)) +// if (const auto *tabBar = qobject_cast<const QTabBar *>(w)) +// if (!tabBar->tabTextColor(tabBar->currentIndex()).isValid()) +// myTab.palette.setColor(QPalette::WindowText, Qt::white); + + if (myTab.documentMode && isDarkMode()) { + bool active = (myTab.state & State_Selected) && (myTab.state & State_Active); + myTab.palette.setColor(QPalette::WindowText, active ? Qt::white : Qt::gray); + } + + 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); + } + break; + case CE_DockWidgetTitle: + if (const auto *dwOpt = qstyleoption_cast<const QStyleOptionDockWidget *>(opt)) { + const bool isVertical = dwOpt->verticalTitleBar; + const auto effectiveRect = isVertical ? opt->rect.transposed() : opt->rect; + p->save(); + if (isVertical) { + p->translate(effectiveRect.left(), effectiveRect.top() + effectiveRect.width()); + p->rotate(-90); + p->translate(-effectiveRect.left(), -effectiveRect.top()); + } + + // fill title bar background + QLinearGradient linearGrad; + linearGrad.setStart(QPointF(0, 0)); + linearGrad.setFinalStop(QPointF(0, 2 * effectiveRect.height())); + linearGrad.setColorAt(0, opt->palette.button().color()); + linearGrad.setColorAt(1, opt->palette.dark().color()); + p->fillRect(effectiveRect, linearGrad); + + // draw horizontal line at bottom + p->setPen(opt->palette.dark().color()); + p->drawLine(effectiveRect.bottomLeft(), effectiveRect.bottomRight()); + + if (!dwOpt->title.isEmpty()) { + auto titleRect = proxy()->subElementRect(SE_DockWidgetTitleBarText, opt); + if (isVertical) + titleRect = QRect(effectiveRect.left() + opt->rect.bottom() - titleRect.bottom(), + effectiveRect.top() + titleRect.left() - opt->rect.left(), + titleRect.height(), + titleRect.width()); + + const auto text = p->fontMetrics().elidedText(dwOpt->title, Qt::ElideRight, titleRect.width()); + proxy()->drawItemText(p, titleRect, Qt::AlignCenter, dwOpt->palette, + dwOpt->state & State_Enabled, text, QPalette::WindowText); + } + p->restore(); + } + break; + case CE_FocusFrame: { +// const auto *ff = qobject_cast<const QFocusFrame *>(w); +// const auto *ffw = ff ? ff->widget() : nullptr; +// const auto ct = [=] { +// if (ffw) { +// if (ffw->inherits("QCheckBox")) +// return QMacStylePrivate::Button_CheckBox; +// if (ffw->inherits("QRadioButton")) +// return QMacStylePrivate::Button_RadioButton; +// if (ffw->inherits("QLineEdit") || ffw->inherits("QTextEdit")) +// return QMacStylePrivate::TextField; +// } +// +// return QMacStylePrivate::Box; // Not really, just make it the default +// } (); +// const auto cs = ffw ? (ffw->testAttribute(Qt::WA_MacMiniSize) ? QStyleHelper::SizeMini : +// ffw->testAttribute(Qt::WA_MacSmallSize) ? QStyleHelper::SizeSmall : +// QStyleHelper::SizeLarge) : +// QStyleHelper::SizeLarge; +// const int hMargin = proxy()->pixelMetric(QStyle::PM_FocusFrameHMargin, opt); +// const int vMargin = proxy()->pixelMetric(QStyle::PM_FocusFrameVMargin, opt); +// d->drawFocusRing(p, opt->rect, hMargin, vMargin, QMacStylePrivate::CocoaControl(ct, cs)); + break; } + case CE_MenuEmptyArea: + // Skip: PE_PanelMenu fills in everything + break; + case CE_MenuItem: + case CE_MenuHMargin: + case CE_MenuVMargin: + case CE_MenuTearoff: + case CE_MenuScroller: + if (const QStyleOptionMenuItem *mi = qstyleoption_cast<const QStyleOptionMenuItem *>(opt)) { + const bool active = mi->state & State_Selected; + if (active) + p->fillRect(mi->rect, mi->palette.highlight()); + + const QStyleHelper::WidgetSizePolicy widgetSize = d->aquaSizeConstrain(opt); + + 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); + } else if (ce == CE_MenuScroller) { + const QSize scrollerSize = QSize(10, 8); + const int scrollerVOffset = 5; + const int left = mi->rect.x() + (mi->rect.width() - scrollerSize.width()) / 2; + const int right = left + scrollerSize.width(); + int top; + int bottom; + if (opt->state & State_DownArrow) { + bottom = mi->rect.y() + scrollerVOffset; + top = bottom + scrollerSize.height(); + } else { + bottom = mi->rect.bottom() - scrollerVOffset; + top = bottom - scrollerSize.height(); + } + p->save(); + p->setRenderHint(QPainter::Antialiasing); + QPainterPath path; + path.moveTo(left, bottom); + path.lineTo(right, bottom); + path.lineTo((left + right) / 2, top); + p->fillPath(path, opt->palette.buttonText()); + p->restore(); + } else if (ce != CE_MenuItem) { + break; + } + + if (mi->menuItemType == QStyleOptionMenuItem::Separator) { + CGColorRef separatorColor = [NSColor quaternaryLabelColor].CGColor; + const QRect separatorRect = QRect(mi->rect.left(), mi->rect.center().y(), mi->rect.width(), 2); + p->fillRect(separatorRect, qt_mac_toQColor(separatorColor)); + break; + } + + const int maxpmw = mi->maxIconWidth; + const bool enabled = mi->state & State_Enabled; + + int xpos = mi->rect.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 = mi->rect.height() + macItemFrame; + const int xp = mi->rect.x() + macItemFrame; + checkmarkOpt.rect = QRect(xp, mi->rect.y() - checkmarkOpt.fontMetrics.descent(), mw, mh); + + checkmarkOpt.state.setFlag(State_On, active); + checkmarkOpt.state.setFlag(State_Enabled, enabled); + if (widgetSize == QStyleHelper::SizeMini) + checkmarkOpt.state |= State_Mini; + else if (widgetSize == QStyleHelper::SizeSmall) + 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); + } + 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); +//#if QT_CONFIG(combobox) +// if (const QComboBox *comboBox = qobject_cast<const QComboBox *>(w)) { +// iconSize = comboBox->iconSize(); +// } +//#endif + QPixmap pixmap = mi->icon.pixmap(opt->window, iconSize, mode); + int pixw = pixmap.width() / pixmap.devicePixelRatio(); + int pixh = pixmap.height() / pixmap.devicePixelRatio(); + QRect cr(xpos, mi->rect.y(), checkcol, mi->rect.height()); + QRect pmr(0, 0, pixw, pixh); + pmr.moveCenter(cr.center()); + p->drawPixmap(pmr.topLeft(), pixmap); + xpos += pixw + 6; + } + + QString s = mi->text; + const auto text_flags = Qt::AlignVCenter | Qt::TextHideMnemonic + | Qt::TextSingleLine | Qt::AlignAbsolute; + int yPos = mi->rect.y(); + if (widgetSize == QStyleHelper::SizeMini) + yPos += 1; + + const bool isSubMenu = mi->menuItemType == QStyleOptionMenuItem::SubMenu; + const int tabwidth = isSubMenu ? 9 : mi->tabWidth; + + QString rightMarginText; + if (isSubMenu) + rightMarginText = QStringLiteral("\u25b6\ufe0e"); // U+25B6 U+FE0E: BLACK RIGHT-POINTING TRIANGLE + + // If present, save and remove embedded shorcut from text + const int tabIndex = s.indexOf(QLatin1Char('\t')); + if (tabIndex >= 0) { + if (!isSubMenu) // ... but ignore it if it's a submenu. + rightMarginText = s.mid(tabIndex + 1); + s = s.left(tabIndex); + } + + p->save(); + if (!rightMarginText.isEmpty()) { +// p->setFont(qt_app_fonts_hash()->value("QMenuItem", p->font())); + int xp = mi->rect.right() - tabwidth - macRightBorder + 2; + if (!isSubMenu) + xp -= macItemHMargin + macItemFrame + 3; // Adjust for shortcut + p->drawText(xp, yPos, tabwidth, mi->rect.height(), text_flags | Qt::AlignRight, rightMarginText); + } + + if (!s.isEmpty()) { + 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()); + + // QTBUG-65653: Our own text rendering doesn't look good enough, especially on non-retina + // displays. Worked around here while waiting for a proper fix in QCoreTextFontEngine. + // Only if we're not using QCoreTextFontEngine we do fallback to our own text rendering. + const auto *fontEngine = QFontPrivate::get(myFont)->engineForScript(QChar::Script_Common); + Q_ASSERT(fontEngine); + if (fontEngine->type() == QFontEngine::Multi) { + fontEngine = static_cast<const QFontEngineMulti *>(fontEngine)->engine(0); + Q_ASSERT(fontEngine); + } + if (fontEngine->type() == QFontEngine::Mac) { + NSFont *f = (NSFont *)(CTFontRef)fontEngine->handle(); + + // Respect the menu item palette as set in the style option. + const auto pc = p->pen().color(); + NSColor *c = [NSColor colorWithSRGBRed:pc.redF() + green:pc.greenF() + blue:pc.blueF() + alpha:pc.alphaF()]; + + s = qt_mac_removeMnemonics(s); + + QMacCGContext cgCtx(p); + d->setupNSGraphicsContext(cgCtx, YES); + + // Draw at point instead of in rect, as the rect we've computed for the menu item + // is based on the font metrics we got from HarfBuzz, so we may risk having CoreText + // line-break the string if it doesn't fit the given rect. It's better to draw outside + // the rect and possibly overlap something than to have part of the text disappear. + [s.toNSString() drawAtPoint:CGPointMake(xpos, yPos) + withAttributes:@{ NSFontAttributeName:f, NSForegroundColorAttributeName:c, + NSObliquenessAttributeName: [NSNumber numberWithDouble: myFont.italic() ? 0.3 : 0.0]}]; + + d->restoreNSGraphicsContext(cgCtx); + } else { + p->setFont(myFont); + p->drawText(xpos, yPos, mi->rect.width() - xm - tabwidth + 1, + mi->rect.height(), text_flags, s); + } + } + p->restore(); + } + break; + case CE_MenuBarItem: + case CE_MenuBarEmptyArea: + if (const QStyleOptionMenuItem *mi = qstyleoption_cast<const QStyleOptionMenuItem *>(opt)) { + const bool selected = (opt->state & State_Selected) && (opt->state & State_Enabled) && (opt->state & State_Sunken); + const QBrush bg = selected ? mi->palette.highlight() : mi->palette.window(); + p->fillRect(mi->rect, bg); + + if (ce != CE_MenuBarItem) + break; + + 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(opt->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_ProgressBarLabel: + case CE_ProgressBarContents: + break; + case CE_ProgressBarGroove: + if (const QStyleOptionProgressBar *pb = qstyleoption_cast<const QStyleOptionProgressBar *>(opt)) { + const bool isIndeterminate = (pb->minimum == 0 && pb->maximum == 0); + const bool inverted = pb->invertedAppearance; + bool reverse = pb->direction == Qt::RightToLeft; + if (inverted) + reverse = !reverse; + + QRect rect = pb->rect; + const CGRect cgRect = rect.toCGRect(); + + const auto aquaSize = d->aquaSizeConstrain(opt); + +// const QProgressStyleAnimation *animation = qobject_cast<QProgressStyleAnimation*>(d->animation(opt->styleObject)); + QIndeterminateProgressIndicator *ipi = nil; +// if (isIndeterminate || animation) + ipi = static_cast<QIndeterminateProgressIndicator *>(d->cocoaControl({ QMacStylePrivate::ProgressIndicator_Indeterminate, aquaSize })); + if (isIndeterminate) { + // QIndeterminateProgressIndicator derives from NSProgressIndicator. We use a single + // instance that we start animating as soon as one of the progress bars is indeterminate. + // Since they will be in sync (as it's the case in Cocoa), we just need to draw it with + // the right geometry when the animation triggers an update. However, we can't hide it + // entirely between frames since that would stop the animation, so we just set its alpha + // value to 0. Same if we remove it from its superview. See QIndeterminateProgressIndicator + // implementation for details. + // + // Quick: consider implementing this animation by using Quick/QML instead. + // +// if (!animation && opt->styleObject) { +// auto *animation = new QProgressStyleAnimation(d->animateSpeed(QMacStylePrivate::AquaProgressBar), opt->styleObject); +// // NSProgressIndicator is heavier to draw than the HITheme API, so we reduce the frame rate a couple notches. +// animation->setFrameRate(QStyleAnimation::FifteenFps); +// d->startAnimation(animation); +// [ipi startAnimation]; +// } + + d->setupNSGraphicsContext(cg, NO); + d->setupVerticalInvertedXform(cg, reverse, false, cgRect); + [ipi drawWithFrame:cgRect inView:d->backingStoreNSView]; + d->restoreNSGraphicsContext(cg); + } else { +// if (animation) { +// d->stopAnimation(opt->styleObject); +// [ipi stopAnimation]; +// } + + const auto cw = QMacStylePrivate::CocoaControl(QMacStylePrivate::ProgressIndicator_Determinate, aquaSize); + auto *pi = static_cast<NSProgressIndicator *>(d->cocoaControl(cw)); + d->drawNSViewInRect(pi, rect, p, ^(CGContextRef ctx, const CGRect &rect) { + QMacAutoReleasePool pool; + d->setupVerticalInvertedXform(ctx, reverse, false, rect); + pi.minValue = pb->minimum; + pi.maxValue = pb->maximum; + pi.doubleValue = pb->progress; + [pi drawRect:rect]; + }); + } + } + break; + case CE_SizeGrip: { + // This is not HIG kosher: Fall back to the old stuff until we decide what to do. +//#ifndef QT_NO_MDIAREA +// if (!w || !qobject_cast<QMdiSubWindow *>(w->parentWidget())) +//#endif +// break; + +// if (w->testAttribute(Qt::WA_MacOpaqueSizeGrip)) +// p->fillRect(opt->rect, opt->palette.window()); + +// QPen lineColor = QColor(82, 82, 82, 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 = 3; +// for (int l = 0; l < NumLines; ++l) { +// const int offset = (l * 4 + 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); +// } +// p->restore(); + break; + } + case CE_Splitter: + if (opt->rect.width() > 1 && opt->rect.height() > 1) { + const bool isVertical = !(opt->state & QStyle::State_Horizontal); + // Qt refers to the layout orientation, while Cocoa refers to the divider's. + const auto ct = isVertical ? QMacStylePrivate::SplitView_Horizontal : QMacStylePrivate::SplitView_Vertical; + const auto cw = QMacStylePrivate::CocoaControl(ct, QStyleHelper::SizeLarge); + auto *sv = static_cast<NSSplitView *>(d->cocoaControl(cw)); + sv.frame = opt->rect.toCGRect(); + d->drawNSViewInRect(sv, opt->rect, p, ^(CGContextRef, const CGRect &rect) { + QMacAutoReleasePool pool; + [sv drawDividerInRect:rect]; + }); + } 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; + case CE_ToolBar: { + const QStyleOptionToolBar *toolBar = qstyleoption_cast<const QStyleOptionToolBar *>(opt); + const bool isDarkMode = qt_mac_applicationIsInDarkMode(); + + // 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) { +//#if QT_CONFIG(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(); + +// // Draw a horizontal separator 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. +// const QPoint windowToolbarEnd = w->mapTo(w->window(), opt->rect.bottomLeft()); +// const bool isEndOfUnifiedArea = !isInMacUnifiedToolbarArea(w->window()->windowHandle(), windowToolbarEnd.y() + 1); +// if (isEndOfUnifiedArea) { +// const int margin = qt_mac_aqua_get_metric(SeparatorSize); +// const auto separatorRect = QRect(opt->rect.left(), opt->rect.bottom(), opt->rect.width(), margin); +// p->fillRect(separatorRect, isDarkMode ? darkModeSeparatorLine : opt->palette.dark().color()); +// } +// 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); + + QColor mainWindowGradientBegin = isDarkMode ? darkMainWindowGradientBegin : lightMainWindowGradientBegin; + QColor mainWindowGradientEnd = isDarkMode ? darkMainWindowGradientEnd : lightMainWindowGradientEnd; + + linearGrad.setColorAt(0, mainWindowGradientBegin); + linearGrad.setColorAt(1, mainWindowGradientEnd); + p->fillRect(opt->rect, linearGrad); + + p->save(); + QRect toolbarRect = isDarkMode ? opt->rect.adjusted(0, 0, 0, 1) : opt->rect; + if (opt->state & State_Horizontal) { + p->setPen(isDarkMode ? darkModeSeparatorLine : mainWindowGradientBegin.lighter(114)); + p->drawLine(toolbarRect.topLeft(), toolbarRect.topRight()); + p->setPen(isDarkMode ? darkModeSeparatorLine :mainWindowGradientEnd.darker(114)); + p->drawLine(toolbarRect.bottomLeft(), toolbarRect.bottomRight()); + } else { + p->setPen(isDarkMode ? darkModeSeparatorLine : mainWindowGradientBegin.lighter(114)); + p->drawLine(toolbarRect.topLeft(), toolbarRect.bottomLeft()); + p->setPen(isDarkMode ? darkModeSeparatorLine : mainWindowGradientEnd.darker(114)); + p->drawLine(toolbarRect.topRight(), toolbarRect.bottomRight()); + } + p->restore(); + + break; } + default: + QCommonStyle::drawControl(ce, opt, p); + 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 +{ + Q_D(const QMacStyle); + QRect rect; + const int controlSize = getControlSize(opt); + + switch (sr) { + case SE_ItemViewItemText: + if (const QStyleOptionViewItem *vopt = qstyleoption_cast<const QStyleOptionViewItem *>(opt)) { + int fw = proxy()->pixelMetric(PM_FocusFrameHMargin, opt); + // We add the focusframeargin between icon and text in commonstyle + rect = QCommonStyle::subElementRect(sr, opt); + if (vopt->features & QStyleOptionViewItem::HasDecoration) + rect.adjust(-fw, 0, 0, 0); + } + break; + case SE_ToolBoxTabContents: + rect = QCommonStyle::subElementRect(sr, opt); + break; + case SE_PushButtonContents: + if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(opt)) { + // Comment from the old HITheme days: + // "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." + // In the future, we should consider using -[NSCell titleRectForBounds:]. + // Since it requires configuring the NSButton fully, i.e. frame, image, + // title and font, we keep things more manual until we are more familiar + // with side effects when changing NSButton state. + const auto ct = cocoaControlType(btn); + const auto cs = d->effectiveAquaSizeConstrain(btn); + const auto cw = QMacStylePrivate::CocoaControl(ct, cs); + auto frameRect = cw.adjustedControlFrame(btn->rect); + frameRect -= cw.titleMargins(); + rect = frameRect.toRect(); + } + break; + case SE_HeaderLabel: { + int margin = proxy()->pixelMetric(QStyle::PM_HeaderMargin, opt); + 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() - (headerSectionArrowHeight) - (margin * 2)); + else + rect.setHeight(rect.height() - (headerSectionArrowHeight) - (margin * 2)); + } + } + rect = visualRect(opt->direction, opt->rect, rect); + break; + } + case SE_HeaderArrow: { + int h = opt->rect.height(); + int w = opt->rect.width(); + int x = opt->rect.x(); + int y = opt->rect.y(); + int margin = proxy()->pixelMetric(QStyle::PM_HeaderMargin, opt); + + if (opt->state & State_Horizontal) { + rect.setRect(x + w - margin * 2 - headerSectionArrowHeight, y + 5, + headerSectionArrowHeight, h - margin * 2 - 5); + } else { + rect.setRect(x + 5, y + h - margin * 2 - headerSectionArrowHeight, + w - margin * 2 - 5, headerSectionArrowHeight); + } + 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: { + rect = opt->rect; + // As previously returned by HIThemeGetButtonContentBounds + rect.setLeft(rect.left() + 2 + DisclosureOffset); + break; + } + case SE_TabWidgetLeftCorner: + if (const QStyleOptionTabWidgetFrame *twf + = qstyleoption_cast<const QStyleOptionTabWidgetFrame *>(opt)) { + switch (twf->shape) { + case QStyleOptionTab::RoundedNorth: + case QStyleOptionTab::TriangularNorth: + rect = QRect(QPoint(0, 0), twf->leftCornerWidgetSize); + break; + case QStyleOptionTab::RoundedSouth: + case QStyleOptionTab::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 QStyleOptionTab::RoundedNorth: + case QStyleOptionTab::TriangularNorth: + rect = QRect(QPoint(twf->rect.width() - twf->rightCornerWidgetSize.width(), 0), + twf->rightCornerWidgetSize); + break; + case QStyleOptionTab::RoundedSouth: + case QStyleOptionTab::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); + if (const auto *twf = qstyleoption_cast<const QStyleOptionTabWidgetFrame *>(opt)) { + if (twf->lineWidth != 0) { + switch (QMacStylePrivate::tabDirection(twf->shape)) { + case QMacStylePrivate::North: + rect.adjust(+1, +14, -1, -1); + break; + case QMacStylePrivate::South: + rect.adjust(+1, +1, -1, -14); + break; + case QMacStylePrivate::West: + rect.adjust(+14, +1, -1, -1); + break; + case QMacStylePrivate::East: + rect.adjust(+1, +1, -14, -1); + } + } + } + break; + case SE_TabBarTabText: + if (const QStyleOptionTab *tab = qstyleoption_cast<const QStyleOptionTab *>(opt)) { + QRect dummyIconRect; + d->tabLayout(tab, &rect, &dummyIconRect); + } + 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); + int horizontalShift = proxy()->pixelMetric(QStyle::PM_TabBarTabShiftHorizontal, tab); + int hpadding = 5; + + bool verticalTabs = tab->shape == QStyleOptionTab::RoundedEast + || tab->shape == QStyleOptionTab::RoundedWest + || tab->shape == QStyleOptionTab::TriangularEast + || tab->shape == QStyleOptionTab::TriangularWest; + + QRect tr = tab->rect; + if (tab->shape == QStyleOptionTab::RoundedSouth || tab->shape == QStyleOptionTab::TriangularSouth) + verticalShift = -verticalShift; + if (verticalTabs) { + qSwap(horizontalShift, verticalShift); + horizontalShift *= -1; + verticalShift *= -1; + } + if (tab->shape == QStyleOptionTab::RoundedWest || tab->shape == QStyleOptionTab::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 QStyleOptionTab::RoundedWest: + case QStyleOptionTab::TriangularWest: + atTheTop = (sr == SE_TabBarTabLeftButton); + break; + case QStyleOptionTab::RoundedEast: + case QStyleOptionTab::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; + case SE_LineEditContents: { + // From using pixelTool with XCode/NSTextTextField + int leftPadding = 4; + int rightPadding = 4; + int topPadding = 4; + int bottomPadding = 0; + + if (opt->state & QStyle::State_Small) { + topPadding = 3; + } else if (opt->state & QStyle::State_Mini) { + topPadding = 2; + } + + rect = QRect(leftPadding, topPadding, opt->rect.width() - leftPadding - rightPadding, + opt->rect.height() - topPadding - bottomPadding); + break; } + case SE_CheckBoxLayoutItem: + rect = opt->rect; + if (controlSize == QStyleHelper::SizeLarge) { + setLayoutItemMargins(+2, +3, -9, -4, &rect, opt->direction); + } else if (controlSize == QStyleHelper::SizeSmall) { + setLayoutItemMargins(+1, +5, 0 /* fix */, -6, &rect, opt->direction); + } else { + setLayoutItemMargins(0, +7, 0 /* fix */, -6, &rect, opt->direction); + } + break; + case SE_ComboBoxLayoutItem: + if (const auto *combo = qstyleoption_cast<const QStyleOptionComboBox *>(opt)) { + //#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 == QStyleHelper::SizeLarge) { + rect.adjust(5, 6, -6, -7); + } else if (controlSize == QStyleHelper::SizeSmall) { + if (combo->editable) + rect.adjust(4, 4, -5, -7); + else + rect.adjust(6, 6, -6, -4); + } else { + if (combo->editable) + rect.adjust(5, 4, -4, -6); + else + rect.adjust(8, 4, -4, -6); + } + } + 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: + if (const QStyleOptionProgressBar *pb = qstyleoption_cast<const QStyleOptionProgressBar *>(opt)) { + const bool isIndeterminate = (pb->minimum == 0 && pb->maximum == 0); + rect = opt->rect; + + if (isIndeterminate) { + rect.adjust(1, 2, -1, -2); + } else { + rect.adjust(1, 1, -1, -2); + } + } + 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 == QStyleHelper::SizeLarge) { + rect.adjust(+6, +4, -6, -8); + } else if (controlSize == QStyleHelper::SizeSmall) { + rect.adjust(+5, +4, -5, -6); + } else { + rect.adjust(+1, 0, -1, -2); + } + break; + case SE_RadioButtonLayoutItem: + rect = opt->rect; + if (controlSize == QStyleHelper::SizeLarge) { + setLayoutItemMargins(+2, +2 /* SHOULD BE +3, done for alignment */, + 0, -4 /* SHOULD BE -3, done for alignment */, &rect, opt->direction); + } else if (controlSize == QStyleHelper::SizeSmall) { + 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 == QStyleOptionSlider::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 == QStyleOptionSlider::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 == QStyleOptionSlider::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_ScrollBarLayoutItem: + if (const QStyleOptionSlider *sliderOpt = qstyleoption_cast<const QStyleOptionSlider *>(opt)) { + rect = opt->rect; + } + 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; + 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 == QStyleOptionTab::RoundedNorth) + rect.setTop(rect.top() + SIZE(6 /* AHIG */, 3 /* guess */, 2 /* AHIG */)); + } + break; + case SE_DockWidgetCloseButton: + case SE_DockWidgetFloatButton: + case SE_DockWidgetTitleBarText: + case SE_DockWidgetIcon: { + int iconSize = proxy()->pixelMetric(PM_SmallIconSize, opt); + int buttonMargin = proxy()->pixelMetric(PM_DockWidgetTitleBarButtonMargin, opt); + 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).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).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; + } + default: + rect = QCommonStyle::subElementRect(sr, opt); + break; + } + return rect; +} + +void QMacStylePrivate::drawToolbarButtonArrow(const QStyleOption *opt, QPainter *p) const +{ + Q_Q(const QMacStyle); + QStyleOption arrowOpt = *opt; + arrowOpt.rect = QRect(opt->rect.right() - (toolButtonArrowSize + toolButtonArrowMargin), + opt->rect.bottom() - (toolButtonArrowSize + toolButtonArrowMargin), + toolButtonArrowSize, + toolButtonArrowSize); + q->proxy()->drawPrimitive(QStyle::PE_IndicatorArrowDown, &arrowOpt, p); +} + +void QMacStylePrivate::setupNSGraphicsContext(CGContextRef cg, bool flipped) const +{ + CGContextSaveGState(cg); + [NSGraphicsContext saveGraphicsState]; + + [NSGraphicsContext setCurrentContext: + [NSGraphicsContext graphicsContextWithCGContext:cg flipped:flipped]]; +} + +void QMacStylePrivate::restoreNSGraphicsContext(CGContextRef cg) const +{ + [NSGraphicsContext restoreGraphicsState]; + CGContextRestoreGState(cg); +} + +void QMacStyle::drawComplexControl(ComplexControl cc, const QStyleOptionComplex *opt, QPainter *p) const +{ + Q_D(const QMacStyle); + const AppearanceSync sync; + + QMacCGContext cg(p); + d->resolveCurrentNSView(opt->window); + + switch (cc) { + case CC_ScrollBar: + if (const QStyleOptionSlider *sb = qstyleoption_cast<const QStyleOptionSlider *>(opt)) { + + const bool drawTrack = sb->subControls & SC_ScrollBarGroove; + const bool drawKnob = sb->subControls & SC_ScrollBarSlider; + if (!drawTrack && !drawKnob) + break; + + const bool isHorizontal = sb->orientation == Qt::Horizontal; + + if (opt && opt->styleObject && !QMacStylePrivate::scrollBars.contains(opt->styleObject)) + QMacStylePrivate::scrollBars.append(QPointer<QObject>(opt->styleObject)); + + static const CGFloat knobWidths[] = { 7.0, 5.0, 5.0 }; + static const CGFloat expandedKnobWidths[] = { 11.0, 9.0, 9.0 }; + const auto cocoaSize = d->effectiveAquaSizeConstrain(opt); + const CGFloat maxExpandScale = expandedKnobWidths[cocoaSize] / knobWidths[cocoaSize]; + + const bool isTransient = proxy()->styleHint(SH_ScrollBar_Transient, opt); +// if (!isTransient) +// d->stopAnimation(opt->styleObject); + bool wasActive = false; + CGFloat opacity = 0.0; + CGFloat expandScale = 1.0; + CGFloat expandOffset = 0.0; + bool shouldExpand = false; + + if (QObject *styleObject = opt->styleObject) { + const int oldPos = styleObject->property("_q_stylepos").toInt(); + const int oldMin = styleObject->property("_q_stylemin").toInt(); + const int oldMax = styleObject->property("_q_stylemax").toInt(); + const QRect oldRect = styleObject->property("_q_stylerect").toRect(); + const QStyle::State oldState = static_cast<QStyle::State>(styleObject->property("_q_stylestate").value<QStyle::State::Int>()); + const 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) + const bool transient = isTransient && !opt->activeSubControls && !(sb->state & State_On); + + if (!transient || + oldPos != sb->sliderPosition || + oldMin != sb->minimum || + oldMax != sb->maximum || + oldRect != sb->rect || + oldState != sb->state || + oldActiveControls != sb->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", sb->sliderPosition); + styleObject->setProperty("_q_stylemin", sb->minimum); + styleObject->setProperty("_q_stylemax", sb->maximum); + styleObject->setProperty("_q_stylerect", sb->rect); + styleObject->setProperty("_q_stylestate", static_cast<QStyle::State::Int>(sb->state)); + styleObject->setProperty("_q_stylecontrols", static_cast<uint>(sb->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 = isTransient && (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 * (1.0 - anim->currentValue()); +// } else { +// // Keep expanded state after the animation ends, and when fading out +// expandScale = maxExpandScale; +// expandOffset = 0.0; +// } + } + } + + d->setupNSGraphicsContext(cg, NO /* flipped */); + + const auto controlType = isHorizontal ? QMacStylePrivate::Scroller_Horizontal : QMacStylePrivate::Scroller_Vertical; + const auto cw = QMacStylePrivate::CocoaControl(controlType, cocoaSize); + NSScroller *scroller = static_cast<NSScroller *>(d->cocoaControl(cw)); + + const QColor bgColor = QStyleHelper::backgroundColor(opt->palette); + const bool hasDarkBg = bgColor.red() < 128 && bgColor.green() < 128 && bgColor.blue() < 128; + if (isTransient) { + // macOS behavior: as soon as one color channel is >= 128, + // the background is considered bright, scroller is dark. + scroller.knobStyle = hasDarkBg? NSScrollerKnobStyleLight : NSScrollerKnobStyleDark; + } else { + scroller.knobStyle = NSScrollerKnobStyleDefault; + } + + scroller.scrollerStyle = isTransient ? NSScrollerStyleOverlay : NSScrollerStyleLegacy; + + if (!setupScroller(scroller, sb)) + break; + + if (isTransient) { + CGContextBeginTransparencyLayerWithRect(cg, scroller.frame, nullptr); + CGContextSetAlpha(cg, opacity); + } + + if (drawTrack) { + // Draw the track when hovering. Expand by shifting the track rect. + if (!isTransient || opt->activeSubControls || wasActive) { + CGRect trackRect = scroller.bounds; + if (isHorizontal) + trackRect.origin.y += expandOffset; + else + trackRect.origin.x += expandOffset; + [scroller drawKnobSlotInRect:trackRect highlight:NO]; + } + } + + if (drawKnob) { + if (shouldExpand) { + // -[NSScroller drawKnob] is not useful here because any scaling applied + // will only be used to draw the hi-DPI artwork. And even if did scale, + // the stretched knob would look wrong, actually. So we need to draw the + // scroller manually when it's being hovered. + const CGFloat scrollerWidth = [NSScroller scrollerWidthForControlSize:scroller.controlSize scrollerStyle:scroller.scrollerStyle]; + const CGFloat knobWidth = knobWidths[cocoaSize] * expandScale; + // Cocoa can help get the exact knob length in the current orientation + const CGRect scrollerKnobRect = CGRectInset([scroller rectForPart:NSScrollerKnob], 1, 1); + const CGFloat knobLength = isHorizontal ? scrollerKnobRect.size.width : scrollerKnobRect.size.height; + const CGFloat knobPos = isHorizontal ? scrollerKnobRect.origin.x : scrollerKnobRect.origin.y; + const CGFloat knobOffset = qRound((scrollerWidth + expandOffset - knobWidth) / 2.0); + const CGFloat knobRadius = knobWidth / 2.0; + CGRect knobRect; + if (isHorizontal) + knobRect = CGRectMake(knobPos, knobOffset, knobLength, knobWidth); + else + knobRect = CGRectMake(knobOffset, knobPos, knobWidth, knobLength); + QCFType<CGPathRef> knobPath = CGPathCreateWithRoundedRect(knobRect, knobRadius, knobRadius, nullptr); + CGContextAddPath(cg, knobPath); + CGContextSetAlpha(cg, 0.5); + CGColorRef knobColor = hasDarkBg ? NSColor.whiteColor.CGColor : NSColor.blackColor.CGColor; + CGContextSetFillColorWithColor(cg, knobColor); + CGContextFillPath(cg); + } else { + [scroller drawKnob]; + + if (!isTransient && opt->state & State_Sunken) { + // The knob should appear darker (going from 0.76 down to 0.49). + // But no blending mode can help darken enough in a single pass, + // so we resort to drawing the knob twice with a small help from + // blending. This brings the gray level to a close enough 0.53. + CGContextSetBlendMode(cg, kCGBlendModePlusDarker); + [scroller drawKnob]; + } + } + } + + if (isTransient) + CGContextEndTransparencyLayer(cg); + + d->restoreNSGraphicsContext(cg); + } + break; + case CC_Slider: + if (const QStyleOptionSlider *sl = qstyleoption_cast<const QStyleOptionSlider *>(opt)) { + const bool isHorizontal = sl->orientation == Qt::Horizontal; + const auto ct = isHorizontal ? QMacStylePrivate::Slider_Horizontal : QMacStylePrivate::Slider_Vertical; + const auto cs = d->effectiveAquaSizeConstrain(opt); + const auto cw = QMacStylePrivate::CocoaControl(ct, cs); + auto *slider = static_cast<NSSlider *>(d->cocoaControl(cw)); + if (!setupSlider(slider, sl)) + break; + + const bool hasTicks = sl->tickPosition != QStyleOptionSlider::NoTicks; + const bool hasDoubleTicks = sl->tickPosition == QStyleOptionSlider::TicksBothSides; + const bool drawKnob = sl->subControls & SC_SliderHandle; + const bool drawBar = sl->subControls & SC_SliderGroove; + const bool drawTicks = sl->subControls & SC_SliderTickmarks; + const bool isPressed = sl->state & State_Sunken; + + CGPoint pressPoint; + if (isPressed && drawKnob) { + const CGRect knobRect = [slider.cell knobRectFlipped:NO]; + pressPoint.x = CGRectGetMidX(knobRect); + pressPoint.y = CGRectGetMidY(knobRect); + [slider.cell startTrackingAt:pressPoint inView:slider]; + } + + d->drawNSViewInRect(slider, opt->rect, p, ^(CGContextRef ctx, const CGRect &rect) { + QMacAutoReleasePool pool; + + // Since the GC is flipped, upsideDown means *not* inverted when vertical. + const bool verticalFlip = !isHorizontal && !sl->upsideDown; // FIXME: && !isSierraOrLater + + if (isHorizontal) { + if (sl->upsideDown) { + CGContextTranslateCTM(ctx, rect.size.width, rect.origin.y); + CGContextScaleCTM(ctx, -1, 1); + } else { + CGContextTranslateCTM(ctx, 0, rect.origin.y); + } + } else if (verticalFlip) { + CGContextTranslateCTM(ctx, rect.origin.x, rect.size.height); + CGContextScaleCTM(ctx, 1, -1); + } + + if (hasDoubleTicks) { + // This ain't HIG kosher: eye-proved constants + if (isHorizontal) + CGContextTranslateCTM(ctx, 0, 4); + else + CGContextTranslateCTM(ctx, 1, 0); + } + +#if 0 + // FIXME: Sadly, this part doesn't work. It seems to somehow polute the + // NSSlider's internal state and, when we need to use the "else" part, + // the slider's frame is not in sync with its cell dimensions. + const bool drawAllParts = drawKnob && drawBar && (!hasTicks || drawTicks); + if (drawAllParts && !hasDoubleTicks && (!verticalFlip || drawTicks)) { + // Draw eveything at once if we're going to, except for inverted vertical + // sliders which need to be drawn part by part because of the shadow below + // the knob. Same for two-sided tickmarks. + if (verticalFlip && drawTicks) { + // Since tickmarks are always rendered symmetrically, a vertically + // flipped slider with tickmarks only needs to get its value flipped. + slider.intValue = slider.maxValue - slider.intValue + slider.minValue; + } + [slider drawRect:CGRectZero]; + } else +#endif + { + [slider calcSize]; + if (!hasDoubleTicks) + fixStaleGeometry(slider); + NSSliderCell *cell = slider.cell; + + const int numberOfTickMarks = slider.numberOfTickMarks; + // This ain't HIG kosher: force tick-less bar position. + if (hasDoubleTicks) + slider.numberOfTickMarks = 0; + + const CGRect barRect = [cell barRectFlipped:hasTicks]; + if (drawBar) { + [cell drawBarInside:barRect flipped:!verticalFlip]; + // This ain't HIG kosher: force unfilled bar look. + if (hasDoubleTicks) + slider.numberOfTickMarks = numberOfTickMarks; + } + + if (drawBar && hasTicks && drawTicks) { + if (!drawBar && hasDoubleTicks) + slider.numberOfTickMarks = numberOfTickMarks; + + [cell drawTickMarks]; + + if (hasDoubleTicks) { + // This ain't HIG kosher: just slap a set of tickmarks on each side, like we used to. + CGAffineTransform tickMarksFlip; + const CGRect tickMarkRect = [cell rectOfTickMarkAtIndex:0]; + if (isHorizontal) { + tickMarksFlip = CGAffineTransformMakeTranslation(0, rect.size.height - tickMarkRect.size.height - 3); + tickMarksFlip = CGAffineTransformScale(tickMarksFlip, 1, -1); + } else { + tickMarksFlip = CGAffineTransformMakeTranslation(rect.size.width - tickMarkRect.size.width / 2, 0); + tickMarksFlip = CGAffineTransformScale(tickMarksFlip, -1, 1); + } + CGContextConcatCTM(ctx, tickMarksFlip); + [cell drawTickMarks]; + CGContextConcatCTM(ctx, CGAffineTransformInvert(tickMarksFlip)); + } + } + + if (drawKnob) { + // This ain't HIG kosher: force round knob look. + if (hasDoubleTicks) + slider.numberOfTickMarks = 0; + [cell drawKnob]; + } + } + }); + + if (isPressed && drawKnob) + [slider.cell stopTracking:pressPoint at:pressPoint inView:slider mouseIsUp:NO]; + } + break; + case CC_SpinBox: + if (const QStyleOptionSpinBox *sb = qstyleoption_cast<const QStyleOptionSpinBox *>(opt)) { + if (sb->frame && (sb->subControls & SC_SpinBoxFrame)) { + const auto lineEditRect = proxy()->subControlRect(CC_SpinBox, sb, SC_SpinBoxEditField); + QStyleOptionFrame frame; + static_cast<QStyleOption &>(frame) = *opt; + frame.rect = lineEditRect; + frame.state |= State_Sunken; + frame.lineWidth = 1; + frame.midLineWidth = 0; + frame.features = QStyleOptionFrame::None; + frame.frameShape = QStyleOptionFrame::Box; + drawPrimitive(PE_FrameLineEdit, &frame, p); + } + if (sb->subControls & (SC_SpinBoxUp | SC_SpinBoxDown)) { + const QRect updown = proxy()->subControlRect(CC_SpinBox, sb, SC_SpinBoxUp) + | proxy()->subControlRect(CC_SpinBox, sb, SC_SpinBoxDown); + + d->setupNSGraphicsContext(cg, NO); + + const auto aquaSize = d->effectiveAquaSizeConstrain(opt); + const auto cw = QMacStylePrivate::CocoaControl(QMacStylePrivate::Stepper, aquaSize); + NSStepperCell *cell = static_cast<NSStepperCell *>(d->cocoaCell(cw)); + cell.enabled = (sb->state & State_Enabled); + + const CGRect newRect = [cell drawingRectForBounds:updown.toCGRect()]; + + const bool upPressed = sb->activeSubControls == SC_SpinBoxUp && (sb->state & State_Sunken); + const bool downPressed = sb->activeSubControls == SC_SpinBoxDown && (sb->state & State_Sunken); + const CGFloat x = CGRectGetMidX(newRect); + const CGFloat y = upPressed ? -3 : 3; // Weird coordinate shift going on. Verified with Hopper + const CGPoint pressPoint = CGPointMake(x, y); + // Pretend we're pressing the mouse on the right button. Unfortunately, NSStepperCell has no + // API to highlight a specific button. The highlighted property works only on the down button. + if (upPressed || downPressed) + [cell startTrackingAt:pressPoint inView:d->backingStoreNSView]; + + [cell drawWithFrame:newRect inView:d->backingStoreNSView]; + + if (upPressed || downPressed) + [cell stopTracking:pressPoint at:pressPoint inView:d->backingStoreNSView mouseIsUp:NO]; + + d->restoreNSGraphicsContext(cg); + } + } + break; + case CC_ComboBox: + if (const auto *combo = qstyleoption_cast<const QStyleOptionComboBox *>(opt)) { + const bool isEnabled = combo->state & State_Enabled; + const bool isPressed = combo->state & State_Sunken; + + const auto ct = cocoaControlType(combo); + const auto cs = d->effectiveAquaSizeConstrain(combo); + const auto cw = QMacStylePrivate::CocoaControl(ct, cs); + auto *cc = static_cast<NSControl *>(d->cocoaControl(cw)); + cc.enabled = isEnabled; + QRectF frameRect = cw.adjustedControlFrame(combo->rect);; + if (cw.type == QMacStylePrivate::Button_PopupButton) { + // Non-editable QComboBox + auto *pb = static_cast<NSPopUpButton *>(cc); + // FIXME Old offsets. Try to move to adjustedControlFrame() + if (cw.size == QStyleHelper::SizeSmall) { + frameRect = frameRect.translated(0, 1); + } else if (cw.size == QStyleHelper::SizeMini) { + // Same 0.5 pt misalignment as AppKit and fit the focus ring + frameRect = frameRect.translated(2, -0.5); + } + pb.frame = frameRect.toCGRect(); + [pb highlight:isPressed]; + d->drawNSViewInRect(pb, frameRect, p, ^(CGContextRef, const CGRect &r) { + QMacAutoReleasePool pool; + [pb.cell drawBezelWithFrame:r inView:pb.superview]; + }); + } else if (cw.type == QMacStylePrivate::ComboBox) { + // Editable QComboBox + auto *cb = static_cast<NSComboBox *>(cc); + const auto frameRect = cw.adjustedControlFrame(combo->rect); + cb.frame = frameRect.toCGRect(); + + // This API was requested to Apple in rdar #36197888. We know it's safe to use up to macOS 10.13.3 + if (NSButtonCell *cell = static_cast<NSButtonCell *>([cc.cell qt_valueForPrivateKey:@"_buttonCell"])) { + cell.highlighted = isPressed; + } else { + // TODO Render to pixmap and darken the button manually + } + + d->drawNSViewInRect(cb, frameRect, p, ^(CGContextRef, const CGRect &r) { + // FIXME This is usually drawn in the control's superview, but we wouldn't get inactive look in this case + QMacAutoReleasePool pool; + [cb.cell drawWithFrame:r inView:cb]; + }); + } + + if (combo->state & State_HasFocus) { + // TODO Remove and use QFocusFrame instead. + const int hMargin = proxy()->pixelMetric(QStyle::PM_FocusFrameHMargin, combo); + const int vMargin = proxy()->pixelMetric(QStyle::PM_FocusFrameVMargin, combo); + QRectF focusRect; + if (cw.type == QMacStylePrivate::Button_PopupButton) { + focusRect = QRectF::fromCGRect([cc alignmentRectForFrame:cc.frame]); + focusRect -= pullDownButtonShadowMargins[cw.size]; + if (cw.size == QStyleHelper::SizeSmall) + focusRect = focusRect.translated(0, 1); + else if (cw.size == QStyleHelper::SizeMini) + focusRect = focusRect.translated(2, -1); + } else if (cw.type == QMacStylePrivate::ComboBox) { + focusRect = frameRect - comboBoxFocusRingMargins[cw.size]; + } + d->drawFocusRing(p, focusRect, hMargin, vMargin, cw); + } + } + break; + case CC_TitleBar: + if (const auto *titlebar = qstyleoption_cast<const QStyleOptionTitleBar *>(opt)) { + const bool isActive = (titlebar->state & State_Active) + && (titlebar->titleBarState & State_Active); + + p->fillRect(opt->rect, Qt::transparent); + p->setRenderHint(QPainter::Antialiasing); + p->setClipRect(opt->rect, Qt::IntersectClip); + + // FIXME A single drawPath() with 0-sized pen + // doesn't look as good as this double fillPath(). + const auto outerFrameRect = QRectF(opt->rect.adjusted(0, 0, 0, opt->rect.height())); + QPainterPath outerFramePath = d->windowPanelPath(outerFrameRect); + p->fillPath(outerFramePath, opt->palette.dark()); + + const auto frameAdjust = 1.0 / p->device()->devicePixelRatioF(); + const auto innerFrameRect = outerFrameRect.adjusted(frameAdjust, frameAdjust, -frameAdjust, 0); + QPainterPath innerFramePath = d->windowPanelPath(innerFrameRect); + if (isActive) { + QLinearGradient g; + g.setStart(QPointF(0, 0)); + g.setFinalStop(QPointF(0, 2 * opt->rect.height())); + g.setColorAt(0, opt->palette.button().color()); + g.setColorAt(1, opt->palette.dark().color()); + p->fillPath(innerFramePath, g); + } else { + p->fillPath(innerFramePath, opt->palette.button()); + } + + if (titlebar->subControls & (SC_TitleBarCloseButton + | SC_TitleBarMaxButton + | SC_TitleBarMinButton + | SC_TitleBarNormalButton)) { + const bool isHovered = (titlebar->state & State_MouseOver); + static const SubControl buttons[] = { + SC_TitleBarCloseButton, SC_TitleBarMinButton, SC_TitleBarMaxButton + }; + for (const auto sc : buttons) { + const auto ct = d->windowButtonCocoaControl(sc); + const auto cw = QMacStylePrivate::CocoaControl(ct, QStyleHelper::SizeLarge); + auto *wb = static_cast<NSButton *>(d->cocoaControl(cw)); + wb.enabled = (sc & titlebar->subControls) && isActive; + [wb highlight:(titlebar->state & State_Sunken) && (sc & titlebar->activeSubControls)]; + Q_UNUSED(isHovered); // FIXME No public API for this + + const auto buttonRect = proxy()->subControlRect(CC_TitleBar, titlebar, sc); + d->drawNSViewInRect(wb, buttonRect, p, ^(CGContextRef, const CGRect &rect) { + QMacAutoReleasePool pool; + auto *wbCell = static_cast<NSButtonCell *>(wb.cell); + [wbCell drawWithFrame:rect inView:wb]; + }); + } + } + + if (titlebar->subControls & SC_TitleBarLabel) { + const auto tr = proxy()->subControlRect(CC_TitleBar, titlebar, SC_TitleBarLabel); + if (!titlebar->icon.isNull()) { + const auto iconExtent = proxy()->pixelMetric(PM_SmallIconSize); + const auto iconSize = QSize(iconExtent, iconExtent); + const auto iconPos = tr.x() - titlebar->icon.actualSize(iconSize).width() - qRound(titleBarIconTitleSpacing); + // Only render the icon if it'll be fully visible + if (iconPos < tr.right() - titleBarIconTitleSpacing) + p->drawPixmap(iconPos, tr.y(), titlebar->icon.pixmap(opt->window, iconSize, QIcon::Normal)); + } + + if (!titlebar->text.isEmpty()) + drawItemText(p, tr, Qt::AlignCenter, opt->palette, isActive, titlebar->text, QPalette::Text); + } + } + 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 + +// const bool didSetFont = widget && widget->testAttribute(Qt::WA_SetFont); +// const bool didModifySubControls = !didSetFont && QApplication::desktopSettingsAware(); +// if (didModifySubControls) +// groupBox.subControls = groupBox.subControls & ~SC_GroupBoxLabel; + QCommonStyle::drawComplexControl(cc, &groupBox, p); +// if (didModifySubControls) { +// const QRect rect = proxy()->subControlRect(CC_GroupBox, &groupBox, SC_GroupBoxLabel); +// const bool rtl = groupBox.direction == Qt::RightToLeft; +// const int alignment = Qt::TextHideMnemonic | (rtl ? Qt::AlignRight : Qt::AlignLeft); +// const QFont savedFont = p->font(); +// if (!flat) +// p->setFont(d->smallSystemFont); +// proxy()->drawItemText(p, rect, alignment, groupBox.palette, groupBox.state & State_Enabled, groupBox.text, QPalette::WindowText); +// if (!flat) +// p->setFont(savedFont); +// } + } + 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); + arrowOpt.rect.setY(arrowOpt.rect.y() + arrowOpt.rect.height() / 2); + arrowOpt.rect.setHeight(arrowOpt.rect.height() / 2); + proxy()->drawPrimitive(PE_IndicatorArrowDown, &arrowOpt, p); + } else if ((tb->features & QStyleOptionToolButton::HasMenu) + && (tb->toolButtonStyle != Qt::ToolButtonTextOnly && !tb->icon.isNull())) { + d->drawToolbarButtonArrow(tb, p); + } + if (tb->state & State_On) { + NSView *view = reinterpret_cast<NSView *>(opt->window->winId()); + bool isKey = false; + if (view) + isKey = [view.window isKeyWindow]; + + QBrush brush(brushForToolButton(isKey)); + 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); + } else +#endif // QT_NO_ACCESSIBILITY + { + auto bflags = tb->state; + if (tb->subControls & SC_ToolButton) + bflags |= State_Sunken; + auto mflags = tb->state; + if (tb->subControls & SC_ToolButtonMenu) + mflags |= State_Sunken; + + if (tb->subControls & SC_ToolButton) { + if (bflags & (State_Sunken | State_On | State_Raised)) { + const bool isEnabled = tb->state & State_Enabled; + const bool isPressed = tb->state & State_Sunken; + const bool isHighlighted = (tb->state & State_Active) && (tb->state & State_On); + const auto ct = QMacStylePrivate::Button_PushButton; + const auto cs = d->effectiveAquaSizeConstrain(opt); + const auto cw = QMacStylePrivate::CocoaControl(ct, cs); + auto *pb = static_cast<NSButton *>(d->cocoaControl(cw)); + pb.bezelStyle = NSShadowlessSquareBezelStyle; // TODO Use NSTexturedRoundedBezelStyle in the future. + pb.frame = opt->rect.toCGRect(); + pb.buttonType = NSPushOnPushOffButton; + pb.enabled = isEnabled; + [pb highlight:isPressed]; + pb.state = isHighlighted && !isPressed ? NSOnState : NSOffState; + const auto buttonRect = proxy()->subControlRect(cc, tb, SC_ToolButton); + d->drawNSViewInRect(pb, buttonRect, p, ^(CGContextRef, const CGRect &rect) { + QMacAutoReleasePool pool; + [pb.cell drawBezelWithFrame:rect inView:pb]; + }); + } + } + + if (tb->subControls & SC_ToolButtonMenu) { + const auto menuRect = proxy()->subControlRect(cc, tb, SC_ToolButtonMenu); + QStyleOption arrowOpt = *tb; + arrowOpt.rect = QRect(menuRect.x() + ((menuRect.width() - toolButtonArrowSize) / 2), + menuRect.height() - (toolButtonArrowSize + toolButtonArrowMargin), + toolButtonArrowSize, + toolButtonArrowSize); + proxy()->drawPrimitive(PE_IndicatorArrowDown, &arrowOpt, p); + } else if (tb->features & QStyleOptionToolButton::HasMenu) { + d->drawToolbarButtonArrow(tb, p); + } + QRect buttonRect = proxy()->subControlRect(CC_ToolButton, tb, SC_ToolButton); + int fw = proxy()->pixelMetric(PM_DefaultFrameWidth, opt); + QStyleOptionToolButton label = *tb; + label.rect = buttonRect.adjusted(fw, fw, -fw, -fw); + proxy()->drawControl(CE_ToolButtonLabel, &label, p); + } + } + break; + case CC_Dial: + if (const QStyleOptionSlider *dial = qstyleoption_cast<const QStyleOptionSlider *>(opt)) + QStyleHelper::drawDial(dial, p); + break; + default: + QCommonStyle::drawComplexControl(cc, opt, p); + break; + } +} + +QStyle::SubControl QMacStyle::hitTestComplexControl(ComplexControl cc, const QStyleOptionComplex *opt, const QPoint &pt) 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); + 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 *sl = qstyleoption_cast<const QStyleOptionSlider *>(opt)) { + if (!sl->rect.contains(pt)) + break; + + const bool hasTicks = sl->tickPosition != QStyleOptionSlider::NoTicks; + const bool isHorizontal = sl->orientation == Qt::Horizontal; + const auto ct = isHorizontal ? QMacStylePrivate::Slider_Horizontal : QMacStylePrivate::Slider_Vertical; + const auto cs = d->effectiveAquaSizeConstrain(opt); + const auto cw = QMacStylePrivate::CocoaControl(ct, cs); + auto *slider = static_cast<NSSlider *>(d->cocoaControl(cw)); + if (!setupSlider(slider, sl)) + break; + + [slider calcSize]; + NSSliderCell *cell = slider.cell; + const auto barRect = QRectF::fromCGRect([cell barRectFlipped:hasTicks]); + const auto knobRect = QRectF::fromCGRect([cell knobRectFlipped:NO]); + if (knobRect.contains(pt)) { + sc = SC_SliderHandle; + } else if (barRect.contains(pt)) { + sc = SC_SliderGroove; + } else if (hasTicks) { + sc = SC_SliderTickmarks; + } + } + break; + case CC_ScrollBar: + if (const QStyleOptionSlider *sb = qstyleoption_cast<const QStyleOptionSlider *>(opt)) { + if (!sb->rect.contains(pt)) { + sc = SC_None; + break; + } + + const bool isHorizontal = sb->orientation == Qt::Horizontal; + const auto ct = isHorizontal ? QMacStylePrivate::Scroller_Horizontal : QMacStylePrivate::Scroller_Vertical; + const auto cs = d->effectiveAquaSizeConstrain(opt); + const auto cw = QMacStylePrivate::CocoaControl(ct, cs); + auto *scroller = static_cast<NSScroller *>(d->cocoaControl(cw)); + if (!setupScroller(scroller, sb)) { + sc = SC_None; + break; + } + + // Since -[NSScroller testPart:] doesn't want to cooperate, we do it the + // straightforward way. In any case, macOS doesn't return line-sized changes + // with NSScroller since 10.7, according to the aforementioned method's doc. + const auto knobRect = QRectF::fromCGRect([scroller rectForPart:NSScrollerKnob]); + if (isHorizontal) { + const bool isReverse = sb->direction == Qt::RightToLeft; + if (pt.x() < knobRect.left()) + sc = isReverse ? SC_ScrollBarAddPage : SC_ScrollBarSubPage; + else if (pt.x() > knobRect.right()) + sc = isReverse ? SC_ScrollBarSubPage : SC_ScrollBarAddPage; + else + sc = SC_ScrollBarSlider; + } else { + if (pt.y() < knobRect.top()) + sc = SC_ScrollBarSubPage; + else if (pt.y() > knobRect.bottom()) + sc = SC_ScrollBarAddPage; + else + sc = SC_ScrollBarSlider; + } + } + break; + default: + sc = QCommonStyle::hitTestComplexControl(cc, opt, pt); + break; + } + return sc; +} + +QRect QMacStyle::subControlRect(ComplexControl cc, const QStyleOptionComplex *opt, SubControl sc) const +{ + Q_D(const QMacStyle); + + QRect ret; + + switch (cc) { + case CC_ScrollBar: + if (const QStyleOptionSlider *sb = qstyleoption_cast<const QStyleOptionSlider *>(opt)) { + const bool isHorizontal = sb->orientation == Qt::Horizontal; + const bool isReverseHorizontal = isHorizontal && (sb->direction == Qt::RightToLeft); + + NSScrollerPart part = NSScrollerNoPart; + if (sc == SC_ScrollBarSlider) { + part = NSScrollerKnob; + } else if (sc == SC_ScrollBarGroove) { + part = NSScrollerKnobSlot; + } else if (sc == SC_ScrollBarSubPage || sc == SC_ScrollBarAddPage) { + if ((!isReverseHorizontal && sc == SC_ScrollBarSubPage) + || (isReverseHorizontal && sc == SC_ScrollBarAddPage)) + part = NSScrollerDecrementPage; + else + part = NSScrollerIncrementPage; + } + // And nothing else since 10.7 + + if (part != NSScrollerNoPart) { + const auto ct = isHorizontal ? QMacStylePrivate::Scroller_Horizontal : QMacStylePrivate::Scroller_Vertical; + const auto cs = d->effectiveAquaSizeConstrain(opt); + const auto cw = QMacStylePrivate::CocoaControl(ct, cs); + auto *scroller = static_cast<NSScroller *>(d->cocoaControl(cw)); + if (setupScroller(scroller, sb)) + ret = QRectF::fromCGRect([scroller rectForPart:part]).toRect(); + } + } + break; + case CC_Slider: + if (const QStyleOptionSlider *sl = qstyleoption_cast<const QStyleOptionSlider *>(opt)) { + const bool hasTicks = sl->tickPosition != QStyleOptionSlider::NoTicks; + const bool isHorizontal = sl->orientation == Qt::Horizontal; + const auto ct = isHorizontal ? QMacStylePrivate::Slider_Horizontal : QMacStylePrivate::Slider_Vertical; + const auto cs = d->effectiveAquaSizeConstrain(opt); + const auto cw = QMacStylePrivate::CocoaControl(ct, cs); + auto *slider = static_cast<NSSlider *>(d->cocoaControl(cw)); + if (!setupSlider(slider, sl)) + break; + + [slider calcSize]; + NSSliderCell *cell = slider.cell; + if (sc == SC_SliderHandle) { + ret = QRectF::fromCGRect([cell knobRectFlipped:NO]).toRect(); + } else if (sc == SC_SliderGroove) { + ret = QRectF::fromCGRect([cell barRectFlipped:hasTicks]).toRect(); + } else if (hasTicks && sc == SC_SliderTickmarks) { + const auto tickMarkRect = QRectF::fromCGRect([cell rectOfTickMarkAtIndex:0]); + if (isHorizontal) + ret = QRect(sl->rect.left(), tickMarkRect.top(), sl->rect.width(), tickMarkRect.height()); + else + ret = QRect(tickMarkRect.left(), sl->rect.top(), tickMarkRect.width(), sl->rect.height()); + } + +// if (sl->upsideDown) { +// if isHorizontal) { +// } else { +// } +// } + } + break; + case CC_TitleBar: + if (const auto *titlebar = qstyleoption_cast<const QStyleOptionTitleBar *>(opt)) { + // The title bar layout is as follows: close, min, zoom, icon, title + // [ x _ + @ Window Title ] + // Center the icon and title until it starts to overlap with the buttons. + // The icon doesn't count towards SC_TitleBarLabel, but it's still rendered + // next to the title text. See drawComplexControl(). + if (sc == SC_TitleBarLabel) { + qreal labelWidth = titlebar->fontMetrics.horizontalAdvance(titlebar->text) + 1; // FIXME Rounding error? + qreal labelHeight = titlebar->fontMetrics.height(); + + const auto lastButtonRect = proxy()->subControlRect(CC_TitleBar, titlebar, SC_TitleBarMaxButton); + qreal controlsSpacing = lastButtonRect.right() + titleBarButtonSpacing; + if (!titlebar->icon.isNull()) { + const auto iconSize = proxy()->pixelMetric(PM_SmallIconSize); + const auto actualIconSize = titlebar->icon.actualSize(QSize(iconSize, iconSize)).width();; + controlsSpacing += actualIconSize + titleBarIconTitleSpacing; + } + + const qreal labelPos = qMax(controlsSpacing, (opt->rect.width() - labelWidth) / 2.0); + labelWidth = qMin(labelWidth, opt->rect.width() - (labelPos + titleBarTitleRightMargin)); + ret = QRect(labelPos, (opt->rect.height() - labelHeight) / 2, + labelWidth, labelHeight); + } else { + const auto currentButton = d->windowButtonCocoaControl(sc); + if (currentButton == QMacStylePrivate::NoControl) + break; + + QPointF buttonPos = titlebar->rect.topLeft() + QPointF(titleBarButtonSpacing, 0); + QSizeF buttonSize; + for (int ct = QMacStylePrivate::Button_WindowClose; ct <= currentButton; ct++) { + const auto cw = QMacStylePrivate::CocoaControl(QMacStylePrivate::CocoaControlType(ct), + QStyleHelper::SizeLarge); + auto *wb = static_cast<NSButton *>(d->cocoaControl(cw)); + if (ct == currentButton) + buttonSize = QSizeF::fromCGSize(wb.frame.size); + else + buttonPos.rx() += wb.frame.size.width + titleBarButtonSpacing; + } + + const auto vOffset = (opt->rect.height() - buttonSize.height()) / 2.0; + ret = QRectF(buttonPos, buttonSize).translated(0, vOffset).toRect(); + } + } + break; + case CC_ComboBox: + if (const QStyleOptionComboBox *combo = qstyleoption_cast<const QStyleOptionComboBox *>(opt)) { + const auto ct = cocoaControlType(combo); + const auto cs = d->effectiveAquaSizeConstrain(combo); + const auto cw = QMacStylePrivate::CocoaControl(ct, cs); + + // Old widget path. Current not understood why it's needed: + //const auto editRect = QMacStylePrivate::comboboxEditBounds(cw.adjustedControlFrame(combo->rect), cw); + + // New path: + QRectF editRect; + switch (cs) { + case QStyleHelper::SizeLarge: + editRect = combo->rect.adjusted(15, 7, -25, -9); + break; + case QStyleHelper::SizeSmall: + if (combo->editable) + editRect = combo->rect.adjusted(15, 6, -22, -9); + else + editRect = combo->rect.adjusted(15, 8, -22, -6); + break; + default: + if (combo->editable) + editRect = combo->rect.adjusted(15, 6, -20, -7); + else + editRect = combo->rect.adjusted(15, 5, -22, -6); + break; + } + + switch (sc) { + case SC_ComboBoxEditField:{ + ret = editRect.toAlignedRect(); + break; } + case SC_ComboBoxArrow:{ + ret = editRect.toAlignedRect(); + ret.setX(ret.x() + ret.width()); + ret.setWidth(combo->rect.right() - ret.right()); + break; } + case SC_ComboBoxListBoxPopup:{ + if (combo->editable) { + const CGRect inner = QMacStylePrivate::comboboxInnerBounds(combo->rect.toCGRect(), cw); + 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 { + 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 + const bool checkable = groupBox->subControls & SC_GroupBoxCheckBox; + const bool fontIsSet = false; +// const bool fontIsSet = (widget && widget->testAttribute(Qt::WA_SetFont)) +// || !QApplication::desktopSettingsAware(); + const int margin = flat || hasNoText ? 0 : 9; + ret = groupBox->rect.adjusted(margin, 0, -margin, 0); + + const QFontMetricsF fm = flat || fontIsSet ? QFontMetricsF(groupBox->fontMetrics) : QFontMetricsF(d->smallSystemFont); + const QSizeF s = fm.size(Qt::AlignHCenter | Qt::AlignVCenter, qt_mac_removeMnemonics(groupBox->text), 0, nullptr); + const int tw = qCeil(s.width()); + const int h = qCeil(fm.height()); + 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); + 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)); + } + break; + } + case SC_GroupBoxContents: + case SC_GroupBoxFrame: { + QFontMetrics fm = groupBox->fontMetrics; + int yOffset = 3; + if (!flat) + 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); + break; + } + } + break; + case CC_SpinBox: + if (const QStyleOptionSpinBox *spin = qstyleoption_cast<const QStyleOptionSpinBox *>(opt)) { + QStyleHelper::WidgetSizePolicy aquaSize = d->effectiveAquaSizeConstrain(spin); + const auto fw = proxy()->pixelMetric(PM_SpinBoxFrameWidth, spin); + int spinner_w; + int spinner_h; + int adjust_y; + int spinBoxSep; + switch (aquaSize) { + case QStyleHelper::SizeLarge: + spinner_w = 14; + spinner_h = 24; + adjust_y = -1; + spinBoxSep = 2; + break; + case QStyleHelper::SizeSmall: + spinner_w = 12; + spinner_h = 20; + adjust_y = -1; + spinBoxSep = 2; + break; + case QStyleHelper::SizeMini: + spinner_w = 10; + spinner_h = 16; + adjust_y = -1; + spinBoxSep = 1; + break; + default: + Q_UNREACHABLE(); + } + + switch (sc) { + case SC_SpinBoxUp: + case SC_SpinBoxDown: { + if (spin->buttonSymbols == QStyleOptionSpinBox::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, spinner_h); + + const auto cw = QMacStylePrivate::CocoaControl(QMacStylePrivate::Stepper, aquaSize); + NSStepperCell *cell = static_cast<NSStepperCell *>(d->cocoaCell(cw)); + const CGRect outRect = [cell drawingRectForBounds:ret.toCGRect()]; + ret = QRectF::fromCGRect(outRect).toRect(); + + 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; + } + // The buttons are drawn with a top-margin (for some reason) into + // the rect. So undo that margin here: + ret.translate(0, adjust_y); + ret = visualRect(spin->direction, spin->rect, ret); + break; + } + case SC_SpinBoxEditField: + ret = spin->rect.adjusted(fw, fw, -fw, -fw); + if (spin->subControls & SC_SpinBoxUp || spin->subControls & SC_SpinBoxDown) { + ret.setWidth(spin->rect.width() - spinBoxSep - spinner_w); + ret = visualRect(spin->direction, spin->rect, ret); + } + break; + default: + ret = QCommonStyle::subControlRect(cc, spin, sc); + break; + } + } + break; + case CC_ToolButton: + ret = QCommonStyle::subControlRect(cc, opt, sc); + if (sc == SC_ToolButtonMenu) { +#ifndef QT_NO_ACCESSIBILITY + if (QStyleHelper::hasAncestor(opt->styleObject, QAccessible::ToolBar)) + ret.adjust(-toolButtonArrowMargin, 0, 0, 0); +#endif + ret.adjust(-1, 0, 0, 0); + } + break; + default: + ret = QCommonStyle::subControlRect(cc, opt, sc); + break; + } + return ret; +} + +QSize QMacStyle::sizeFromContents(ContentsType ct, const QStyleOption *opt, const QSize &csz) const +{ + Q_D(const QMacStyle); + + QSize sz(csz); + bool useAquaGuideline = true; + + switch (ct) { + case CT_SpinBox: + if (const QStyleOptionSpinBox *vopt = qstyleoption_cast<const QStyleOptionSpinBox *>(opt)) { + if (vopt->subControls == SC_SpinBoxFrame) { + const QSize minimumSize(20, 24); + if (sz.width() < minimumSize.width()) + sz.setWidth(minimumSize.width()); + if (sz.height() < minimumSize.height()) + sz.setHeight(minimumSize.height()); + } else { + const QSize buttonSize = proxy()->subControlRect(CC_SpinBox, vopt, SC_SpinBoxUp).size(); + const int upAndDownTogetherHeight = buttonSize.height() * 2; + sz += QSize(buttonSize.width(), upAndDownTogetherHeight); + } + } + break; + 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); + /** + 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" + if (const QStyleOptionTabWidgetFrame *twf + = qstyleoption_cast<const QStyleOptionTabWidgetFrame *>(opt)) { + QSize extra(0,0); + const int overlap = pixelMetric(PM_TabBarBaseOverlap, opt); + const int gapBetweenTabbarAndStackWidget = 2 + 14 - overlap; + + const auto tabDirection = QMacStylePrivate::tabDirection(twf->shape); + if (tabDirection == QMacStylePrivate::North + || tabDirection == QMacStylePrivate::South) { + extra = QSize(2, gapBetweenTabbarAndStackWidget + 1); + } else { + extra = QSize(gapBetweenTabbarAndStackWidget + 1, 2); + } + sz+= extra; + } + break; + case QStyle::CT_TabBarTab: + if (const QStyleOptionTab *tab = qstyleoption_cast<const QStyleOptionTab *>(opt)) { +// const bool differentFont = (widget && widget->testAttribute(Qt::WA_SetFont)) +// || !QApplication::desktopSettingsAware(); + const bool differentFont = false; + const auto tabDirection = QMacStylePrivate::tabDirection(tab->shape); + const bool verticalTabs = tabDirection == QMacStylePrivate::East + || tabDirection == QMacStylePrivate::West; + if (verticalTabs) + sz = sz.transposed(); + + int defaultTabHeight; + const auto cs = d->effectiveAquaSizeConstrain(opt); + switch (cs) { + case QStyleHelper::SizeLarge: + if (tab->documentMode) + defaultTabHeight = 24; + else + defaultTabHeight = 21; + break; + case QStyleHelper::SizeSmall: + defaultTabHeight = 18; + break; + case QStyleHelper::SizeMini: + defaultTabHeight = 16; + break; + default: + break; + } + + const bool widthSet = !differentFont && tab->icon.isNull(); + if (widthSet) { + const auto textSize = opt->fontMetrics.size(Qt::TextShowMnemonic, tab->text); + sz.rwidth() = textSize.width(); + sz.rheight() = qMax(defaultTabHeight, textSize.height()); + } else { + sz.rheight() = qMax(defaultTabHeight, sz.height()); + } + sz.rwidth() += proxy()->pixelMetric(PM_TabBarTabHSpace, tab); + + if (verticalTabs) + 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 (verticalTabs) { + sz.setWidth(qMax(sz.width(), maxWidgetWidth)); + sz.setHeight(sz.height() + widgetHeight + padding); + } else { + if (widthSet) + sz.setWidth(sz.width() + widgetWidth + padding); + sz.setHeight(qMax(sz.height(), maxWidgetHeight)); + } + } + break; + case CT_LineEdit: + if (const QStyleOptionFrame *f = qstyleoption_cast<const QStyleOptionFrame *>(opt)) { + if (sz.isEmpty()) { + // Minimum size (with padding: 18x24) + sz.rwidth() = 10; + sz.rheight() = 20; + } + // From using pixelTool with XCode/NSTextTextField + int leftPadding = 4; + int rightPadding = 4; + int topPadding = 4; + int bottomPadding = 0; + + if (opt->state & QStyle::State_Small) { + topPadding = 3; + } else if (opt->state & QStyle::State_Mini) { + topPadding = 2; + } + + sz.rwidth() += leftPadding + rightPadding; + sz.rheight() += topPadding + bottomPadding; + } + break; + case QStyle::CT_PushButton: { + if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(opt)) + if (btn->features & QStyleOptionButton::CommandLinkButton) + return QCommonStyle::sizeFromContents(ct, opt, sz); + + // By default, we fit the contents inside a normal rounded push button. + // Do this by add enough space around the contents so that rounded + // borders (including highlighting when active) will show. + // TODO Use QFocusFrame and get rid of these horrors. + QSize macsz; + const auto controlSize = d->effectiveAquaSizeConstrain(opt, CT_PushButton, sz, &macsz); + // FIXME See comment in CT_PushButton case in qt_aqua_get_known_size(). + if (macsz.width() != -1) + sz.setWidth(macsz.width()); + else + sz.rwidth() += QMacStylePrivate::PushButtonLeftOffset + QMacStylePrivate::PushButtonRightOffset + 12; + // All values as measured from HIThemeGetButtonBackgroundBounds() + if (controlSize != QStyleHelper::SizeMini) + sz.rwidth() += 12; // We like 12 over here. + if (controlSize == QStyleHelper::SizeLarge && sz.height() > 16) + sz.rheight() += pushButtonDefaultHeight[QStyleHelper::SizeLarge] - 16; + else if (controlSize == QStyleHelper::SizeMini) + sz.setHeight(24); // FIXME Our previous HITheme-based logic returned this. + else + sz.setHeight(pushButtonDefaultHeight[controlSize]); + break; + } + case QStyle::CT_MenuItem: + if (const QStyleOptionMenuItem *mi = qstyleoption_cast<const QStyleOptionMenuItem *>(opt)) { + int maxpmw = mi->maxIconWidth; + int w = sz.width(); + int h = sz.height(); + +//#if QT_CONFIG(combobox) +// const QComboBox *comboBox = qobject_cast<const QComboBox *>(widget); +//#endif + + if (mi->menuItemType == QStyleOptionMenuItem::Separator) { + w = 10; + h = qt_mac_aqua_get_metric(MenuSeparatorHeight); + } else { + h = mi->fontMetrics.height() + 2; + if (!mi->icon.isNull()) { +//#if QT_CONFIG(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; + else if (mi->menuItemType == QStyleOptionMenuItem::SubMenu) + w += 35; // Not quite exactly as it seems to depend on other factors + if (maxpmw) + w += maxpmw + 6; + // add space for a check. All items have place for a check too. + w += 20; +// 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 { +// 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; + if (const auto *tb = qstyleoption_cast<const QStyleOptionToolButton *>(opt)) + if (tb->features & QStyleOptionToolButton::Menu) + sz.rwidth() += toolButtonArrowMargin; + return sz; + case CT_ComboBox: + if (const auto *cb = qstyleoption_cast<const QStyleOptionComboBox *>(opt)) { + const int controlSize = getControlSize(opt); + + if (!cb->editable) { + if (sz.width() < 10) { + // minimumSize (to ensure a nice nine patch image) + sz.rwidth() += 10; + } + // Same as CT_PushButton, because we have to fit the focus + // ring and a non-editable combo box is a NSPopUpButton. + sz.rwidth() += QMacStylePrivate::PushButtonLeftOffset + QMacStylePrivate::PushButtonRightOffset; + + if (controlSize == QStyleHelper::SizeLarge) { + sz.rwidth() += 30; + } else if (controlSize == QStyleHelper::SizeSmall) { + sz.rwidth() += 26; + } else { + sz.rwidth() += 21; + } + } else { + sz.rwidth() += 50; // FIXME Double check this + } + + // This should be enough to fit the focus ring + if (controlSize == QStyleHelper::SizeMini) + sz.setHeight(24); // FIXME Our previous HITheme-based logic returned this for CT_PushButton. + else + sz.setHeight(pushButtonDefaultHeight[controlSize]); + + return sz; + } + 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, &menuMask)) + sz = menuMask.region.boundingRect().size(); + } + break; } + case CT_HeaderSection:{ + const QStyleOptionHeader *header = qstyleoption_cast<const QStyleOptionHeader *>(opt); + sz = QCommonStyle::sizeFromContents(ct, opt, csz); + 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 minimumWidth = 24; + const int absoluteHeight = 14; + if (slider->orientation == Qt::Horizontal) { + sz = sz.expandedTo(QSize(minimumWidth, sz.height())); + sz.setHeight(absoluteHeight); + } else { + sz = sz.expandedTo(QSize(sz.width(), minimumWidth)); + sz.setWidth(absoluteHeight); + } + } + break; + case CT_ItemViewItem: + if (const QStyleOptionViewItem *vopt = qstyleoption_cast<const QStyleOptionViewItem *>(opt)) { + sz = QCommonStyle::sizeFromContents(ct, vopt, csz); + sz.setHeight(sz.height() + 2); + } + break; + default: + sz = QCommonStyle::sizeFromContents(ct, opt, csz); + } + + if (useAquaGuideline && ct != CT_PushButton) { + // TODO Probably going away at some point + QSize macsz; + if (d->aquaSizeConstrain(opt, ct, sz, &macsz) != QStyleHelper::SizeDefault) { + 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)){ + if (combo->editable) { + const auto widgetSize = d->aquaSizeConstrain(opt); + QMacStylePrivate::CocoaControl cw; + cw.type = combo->editable ? QMacStylePrivate::ComboBox : QMacStylePrivate::Button_PopupButton; + cw.size = widgetSize; + const CGRect diffRect = QMacStylePrivate::comboboxInnerBounds(CGRectZero, cw); + sz.rwidth() -= qRound(diffRect.size.width); + sz.rheight() -= qRound(diffRect.size.height); + } + } + return sz; +} + +QFont QMacStyle::font(QStyle::ControlElement element, const QStyle::State state) const +{ + QFont font = QCommonStyle::font(element, state); + + if (state & QStyle::State_Small) { + font.setPixelSize(11); + } else if (state & QStyle::State_Mini) { + font.setPixelSize(9); + } + + return font; +} + +QMargins QMacStyle::ninePatchMargins(QStyle::ComplexControl cc, const QStyleOptionComplex *opt, const QSize &imageSize) const +{ + QMargins margins; + + switch (cc) { + case CC_ComboBox: { + const QRect arrow = subControlRect(CC_ComboBox, opt, SC_ComboBoxArrow); + margins = QMargins(10, 0, arrow.width(), -1); + break; } + default: + margins = QCommonStyle::ninePatchMargins(cc, opt, imageSize); + break; + } + + return margins; +} + +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); +} + +QIcon QMacStyle::standardIcon(StandardPixmap standardIcon, const QStyleOption *opt) const +{ + switch (standardIcon) { + default: + return QCommonStyle::standardIcon(standardIcon, opt); + 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; + } + } +} + +} // namespace QQC2 + +QT_END_NAMESPACE diff --git a/src/imports/nativestyle/qstyle/mac/qquickmacstyle_mac_p.h b/src/imports/nativestyle/qstyle/mac/qquickmacstyle_mac_p.h new file mode 100644 index 00000000..10f83e5b --- /dev/null +++ b/src/imports/nativestyle/qstyle/mac/qquickmacstyle_mac_p.h @@ -0,0 +1,102 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.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 "qquickcommonstyle.h" + +QT_BEGIN_NAMESPACE + +class QPalette; + +namespace QQC2 { + +class QStyleOptionButton; +class QMacStylePrivate; + +class QMacStyle : public QCommonStyle +{ + Q_OBJECT +public: + QMacStyle(); + virtual ~QMacStyle(); + + void drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, QPainter *p) const; + void drawControl(ControlElement element, const QStyleOption *opt, QPainter *p) const; + void drawComplexControl(ComplexControl cc, const QStyleOptionComplex *opt, QPainter *p) const; + + QRect subElementRect(SubElement r, const QStyleOption *opt) const; + QRect subControlRect(ComplexControl cc, const QStyleOptionComplex *opt, SubControl sc) const; + SubControl hitTestComplexControl(ComplexControl cc, const QStyleOptionComplex *opt, const QPoint &pt) const; + + QSize sizeFromContents(ContentsType ct, const QStyleOption *opt, const QSize &contentsSize) const; + QFont font(ControlElement element, const QStyle::State state) const; + QMargins ninePatchMargins(ComplexControl cc, const QStyleOptionComplex *opt, const QSize &imageSize) const override; + + int pixelMetric(PixelMetric pm, const QStyleOption *opt = 0) const; + virtual int styleHint(StyleHint sh, const QStyleOption *opt = 0, QStyleHintReturn *shret = 0) const; + + QPixmap standardPixmap(StandardPixmap sp, const QStyleOption *opt) 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; + + QIcon standardIcon(StandardPixmap standardIcon, const QStyleOption *opt = 0) const; + +private: + Q_DISABLE_COPY_MOVE(QMacStyle) + Q_DECLARE_PRIVATE(QMacStyle) +}; + +} // namespace QQC2 + +QT_END_NAMESPACE + +#endif // QMACSTYLE_MAC_P_H diff --git a/src/imports/nativestyle/qstyle/mac/qquickmacstyle_mac_p_p.h b/src/imports/nativestyle/qstyle/mac/qquickmacstyle_mac_p_p.h new file mode 100644 index 00000000..548ef92a --- /dev/null +++ b/src/imports/nativestyle/qstyle/mac/qquickmacstyle_mac_p_p.h @@ -0,0 +1,232 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef QMACSTYLE_MAC_P_P_H +#define QMACSTYLE_MAC_P_P_H + +#include "qquickmacstyle_mac_p.h" +#include "qquickcommonstyle_p.h" +#include "qquickstylehelper_p.h" + +#include <QtCore/qdebug.h> +#include <QtCore/qhash.h> +#include <QtCore/qmap.h> +#include <QtCore/qmath.h> +#include <QtCore/qpair.h> +#include <QtCore/qpointer.h> +#include <QtCore/qtextstream.h> +#include <QtCore/qvector.h> + +#include <QtGui/qbitmap.h> +#include <QtGui/qevent.h> +#include <QtGui/qpaintdevice.h> +#include <QtGui/qpainter.h> +#include <QtGui/qpixmapcache.h> + +#include <QtQuick/qquickitem.h> + +#include <QtCore/private/qcore_mac_p.h> +#include <QtGui/private/qpainter_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. +// + +Q_FORWARD_DECLARE_MUTABLE_CG_TYPE(CGContext); + +Q_FORWARD_DECLARE_OBJC_CLASS(NSView); +Q_FORWARD_DECLARE_OBJC_CLASS(NSCell); + +QT_BEGIN_NAMESPACE + +namespace QQC2 { + +/* + AHIG: + macOS Human Interface Guidelines + https://developer.apple.com/macos/human-interface-guidelines/overview/themes/ + + Builder: + Interface Builder in Xcode 8 or later +*/ + +// 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)) + +#define SIZE(large, small, mini) \ + (controlSize == QStyleHelper::SizeLarge ? (large) : controlSize == QStyleHelper::SizeSmall ? (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 (false) + +class QMacStylePrivate : public QCommonStylePrivate +{ + Q_DECLARE_PUBLIC(QMacStyle) + +public: + enum Direction { + North, South, East, West + }; + + enum CocoaControlType { + NoControl, // For when there's no such a control in Cocoa + Box, // QGroupBox + Box_Dark, // FIXME See render code in drawPrimitive(PE_FrameTabWidget) + Button_CheckBox, + Button_Disclosure, // Disclosure triangle, like in QTreeView + Button_PopupButton, // Non-editable QComboBox + Button_PullDown, // QPushButton with menu + Button_PushButton, // Plain QPushButton and QTabBar buttons + Button_RadioButton, + Button_SquareButton, // Oversized QPushButton + Button_WindowClose, + Button_WindowMiniaturize, + Button_WindowZoom, + ComboBox, // Editable QComboBox + ProgressIndicator_Determinate, + ProgressIndicator_Indeterminate, + Scroller_Horizontal, + Scroller_Vertical, + SegmentedControl_First, // QTabBar buttons focus ring + SegmentedControl_Middle, + SegmentedControl_Last, + SegmentedControl_Single, + Slider_Horizontal, + Slider_Vertical, + SplitView_Horizontal, + SplitView_Vertical, + Stepper, // QSpinBox buttons + TextField + }; + + struct CocoaControl { + CocoaControl(); + CocoaControl(CocoaControlType t, QStyleHelper::WidgetSizePolicy s); + + CocoaControlType type; + QStyleHelper::WidgetSizePolicy size; + + bool operator==(const CocoaControl &other) const; + + QSizeF defaultFrameSize() const; + QRectF adjustedControlFrame(const QRectF &rect) const; + QMarginsF titleMargins() const; + + bool getCocoaButtonTypeAndBezelStyle(NSButtonType *buttonType, NSBezelStyle *bezelStyle) const; + }; + + typedef void (^DrawRectBlock)(CGContextRef, const CGRect &); + + QMacStylePrivate(); + ~QMacStylePrivate(); + + // Ideally these wouldn't exist, but since they already exist we need some accessors. + static const int PushButtonLeftOffset; + static const int PushButtonRightOffset; + static const int PushButtonContentPadding; + + enum Animates { AquaPushButton, AquaProgressBar, AquaListViewItemOpen, AquaScrollBar }; + QStyleHelper::WidgetSizePolicy aquaSizeConstrain(const QStyleOption *option, + QStyle::ContentsType ct = QStyle::CT_CustomBase, + QSize szHint=QSize(-1, -1), QSize *insz = 0) const; + QStyleHelper::WidgetSizePolicy effectiveAquaSizeConstrain(const QStyleOption *option, + QStyle::ContentsType ct = QStyle::CT_CustomBase, + QSize szHint=QSize(-1, -1), QSize *insz = 0) const; + inline int animateSpeed(Animates) const { return 33; } + + // Utility functions + static CGRect comboboxInnerBounds(const CGRect &outterBounds, const CocoaControl &cocoaWidget); + + static QRectF comboboxEditBounds(const QRectF &outterBounds, const CocoaControl &cw); + + void setAutoDefaultButton(QObject *button) const; + + NSView *cocoaControl(CocoaControl cocoaControl) const; + NSCell *cocoaCell(CocoaControl cocoaControl) const; + + void setupNSGraphicsContext(CGContextRef cg, bool flipped) const; + void restoreNSGraphicsContext(CGContextRef cg) const; + + void setupVerticalInvertedXform(CGContextRef cg, bool reverse, bool vertical, const CGRect &rect) const; + + void drawNSViewInRect(NSView *view, const QRectF &rect, QPainter *p, DrawRectBlock drawRectBlock = nil) const; + void resolveCurrentNSView(QWindow *window) const; + + void drawFocusRing(QPainter *p, const QRectF &targetRect, int hMargin, int vMargin, const CocoaControl &cw) const; + + void drawToolbarButtonArrow(const QStyleOption *opt, QPainter *p) const; + + QPainterPath windowPanelPath(const QRectF &r) const; + + CocoaControlType windowButtonCocoaControl(QStyle::SubControl sc) const; + + void tabLayout(const QStyleOptionTab *opt, QRect *textRect, QRect *iconRect) const override; + static Direction tabDirection(QStyleOptionTab::Shape shape); + static bool verticalTabs(QMacStylePrivate::Direction tabDirection); + +public: + mutable QPointer<QObject> autoDefaultButton; + static QVector<QPointer<QObject> > scrollBars; + + mutable QPointer<QQuickItem> focusWidget; // TODO: rename to focusItem + mutable NSView *backingStoreNSView; + mutable QHash<CocoaControl, NSView *> cocoaControls; + mutable QHash<CocoaControl, NSCell *> cocoaCells; + + QFont smallSystemFont; + QFont miniSystemFont; + + QMacKeyValueObserver appearanceObserver; +}; + +} // namespace QQC2 + +QT_END_NAMESPACE + +#endif // QMACSTYLE_MAC_P_P_H diff --git a/src/imports/nativestyle/qstyle/qquickcommonstyle.cpp b/src/imports/nativestyle/qstyle/qquickcommonstyle.cpp new file mode 100644 index 00000000..41f12545 --- /dev/null +++ b/src/imports/nativestyle/qstyle/qquickcommonstyle.cpp @@ -0,0 +1,6078 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickcommonstyle.h" +#include "qquickcommonstyle_p.h" +#include "qquickcommonstylepixmaps_p.h" + +#include "qquickstyleoption.h" +#include "qquickdrawutil.h" +#include "qquickstylehelper_p.h" + +#include <QtGui/QWindow> +#include <qfile.h> +#include <private/qguiapplication_p.h> +#include <qpa/qplatformtheme.h> +#include <qbitmap.h> +#include <qcache.h> +#include <qmath.h> +#include <qpainter.h> +#include <qpaintengine.h> +#include <qpainterpath.h> +#include <private/qmath_p.h> +#include <qdebug.h> +#include <qtextformat.h> +#include <qfileinfo.h> +#include <qdir.h> +#include <qsettings.h> +#include <qvariant.h> +#include <qpixmapcache.h> +#include <qmatrix4x4.h> + +#include <limits.h> + +#include <private/qtextengine_p.h> +#include <QtGui/private/qhexstring_p.h> + +QT_BEGIN_NAMESPACE + +/*! + \class QCommonStyle + \brief The QCommonStyle class encapsulates the common Look and Feel of a GUI. + + \ingroup appearance + \inmodule QtWidgets + + This abstract class implements some of the widget's look and feel + that is common to all GUI styles provided and shipped as part of + Qt. + + Since QCommonStyle inherits QStyle, all of its functions are fully documented + in the QStyle documentation. + \omit + , although the + extra functions that QCommonStyle provides, e.g. + drawComplexControl(), drawControl(), drawPrimitive(), + hitTestComplexControl(), subControlRect(), sizeFromContents(), and + subElementRect() are documented here. + \endomit + + \sa QStyle, QProxyStyle +*/ + +namespace QQC2 { + +QCommonStyle::QCommonStyle() + : QStyle(*new QCommonStylePrivate) +{ } + +QCommonStyle::QCommonStyle(QCommonStylePrivate &dd) + : QStyle(dd) +{ } + +QCommonStyle::~QCommonStyle() +{ } + +void QCommonStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, QPainter *p) const +{ + Q_D(const QCommonStyle); + switch (pe) { + case PE_FrameButtonBevel: + case PE_FrameButtonTool: + qDrawShadeRect(p, opt->rect, opt->palette, + opt->state & (State_Sunken | State_On), 1, 0); + break; + case PE_PanelButtonCommand: + case PE_PanelButtonBevel: + case PE_PanelButtonTool: + case PE_IndicatorButtonDropDown: + qDrawShadePanel(p, opt->rect, opt->palette, + opt->state & (State_Sunken | State_On), 1, + &opt->palette.brush(QPalette::Button)); + break; + case PE_IndicatorItemViewItemCheck: + proxy()->drawPrimitive(PE_IndicatorCheckBox, opt, p); + break; + case PE_IndicatorCheckBox: + if (opt->state & State_NoChange) { + p->setPen(opt->palette.windowText().color()); + p->fillRect(opt->rect, opt->palette.brush(QPalette::Button)); + p->drawRect(opt->rect); + p->drawLine(opt->rect.topLeft(), opt->rect.bottomRight()); + } else { + qDrawShadePanel(p, opt->rect.x(), opt->rect.y(), opt->rect.width(), opt->rect.height(), + opt->palette, opt->state & (State_Sunken | State_On), 1, + &opt->palette.brush(QPalette::Button)); + } + break; + case PE_IndicatorRadioButton: { + QRect ir = opt->rect; + p->setPen(opt->palette.dark().color()); + p->drawArc(opt->rect, 0, 5760); + if (opt->state & (State_Sunken | State_On)) { + ir.adjust(2, 2, -2, -2); + p->setBrush(opt->palette.windowText()); + bool oldQt4CompatiblePainting = p->testRenderHint(QPainter::Qt4CompatiblePainting); + p->setRenderHint(QPainter::Qt4CompatiblePainting); + p->drawEllipse(ir); + p->setRenderHint(QPainter::Qt4CompatiblePainting, oldQt4CompatiblePainting); + } + break; } + case PE_FrameFocusRect: + 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.windowText().color()); + } + QRect focusRect = opt->rect.adjusted(1, 1, -1, -1); + p->drawRect(focusRect.adjusted(0, 0, -1, -1)); //draw pen inclusive + p->setPen(oldPen); + } + break; + case PE_IndicatorMenuCheckMark: { + const int markW = opt->rect.width() > 7 ? 7 : opt->rect.width(); + const int markH = markW; + int posX = opt->rect.x() + (opt->rect.width() - markW)/2 + 1; + int posY = opt->rect.y() + (opt->rect.height() - markH)/2; + + QVector<QLineF> a; + a.reserve(markH); + + int i, xx, yy; + xx = posX; + yy = 3 + posY; + for (i = 0; i < markW/2; ++i) { + a << QLineF(xx, yy, xx, yy + 2); + ++xx; + ++yy; + } + yy -= 2; + for (; i < markH; ++i) { + a << QLineF(xx, yy, xx, yy + 2); + ++xx; + --yy; + } + if (!(opt->state & State_Enabled) && !(opt->state & State_On)) { + p->save(); + p->translate(1, 1); + p->setPen(opt->palette.light().color()); + p->drawLines(a); + p->restore(); + } + p->setPen((opt->state & State_On) ? opt->palette.highlightedText().color() : opt->palette.text().color()); + p->drawLines(a); + break; } + case PE_Frame: + case PE_FrameMenu: + if (const QStyleOptionFrame *frame = qstyleoption_cast<const QStyleOptionFrame *>(opt)) { + if (pe == PE_FrameMenu || (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.windowText().color(), frame->lineWidth); + } + } + break; + case PE_PanelMenuBar: + if (const QStyleOptionFrame *frame = qstyleoption_cast<const QStyleOptionFrame *>(opt)){ + qDrawShadePanel(p, frame->rect, frame->palette, false, frame->lineWidth, + &frame->palette.brush(QPalette::Button)); + + } + else if (const QStyleOptionToolBar *frame = qstyleoption_cast<const QStyleOptionToolBar *>(opt)){ + qDrawShadePanel(p, frame->rect, frame->palette, false, frame->lineWidth, + &frame->palette.brush(QPalette::Button)); + } + + break; + case PE_PanelMenu: + break; + case PE_PanelToolBar: + break; + case PE_IndicatorProgressChunk: + { + p->fillRect(opt->rect.x(), opt->rect.y() + 3, opt->rect.width() -2, opt->rect.height() - 6, + opt->palette.brush(QPalette::Highlight)); + } + break; + case PE_IndicatorBranch: { + static const int decoration_size = 9; + int mid_h = opt->rect.x() + opt->rect.width() / 2; + int mid_v = opt->rect.y() + opt->rect.height() / 2; + int bef_h = mid_h; + int bef_v = mid_v; + int aft_h = mid_h; + int aft_v = mid_v; + if (opt->state & State_Children) { + int delta = decoration_size / 2; + bef_h -= delta; + bef_v -= delta; + aft_h += delta; + aft_v += delta; + p->drawLine(bef_h + 2, bef_v + 4, bef_h + 6, bef_v + 4); + if (!(opt->state & State_Open)) + p->drawLine(bef_h + 4, bef_v + 2, bef_h + 4, bef_v + 6); + QPen oldPen = p->pen(); + p->setPen(opt->palette.dark().color()); + p->drawRect(bef_h, bef_v, decoration_size - 1, decoration_size - 1); + p->setPen(oldPen); + } + QBrush brush(opt->palette.dark().color(), Qt::Dense4Pattern); + if (opt->state & State_Item) { + if (opt->direction == Qt::RightToLeft) + p->fillRect(opt->rect.left(), mid_v, bef_h - opt->rect.left(), 1, brush); + else + p->fillRect(aft_h, mid_v, opt->rect.right() - aft_h + 1, 1, brush); + } + if (opt->state & State_Sibling) + p->fillRect(mid_h, aft_v, 1, opt->rect.bottom() - aft_v + 1, brush); + if (opt->state & (State_Open | State_Children | State_Item | State_Sibling)) + p->fillRect(mid_h, opt->rect.y(), 1, bef_v - opt->rect.y(), brush); + break; } + case PE_FrameStatusBarItem: + qDrawShadeRect(p, opt->rect, opt->palette, true, 1, 0, nullptr); + break; + case PE_IndicatorHeaderArrow: + if (const QStyleOptionHeader *header = qstyleoption_cast<const QStyleOptionHeader *>(opt)) { + QPen oldPen = p->pen(); + if (header->sortIndicator & QStyleOptionHeader::SortUp) { + p->setPen(QPen(opt->palette.light(), 0)); + p->drawLine(opt->rect.x() + opt->rect.width(), opt->rect.y(), + opt->rect.x() + opt->rect.width() / 2, opt->rect.y() + opt->rect.height()); + p->setPen(QPen(opt->palette.dark(), 0)); + const QPoint points[] = { + QPoint(opt->rect.x() + opt->rect.width() / 2, opt->rect.y() + opt->rect.height()), + QPoint(opt->rect.x(), opt->rect.y()), + QPoint(opt->rect.x() + opt->rect.width(), opt->rect.y()), + }; + p->drawPolyline(points, sizeof points / sizeof *points); + } else if (header->sortIndicator & QStyleOptionHeader::SortDown) { + p->setPen(QPen(opt->palette.light(), 0)); + const QPoint points[] = { + QPoint(opt->rect.x(), opt->rect.y() + opt->rect.height()), + QPoint(opt->rect.x() + opt->rect.width(), opt->rect.y() + opt->rect.height()), + QPoint(opt->rect.x() + opt->rect.width() / 2, opt->rect.y()), + }; + p->drawPolyline(points, sizeof points / sizeof *points); + p->setPen(QPen(opt->palette.dark(), 0)); + p->drawLine(opt->rect.x(), opt->rect.y() + opt->rect.height(), + opt->rect.x() + opt->rect.width() / 2, opt->rect.y()); + } + p->setPen(oldPen); + } + break; + case PE_FrameTabBarBase: + if (const QStyleOptionTabBarBase *tbb + = qstyleoption_cast<const QStyleOptionTabBarBase *>(opt)) { + p->save(); + switch (tbb->shape) { + case QStyleOptionTab::RoundedNorth: + case QStyleOptionTab::TriangularNorth: + p->setPen(QPen(tbb->palette.light(), 0)); + p->drawLine(tbb->rect.topLeft(), tbb->rect.topRight()); + break; + case QStyleOptionTab::RoundedWest: + case QStyleOptionTab::TriangularWest: + p->setPen(QPen(tbb->palette.light(), 0)); + p->drawLine(tbb->rect.topLeft(), tbb->rect.bottomLeft()); + break; + case QStyleOptionTab::RoundedSouth: + case QStyleOptionTab::TriangularSouth: + p->setPen(QPen(tbb->palette.shadow(), 0)); + p->drawLine(tbb->rect.left(), tbb->rect.bottom(), + tbb->rect.right(), tbb->rect.bottom()); + p->setPen(QPen(tbb->palette.dark(), 0)); + p->drawLine(tbb->rect.left(), tbb->rect.bottom() - 1, + tbb->rect.right() - 1, tbb->rect.bottom() - 1); + break; + case QStyleOptionTab::RoundedEast: + case QStyleOptionTab::TriangularEast: + p->setPen(QPen(tbb->palette.dark(), 0)); + p->drawLine(tbb->rect.topRight(), tbb->rect.bottomRight()); + break; + } + p->restore(); + } + break; + case PE_IndicatorTabClose: { + if (d->tabBarcloseButtonIcon.isNull()) { + d->tabBarcloseButtonIcon.addPixmap(QPixmap( + QLatin1String(":/qt-project.org/styles/commonstyle/images/standardbutton-closetab-16.png")), + QIcon::Normal, QIcon::Off); + d->tabBarcloseButtonIcon.addPixmap(QPixmap( + QLatin1String(":/qt-project.org/styles/commonstyle/images/standardbutton-closetab-down-16.png")), + QIcon::Normal, QIcon::On); + d->tabBarcloseButtonIcon.addPixmap(QPixmap( + QLatin1String(":/qt-project.org/styles/commonstyle/images/standardbutton-closetab-hover-16.png")), + QIcon::Active, QIcon::Off); + } + + int size = proxy()->pixelMetric(QStyle::PM_SmallIconSize); + QIcon::Mode mode = opt->state & State_Enabled ? + (opt->state & State_Raised ? QIcon::Active : QIcon::Normal) + : QIcon::Disabled; + if (!(opt->state & State_Raised) + && !(opt->state & State_Sunken) + && !(opt->state & QStyle::State_Selected)) + mode = QIcon::Disabled; + + QIcon::State state = opt->state & State_Sunken ? QIcon::On : QIcon::Off; + QPixmap pixmap = d->tabBarcloseButtonIcon.pixmap(opt->window, QSize(size, size), mode, state); + proxy()->drawItemPixmap(p, opt->rect, Qt::AlignCenter, pixmap); + break; + } + case PE_FrameTabWidget: + case PE_FrameWindow: + qDrawWinPanel(p, opt->rect, opt->palette, false, nullptr); + break; + case PE_FrameLineEdit: + proxy()->drawPrimitive(PE_Frame, opt, p); + break; + case 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); + } + } + break; + case PE_FrameDockWidget: + if (const QStyleOptionFrame *frame = qstyleoption_cast<const QStyleOptionFrame *>(opt)) { + int lw = frame->lineWidth; + if (lw <= 0) + lw = proxy()->pixelMetric(PM_DockWidgetFrameWidth); + + qDrawShadePanel(p, frame->rect, frame->palette, false, lw); + } + break; + case PE_IndicatorToolBarHandle: + p->save(); + p->translate(opt->rect.x(), opt->rect.y()); + if (opt->state & State_Horizontal) { + int x = opt->rect.width() / 3; + if (opt->direction == Qt::RightToLeft) + x -= 2; + if (opt->rect.height() > 4) { + qDrawShadePanel(p, x, 2, 3, opt->rect.height() - 4, + opt->palette, false, 1, nullptr); + qDrawShadePanel(p, x+3, 2, 3, opt->rect.height() - 4, + opt->palette, false, 1, nullptr); + } + } else { + if (opt->rect.width() > 4) { + int y = opt->rect.height() / 3; + qDrawShadePanel(p, 2, y, opt->rect.width() - 4, 3, + opt->palette, false, 1, nullptr); + qDrawShadePanel(p, 2, y+3, opt->rect.width() - 4, 3, + opt->palette, false, 1, nullptr); + } + } + p->restore(); + break; + case PE_IndicatorToolBarSeparator: + { + QPoint p1, p2; + if (opt->state & State_Horizontal) { + p1 = QPoint(opt->rect.width()/2, 0); + p2 = QPoint(p1.x(), opt->rect.height()); + } else { + p1 = QPoint(0, opt->rect.height()/2); + p2 = QPoint(opt->rect.width(), p1.y()); + } + qDrawShadeLine(p, p1, p2, opt->palette, 1, 1, 0); + break; + } + case PE_IndicatorSpinPlus: + case PE_IndicatorSpinMinus: { + QRect r = opt->rect; + int fw = proxy()->pixelMetric(PM_DefaultFrameWidth, opt); + QRect br = r.adjusted(fw, fw, -fw, -fw); + + int offset = (opt->state & State_Sunken) ? 1 : 0; + int step = (br.width() + 4) / 5; + p->fillRect(br.x() + offset, br.y() + offset +br.height() / 2 - step / 2, + br.width(), step, + opt->palette.buttonText()); + if (pe == PE_IndicatorSpinPlus) + p->fillRect(br.x() + br.width() / 2 - step / 2 + offset, br.y() + offset, + step, br.height(), + opt->palette.buttonText()); + + break; } + case PE_IndicatorSpinUp: + case PE_IndicatorSpinDown: { + QRect r = opt->rect; + int fw = proxy()->pixelMetric(PM_DefaultFrameWidth, opt); + // QRect br = r.adjusted(fw, fw, -fw, -fw); + int x = r.x(), y = r.y(), w = r.width(), h = r.height(); + int sw = w-4; + if (sw < 3) + break; + else if (!(sw & 1)) + sw--; + sw -= (sw / 7) * 2; // Empty border + int sh = sw/2 + 2; // Must have empty row at foot of arrow + + int sx = x + w / 2 - sw / 2; + int sy = y + h / 2 - sh / 2; + + if (pe == PE_IndicatorSpinUp && fw) + --sy; + + int bsx = 0; + int bsy = 0; + if (opt->state & State_Sunken) { + bsx = proxy()->pixelMetric(PM_ButtonShiftHorizontal); + bsy = proxy()->pixelMetric(PM_ButtonShiftVertical); + } + p->save(); + p->translate(sx + bsx, sy + bsy); + p->setPen(opt->palette.buttonText().color()); + p->setBrush(opt->palette.buttonText()); + p->setRenderHint(QPainter::Qt4CompatiblePainting); + if (pe == PE_IndicatorSpinDown) { + const QPoint points[] = { QPoint(0, 1), QPoint(sw-1, 1), QPoint(sh-2, sh-1) }; + p->drawPolygon(points, sizeof points / sizeof *points); + } else { + const QPoint points[] = { QPoint(0, sh-1), QPoint(sw-1, sh-1), QPoint(sh-2, 1) }; + p->drawPolygon(points, sizeof points / sizeof *points); + } + p->restore(); + break; } + case PE_PanelTipLabel: { + const QBrush brush(opt->palette.toolTipBase()); + qDrawPlainRect(p, opt->rect, opt->palette.toolTipText().color(), 1, &brush); + break; + } + case PE_IndicatorTabTear: + if (const QStyleOptionTab *tab = qstyleoption_cast<const QStyleOptionTab *>(opt)) { + bool rtl = tab->direction == Qt::RightToLeft; + const bool horizontal = tab->rect.height() > tab->rect.width(); + const int margin = 4; + QPainterPath path; + + if (horizontal) { + QRect rect = tab->rect.adjusted(rtl ? margin : 0, 0, rtl ? 1 : -margin, 0); + rect.setTop(rect.top() + ((tab->state & State_Selected) ? 1 : 3)); + rect.setBottom(rect.bottom() - ((tab->state & State_Selected) ? 0 : 2)); + + path.moveTo(QPoint(rtl ? rect.right() : rect.left(), rect.top())); + int count = 4; + for (int jags = 1; jags <= count; ++jags, rtl = !rtl) + path.lineTo(QPoint(rtl ? rect.left() : rect.right(), rect.top() + jags * rect.height()/count)); + } else { + QRect rect = tab->rect.adjusted(0, 0, 0, -margin); + rect.setLeft(rect.left() + ((tab->state & State_Selected) ? 1 : 3)); + rect.setRight(rect.right() - ((tab->state & State_Selected) ? 0 : 2)); + + path.moveTo(QPoint(rect.left(), rect.top())); + int count = 4; + for (int jags = 1; jags <= count; ++jags, rtl = !rtl) + path.lineTo(QPoint(rect.left() + jags * rect.width()/count, rtl ? rect.top() : rect.bottom())); + } + + p->setPen(QPen(tab->palette.dark(), qreal(.8))); + p->setBrush(tab->palette.window()); + p->setRenderHint(QPainter::Antialiasing); + p->drawPath(path); + } + break; + case PE_PanelLineEdit: + if (const QStyleOptionFrame *panel = qstyleoption_cast<const QStyleOptionFrame *>(opt)) { + p->fillRect(panel->rect.adjusted(panel->lineWidth, panel->lineWidth, -panel->lineWidth, -panel->lineWidth), + panel->palette.brush(QPalette::Base)); + + if (panel->lineWidth > 0) + proxy()->drawPrimitive(PE_FrameLineEdit, panel, p); + } + break; + case PE_IndicatorColumnViewArrow: { + if (const QStyleOptionViewItem *viewOpt = qstyleoption_cast<const QStyleOptionViewItem *>(opt)) { + bool reverse = (viewOpt->direction == Qt::RightToLeft); + p->save(); + QPainterPath path; + int x = viewOpt->rect.x() + 1; + int offset = (viewOpt->rect.height() / 3); + int height = (viewOpt->rect.height()) - offset * 2; + if (height % 2 == 1) + --height; + int x2 = x + height - 1; + if (reverse) { + x = viewOpt->rect.x() + viewOpt->rect.width() - 1; + x2 = x - height + 1; + } + path.moveTo(x, viewOpt->rect.y() + offset); + path.lineTo(x, viewOpt->rect.y() + offset + height); + path.lineTo(x2, viewOpt->rect.y() + offset+height/2); + path.closeSubpath(); + if (viewOpt->state & QStyle::State_Selected ) { + if (viewOpt->showDecorationSelected) { + QColor color = viewOpt->palette.color(QPalette::Active, QPalette::HighlightedText); + p->setPen(color); + p->setBrush(color); + } else { + QColor color = viewOpt->palette.color(QPalette::Active, QPalette::WindowText); + p->setPen(color); + p->setBrush(color); + } + + } else { + QColor color = viewOpt->palette.color(QPalette::Active, QPalette::Mid); + p->setPen(color); + p->setBrush(color); + } + p->drawPath(path); + + // draw the vertical and top triangle line + if (!(viewOpt->state & QStyle::State_Selected)) { + QPainterPath lines; + lines.moveTo(x, viewOpt->rect.y() + offset); + lines.lineTo(x, viewOpt->rect.y() + offset + height); + lines.moveTo(x, viewOpt->rect.y() + offset); + lines.lineTo(x2, viewOpt->rect.y() + offset+height/2); + QColor color = viewOpt->palette.color(QPalette::Active, QPalette::Dark); + p->setPen(color); + p->drawPath(lines); + } + p->restore(); + } + break; } + case PE_IndicatorItemViewItemDrop: { + QRect rect = opt->rect; + if (opt->rect.height() == 0) + p->drawLine(rect.topLeft(), rect.topRight()); + else + p->drawRect(rect); + break; } + case PE_PanelItemViewRow: + if (const QStyleOptionViewItem *vopt = qstyleoption_cast<const QStyleOptionViewItem *>(opt)) { + QPalette::ColorGroup cg = vopt->state & QStyle::State_Enabled ? QPalette::Normal : QPalette::Disabled; + if (cg == QPalette::Normal && !(vopt->state & QStyle::State_Active)) + cg = QPalette::Inactive; + + if ((vopt->state & QStyle::State_Selected) && proxy()->styleHint(QStyle::SH_ItemView_ShowDecorationSelected, opt)) + p->fillRect(vopt->rect, vopt->palette.brush(cg, QPalette::Highlight)); + else if (vopt->features & QStyleOptionViewItem::Alternate) + p->fillRect(vopt->rect, vopt->palette.brush(cg, QPalette::AlternateBase)); + } + break; + case PE_PanelItemViewItem: + if (const QStyleOptionViewItem *vopt = qstyleoption_cast<const QStyleOptionViewItem *>(opt)) { + QPalette::ColorGroup cg = vopt->state & QStyle::State_Enabled ? QPalette::Normal : QPalette::Disabled; + if (cg == QPalette::Normal && !(vopt->state & QStyle::State_Active)) + cg = QPalette::Inactive; + + if (vopt->showDecorationSelected && (vopt->state & QStyle::State_Selected)) { + p->fillRect(vopt->rect, vopt->palette.brush(cg, QPalette::Highlight)); + } else { + if (vopt->backgroundBrush.style() != Qt::NoBrush) { + QPointF oldBO = p->brushOrigin(); + p->setBrushOrigin(vopt->rect.topLeft()); + p->fillRect(vopt->rect, vopt->backgroundBrush); + p->setBrushOrigin(oldBO); + } + + if (vopt->state & QStyle::State_Selected) { + QRect textRect = subElementRect(QStyle::SE_ItemViewItemText, opt); + p->fillRect(textRect, vopt->palette.brush(cg, QPalette::Highlight)); + } + } + } + break; + case PE_PanelScrollAreaCorner: { + const QBrush brush(opt->palette.brush(QPalette::Window)); + p->fillRect(opt->rect, brush); + } break; + case PE_IndicatorArrowUp: + case PE_IndicatorArrowDown: + case PE_IndicatorArrowRight: + case PE_IndicatorArrowLeft: + { + if (opt->rect.width() <= 1 || opt->rect.height() <= 1) + break; + QRect r = opt->rect; + int size = qMin(r.height(), r.width()); + QPixmap pixmap; + QString pixmapName = QStyleHelper::uniqueName(QLatin1String("$qt_ia-") + % QLatin1String(metaObject()->className()), opt, QSize(size, size)) + % HexString<uint>(pe); + if (!QPixmapCache::find(pixmapName, &pixmap)) { + qreal pixelRatio = p->device()->devicePixelRatioF(); + int border = qRound(pixelRatio*(size/5)); + int sqsize = qRound(pixelRatio*(2*(size/2))); + QImage image(sqsize, sqsize, QImage::Format_ARGB32_Premultiplied); + image.fill(0); + QPainter imagePainter(&image); + + QPolygon a; + switch (pe) { + case PE_IndicatorArrowUp: + a.setPoints(3, border, sqsize/2, sqsize/2, border, sqsize - border, sqsize/2); + break; + case PE_IndicatorArrowDown: + a.setPoints(3, border, sqsize/2, sqsize/2, sqsize - border, sqsize - border, sqsize/2); + break; + case PE_IndicatorArrowRight: + a.setPoints(3, sqsize - border, sqsize/2, sqsize/2, border, sqsize/2, sqsize - border); + break; + case PE_IndicatorArrowLeft: + a.setPoints(3, border, sqsize/2, sqsize/2, border, sqsize/2, sqsize - border); + break; + default: + break; + } + + int bsx = 0; + int bsy = 0; + + if (opt->state & State_Sunken) { + bsx = proxy()->pixelMetric(PM_ButtonShiftHorizontal, opt); + bsy = proxy()->pixelMetric(PM_ButtonShiftVertical, opt); + } + + QRect bounds = a.boundingRect(); + int sx = sqsize / 2 - bounds.center().x() - 1; + int sy = sqsize / 2 - bounds.center().y() - 1; + imagePainter.translate(sx + bsx, sy + bsy); + imagePainter.setPen(opt->palette.buttonText().color()); + imagePainter.setBrush(opt->palette.buttonText()); + imagePainter.setRenderHint(QPainter::Qt4CompatiblePainting); + + if (!(opt->state & State_Enabled)) { + imagePainter.translate(1, 1); + imagePainter.setBrush(opt->palette.light().color()); + imagePainter.setPen(opt->palette.light().color()); + imagePainter.drawPolygon(a); + imagePainter.translate(-1, -1); + imagePainter.setBrush(opt->palette.mid().color()); + imagePainter.setPen(opt->palette.mid().color()); + } + + imagePainter.drawPolygon(a); + imagePainter.end(); + pixmap = QPixmap::fromImage(image); + pixmap.setDevicePixelRatio(pixelRatio); + QPixmapCache::insert(pixmapName, pixmap); + } + int xOffset = r.x() + (r.width() - size)/2; + int yOffset = r.y() + (r.height() - size)/2; + p->drawPixmap(xOffset, yOffset, pixmap); + } + break; + default: + break; + } +} + +static void drawArrow(const QStyle *style, const QStyleOptionToolButton *toolbutton, + const QRect &rect, QPainter *painter) +{ + QStyle::PrimitiveElement pe; + switch (toolbutton->arrowType) { + case Qt::LeftArrow: + pe = QStyle::PE_IndicatorArrowLeft; + break; + case Qt::RightArrow: + pe = QStyle::PE_IndicatorArrowRight; + break; + case Qt::UpArrow: + pe = QStyle::PE_IndicatorArrowUp; + break; + case Qt::DownArrow: + pe = QStyle::PE_IndicatorArrowDown; + break; + default: + return; + } + QStyleOption arrowOpt = *toolbutton; + arrowOpt.rect = rect; + style->drawPrimitive(pe, &arrowOpt, painter); +} + +static QSizeF viewItemTextLayout(QTextLayout &textLayout, int lineWidth, int maxHeight = -1, int *lastVisibleLine = nullptr) +{ + if (lastVisibleLine) + *lastVisibleLine = -1; + qreal height = 0; + qreal widthUsed = 0; + textLayout.beginLayout(); + int i = 0; + while (true) { + QTextLine line = textLayout.createLine(); + if (!line.isValid()) + break; + line.setLineWidth(lineWidth); + line.setPosition(QPointF(0, height)); + height += line.height(); + widthUsed = qMax(widthUsed, line.naturalTextWidth()); + // we assume that the height of the next line is the same as the current one + if (maxHeight > 0 && lastVisibleLine && height + line.height() > maxHeight) { + const QTextLine nextLine = textLayout.createLine(); + *lastVisibleLine = nextLine.isValid() ? i : -1; + break; + } + ++i; + } + textLayout.endLayout(); + return QSizeF(widthUsed, height); +} + +QString QCommonStylePrivate::calculateElidedText(const QString &text, const QTextOption &textOption, + const QFont &font, const QRect &textRect, const Qt::Alignment valign, + Qt::TextElideMode textElideMode, int flags, + bool lastVisibleLineShouldBeElided, QPointF *paintStartPosition) const +{ + QTextLayout textLayout(text, font); + textLayout.setTextOption(textOption); + + // In AlignVCenter mode when more than one line is displayed and the height only allows + // some of the lines it makes no sense to display those. From a users perspective it makes + // more sense to see the start of the text instead something inbetween. + const bool vAlignmentOptimization = paintStartPosition && valign.testFlag(Qt::AlignVCenter); + + int lastVisibleLine = -1; + viewItemTextLayout(textLayout, textRect.width(), vAlignmentOptimization ? textRect.height() : -1, &lastVisibleLine); + + const QRectF boundingRect = textLayout.boundingRect(); + // don't care about LTR/RTL here, only need the height + const QRect layoutRect = QStyle::alignedRect(Qt::LayoutDirectionAuto, valign, + boundingRect.size().toSize(), textRect); + + if (paintStartPosition) + *paintStartPosition = QPointF(textRect.x(), layoutRect.top()); + + QString ret; + qreal height = 0; + const int lineCount = textLayout.lineCount(); + for (int i = 0; i < lineCount; ++i) { + const QTextLine line = textLayout.lineAt(i); + height += line.height(); + + // above visible rect + if (height + layoutRect.top() <= textRect.top()) { + if (paintStartPosition) + paintStartPosition->ry() += line.height(); + continue; + } + + const int start = line.textStart(); + const int length = line.textLength(); + const bool drawElided = line.naturalTextWidth() > textRect.width(); + bool elideLastVisibleLine = lastVisibleLine == i; + if (!drawElided && i + 1 < lineCount && lastVisibleLineShouldBeElided) { + const QTextLine nextLine = textLayout.lineAt(i + 1); + const int nextHeight = height + nextLine.height() / 2; + // elide when less than the next half line is visible + if (nextHeight + layoutRect.top() > textRect.height() + textRect.top()) + elideLastVisibleLine = true; + } + + QString text = textLayout.text().mid(start, length); + if (drawElided || elideLastVisibleLine) { + if (elideLastVisibleLine) { + if (text.endsWith(QChar::LineSeparator)) + text.chop(1); + text += QChar(0x2026); + } + const QStackTextEngine engine(text, font); + ret += engine.elidedText(textElideMode, textRect.width(), flags); + + // no newline for the last line (last visible or real) + // sometimes drawElided is true but no eliding is done so the text ends + // with QChar::LineSeparator - don't add another one. This happened with + // arabic text in the testcase for QTBUG-72805 + if (i < lineCount - 1 && + !ret.endsWith(QChar::LineSeparator)) + ret += QChar::LineSeparator; + } else { + ret += text; + } + + // below visible text, can stop + if ((height + layoutRect.top() >= textRect.bottom()) || + (lastVisibleLine >= 0 && lastVisibleLine == i)) + break; + } + return ret; +} + +QSize QCommonStylePrivate::viewItemSize(const QStyleOptionViewItem *option, int role) const +{ + switch (role) { + case Qt::CheckStateRole: + if (option->features & QStyleOptionViewItem::HasCheckIndicator) + return QSize(proxyStyle->pixelMetric(QStyle::PM_IndicatorWidth, option), + proxyStyle->pixelMetric(QStyle::PM_IndicatorHeight, option)); + break; + case Qt::DisplayRole: + if (option->features & QStyleOptionViewItem::HasDisplay) { + QTextOption textOption; + textOption.setWrapMode(QTextOption::WordWrap); + QTextLayout textLayout(option->text, option->font); + textLayout.setTextOption(textOption); + const bool wrapText = option->features & QStyleOptionViewItem::WrapText; + const int textMargin = proxyStyle->pixelMetric(QStyle::PM_FocusFrameHMargin, option) + 1; + QRect bounds = option->rect; + switch (option->decorationPosition) { + case QStyleOptionViewItem::Left: + case QStyleOptionViewItem::Right: { + if (wrapText && bounds.isValid()) { + int width = bounds.width() - 2 * textMargin; + if (option->features & QStyleOptionViewItem::HasDecoration) + width -= option->decorationSize.width() + 2 * textMargin; + bounds.setWidth(width); + } else + bounds.setWidth(QFIXED_MAX); + break; + } + case QStyleOptionViewItem::Top: + case QStyleOptionViewItem::Bottom: + if (wrapText) + bounds.setWidth(bounds.isValid() ? bounds.width() - 2 * textMargin : option->decorationSize.width()); + else + bounds.setWidth(QFIXED_MAX); + break; + default: + break; + } + + if (wrapText && option->features & QStyleOptionViewItem::HasCheckIndicator) + bounds.setWidth(bounds.width() - proxyStyle->pixelMetric(QStyle::PM_IndicatorWidth) - 2 * textMargin); + + const int lineWidth = bounds.width(); + const QSizeF size = viewItemTextLayout(textLayout, lineWidth); + return QSize(qCeil(size.width()) + 2 * textMargin, qCeil(size.height())); + } + break; + case Qt::DecorationRole: + if (option->features & QStyleOptionViewItem::HasDecoration) { + return option->decorationSize; + } + break; + default: + break; + } + + return QSize(0, 0); +} + +void QCommonStylePrivate::viewItemDrawText(QPainter *p, const QStyleOptionViewItem *option, const QRect &rect) const +{ + const int textMargin = proxyStyle->pixelMetric(QStyle::PM_FocusFrameHMargin, nullptr) + 1; + + QRect textRect = rect.adjusted(textMargin, 0, -textMargin, 0); // remove width padding + const bool wrapText = option->features & QStyleOptionViewItem::WrapText; + QTextOption textOption; + textOption.setWrapMode(wrapText ? QTextOption::WordWrap : QTextOption::ManualWrap); + textOption.setTextDirection(option->direction); + textOption.setAlignment(QStyle::visualAlignment(option->direction, option->displayAlignment)); + + QPointF paintPosition; + const QString newText = calculateElidedText(option->text, textOption, + option->font, textRect, option->displayAlignment, + option->textElideMode, 0, + true, &paintPosition); + + QTextLayout textLayout(newText, option->font); + textLayout.setTextOption(textOption); + viewItemTextLayout(textLayout, textRect.width()); + textLayout.draw(p, paintPosition); +} + +/*! \internal + compute the position for the different component of an item (pixmap, text, checkbox) + + Set sizehint to false to layout the elements inside opt->rect. Set sizehint to true to ignore + opt->rect and return rectangles in infinite space + + Code duplicated in QItemDelegate::doLayout +*/ +void QCommonStylePrivate::viewItemLayout(const QStyleOptionViewItem *opt, QRect *checkRect, + QRect *pixmapRect, QRect *textRect, bool sizehint) const +{ + Q_ASSERT(checkRect && pixmapRect && textRect); + *pixmapRect = QRect(QPoint(0, 0), viewItemSize(opt, Qt::DecorationRole)); + *textRect = QRect(QPoint(0, 0), viewItemSize(opt, Qt::DisplayRole)); + *checkRect = QRect(QPoint(0, 0), viewItemSize(opt, Qt::CheckStateRole)); + + const bool hasCheck = checkRect->isValid(); + const bool hasPixmap = pixmapRect->isValid(); + const bool hasText = textRect->isValid(); + const bool hasMargin = (hasText | hasPixmap | hasCheck); + const int frameHMargin = hasMargin ? + proxyStyle->pixelMetric(QStyle::PM_FocusFrameHMargin, opt) + 1 : 0; + const int textMargin = hasText ? frameHMargin : 0; + const int pixmapMargin = hasPixmap ? frameHMargin : 0; + const int checkMargin = hasCheck ? frameHMargin : 0; + const int x = opt->rect.left(); + const int y = opt->rect.top(); + int w, h; + + if (textRect->height() == 0 && (!hasPixmap || !sizehint)) { + //if there is no text, we still want to have a decent height for the item sizeHint and the editor size + textRect->setHeight(opt->fontMetrics.height()); + } + + QSize pm(0, 0); + if (hasPixmap) { + pm = pixmapRect->size(); + pm.rwidth() += 2 * pixmapMargin; + } + if (sizehint) { + h = qMax(checkRect->height(), qMax(textRect->height(), pm.height())); + if (opt->decorationPosition == QStyleOptionViewItem::Left + || opt->decorationPosition == QStyleOptionViewItem::Right) { + w = textRect->width() + pm.width(); + } else { + w = qMax(textRect->width(), pm.width()); + } + } else { + w = opt->rect.width(); + h = opt->rect.height(); + } + + int cw = 0; + QRect check; + if (hasCheck) { + cw = checkRect->width() + 2 * checkMargin; + if (sizehint) w += cw; + if (opt->direction == Qt::RightToLeft) { + check.setRect(x + w - cw, y, cw, h); + } else { + check.setRect(x, y, cw, h); + } + } + + QRect display; + QRect decoration; + switch (opt->decorationPosition) { + case QStyleOptionViewItem::Top: { + if (hasPixmap) + pm.setHeight(pm.height() + pixmapMargin); // add space + h = sizehint ? textRect->height() : h - pm.height(); + + if (opt->direction == Qt::RightToLeft) { + decoration.setRect(x, y, w - cw, pm.height()); + display.setRect(x, y + pm.height(), w - cw, h); + } else { + decoration.setRect(x + cw, y, w - cw, pm.height()); + display.setRect(x + cw, y + pm.height(), w - cw, h); + } + break; } + case QStyleOptionViewItem::Bottom: { + if (hasText) + textRect->setHeight(textRect->height() + textMargin); // add space + h = sizehint ? textRect->height() + pm.height() : h; + + if (opt->direction == Qt::RightToLeft) { + display.setRect(x, y, w - cw, textRect->height()); + decoration.setRect(x, y + textRect->height(), w - cw, h - textRect->height()); + } else { + display.setRect(x + cw, y, w - cw, textRect->height()); + decoration.setRect(x + cw, y + textRect->height(), w - cw, h - textRect->height()); + } + break; } + case QStyleOptionViewItem::Left: { + if (opt->direction == Qt::LeftToRight) { + decoration.setRect(x + cw, y, pm.width(), h); + display.setRect(decoration.right() + 1, y, w - pm.width() - cw, h); + } else { + display.setRect(x, y, w - pm.width() - cw, h); + decoration.setRect(display.right() + 1, y, pm.width(), h); + } + break; } + case QStyleOptionViewItem::Right: { + if (opt->direction == Qt::LeftToRight) { + display.setRect(x + cw, y, w - pm.width() - cw, h); + decoration.setRect(display.right() + 1, y, pm.width(), h); + } else { + decoration.setRect(x, y, pm.width(), h); + display.setRect(decoration.right() + 1, y, w - pm.width() - cw, h); + } + break; } + default: + qWarning("doLayout: decoration position is invalid"); + decoration = *pixmapRect; + break; + } + + if (!sizehint) { // we only need to do the internal layout if we are going to paint + *checkRect = QStyle::alignedRect(opt->direction, Qt::AlignCenter, + checkRect->size(), check); + *pixmapRect = QStyle::alignedRect(opt->direction, opt->decorationAlignment, + pixmapRect->size(), decoration); + // the text takes up all available space, unless the decoration is not shown as selected + if (opt->showDecorationSelected) + *textRect = display; + else + *textRect = QStyle::alignedRect(opt->direction, opt->displayAlignment, + textRect->size().boundedTo(display.size()), display); + } else { + *checkRect = check; + *pixmapRect = decoration; + *textRect = display; + } +} + +QString QCommonStylePrivate::toolButtonElideText(const QStyleOptionToolButton *option, + const QRect &textRect, int flags) const +{ + if (option->fontMetrics.horizontalAdvance(option->text) <= textRect.width()) + return option->text; + + QString text = option->text; + text.replace(QLatin1Char('\n'), QChar::LineSeparator); + QTextOption textOption; + textOption.setWrapMode(QTextOption::ManualWrap); + textOption.setTextDirection(option->direction); + + return calculateElidedText(text, textOption, + option->font, textRect, Qt::AlignTop, + Qt::ElideMiddle, flags, + false, nullptr); +} + +/*! \internal + Compute the textRect and the pixmapRect from the opt rect + + Uses the same computation than in QTabBar::tabSizeHint + */ +void QCommonStylePrivate::tabLayout(const QStyleOptionTab *opt, QRect *textRect, QRect *iconRect) const +{ + Q_ASSERT(textRect); + Q_ASSERT(iconRect); + QRect tr = opt->rect; + bool verticalTabs = opt->shape == QStyleOptionTab::RoundedEast + || opt->shape == QStyleOptionTab::RoundedWest + || opt->shape == QStyleOptionTab::TriangularEast + || opt->shape == QStyleOptionTab::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); + int horizontalShift = proxyStyle->pixelMetric(QStyle::PM_TabBarTabShiftHorizontal, opt); + int hpadding = proxyStyle->pixelMetric(QStyle::PM_TabBarTabHSpace, opt) / 2; + int vpadding = proxyStyle->pixelMetric(QStyle::PM_TabBarTabVSpace, opt) / 2; + if (opt->shape == QStyleOptionTab::RoundedSouth || opt->shape == QStyleOptionTab::TriangularSouth) + verticalShift = -verticalShift; + tr.adjust(hpadding, verticalShift - vpadding, horizontalShift - hpadding, vpadding); + bool selected = opt->state & QStyle::State_Selected; + if (selected) { + tr.setTop(tr.top() - verticalShift); + tr.setRight(tr.right() - horizontalShift); + } + + // left widget + if (!opt->leftButtonSize.isEmpty()) { + tr.setLeft(tr.left() + 4 + + (verticalTabs ? opt->leftButtonSize.height() : opt->leftButtonSize.width())); + } + // right widget + if (!opt->rightButtonSize.isEmpty()) { + tr.setRight(tr.right() - 4 - + (verticalTabs ? opt->rightButtonSize.height() : opt->rightButtonSize.width())); + } + + // icon + if (!opt->icon.isNull()) { + QSize iconSize = opt->iconSize; + if (!iconSize.isValid()) { + int iconExtent = proxyStyle->pixelMetric(QStyle::PM_SmallIconSize); + iconSize = QSize(iconExtent, iconExtent); + } + QSize tabIconSize = opt->icon.actualSize(iconSize, + (opt->state & QStyle::State_Enabled) ? QIcon::Normal : QIcon::Disabled, + (opt->state & QStyle::State_Selected) ? QIcon::On : QIcon::Off); + // High-dpi icons do not need adjustment; make sure tabIconSize is not larger than iconSize + tabIconSize = QSize(qMin(tabIconSize.width(), iconSize.width()), qMin(tabIconSize.height(), iconSize.height())); + + const int offsetX = (iconSize.width() - tabIconSize.width()) / 2; + *iconRect = QRect(tr.left() + offsetX, tr.center().y() - tabIconSize.height() / 2, + tabIconSize.width(), tabIconSize.height()); + if (!verticalTabs) + *iconRect = QStyle::visualRect(opt->direction, opt->rect, *iconRect); + tr.setLeft(tr.left() + tabIconSize.width() + 4); + } + + if (!verticalTabs) + tr = QStyle::visualRect(opt->direction, opt->rect, tr); + + *textRect = tr; +} + +/*! + \reimp +*/ +void QCommonStyle::drawControl(ControlElement element, const QStyleOption *opt, QPainter *p) const +{ + Q_D(const QCommonStyle); + + // TODO: Set opt->window manually for now before calling any of the drawing functions. opt->window is + // pulled of the widget is QStyle. But now that we have no widget, we need some other + // solution. + Q_ASSERT(opt->window); + + switch (element) { + + case CE_PushButton: + if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(opt)) { + proxy()->drawControl(CE_PushButtonBevel, btn, p); + QStyleOptionButton subopt = *btn; + subopt.rect = subElementRect(SE_PushButtonContents, btn); + proxy()->drawControl(CE_PushButtonLabel, &subopt, p); + } + break; + case CE_PushButtonBevel: + if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(opt)) { + QRect br = btn->rect; + int dbi = proxy()->pixelMetric(PM_ButtonDefaultIndicator, btn); + if (btn->features & QStyleOptionButton::DefaultButton) + proxy()->drawPrimitive(PE_FrameDefaultButton, opt, p); + if (btn->features & QStyleOptionButton::AutoDefaultButton) + br.setCoords(br.left() + dbi, br.top() + dbi, br.right() - dbi, br.bottom() - dbi); + if (!(btn->features & (QStyleOptionButton::Flat | QStyleOptionButton::CommandLinkButton)) + || btn->state & (State_Sunken | State_On) + || (btn->features & QStyleOptionButton::CommandLinkButton && btn->state & State_MouseOver)) { + QStyleOptionButton tmpBtn = *btn; + tmpBtn.rect = br; + proxy()->drawPrimitive(PE_PanelButtonCommand, &tmpBtn, p); + } + if (btn->features & QStyleOptionButton::HasMenu) { + int mbi = proxy()->pixelMetric(PM_MenuButtonIndicator, btn); + QRect ir = btn->rect; + QStyleOptionButton newBtn = *btn; + newBtn.rect = QRect(ir.right() - mbi + 2, ir.height()/2 - mbi/2 + 3, mbi - 6, mbi - 6); + proxy()->drawPrimitive(PE_IndicatorArrowDown, &newBtn, p); + } + if (btn->state & State_HasFocus) { + QStyleOptionFocusRect fropt; + fropt.QStyleOption::operator=(*btn); + fropt.rect = subElementRect(SE_PushButtonFocusRect, btn); + proxy()->drawPrimitive(PE_FrameFocusRect, &fropt, p); + } + } + break; + case CE_PushButtonLabel: + if (const QStyleOptionButton *button = qstyleoption_cast<const QStyleOptionButton *>(opt)) { + QRect textRect = button->rect; + uint tf = Qt::AlignVCenter | Qt::TextShowMnemonic; + if (!proxy()->styleHint(SH_UnderlineShortcut, button)) + tf |= Qt::TextHideMnemonic; + + if (!button->icon.isNull()) { + //Center both icon and text + QIcon::Mode mode = button->state & State_Enabled ? QIcon::Normal : QIcon::Disabled; + if (mode == QIcon::Normal && button->state & State_HasFocus) + mode = QIcon::Active; + QIcon::State state = QIcon::Off; + if (button->state & State_On) + state = QIcon::On; + + QPixmap pixmap = button->icon.pixmap(opt->window, button->iconSize, mode, state); + int pixmapWidth = pixmap.width() / pixmap.devicePixelRatio(); + int pixmapHeight = pixmap.height() / pixmap.devicePixelRatio(); + int labelWidth = pixmapWidth; + int labelHeight = pixmapHeight; + int iconSpacing = 4;//### 4 is currently hardcoded in QPushButton::sizeHint() + if (!button->text.isEmpty()) { + int textWidth = button->fontMetrics.boundingRect(opt->rect, tf, button->text).width(); + labelWidth += (textWidth + iconSpacing); + } + + QRect iconRect = QRect(textRect.x() + (textRect.width() - labelWidth) / 2, + textRect.y() + (textRect.height() - labelHeight) / 2, + pixmapWidth, pixmapHeight); + + iconRect = visualRect(button->direction, textRect, iconRect); + + if (button->direction == Qt::RightToLeft) { + tf |= Qt::AlignRight; + textRect.setRight(iconRect.left() - iconSpacing); + } else { + tf |= Qt::AlignLeft; //left align, we adjust the text-rect instead + textRect.setLeft(iconRect.left() + iconRect.width() + iconSpacing); + } + + if (button->state & (State_On | State_Sunken)) + iconRect.translate(proxy()->pixelMetric(PM_ButtonShiftHorizontal, opt), + proxy()->pixelMetric(PM_ButtonShiftVertical, opt)); + p->drawPixmap(iconRect, pixmap); + } else { + tf |= Qt::AlignHCenter; + } + if (button->state & (State_On | State_Sunken)) + textRect.translate(proxy()->pixelMetric(PM_ButtonShiftHorizontal, opt), + proxy()->pixelMetric(PM_ButtonShiftVertical, opt)); + + if (button->features & QStyleOptionButton::HasMenu) { + int indicatorSize = proxy()->pixelMetric(PM_MenuButtonIndicator, button); + if (button->direction == Qt::LeftToRight) + textRect = textRect.adjusted(0, 0, -indicatorSize, 0); + else + textRect = textRect.adjusted(indicatorSize, 0, 0, 0); + } + proxy()->drawItemText(p, textRect, tf, button->palette, (button->state & State_Enabled), + button->text, QPalette::ButtonText); + } + break; + case CE_RadioButton: + case CE_CheckBox: + if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(opt)) { + bool isRadio = (element == CE_RadioButton); + QStyleOptionButton subopt = *btn; + subopt.rect = subElementRect(isRadio ? SE_RadioButtonIndicator + : SE_CheckBoxIndicator, btn); + proxy()->drawPrimitive(isRadio ? PE_IndicatorRadioButton : PE_IndicatorCheckBox, + &subopt, p); + subopt.rect = subElementRect(isRadio ? SE_RadioButtonContents + : SE_CheckBoxContents, btn); + proxy()->drawControl(isRadio ? CE_RadioButtonLabel : CE_CheckBoxLabel, &subopt, p); + if (btn->state & State_HasFocus) { + QStyleOptionFocusRect fropt; + fropt.QStyleOption::operator=(*btn); + fropt.rect = subElementRect(isRadio ? SE_RadioButtonFocusRect + : SE_CheckBoxFocusRect, btn); + proxy()->drawPrimitive(PE_FrameFocusRect, &fropt, p); + } + } + break; + case CE_RadioButtonLabel: + case CE_CheckBoxLabel: + if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(opt)) { + uint alignment = visualAlignment(btn->direction, Qt::AlignLeft | Qt::AlignVCenter); + + if (!proxy()->styleHint(SH_UnderlineShortcut, btn)) + alignment |= Qt::TextHideMnemonic; + QPixmap pix; + QRect textRect = btn->rect; + if (!btn->icon.isNull()) { + pix = btn->icon.pixmap(opt->window, btn->iconSize, btn->state & State_Enabled ? QIcon::Normal : QIcon::Disabled); + proxy()->drawItemPixmap(p, btn->rect, alignment, pix); + if (btn->direction == Qt::RightToLeft) + textRect.setRight(textRect.right() - btn->iconSize.width() - 4); + else + textRect.setLeft(textRect.left() + btn->iconSize.width() + 4); + } + if (!btn->text.isEmpty()){ + proxy()->drawItemText(p, textRect, alignment | Qt::TextShowMnemonic, + btn->palette, btn->state & State_Enabled, btn->text, QPalette::WindowText); + } + } + break; + case CE_MenuScroller: { + QStyleOption arrowOpt = *opt; + arrowOpt.state |= State_Enabled; + proxy()->drawPrimitive(((opt->state & State_DownArrow) ? PE_IndicatorArrowDown : PE_IndicatorArrowUp), &arrowOpt, p); + break; } + case CE_MenuTearoff: + if (opt->state & State_Selected) + p->fillRect(opt->rect, opt->palette.brush(QPalette::Highlight)); + else + p->fillRect(opt->rect, opt->palette.brush(QPalette::Button)); + p->setPen(QPen(opt->palette.dark().color(), 1, Qt::DashLine)); + p->drawLine(opt->rect.x() + 2, opt->rect.y() + opt->rect.height() / 2 - 1, + opt->rect.x() + opt->rect.width() - 4, + opt->rect.y() + opt->rect.height() / 2 - 1); + p->setPen(QPen(opt->palette.light().color(), 1, Qt::DashLine)); + p->drawLine(opt->rect.x() + 2, opt->rect.y() + opt->rect.height() / 2, + opt->rect.x() + opt->rect.width() - 4, opt->rect.y() + opt->rect.height() / 2); + break; + case CE_MenuBarItem: + if (const QStyleOptionMenuItem *mbi = qstyleoption_cast<const QStyleOptionMenuItem *>(opt)) { + uint alignment = Qt::AlignCenter | Qt::TextShowMnemonic | Qt::TextDontClip + | Qt::TextSingleLine; + if (!proxy()->styleHint(SH_UnderlineShortcut, mbi)) + alignment |= Qt::TextHideMnemonic; + int iconExtent = proxy()->pixelMetric(PM_SmallIconSize); + QPixmap pix = mbi->icon.pixmap(opt->window, QSize(iconExtent, iconExtent), (mbi->state & State_Enabled) ? QIcon::Normal : QIcon::Disabled); + if (!pix.isNull()) + proxy()->drawItemPixmap(p,mbi->rect, alignment, pix); + else + proxy()->drawItemText(p, mbi->rect, alignment, mbi->palette, mbi->state & State_Enabled, + mbi->text, QPalette::ButtonText); + } + break; + case CE_MenuBarEmptyArea: + break; + case CE_ProgressBar: + if (const QStyleOptionProgressBar *pb + = qstyleoption_cast<const QStyleOptionProgressBar *>(opt)) { + QStyleOptionProgressBar subopt = *pb; + subopt.rect = subElementRect(SE_ProgressBarGroove, pb); + proxy()->drawControl(CE_ProgressBarGroove, &subopt, p); + subopt.rect = subElementRect(SE_ProgressBarContents, pb); + proxy()->drawControl(CE_ProgressBarContents, &subopt, p); + if (pb->textVisible) { + subopt.rect = subElementRect(SE_ProgressBarLabel, pb); + proxy()->drawControl(CE_ProgressBarLabel, &subopt, p); + } + } + break; + case CE_ProgressBarGroove: + if (opt->rect.isValid()) + qDrawShadePanel(p, opt->rect, opt->palette, true, 1, + &opt->palette.brush(QPalette::Window)); + break; + case CE_ProgressBarLabel: + if (const QStyleOptionProgressBar *pb = qstyleoption_cast<const QStyleOptionProgressBar *>(opt)) { + QPalette::ColorRole textRole = QPalette::NoRole; + if ((pb->textAlignment & Qt::AlignCenter) && pb->textVisible + && ((qint64(pb->progress) - qint64(pb->minimum)) * 2 >= (qint64(pb->maximum) - qint64(pb->minimum)))) { + textRole = QPalette::HighlightedText; + //Draw text shadow, This will increase readability when the background of same color + QRect shadowRect(pb->rect); + shadowRect.translate(1,1); + QColor shadowColor = (pb->palette.color(textRole).value() <= 128) + ? QColor(255,255,255,160) : QColor(0,0,0,160); + QPalette shadowPalette = pb->palette; + shadowPalette.setColor(textRole, shadowColor); + proxy()->drawItemText(p, shadowRect, Qt::AlignCenter | Qt::TextSingleLine, shadowPalette, + pb->state & State_Enabled, pb->text, textRole); + } + proxy()->drawItemText(p, pb->rect, Qt::AlignCenter | Qt::TextSingleLine, pb->palette, + pb->state & State_Enabled, pb->text, textRole); + } + break; + case CE_ProgressBarContents: + if (const QStyleOptionProgressBar *pb = qstyleoption_cast<const QStyleOptionProgressBar *>(opt)) { + + QRect rect = pb->rect; + const bool inverted = pb->invertedAppearance; + qint64 minimum = qint64(pb->minimum); + qint64 maximum = qint64(pb->maximum); + qint64 progress = qint64(pb->progress); + + QPalette pal2 = pb->palette; + // Correct the highlight color if it is the same as the background + if (pal2.highlight() == pal2.window()) + pal2.setColor(QPalette::Highlight, pb->palette.color(QPalette::Active, + QPalette::Highlight)); + bool reverse = pb->direction == Qt::RightToLeft; + if (inverted) + reverse = !reverse; + int w = rect.width(); + if (pb->minimum == 0 && pb->maximum == 0) { + // draw busy indicator + int x = (progress - minimum) % (w * 2); + if (x > w) + x = 2 * w - x; + x = reverse ? rect.right() - x : x + rect.x(); + p->setPen(QPen(pal2.highlight().color(), 4)); + p->drawLine(x, rect.y(), x, rect.height()); + } else { + const int unit_width = proxy()->pixelMetric(PM_ProgressBarChunkWidth, pb); + if (!unit_width) + return; + + int u; + if (unit_width > 1) + u = ((rect.width() + unit_width) / unit_width); + else + u = w / unit_width; + qint64 p_v = progress - minimum; + qint64 t_s = (maximum - minimum) ? (maximum - minimum) : qint64(1); + + if (u > 0 && p_v >= INT_MAX / u && t_s >= u) { + // scale down to something usable. + p_v /= u; + t_s /= u; + } + + // nu < tnu, if last chunk is only a partial chunk + int tnu, nu; + tnu = nu = p_v * u / t_s; + + if (nu * unit_width > w) + --nu; + + // Draw nu units out of a possible u of unit_width + // width, each a rectangle bordered by background + // color, all in a sunken panel with a percentage text + // display at the end. + int x = 0; + int x0 = reverse ? rect.right() - ((unit_width > 1) ? unit_width : 0) + : rect.x(); + + QStyleOptionProgressBar pbBits = *pb; + pbBits.rect = rect; + pbBits.palette = pal2; + int myY = pbBits.rect.y(); + int myHeight = pbBits.rect.height(); + pbBits.state = State_None; + QMatrix4x4 m; + for (int i = 0; i < nu; ++i) { + pbBits.rect.setRect(x0 + x, myY, unit_width, myHeight); + pbBits.rect = m.mapRect(QRectF(pbBits.rect)).toRect(); + proxy()->drawPrimitive(PE_IndicatorProgressChunk, &pbBits, p); + x += reverse ? -unit_width : unit_width; + } + + // Draw the last partial chunk to fill up the + // progress bar entirely + if (nu < tnu) { + int pixels_left = w - (nu * unit_width); + int offset = reverse ? x0 + x + unit_width-pixels_left : x0 + x; + pbBits.rect.setRect(offset, myY, pixels_left, myHeight); + pbBits.rect = m.mapRect(QRectF(pbBits.rect)).toRect(); + proxy()->drawPrimitive(PE_IndicatorProgressChunk, &pbBits, p); + } + } + } + break; + case CE_HeaderLabel: + if (const QStyleOptionHeader *header = qstyleoption_cast<const QStyleOptionHeader *>(opt)) { + QRect rect = header->rect; + if (!header->icon.isNull()) { + int iconExtent = proxy()->pixelMetric(PM_SmallIconSize); + QPixmap pixmap + = header->icon.pixmap(opt->window, QSize(iconExtent, iconExtent), (header->state & State_Enabled) ? QIcon::Normal : QIcon::Disabled); + int pixw = pixmap.width() / pixmap.devicePixelRatio(); + + QRect aligned = alignedRect(header->direction, QFlag(header->iconAlignment), pixmap.size() / pixmap.devicePixelRatio(), rect); + QRect inter = aligned.intersected(rect); + p->drawPixmap(inter.x(), inter.y(), pixmap, + inter.x() - aligned.x(), inter.y() - aligned.y(), + aligned.width() * pixmap.devicePixelRatio(), + pixmap.height() * pixmap.devicePixelRatio()); + + const int margin = proxy()->pixelMetric(QStyle::PM_HeaderMargin, opt); + if (header->direction == Qt::LeftToRight) + rect.setLeft(rect.left() + pixw + margin); + else + rect.setRight(rect.right() - pixw - margin); + } + if (header->state & QStyle::State_On) { + QFont fnt = p->font(); + fnt.setBold(true); + p->setFont(fnt); + } + proxy()->drawItemText(p, rect, header->textAlignment, header->palette, + (header->state & State_Enabled), header->text, QPalette::ButtonText); + } + break; + case CE_ToolButtonLabel: + if (const QStyleOptionToolButton *toolbutton + = qstyleoption_cast<const QStyleOptionToolButton *>(opt)) { + QRect rect = toolbutton->rect; + int shiftX = 0; + int shiftY = 0; + if (toolbutton->state & (State_Sunken | State_On)) { + shiftX = proxy()->pixelMetric(PM_ButtonShiftHorizontal, toolbutton); + shiftY = proxy()->pixelMetric(PM_ButtonShiftVertical, toolbutton); + } + // Arrow type always overrules and is always shown + bool hasArrow = toolbutton->features & QStyleOptionToolButton::Arrow; + if (((!hasArrow && toolbutton->icon.isNull()) && !toolbutton->text.isEmpty()) + || toolbutton->toolButtonStyle == Qt::ToolButtonTextOnly) { + int alignment = Qt::AlignCenter | Qt::TextShowMnemonic; + if (!proxy()->styleHint(SH_UnderlineShortcut, opt)) + alignment |= Qt::TextHideMnemonic; + rect.translate(shiftX, shiftY); + p->setFont(toolbutton->font); + proxy()->drawItemText(p, rect, alignment, toolbutton->palette, + opt->state & State_Enabled, toolbutton->text, + QPalette::ButtonText); + } else { + QPixmap pm; + QSize pmSize = toolbutton->iconSize; + if (!toolbutton->icon.isNull()) { + QIcon::State state = toolbutton->state & State_On ? QIcon::On : QIcon::Off; + QIcon::Mode mode; + if (!(toolbutton->state & State_Enabled)) + mode = QIcon::Disabled; + else if ((opt->state & State_MouseOver) && (opt->state & State_AutoRaise)) + mode = QIcon::Active; + else + mode = QIcon::Normal; + pm = toolbutton->icon.pixmap(opt->window, toolbutton->rect.size().boundedTo(toolbutton->iconSize), mode, state); + pmSize = pm.size() / pm.devicePixelRatio(); + } + + if (toolbutton->toolButtonStyle != Qt::ToolButtonIconOnly) { + p->setFont(toolbutton->font); + QRect pr = rect, + tr = rect; + int alignment = Qt::TextShowMnemonic; + if (!proxy()->styleHint(SH_UnderlineShortcut, opt)) + alignment |= Qt::TextHideMnemonic; + + if (toolbutton->toolButtonStyle == Qt::ToolButtonTextUnderIcon) { + pr.setHeight(pmSize.height() + 4); //### 4 is currently hardcoded in QToolButton::sizeHint() + tr.adjust(0, pr.height() - 1, 0, -1); + pr.translate(shiftX, shiftY); + if (!hasArrow) { + proxy()->drawItemPixmap(p, pr, Qt::AlignCenter, pm); + } else { + drawArrow(proxy(), toolbutton, pr, p); + } + alignment |= Qt::AlignCenter; + } else { + pr.setWidth(pmSize.width() + 4); //### 4 is currently hardcoded in QToolButton::sizeHint() + tr.adjust(pr.width(), 0, 0, 0); + pr.translate(shiftX, shiftY); + if (!hasArrow) { + proxy()->drawItemPixmap(p, QStyle::visualRect(opt->direction, rect, pr), Qt::AlignCenter, pm); + } else { + drawArrow(proxy(), toolbutton, pr, p); + } + alignment |= Qt::AlignLeft | Qt::AlignVCenter; + } + tr.translate(shiftX, shiftY); + const QString text = d->toolButtonElideText(toolbutton, tr, alignment); + proxy()->drawItemText(p, QStyle::visualRect(opt->direction, rect, tr), alignment, toolbutton->palette, + toolbutton->state & State_Enabled, text, + QPalette::ButtonText); + } else { + rect.translate(shiftX, shiftY); + if (hasArrow) { + drawArrow(proxy(), toolbutton, rect, p); + } else { + proxy()->drawItemPixmap(p, rect, Qt::AlignCenter, pm); + } + } + } + } + break; + case CE_ToolBoxTab: + if (const QStyleOptionToolBox *tb = qstyleoption_cast<const QStyleOptionToolBox *>(opt)) { + proxy()->drawControl(CE_ToolBoxTabShape, tb, p); + proxy()->drawControl(CE_ToolBoxTabLabel, tb, p); + } + break; + case CE_ToolBoxTabShape: + if (const QStyleOptionToolBox *tb = qstyleoption_cast<const QStyleOptionToolBox *>(opt)) { + p->setPen(tb->palette.mid().color().darker(150)); + bool oldQt4CompatiblePainting = p->testRenderHint(QPainter::Qt4CompatiblePainting); + p->setRenderHint(QPainter::Qt4CompatiblePainting); + int d = 20 + tb->rect.height() - 3; + if (tb->direction != Qt::RightToLeft) { + const QPoint points[] = { + QPoint(-1, tb->rect.height() + 1), + QPoint(-1, 1), + QPoint(tb->rect.width() - d, 1), + QPoint(tb->rect.width() - 20, tb->rect.height() - 2), + QPoint(tb->rect.width() - 1, tb->rect.height() - 2), + QPoint(tb->rect.width() - 1, tb->rect.height() + 1), + QPoint(-1, tb->rect.height() + 1), + }; + p->drawPolygon(points, sizeof points / sizeof *points); + } else { + const QPoint points[] = { + QPoint(tb->rect.width(), tb->rect.height() + 1), + QPoint(tb->rect.width(), 1), + QPoint(d - 1, 1), + QPoint(20 - 1, tb->rect.height() - 2), + QPoint(0, tb->rect.height() - 2), + QPoint(0, tb->rect.height() + 1), + QPoint(tb->rect.width(), tb->rect.height() + 1), + }; + p->drawPolygon(points, sizeof points / sizeof *points); + } + p->setRenderHint(QPainter::Qt4CompatiblePainting, oldQt4CompatiblePainting); + p->setPen(tb->palette.light().color()); + if (tb->direction != Qt::RightToLeft) { + p->drawLine(0, 2, tb->rect.width() - d, 2); + p->drawLine(tb->rect.width() - d - 1, 2, tb->rect.width() - 21, tb->rect.height() - 1); + p->drawLine(tb->rect.width() - 20, tb->rect.height() - 1, + tb->rect.width(), tb->rect.height() - 1); + } else { + p->drawLine(tb->rect.width() - 1, 2, d - 1, 2); + p->drawLine(d, 2, 20, tb->rect.height() - 1); + p->drawLine(19, tb->rect.height() - 1, + -1, tb->rect.height() - 1); + } + p->setBrush(Qt::NoBrush); + } + break; + case CE_TabBarTab: + if (const QStyleOptionTab *tab = qstyleoption_cast<const QStyleOptionTab *>(opt)) { + proxy()->drawControl(CE_TabBarTabShape, tab, p); + proxy()->drawControl(CE_TabBarTabLabel, tab, p); + } + break; + case CE_TabBarTabShape: + if (const QStyleOptionTab *tab = qstyleoption_cast<const QStyleOptionTab *>(opt)) { + p->save(); + + QRect rect(tab->rect); + bool selected = tab->state & State_Selected; + bool onlyOne = tab->position == QStyleOptionTab::OnlyOneTab; + int tabOverlap = onlyOne ? 0 : proxy()->pixelMetric(PM_TabBarTabOverlap, opt); + + if (!selected) { + switch (tab->shape) { + case QStyleOptionTab::TriangularNorth: + rect.adjust(0, 0, 0, -tabOverlap); + if(!selected) + rect.adjust(1, 1, -1, 0); + break; + case QStyleOptionTab::TriangularSouth: + rect.adjust(0, tabOverlap, 0, 0); + if(!selected) + rect.adjust(1, 0, -1, -1); + break; + case QStyleOptionTab::TriangularEast: + rect.adjust(tabOverlap, 0, 0, 0); + if(!selected) + rect.adjust(0, 1, -1, -1); + break; + case QStyleOptionTab::TriangularWest: + rect.adjust(0, 0, -tabOverlap, 0); + if(!selected) + rect.adjust(1, 1, 0, -1); + break; + default: + break; + } + } + + p->setPen(QPen(tab->palette.windowText(), 0)); + if (selected) { + p->setBrush(tab->palette.base()); + } else { + p->setBrush(tab->palette.window()); + } + + int y; + int x; + QPolygon a(10); + switch (tab->shape) { + case QStyleOptionTab::TriangularNorth: + case QStyleOptionTab::TriangularSouth: { + a.setPoint(0, 0, -1); + a.setPoint(1, 0, 0); + y = rect.height() - 2; + x = y / 3; + a.setPoint(2, x++, y - 1); + ++x; + a.setPoint(3, x++, y++); + a.setPoint(4, x, y); + + int i; + int right = rect.width() - 1; + for (i = 0; i < 5; ++i) + a.setPoint(9 - i, right - a.point(i).x(), a.point(i).y()); + if (tab->shape == QStyleOptionTab::TriangularNorth) + for (i = 0; i < 10; ++i) + a.setPoint(i, a.point(i).x(), rect.height() - 1 - a.point(i).y()); + + a.translate(rect.left(), rect.top()); + p->setRenderHint(QPainter::Antialiasing); + p->translate(0, 0.5); + + QPainterPath path; + path.addPolygon(a); + p->drawPath(path); + break; } + case QStyleOptionTab::TriangularEast: + case QStyleOptionTab::TriangularWest: { + a.setPoint(0, -1, 0); + a.setPoint(1, 0, 0); + x = rect.width() - 2; + y = x / 3; + a.setPoint(2, x - 1, y++); + ++y; + a.setPoint(3, x++, y++); + a.setPoint(4, x, y); + int i; + int bottom = rect.height() - 1; + for (i = 0; i < 5; ++i) + a.setPoint(9 - i, a.point(i).x(), bottom - a.point(i).y()); + if (tab->shape == QStyleOptionTab::TriangularWest) + for (i = 0; i < 10; ++i) + a.setPoint(i, rect.width() - 1 - a.point(i).x(), a.point(i).y()); + a.translate(rect.left(), rect.top()); + p->setRenderHint(QPainter::Antialiasing); + p->translate(0.5, 0); + QPainterPath path; + path.addPolygon(a); + p->drawPath(path); + break; } + default: + break; + } + p->restore(); + } + break; + case CE_ToolBoxTabLabel: + if (const QStyleOptionToolBox *tb = qstyleoption_cast<const QStyleOptionToolBox *>(opt)) { + bool enabled = tb->state & State_Enabled; + bool selected = tb->state & State_Selected; + int iconExtent = proxy()->pixelMetric(QStyle::PM_SmallIconSize, tb); + QPixmap pm = tb->icon.pixmap(opt->window, QSize(iconExtent, iconExtent), + enabled ? QIcon::Normal : QIcon::Disabled); + + QRect cr = subElementRect(QStyle::SE_ToolBoxTabContents, tb); + QRect tr, ir; + int ih = 0; + if (pm.isNull()) { + tr = cr; + tr.adjust(4, 0, -8, 0); + } else { + int iw = pm.width() / pm.devicePixelRatio() + 4; + ih = pm.height()/ pm.devicePixelRatio(); + ir = QRect(cr.left() + 4, cr.top(), iw + 2, ih); + tr = QRect(ir.right(), cr.top(), cr.width() - ir.right() - 4, cr.height()); + } + + if (selected && proxy()->styleHint(QStyle::SH_ToolBox_SelectedPageTitleBold, tb)) { + QFont f(p->font()); + f.setBold(true); + p->setFont(f); + } + + QString txt = tb->fontMetrics.elidedText(tb->text, Qt::ElideRight, tr.width()); + + if (ih) + p->drawPixmap(ir.left(), (tb->rect.height() - ih) / 2, pm); + + int alignment = Qt::AlignLeft | Qt::AlignVCenter | Qt::TextShowMnemonic; + if (!proxy()->styleHint(QStyle::SH_UnderlineShortcut, tb)) + alignment |= Qt::TextHideMnemonic; + proxy()->drawItemText(p, tr, alignment, tb->palette, enabled, txt, QPalette::ButtonText); + + if (!txt.isEmpty() && opt->state & State_HasFocus) { + QStyleOptionFocusRect opt; + opt.rect = tr; + opt.palette = tb->palette; + opt.state = QStyle::State_None; + proxy()->drawPrimitive(QStyle::PE_FrameFocusRect, &opt, p); + } + } + break; + case CE_TabBarTabLabel: + if (const QStyleOptionTab *tab = qstyleoption_cast<const QStyleOptionTab *>(opt)) { + QRect tr = tab->rect; + bool verticalTabs = tab->shape == QStyleOptionTab::RoundedEast + || tab->shape == QStyleOptionTab::RoundedWest + || tab->shape == QStyleOptionTab::TriangularEast + || tab->shape == QStyleOptionTab::TriangularWest; + + int alignment = Qt::AlignCenter | Qt::TextShowMnemonic; + if (!proxy()->styleHint(SH_UnderlineShortcut, opt)) + alignment |= Qt::TextHideMnemonic; + + if (verticalTabs) { + p->save(); + int newX, newY, newRot; + if (tab->shape == QStyleOptionTab::RoundedEast || tab->shape == QStyleOptionTab::TriangularEast) { + newX = tr.width() + tr.x(); + newY = tr.y(); + newRot = 90; + } else { + newX = tr.x(); + newY = tr.y() + tr.height(); + newRot = -90; + } + QTransform m = QTransform::fromTranslate(newX, newY); + m.rotate(newRot); + p->setTransform(m, true); + } + QRect iconRect; + d->tabLayout(tab, &tr, &iconRect); + tr = proxy()->subElementRect(SE_TabBarTabText, opt); //we compute tr twice because the style may override subElementRect + + if (!tab->icon.isNull()) { + QPixmap tabIcon = tab->icon.pixmap(opt->window, tab->iconSize, + (tab->state & State_Enabled) ? QIcon::Normal + : QIcon::Disabled, + (tab->state & State_Selected) ? QIcon::On + : QIcon::Off); + p->drawPixmap(iconRect.x(), iconRect.y(), tabIcon); + } + + proxy()->drawItemText(p, tr, alignment, tab->palette, tab->state & State_Enabled, tab->text, QPalette::WindowText); + if (verticalTabs) + p->restore(); + + if (tab->state & State_HasFocus) { + const int OFFSET = 1 + pixelMetric(PM_DefaultFrameWidth); + + int x1, x2; + x1 = tab->rect.left(); + x2 = tab->rect.right() - 1; + + QStyleOptionFocusRect fropt; + fropt.QStyleOption::operator=(*tab); + fropt.rect.setRect(x1 + 1 + OFFSET, tab->rect.y() + OFFSET, + x2 - x1 - 2*OFFSET, tab->rect.height() - 2*OFFSET); + drawPrimitive(PE_FrameFocusRect, &fropt, p); + } + } + break; + case CE_SizeGrip: { + p->save(); + int x, y, w, h; + opt->rect.getRect(&x, &y, &w, &h); + + int sw = qMin(h, w); + if (h > w) + p->translate(0, h - w); + else + p->translate(w - h, 0); + + int sx = x; + int sy = y; + int s = sw / 3; + + Qt::Corner corner; + if (const QStyleOptionSizeGrip *sgOpt = qstyleoption_cast<const QStyleOptionSizeGrip *>(opt)) + corner = sgOpt->corner; + else if (opt->direction == Qt::RightToLeft) + corner = Qt::BottomLeftCorner; + else + corner = Qt::BottomRightCorner; + + if (corner == Qt::BottomLeftCorner) { + sx = x + sw; + for (int i = 0; i < 4; ++i) { + p->setPen(QPen(opt->palette.light().color(), 1)); + p->drawLine(x, sy - 1 , sx + 1, sw); + p->setPen(QPen(opt->palette.dark().color(), 1)); + p->drawLine(x, sy, sx, sw); + p->setPen(QPen(opt->palette.dark().color(), 1)); + p->drawLine(x, sy + 1, sx - 1, sw); + sx -= s; + sy += s; + } + } else if (corner == Qt::BottomRightCorner) { + for (int i = 0; i < 4; ++i) { + p->setPen(QPen(opt->palette.light().color(), 1)); + p->drawLine(sx - 1, sw, sw, sy - 1); + p->setPen(QPen(opt->palette.dark().color(), 1)); + p->drawLine(sx, sw, sw, sy); + p->setPen(QPen(opt->palette.dark().color(), 1)); + p->drawLine(sx + 1, sw, sw, sy + 1); + sx += s; + sy += s; + } + } else if (corner == Qt::TopRightCorner) { + sy = y + sw; + for (int i = 0; i < 4; ++i) { + p->setPen(QPen(opt->palette.light().color(), 1)); + p->drawLine(sx - 1, y, sw, sy + 1); + p->setPen(QPen(opt->palette.dark().color(), 1)); + p->drawLine(sx, y, sw, sy); + p->setPen(QPen(opt->palette.dark().color(), 1)); + p->drawLine(sx + 1, y, sw, sy - 1); + sx += s; + sy -= s; + } + } else if (corner == Qt::TopLeftCorner) { + for (int i = 0; i < 4; ++i) { + p->setPen(QPen(opt->palette.light().color(), 1)); + p->drawLine(x, sy - 1, sx - 1, y); + p->setPen(QPen(opt->palette.dark().color(), 1)); + p->drawLine(x, sy, sx, y); + p->setPen(QPen(opt->palette.dark().color(), 1)); + p->drawLine(x, sy + 1, sx + 1, y); + sx += s; + sy += s; + } + } + p->restore(); + break; } + case CE_RubberBand: { + if (const QStyleOptionRubberBand *rbOpt = qstyleoption_cast<const QStyleOptionRubberBand *>(opt)) { + QPixmap tiledPixmap(16, 16); + QPainter pixmapPainter(&tiledPixmap); + pixmapPainter.setPen(Qt::NoPen); + pixmapPainter.setBrush(Qt::Dense4Pattern); + pixmapPainter.setBackground(QBrush(opt->palette.base())); + pixmapPainter.setBackgroundMode(Qt::OpaqueMode); + pixmapPainter.drawRect(0, 0, tiledPixmap.width(), tiledPixmap.height()); + pixmapPainter.end(); + // ### workaround for borked XRENDER + tiledPixmap = QPixmap::fromImage(tiledPixmap.toImage()); + + p->save(); + QRect r = opt->rect; + QStyleHintReturnMask mask; + if (proxy()->styleHint(QStyle::SH_RubberBand_Mask, opt, &mask)) + p->setClipRegion(mask.region); + p->drawTiledPixmap(r.x(), r.y(), r.width(), r.height(), tiledPixmap); + p->setPen(opt->palette.color(QPalette::Active, QPalette::WindowText)); + p->setBrush(Qt::NoBrush); + p->drawRect(r.adjusted(0, 0, -1, -1)); + if (rbOpt->shape == QStyleOptionRubberBand::Rectangle) + p->drawRect(r.adjusted(3, 3, -4, -4)); + p->restore(); + } + break; } + case CE_DockWidgetTitle: + if (const QStyleOptionDockWidget *dwOpt = qstyleoption_cast<const QStyleOptionDockWidget *>(opt)) { + QRect r = dwOpt->rect.adjusted(0, 0, -1, -1); + if (dwOpt->movable) { + p->setPen(dwOpt->palette.color(QPalette::Dark)); + p->drawRect(r); + } + + if (!dwOpt->title.isEmpty()) { + const bool verticalTitleBar = dwOpt->verticalTitleBar; + + if (verticalTitleBar) { + r = r.transposed(); + + p->save(); + p->translate(r.left(), r.top() + r.width()); + p->rotate(-90); + p->translate(-r.left(), -r.top()); + } + + const int indent = p->fontMetrics().descent(); + proxy()->drawItemText(p, r.adjusted(indent + 1, 1, -indent - 1, -1), + Qt::AlignLeft | Qt::AlignVCenter, dwOpt->palette, + dwOpt->state & State_Enabled, dwOpt->title, + QPalette::WindowText); + + if (verticalTitleBar) + p->restore(); + } + } + break; + case CE_Header: + if (const QStyleOptionHeader *header = qstyleoption_cast<const QStyleOptionHeader *>(opt)) { + QRegion clipRegion = p->clipRegion(); + p->setClipRect(opt->rect); + proxy()->drawControl(CE_HeaderSection, header, p); + QStyleOptionHeader subopt = *header; + subopt.rect = subElementRect(SE_HeaderLabel, header); + if (subopt.rect.isValid()) + proxy()->drawControl(CE_HeaderLabel, &subopt, p); + if (header->sortIndicator != QStyleOptionHeader::None) { + subopt.rect = subElementRect(SE_HeaderArrow, opt); + proxy()->drawPrimitive(PE_IndicatorHeaderArrow, &subopt, p); + } + p->setClipRegion(clipRegion); + } + break; + case CE_FocusFrame: + p->fillRect(opt->rect, opt->palette.windowText()); + break; + case CE_HeaderSection: + qDrawShadePanel(p, opt->rect, opt->palette, + opt->state & State_Sunken, 1, + &opt->palette.brush(QPalette::Button)); + break; + case CE_HeaderEmptyArea: + p->fillRect(opt->rect, opt->palette.window()); + break; + case CE_ComboBoxLabel: + if (const QStyleOptionComboBox *cb = qstyleoption_cast<const QStyleOptionComboBox *>(opt)) { + QRect editRect = proxy()->subControlRect(CC_ComboBox, cb, SC_ComboBoxEditField); + p->save(); + p->setClipRect(editRect); + if (!cb->currentIcon.isNull()) { + QIcon::Mode mode = cb->state & State_Enabled ? QIcon::Normal + : QIcon::Disabled; + QPixmap pixmap = cb->currentIcon.pixmap(opt->window, cb->iconSize, mode); + QRect iconRect(editRect); + iconRect.setWidth(cb->iconSize.width() + 4); + iconRect = alignedRect(cb->direction, + Qt::AlignLeft | Qt::AlignVCenter, + iconRect.size(), editRect); + if (cb->editable) + p->fillRect(iconRect, opt->palette.brush(QPalette::Base)); + proxy()->drawItemPixmap(p, iconRect, Qt::AlignCenter, pixmap); + + if (cb->direction == Qt::RightToLeft) + editRect.translate(-4 - cb->iconSize.width(), 0); + else + editRect.translate(cb->iconSize.width() + 4, 0); + } + if (!cb->currentText.isEmpty() && !cb->editable) { + proxy()->drawItemText(p, editRect.adjusted(1, 0, -1, 0), + visualAlignment(cb->direction, Qt::AlignLeft | Qt::AlignVCenter), + cb->palette, cb->state & State_Enabled, cb->currentText); + } + p->restore(); + } + break; + case CE_ToolBar: + if (const QStyleOptionToolBar *toolBar = qstyleoption_cast<const QStyleOptionToolBar *>(opt)) { + // Compatibility with styles that use PE_PanelToolBar + QStyleOptionFrame frame; + frame.QStyleOption::operator=(*toolBar); + frame.lineWidth = toolBar->lineWidth; + frame.midLineWidth = toolBar->midLineWidth; + proxy()->drawPrimitive(PE_PanelToolBar, opt, p); + + qDrawShadePanel(p, toolBar->rect, toolBar->palette, false, toolBar->lineWidth, + &toolBar->palette.brush(QPalette::Button)); + } + break; + case CE_ColumnViewGrip: { + // draw background gradients + QLinearGradient g(0, 0, opt->rect.width(), 0); + g.setColorAt(0, opt->palette.color(QPalette::Active, QPalette::Mid)); + g.setColorAt(0.5, Qt::white); + p->fillRect(QRect(0, 0, opt->rect.width(), opt->rect.height()), g); + + // draw the two lines + QPen pen(p->pen()); + pen.setWidth(opt->rect.width()/20); + pen.setColor(opt->palette.color(QPalette::Active, QPalette::Dark)); + p->setPen(pen); + + int line1starting = opt->rect.width()*8 / 20; + int line2starting = opt->rect.width()*13 / 20; + int top = opt->rect.height()*20/75; + int bottom = opt->rect.height() - 1 - top; + p->drawLine(line1starting, top, line1starting, bottom); + p->drawLine(line2starting, top, line2starting, bottom); + } + break; + case CE_ItemViewItem: + if (const QStyleOptionViewItem *vopt = qstyleoption_cast<const QStyleOptionViewItem *>(opt)) { + p->save(); + p->setClipRect(opt->rect); + + QRect checkRect = proxy()->subElementRect(SE_ItemViewItemCheckIndicator, vopt); + QRect iconRect = proxy()->subElementRect(SE_ItemViewItemDecoration, vopt); + QRect textRect = proxy()->subElementRect(SE_ItemViewItemText, vopt); + + // draw the background + proxy()->drawPrimitive(PE_PanelItemViewItem, opt, p); + + // draw the check mark + if (vopt->features & QStyleOptionViewItem::HasCheckIndicator) { + QStyleOptionViewItem option(*vopt); + option.rect = checkRect; + option.state = option.state & ~QStyle::State_HasFocus; + + switch (vopt->checkState) { + case Qt::Unchecked: + option.state |= QStyle::State_Off; + break; + case Qt::PartiallyChecked: + option.state |= QStyle::State_NoChange; + break; + case Qt::Checked: + option.state |= QStyle::State_On; + break; + } + proxy()->drawPrimitive(QStyle::PE_IndicatorItemViewItemCheck, &option, p); + } + + // draw the icon + QIcon::Mode mode = QIcon::Normal; + if (!(vopt->state & QStyle::State_Enabled)) + mode = QIcon::Disabled; + else if (vopt->state & QStyle::State_Selected) + mode = QIcon::Selected; + QIcon::State state = vopt->state & QStyle::State_Open ? QIcon::On : QIcon::Off; + vopt->icon.paint(p, iconRect, vopt->decorationAlignment, mode, state); + + // draw the text + if (!vopt->text.isEmpty()) { + QPalette::ColorGroup cg = vopt->state & QStyle::State_Enabled + ? QPalette::Normal : QPalette::Disabled; + if (cg == QPalette::Normal && !(vopt->state & QStyle::State_Active)) + cg = QPalette::Inactive; + + if (vopt->state & QStyle::State_Selected) { + p->setPen(vopt->palette.color(cg, QPalette::HighlightedText)); + } else { + p->setPen(vopt->palette.color(cg, QPalette::Text)); + } + if (vopt->state & QStyle::State_Editing) { + p->setPen(vopt->palette.color(cg, QPalette::Text)); + p->drawRect(textRect.adjusted(0, 0, -1, -1)); + } + + d->viewItemDrawText(p, vopt, textRect); + } + + // draw the focus rect + if (vopt->state & QStyle::State_HasFocus) { + QStyleOptionFocusRect o; + o.QStyleOption::operator=(*vopt); + o.rect = proxy()->subElementRect(SE_ItemViewItemFocusRect, vopt); + o.state |= QStyle::State_KeyboardFocusChange; + o.state |= QStyle::State_Item; + QPalette::ColorGroup cg = (vopt->state & QStyle::State_Enabled) + ? QPalette::Normal : QPalette::Disabled; + o.backgroundColor = vopt->palette.color(cg, (vopt->state & QStyle::State_Selected) + ? QPalette::Highlight : QPalette::Window); + proxy()->drawPrimitive(QStyle::PE_FrameFocusRect, &o, p); + } + + p->restore(); + } + break; + case CE_ShapedFrame: + if (const QStyleOptionFrame *f = qstyleoption_cast<const QStyleOptionFrame *>(opt)) { + int frameShape = f->frameShape; + int frameShadow = QStyleOptionFrame::Plain; + if (f->state & QStyle::State_Sunken) { + frameShadow = QStyleOptionFrame::Sunken; + } else if (f->state & QStyle::State_Raised) { + frameShadow = QStyleOptionFrame::Raised; + } + + int lw = f->lineWidth; + int mlw = f->midLineWidth; + QPalette::ColorRole foregroundRole = QPalette::WindowText; + + switch (frameShape) { + case QStyleOptionFrame::Box: + if (frameShadow == QStyleOptionFrame::Plain) { + qDrawPlainRect(p, f->rect, f->palette.color(foregroundRole), lw); + } else { + qDrawShadeRect(p, f->rect, f->palette, frameShadow == QStyleOptionFrame::Sunken, lw, mlw); + } + break; + case QStyleOptionFrame::StyledPanel: + proxy()->drawPrimitive(QStyle::PE_Frame, opt, p); + break; + case QStyleOptionFrame::Panel: + if (frameShadow == QStyleOptionFrame::Plain) { + qDrawPlainRect(p, f->rect, f->palette.color(foregroundRole), lw); + } else { + qDrawShadePanel(p, f->rect, f->palette, frameShadow == QStyleOptionFrame::Sunken, lw); + } + break; + case QStyleOptionFrame::WinPanel: + if (frameShadow == QStyleOptionFrame::Plain) { + qDrawPlainRect(p, f->rect, f->palette.color(foregroundRole), lw); + } else { + qDrawWinPanel(p, f->rect, f->palette, frameShadow == QStyleOptionFrame::Sunken); + } + break; + case QStyleOptionFrame::HLine: + case QStyleOptionFrame::VLine: { + QPoint p1, p2; + if (frameShape == QStyleOptionFrame::HLine) { + p1 = QPoint(opt->rect.x(), opt->rect.y() + opt->rect.height() / 2); + p2 = QPoint(opt->rect.x() + opt->rect.width(), p1.y()); + } else { + p1 = QPoint(opt->rect.x() + opt->rect.width() / 2, opt->rect.y()); + p2 = QPoint(p1.x(), p1.y() + opt->rect.height()); + } + if (frameShadow == QStyleOptionFrame::Plain) { + QPen oldPen = p->pen(); + p->setPen(QPen(opt->palette.brush(foregroundRole), lw)); + p->drawLine(p1, p2); + p->setPen(oldPen); + } else { + qDrawShadeLine(p, p1, p2, f->palette, frameShadow == QStyleOptionFrame::Sunken, lw, mlw); + } + break; + } + } + } + break; + default: + break; + } +} + +QRect QCommonStyle::subElementRect(SubElement sr, const QStyleOption *opt) const +{ + Q_D(const QCommonStyle); + QRect r; + switch (sr) { + case SE_PushButtonContents: + if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(opt)) { + int dx1, dx2; + dx1 = proxy()->pixelMetric(PM_DefaultFrameWidth, btn); + if (btn->features & QStyleOptionButton::AutoDefaultButton) + dx1 += proxy()->pixelMetric(PM_ButtonDefaultIndicator, btn); + dx2 = dx1 * 2; + r.setRect(opt->rect.x() + dx1, opt->rect.y() + dx1, opt->rect.width() - dx2, + opt->rect.height() - dx2); + r = visualRect(opt->direction, opt->rect, r); + } + break; + case SE_PushButtonFocusRect: + if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(opt)) { + int dbw1 = 0, dbw2 = 0; + if (btn->features & QStyleOptionButton::AutoDefaultButton){ + dbw1 = proxy()->pixelMetric(PM_ButtonDefaultIndicator, btn); + dbw2 = dbw1 * 2; + } + + int dfw1 = proxy()->pixelMetric(PM_DefaultFrameWidth, btn) + 1, + dfw2 = dfw1 * 2; + + r.setRect(btn->rect.x() + dfw1 + dbw1, btn->rect.y() + dfw1 + dbw1, + btn->rect.width() - dfw2 - dbw2, btn->rect.height()- dfw2 - dbw2); + r = visualRect(opt->direction, opt->rect, r); + } + break; + case SE_CheckBoxIndicator: + { + int h = proxy()->pixelMetric(PM_IndicatorHeight, opt); + r.setRect(opt->rect.x(), opt->rect.y() + ((opt->rect.height() - h) / 2), + proxy()->pixelMetric(PM_IndicatorWidth, opt), h); + r = visualRect(opt->direction, opt->rect, r); + } + break; + + case SE_CheckBoxContents: + { + // Deal with the logical first, then convert it back to screen coords. + QRect ir = visualRect(opt->direction, opt->rect, + subElementRect(SE_CheckBoxIndicator, opt)); + int spacing = proxy()->pixelMetric(PM_CheckBoxLabelSpacing, opt); + r.setRect(ir.right() + spacing, opt->rect.y(), opt->rect.width() - ir.width() - spacing, + opt->rect.height()); + r = visualRect(opt->direction, opt->rect, r); + } + break; + + case SE_CheckBoxFocusRect: + if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(opt)) { + if (btn->icon.isNull() && btn->text.isEmpty()) { + r = subElementRect(SE_CheckBoxIndicator, opt); + r.adjust(1, 1, -1, -1); + break; + } + // As above, deal with the logical first, then convert it back to screen coords. + QRect cr = visualRect(btn->direction, btn->rect, subElementRect(SE_CheckBoxContents, btn)); + + QRect iconRect, textRect; + if (!btn->text.isEmpty()) { + textRect = itemTextRect(opt->fontMetrics, cr, Qt::AlignAbsolute | Qt::AlignLeft + | Qt::AlignVCenter | Qt::TextShowMnemonic, + btn->state & State_Enabled, btn->text); + } + if (!btn->icon.isNull()) { + iconRect = itemPixmapRect(cr, Qt::AlignAbsolute | Qt::AlignLeft | Qt::AlignVCenter + | Qt::TextShowMnemonic, + btn->icon.pixmap(opt->window, btn->iconSize, QIcon::Normal)); + if (!textRect.isEmpty()) + textRect.translate(iconRect.right() + 4, 0); + } + r = iconRect | textRect; + r.adjust(-3, -2, 3, 2); + r = r.intersected(btn->rect); + r = visualRect(btn->direction, btn->rect, r); + } + break; + + case SE_RadioButtonIndicator: + { + int h = proxy()->pixelMetric(PM_ExclusiveIndicatorHeight, opt); + r.setRect(opt->rect.x(), opt->rect.y() + ((opt->rect.height() - h) / 2), + proxy()->pixelMetric(PM_ExclusiveIndicatorWidth, opt), h); + r = visualRect(opt->direction, opt->rect, r); + } + break; + + case SE_RadioButtonContents: + { + QRect ir = visualRect(opt->direction, opt->rect, + subElementRect(SE_RadioButtonIndicator, opt)); + int spacing = proxy()->pixelMetric(PM_RadioButtonLabelSpacing, opt); + r.setRect(ir.left() + ir.width() + spacing, opt->rect.y(), opt->rect.width() - ir.width() - spacing, + opt->rect.height()); + r = visualRect(opt->direction, opt->rect, r); + break; + } + + case SE_RadioButtonFocusRect: + if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(opt)) { + if (btn->icon.isNull() && btn->text.isEmpty()) { + r = subElementRect(SE_RadioButtonIndicator, opt); + r.adjust(1, 1, -1, -1); + break; + } + QRect cr = visualRect(btn->direction, btn->rect, subElementRect(SE_RadioButtonContents, opt)); + + QRect iconRect, textRect; + if (!btn->text.isEmpty()){ + textRect = itemTextRect(opt->fontMetrics, cr, Qt::AlignAbsolute | Qt::AlignLeft | Qt::AlignVCenter + | Qt::TextShowMnemonic, btn->state & State_Enabled, btn->text); + } + if (!btn->icon.isNull()) { + iconRect = itemPixmapRect(cr, Qt::AlignAbsolute | Qt::AlignLeft | Qt::AlignVCenter | Qt::TextShowMnemonic, + btn->icon.pixmap(opt->window, btn->iconSize, QIcon::Normal)); + if (!textRect.isEmpty()) + textRect.translate(iconRect.right() + 4, 0); + } + r = iconRect | textRect; + r.adjust(-3, -2, 3, 2); + r = r.intersected(btn->rect); + r = visualRect(btn->direction, btn->rect, r); + } + break; + case SE_SliderFocusRect: + if (const QStyleOptionSlider *slider = qstyleoption_cast<const QStyleOptionSlider *>(opt)) { + int tickOffset = proxy()->pixelMetric(PM_SliderTickmarkOffset, slider); + int thickness = proxy()->pixelMetric(PM_SliderControlThickness, slider); + if (slider->orientation == Qt::Horizontal) + r.setRect(0, tickOffset - 1, slider->rect.width(), thickness + 2); + else + r.setRect(tickOffset - 1, 0, thickness + 2, slider->rect.height()); + r = r.intersected(slider->rect); + r = visualRect(opt->direction, opt->rect, r); + } + break; + case SE_ProgressBarGroove: + case SE_ProgressBarContents: + case SE_ProgressBarLabel: + if (const QStyleOptionProgressBar *pb = qstyleoption_cast<const QStyleOptionProgressBar *>(opt)) { + int textw = 0; + if (pb->textVisible) + textw = qMax(pb->fontMetrics.horizontalAdvance(pb->text), pb->fontMetrics.horizontalAdvance(QLatin1String("100%"))) + 6; + + if ((pb->textAlignment & Qt::AlignCenter) == 0) { + if (sr != SE_ProgressBarLabel) + r.setCoords(pb->rect.left(), pb->rect.top(), + pb->rect.right() - textw, pb->rect.bottom()); + else + r.setCoords(pb->rect.right() - textw, pb->rect.top(), + pb->rect.right(), pb->rect.bottom()); + } else { + r = pb->rect; + } + r = visualRect(pb->direction, pb->rect, r); + } + break; + case SE_ComboBoxFocusRect: + if (const QStyleOptionComboBox *cb = qstyleoption_cast<const QStyleOptionComboBox *>(opt)) { + int margin = cb->frame ? 3 : 0; + r.setRect(opt->rect.left() + margin, opt->rect.top() + margin, + opt->rect.width() - 2*margin - 16, opt->rect.height() - 2*margin); + r = visualRect(opt->direction, opt->rect, r); + } + break; + case SE_ToolBoxTabContents: + r = opt->rect; + r.adjust(0, 0, -30, 0); + break; + case SE_HeaderLabel: { + int margin = proxy()->pixelMetric(QStyle::PM_HeaderMargin, opt); + r.setRect(opt->rect.x() + margin, opt->rect.y() + margin, + opt->rect.width() - margin * 2, opt->rect.height() - margin * 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) + r.setWidth(r.width() - (opt->rect.height() / 2) - (margin * 2)); + else + r.setHeight(r.height() - (opt->rect.width() / 2) - (margin * 2)); + } + } + r = visualRect(opt->direction, opt->rect, r); + break; } + case SE_HeaderArrow: { + int h = opt->rect.height(); + int w = opt->rect.width(); + int x = opt->rect.x(); + int y = opt->rect.y(); + int margin = proxy()->pixelMetric(QStyle::PM_HeaderMargin, opt); + + if (opt->state & State_Horizontal) { + int horiz_size = h / 2; + r.setRect(x + w - margin * 2 - horiz_size, y + 5, + horiz_size, h - margin * 2 - 5); + } else { + int vert_size = w / 2; + r.setRect(x + 5, y + h - margin * 2 - vert_size, + w - margin * 2 - 5, vert_size); + } + r = visualRect(opt->direction, opt->rect, r); + break; } + + case SE_RadioButtonClickRect: + r = subElementRect(SE_RadioButtonFocusRect, opt); + r |= subElementRect(SE_RadioButtonIndicator, opt); + break; + case SE_CheckBoxClickRect: + r = subElementRect(SE_CheckBoxFocusRect, opt); + r |= subElementRect(SE_CheckBoxIndicator, opt); + break; + case SE_TabWidgetTabBar: + if (const QStyleOptionTabWidgetFrame *twf + = qstyleoption_cast<const QStyleOptionTabWidgetFrame *>(opt)) { + r.setSize(twf->tabBarSize); + const uint alingMask = Qt::AlignLeft | Qt::AlignRight | Qt::AlignHCenter; + switch (twf->shape) { + case QStyleOptionTab::RoundedNorth: + case QStyleOptionTab::TriangularNorth: + // Constrain the size now, otherwise, center could get off the page + // This of course repeated for all the other directions + r.setWidth(qMin(r.width(), twf->rect.width() + - twf->leftCornerWidgetSize.width() + - twf->rightCornerWidgetSize.width())); + switch (proxy()->styleHint(SH_TabBar_Alignment, twf) & alingMask) { + default: + case Qt::AlignLeft: + r.moveTopLeft(QPoint(twf->leftCornerWidgetSize.width(), 0)); + break; + case Qt::AlignHCenter: + r.moveTopLeft(QPoint(twf->rect.center().x() - qRound(r.width() / 2.0f) + + (twf->leftCornerWidgetSize.width() / 2) + - (twf->rightCornerWidgetSize.width() / 2), 0)); + break; + case Qt::AlignRight: + r.moveTopLeft(QPoint(twf->rect.width() - twf->tabBarSize.width() + - twf->rightCornerWidgetSize.width(), 0)); + break; + } + r = visualRect(twf->direction, twf->rect, r); + break; + case QStyleOptionTab::RoundedSouth: + case QStyleOptionTab::TriangularSouth: + r.setWidth(qMin(r.width(), twf->rect.width() + - twf->leftCornerWidgetSize.width() + - twf->rightCornerWidgetSize.width())); + switch (proxy()->styleHint(SH_TabBar_Alignment, twf) & alingMask) { + default: + case Qt::AlignLeft: + r.moveTopLeft(QPoint(twf->leftCornerWidgetSize.width(), + twf->rect.height() - twf->tabBarSize.height())); + break; + case Qt::AlignHCenter: + r.moveTopLeft(QPoint(twf->rect.center().x() - qRound(r.width() / 2.0f) + + (twf->leftCornerWidgetSize.width() / 2) + - (twf->rightCornerWidgetSize.width() / 2), + twf->rect.height() - twf->tabBarSize.height())); + break; + case Qt::AlignRight: + r.moveTopLeft(QPoint(twf->rect.width() - twf->tabBarSize.width() + - twf->rightCornerWidgetSize.width(), + twf->rect.height() - twf->tabBarSize.height())); + break; + } + r = visualRect(twf->direction, twf->rect, r); + break; + case QStyleOptionTab::RoundedEast: + case QStyleOptionTab::TriangularEast: + r.setHeight(qMin(r.height(), twf->rect.height() + - twf->leftCornerWidgetSize.height() + - twf->rightCornerWidgetSize.height())); + switch (proxy()->styleHint(SH_TabBar_Alignment, twf) & alingMask) { + default: + case Qt::AlignLeft: + r.moveTopLeft(QPoint(twf->rect.width() - twf->tabBarSize.width(), + twf->leftCornerWidgetSize.height())); + break; + case Qt::AlignHCenter: + r.moveTopLeft(QPoint(twf->rect.width() - twf->tabBarSize.width(), + twf->rect.center().y() - r.height() / 2)); + break; + case Qt::AlignRight: + r.moveTopLeft(QPoint(twf->rect.width() - twf->tabBarSize.width(), + twf->rect.height() - twf->tabBarSize.height() + - twf->rightCornerWidgetSize.height())); + break; + } + break; + case QStyleOptionTab::RoundedWest: + case QStyleOptionTab::TriangularWest: + r.setHeight(qMin(r.height(), twf->rect.height() + - twf->leftCornerWidgetSize.height() + - twf->rightCornerWidgetSize.height())); + switch (proxy()->styleHint(SH_TabBar_Alignment, twf) & alingMask) { + default: + case Qt::AlignLeft: + r.moveTopLeft(QPoint(0, twf->leftCornerWidgetSize.height())); + break; + case Qt::AlignHCenter: + r.moveTopLeft(QPoint(0, twf->rect.center().y() - r.height() / 2)); + break; + case Qt::AlignRight: + r.moveTopLeft(QPoint(0, twf->rect.height() - twf->tabBarSize.height() + - twf->rightCornerWidgetSize.height())); + break; + } + break; + } + } + break; + case SE_TabWidgetTabPane: + case SE_TabWidgetTabContents: + if (const QStyleOptionTabWidgetFrame *twf = qstyleoption_cast<const QStyleOptionTabWidgetFrame *>(opt)) { + QStyleOptionTab tabopt; + tabopt.shape = twf->shape; + int overlap = proxy()->pixelMetric(PM_TabBarBaseOverlap, &tabopt); + if (twf->lineWidth == 0) + overlap = 0; + switch (twf->shape) { + case QStyleOptionTab::RoundedNorth: + case QStyleOptionTab::TriangularNorth: + r = QRect(QPoint(0,qMax(twf->tabBarSize.height() - overlap, 0)), + QSize(twf->rect.width(), qMin(twf->rect.height() - twf->tabBarSize.height() + overlap, twf->rect.height()))); + break; + case QStyleOptionTab::RoundedSouth: + case QStyleOptionTab::TriangularSouth: + r = QRect(QPoint(0,0), QSize(twf->rect.width(), qMin(twf->rect.height() - twf->tabBarSize.height() + overlap, twf->rect.height()))); + break; + case QStyleOptionTab::RoundedEast: + case QStyleOptionTab::TriangularEast: + r = QRect(QPoint(0, 0), QSize(qMin(twf->rect.width() - twf->tabBarSize.width() + overlap, twf->rect.width()), twf->rect.height())); + break; + case QStyleOptionTab::RoundedWest: + case QStyleOptionTab::TriangularWest: + r = QRect(QPoint(qMax(twf->tabBarSize.width() - overlap, 0), 0), + QSize(qMin(twf->rect.width() - twf->tabBarSize.width() + overlap, twf->rect.width()), twf->rect.height())); + break; + } + if (sr == SE_TabWidgetTabContents && twf->lineWidth > 0) + r.adjust(2, 2, -2, -2); + } + break; + case SE_TabWidgetLeftCorner: + if (const QStyleOptionTabWidgetFrame *twf = qstyleoption_cast<const QStyleOptionTabWidgetFrame *>(opt)) { + QRect paneRect = subElementRect(SE_TabWidgetTabPane, twf); + switch (twf->shape) { + case QStyleOptionTab::RoundedNorth: + case QStyleOptionTab::TriangularNorth: + r = QRect(QPoint(paneRect.x(), paneRect.y() - twf->leftCornerWidgetSize.height()), + twf->leftCornerWidgetSize); + break; + case QStyleOptionTab::RoundedSouth: + case QStyleOptionTab::TriangularSouth: + r = QRect(QPoint(paneRect.x(), paneRect.height()), twf->leftCornerWidgetSize); + break; + default: + break; + } + r = visualRect(twf->direction, twf->rect, r); + } + break; + case SE_TabWidgetRightCorner: + if (const QStyleOptionTabWidgetFrame *twf = qstyleoption_cast<const QStyleOptionTabWidgetFrame *>(opt)) { + QRect paneRect = subElementRect(SE_TabWidgetTabPane, twf); + switch (twf->shape) { + case QStyleOptionTab::RoundedNorth: + case QStyleOptionTab::TriangularNorth: + r = QRect(QPoint(paneRect.width() - twf->rightCornerWidgetSize.width(), + paneRect.y() - twf->rightCornerWidgetSize.height()), + twf->rightCornerWidgetSize); + break; + case QStyleOptionTab::RoundedSouth: + case QStyleOptionTab::TriangularSouth: + r = QRect(QPoint(paneRect.width() - twf->rightCornerWidgetSize.width(), + paneRect.height()), twf->rightCornerWidgetSize); + break; + default: + break; + } + r = visualRect(twf->direction, twf->rect, r); + } + break; + case SE_TabBarTabText: + if (const QStyleOptionTab *tab = qstyleoption_cast<const QStyleOptionTab *>(opt)) { + QRect dummyIconRect; + d->tabLayout(tab, &r, &dummyIconRect); + } + 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); + int horizontalShift = proxy()->pixelMetric(QStyle::PM_TabBarTabShiftHorizontal, tab); + int hpadding = proxy()->pixelMetric(QStyle::PM_TabBarTabHSpace, opt) / 2; + hpadding = qMax(hpadding, 4); //workaround KStyle returning 0 because they workaround an old bug in Qt + + bool verticalTabs = tab->shape == QStyleOptionTab::RoundedEast + || tab->shape == QStyleOptionTab::RoundedWest + || tab->shape == QStyleOptionTab::TriangularEast + || tab->shape == QStyleOptionTab::TriangularWest; + + QRect tr = tab->rect; + if (tab->shape == QStyleOptionTab::RoundedSouth || tab->shape == QStyleOptionTab::TriangularSouth) + verticalShift = -verticalShift; + if (verticalTabs) { + qSwap(horizontalShift, verticalShift); + horizontalShift *= -1; + verticalShift *= -1; + } + if (tab->shape == QStyleOptionTab::RoundedWest || tab->shape == QStyleOptionTab::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 QStyleOptionTab::RoundedWest: + case QStyleOptionTab::TriangularWest: + atTheTop = (sr == SE_TabBarTabLeftButton); + break; + case QStyleOptionTab::RoundedEast: + case QStyleOptionTab::TriangularEast: + atTheTop = (sr == SE_TabBarTabRightButton); + break; + default: + if (sr == SE_TabBarTabLeftButton) + r = QRect(tab->rect.x() + hpadding, midHeight, w, h); + else + r = QRect(tab->rect.right() - w - hpadding, midHeight, w, h); + r = visualRect(tab->direction, tab->rect, r); + } + if (verticalTabs) { + if (atTheTop) + r = QRect(midWidth, tr.y() + tab->rect.height() - hpadding - h, w, h); + else + r = QRect(midWidth, tr.y() + hpadding, w, h); + } + } + + break; + case SE_TabBarTearIndicator: + if (const QStyleOptionTab *tab = qstyleoption_cast<const QStyleOptionTab *>(opt)) { + switch (tab->shape) { + case QStyleOptionTab::RoundedNorth: + case QStyleOptionTab::TriangularNorth: + case QStyleOptionTab::RoundedSouth: + case QStyleOptionTab::TriangularSouth: + r.setRect(tab->rect.left(), tab->rect.top(), 8, opt->rect.height()); + break; + case QStyleOptionTab::RoundedWest: + case QStyleOptionTab::TriangularWest: + case QStyleOptionTab::RoundedEast: + case QStyleOptionTab::TriangularEast: + r.setRect(tab->rect.left(), tab->rect.top(), opt->rect.width(), 8); + break; + default: + break; + } + r = visualRect(opt->direction, opt->rect, r); + } + break; + case SE_TabBarScrollLeftButton: { + const bool vertical = opt->rect.width() < opt->rect.height(); + const Qt::LayoutDirection ld = opt->direction; + const int buttonWidth = proxy()->pixelMetric(QStyle::PM_TabBarScrollButtonWidth, nullptr); + const int buttonOverlap = proxy()->pixelMetric(QStyle::PM_TabBar_ScrollButtonOverlap, nullptr); + + r = vertical ? QRect(0, opt->rect.height() - (buttonWidth * 2) + buttonOverlap, opt->rect.width(), buttonWidth) + : QStyle::visualRect(ld, opt->rect, QRect(opt->rect.width() - (buttonWidth * 2) + buttonOverlap, 0, buttonWidth, opt->rect.height())); + break; } + case SE_TabBarScrollRightButton: { + const bool vertical = opt->rect.width() < opt->rect.height(); + const Qt::LayoutDirection ld = opt->direction; + const int buttonWidth = proxy()->pixelMetric(QStyle::PM_TabBarScrollButtonWidth, nullptr); + + r = vertical ? QRect(0, opt->rect.height() - buttonWidth, opt->rect.width(), buttonWidth) + : QStyle::visualRect(ld, opt->rect, QRect(opt->rect.width() - buttonWidth, 0, buttonWidth, opt->rect.height())); + break; } + case SE_TreeViewDisclosureItem: + r = opt->rect; + break; + case SE_LineEditContents: + if (const QStyleOptionFrame *f = qstyleoption_cast<const QStyleOptionFrame *>(opt)) { + r = f->rect.adjusted(f->lineWidth, f->lineWidth, -f->lineWidth, -f->lineWidth); + r = visualRect(opt->direction, opt->rect, r); + } + break; + case SE_FrameContents: + if (const QStyleOptionFrame *f = qstyleoption_cast<const QStyleOptionFrame *>(opt)) { + int fw = proxy()->pixelMetric(PM_DefaultFrameWidth, f); + r = opt->rect.adjusted(fw, fw, -fw, -fw); + r = visualRect(opt->direction, opt->rect, r); + } + break; + case SE_ShapedFrameContents: + if (const QStyleOptionFrame *f = qstyleoption_cast<const QStyleOptionFrame *>(opt)) { + int frameShape = f->frameShape; + int frameShadow = QStyleOptionFrame::Plain; + if (f->state & QStyle::State_Sunken) { + frameShadow = QStyleOptionFrame::Sunken; + } else if (f->state & QStyle::State_Raised) { + frameShadow = QStyleOptionFrame::Raised; + } + + int frameWidth = 0; + + switch (frameShape) { + case QStyleOptionFrame::NoFrame: + frameWidth = 0; + break; + + case QStyleOptionFrame::Box: + case QStyleOptionFrame::HLine: + case QStyleOptionFrame::VLine: + switch (frameShadow) { + case QStyleOptionFrame::Plain: + frameWidth = f->lineWidth; + break; + case QStyleOptionFrame::Raised: + case QStyleOptionFrame::Sunken: + frameWidth = (short)(f->lineWidth*2 + f->midLineWidth); + break; + } + break; + + case QStyleOptionFrame::StyledPanel: + //keep the compatibility with Qt 4.4 if there is a proxy style. + //be sure to call drawPrimitive(QStyle::SE_FrameContents) on the proxy style + return subElementRect(QStyle::SE_FrameContents, opt); + + case QStyleOptionFrame::WinPanel: + frameWidth = 2; + break; + + case QStyleOptionFrame::Panel: + switch (frameShadow) { + case QStyleOptionFrame::Plain: + case QStyleOptionFrame::Raised: + case QStyleOptionFrame::Sunken: + frameWidth = f->lineWidth; + break; + } + break; + } + r = f->rect.adjusted(frameWidth, frameWidth, -frameWidth, -frameWidth); + } + break; + case SE_DockWidgetCloseButton: + case SE_DockWidgetFloatButton: + case SE_DockWidgetTitleBarText: + case SE_DockWidgetIcon: { + int iconSize = proxy()->pixelMetric(PM_SmallIconSize, opt); + int buttonMargin = proxy()->pixelMetric(PM_DockWidgetTitleBarButtonMargin, opt); + QRect rect = opt->rect; + + const QStyleOptionDockWidget *dwOpt + = qstyleoption_cast<const QStyleOptionDockWidget*>(opt); + bool canClose = dwOpt == nullptr ? true : dwOpt->closable; + bool canFloat = dwOpt == nullptr ? false : dwOpt->floatable; + + const bool verticalTitleBar = dwOpt && dwOpt->verticalTitleBar; + + // If this is a vertical titlebar, we transpose and work as if it was + // horizontal, then transpose again. + + if (verticalTitleBar) + rect = rect.transposed(); + + do { + int right = rect.right(); + int left = rect.left(); + + QRect closeRect; + if (canClose) { + QSize sz = proxy()->standardIcon(QStyle::SP_TitleBarCloseButton, + opt).actualSize(QSize(iconSize, iconSize)); + sz += QSize(buttonMargin, buttonMargin); + if (verticalTitleBar) + sz = sz.transposed(); + closeRect = QRect(right - sz.width(), + rect.center().y() - sz.height()/2, + sz.width(), sz.height()); + right = closeRect.left() - 1; + } + if (sr == SE_DockWidgetCloseButton) { + r = closeRect; + break; + } + + QRect floatRect; + if (canFloat) { + QSize sz = proxy()->standardIcon(QStyle::SP_TitleBarNormalButton, + opt).actualSize(QSize(iconSize, iconSize)); + sz += QSize(buttonMargin, buttonMargin); + if (verticalTitleBar) + sz = sz.transposed(); + floatRect = QRect(right - sz.width(), + rect.center().y() - sz.height()/2, + sz.width(), sz.height()); + right = floatRect.left() - 1; + } + if (sr == SE_DockWidgetFloatButton) { + r = floatRect; + break; + } + + QRect iconRect; + if (sr == SE_DockWidgetIcon) { + r = iconRect; + break; + } + + QRect textRect = QRect(left, rect.top(), + right - left, rect.height()); + if (sr == SE_DockWidgetTitleBarText) { + r = textRect; + break; + } + + } while (false); + + if (verticalTitleBar) { + r = QRect(rect.left() + r.top() - rect.top(), + rect.top() + rect.right() - r.right(), + r.height(), r.width()); + } else { + r = visualRect(opt->direction, rect, r); + } + break; + } + case SE_ItemViewItemCheckIndicator: + if (!qstyleoption_cast<const QStyleOptionViewItem *>(opt)) { + r = subElementRect(SE_CheckBoxIndicator, opt); + break; + } + Q_FALLTHROUGH(); + case SE_ItemViewItemDecoration: + case SE_ItemViewItemText: + case SE_ItemViewItemFocusRect: + if (const QStyleOptionViewItem *vopt = qstyleoption_cast<const QStyleOptionViewItem *>(opt)) { + if (!d->isViewItemCached(*vopt)) { + d->viewItemLayout(vopt, &d->checkRect, &d->decorationRect, &d->displayRect, false); + if (d->cachedOption) { + delete d->cachedOption; + d->cachedOption = nullptr; + } + d->cachedOption = new QStyleOptionViewItem(*vopt); + } + if (sr == SE_ItemViewItemCheckIndicator) + r = d->checkRect; + else if (sr == SE_ItemViewItemDecoration) + r = d->decorationRect; + else if (sr == SE_ItemViewItemText || sr == SE_ItemViewItemFocusRect) + r = d->displayRect; + } + break; + case SE_ToolBarHandle: + if (const QStyleOptionToolBar *tbopt = qstyleoption_cast<const QStyleOptionToolBar *>(opt)) { + if (tbopt->features & QStyleOptionToolBar::Movable) { + ///we need to access the widget here because the style option doesn't + //have all the information we need (ie. the layout's margin) + const QMargins margins(2, 2, 2, 2); + const int handleExtent = proxy()->pixelMetric(QStyle::PM_ToolBarHandleExtent, opt); + if (tbopt->state & QStyle::State_Horizontal) { + r = QRect(margins.left(), margins.top(), + handleExtent, + tbopt->rect.height() - (margins.top() + margins.bottom())); + r = QStyle::visualRect(tbopt->direction, tbopt->rect, r); + } else { + r = QRect(margins.left(), margins.top(), + tbopt->rect.width() - (margins.left() + margins.right()), + handleExtent); + } + } + } + break; + default: + break; + } + return r; +} + +// in lieu of std::array, minimal API +template <int N> +struct StaticPolygonF +{ + QPointF data[N]; + + Q_DECL_CONSTEXPR int size() const { return N; } + Q_DECL_CONSTEXPR const QPointF *cbegin() const { return data; } + Q_DECL_CONSTEXPR const QPointF &operator[](int idx) const { return data[idx]; } +}; + +static StaticPolygonF<3> calcArrow(const QStyleOptionSlider *dial, qreal &a) +{ + int width = dial->rect.width(); + int height = dial->rect.height(); + int r = qMin(width, height) / 2; + int currentSliderPosition = dial->upsideDown ? dial->sliderPosition : (dial->maximum - dial->sliderPosition); + + if (dial->maximum == dial->minimum) + a = Q_PI / 2; + else if (dial->dialWrapping) + a = Q_PI * 3 / 2 - (currentSliderPosition - dial->minimum) * 2 * Q_PI + / (dial->maximum - dial->minimum); + else + a = (Q_PI * 8 - (currentSliderPosition - dial->minimum) * 10 * Q_PI + / (dial->maximum - dial->minimum)) / 6; + + int xc = width / 2; + int yc = height / 2; + + int len = r - QStyleHelper::calcBigLineSize(r) - 5; + if (len < 5) + len = 5; + int back = len / 2; + + StaticPolygonF<3> arrow = {{ + QPointF(0.5 + xc + len * qCos(a), + 0.5 + yc - len * qSin(a)), + QPointF(0.5 + xc + back * qCos(a + Q_PI * 5 / 6), + 0.5 + yc - back * qSin(a + Q_PI * 5 / 6)), + QPointF(0.5 + xc + back * qCos(a - Q_PI * 5 / 6), + 0.5 + yc - back * qSin(a - Q_PI * 5 / 6)), + }}; + return arrow; +} + +void QCommonStyle::drawComplexControl(ComplexControl cc, const QStyleOptionComplex *opt, QPainter *p) const +{ + switch (cc) { + case CC_Slider: + if (const QStyleOptionSlider *slider = qstyleoption_cast<const QStyleOptionSlider *>(opt)) { + if (slider->subControls == SC_SliderTickmarks) { + int tickOffset = proxy()->pixelMetric(PM_SliderTickmarkOffset, slider); + int ticks = slider->tickPosition; + int thickness = proxy()->pixelMetric(PM_SliderControlThickness, slider); + int len = proxy()->pixelMetric(PM_SliderLength, slider); + int available = proxy()->pixelMetric(PM_SliderSpaceAvailable, slider); + 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; + // Since there is no subrect for tickmarks do a translation here. + p->save(); + p->translate(slider->rect.x(), slider->rect.y()); + p->setPen(slider->palette.windowText().color()); + int v = slider->minimum; + while (v <= slider->maximum + 1) { + if (v == slider->maximum + 1 && interval == 1) + break; + const int v_ = qMin(v, slider->maximum); + pos = QStyle::sliderPositionFromValue(slider->minimum, slider->maximum, + v_, available) + fudge; + if (slider->orientation == Qt::Horizontal) { + if (ticks & QStyleOptionSlider::TicksAbove) + p->drawLine(pos, 0, pos, tickOffset - 2); + if (ticks & QStyleOptionSlider::TicksBelow) + p->drawLine(pos, tickOffset + thickness + 1, pos, + slider->rect.height()-1); + } else { + if (ticks & QStyleOptionSlider::TicksAbove) + p->drawLine(0, pos, tickOffset - 2, pos); + if (ticks & QStyleOptionSlider::TicksBelow) + p->drawLine(tickOffset + thickness + 1, pos, + slider->rect.width()-1, pos); + } + // in the case where maximum is max int + int nextInterval = v + interval; + if (nextInterval < v) + break; + v = nextInterval; + } + p->restore(); + } + } + break; + case CC_ScrollBar: + if (const QStyleOptionSlider *scrollbar = qstyleoption_cast<const QStyleOptionSlider *>(opt)) { + // Make a copy here and reset it for each primitive. + QStyleOptionSlider newScrollbar = *scrollbar; + State saveFlags = scrollbar->state; + + if (scrollbar->subControls & SC_ScrollBarSubLine) { + newScrollbar.state = saveFlags; + newScrollbar.rect = proxy()->subControlRect(cc, &newScrollbar, SC_ScrollBarSubLine); + if (newScrollbar.rect.isValid()) { + if (!(scrollbar->activeSubControls & SC_ScrollBarSubLine)) + newScrollbar.state &= ~(State_Sunken | State_MouseOver); + proxy()->drawControl(CE_ScrollBarSubLine, &newScrollbar, p); + } + } + if (scrollbar->subControls & SC_ScrollBarAddLine) { + newScrollbar.rect = scrollbar->rect; + newScrollbar.state = saveFlags; + newScrollbar.rect = proxy()->subControlRect(cc, &newScrollbar, SC_ScrollBarAddLine); + if (newScrollbar.rect.isValid()) { + if (!(scrollbar->activeSubControls & SC_ScrollBarAddLine)) + newScrollbar.state &= ~(State_Sunken | State_MouseOver); + proxy()->drawControl(CE_ScrollBarAddLine, &newScrollbar, p); + } + } + if (scrollbar->subControls & SC_ScrollBarSubPage) { + newScrollbar.rect = scrollbar->rect; + newScrollbar.state = saveFlags; + newScrollbar.rect = proxy()->subControlRect(cc, &newScrollbar, SC_ScrollBarSubPage); + if (newScrollbar.rect.isValid()) { + if (!(scrollbar->activeSubControls & SC_ScrollBarSubPage)) + newScrollbar.state &= ~(State_Sunken | State_MouseOver); + proxy()->drawControl(CE_ScrollBarSubPage, &newScrollbar, p); + } + } + if (scrollbar->subControls & SC_ScrollBarAddPage) { + newScrollbar.rect = scrollbar->rect; + newScrollbar.state = saveFlags; + newScrollbar.rect = proxy()->subControlRect(cc, &newScrollbar, SC_ScrollBarAddPage); + if (newScrollbar.rect.isValid()) { + if (!(scrollbar->activeSubControls & SC_ScrollBarAddPage)) + newScrollbar.state &= ~(State_Sunken | State_MouseOver); + proxy()->drawControl(CE_ScrollBarAddPage, &newScrollbar, p); + } + } + if (scrollbar->subControls & SC_ScrollBarFirst) { + newScrollbar.rect = scrollbar->rect; + newScrollbar.state = saveFlags; + newScrollbar.rect = proxy()->subControlRect(cc, &newScrollbar, SC_ScrollBarFirst); + if (newScrollbar.rect.isValid()) { + if (!(scrollbar->activeSubControls & SC_ScrollBarFirst)) + newScrollbar.state &= ~(State_Sunken | State_MouseOver); + proxy()->drawControl(CE_ScrollBarFirst, &newScrollbar, p); + } + } + if (scrollbar->subControls & SC_ScrollBarLast) { + newScrollbar.rect = scrollbar->rect; + newScrollbar.state = saveFlags; + newScrollbar.rect = proxy()->subControlRect(cc, &newScrollbar, SC_ScrollBarLast); + if (newScrollbar.rect.isValid()) { + if (!(scrollbar->activeSubControls & SC_ScrollBarLast)) + newScrollbar.state &= ~(State_Sunken | State_MouseOver); + proxy()->drawControl(CE_ScrollBarLast, &newScrollbar, p); + } + } + if (scrollbar->subControls & SC_ScrollBarSlider) { + newScrollbar.rect = scrollbar->rect; + newScrollbar.state = saveFlags; + newScrollbar.rect = proxy()->subControlRect(cc, &newScrollbar, SC_ScrollBarSlider); + if (newScrollbar.rect.isValid()) { + if (!(scrollbar->activeSubControls & SC_ScrollBarSlider)) + newScrollbar.state &= ~(State_Sunken | State_MouseOver); + proxy()->drawControl(CE_ScrollBarSlider, &newScrollbar, p); + + if (scrollbar->state & State_HasFocus) { + QStyleOptionFocusRect fropt; + fropt.QStyleOption::operator=(newScrollbar); + fropt.rect.setRect(newScrollbar.rect.x() + 2, newScrollbar.rect.y() + 2, + newScrollbar.rect.width() - 5, + newScrollbar.rect.height() - 5); + proxy()->drawPrimitive(PE_FrameFocusRect, &fropt, p); + } + } + } + } + break; + case CC_SpinBox: + if (const QStyleOptionSpinBox *sb = qstyleoption_cast<const QStyleOptionSpinBox *>(opt)) { + QStyleOptionSpinBox copy = *sb; + PrimitiveElement pe; + + if (sb->frame && (sb->subControls & SC_SpinBoxFrame)) { + QRect r = proxy()->subControlRect(CC_SpinBox, sb, SC_SpinBoxFrame); + qDrawWinPanel(p, r, sb->palette, true); + } + + if (sb->subControls & SC_SpinBoxUp) { + copy.subControls = SC_SpinBoxUp; + QPalette pal2 = sb->palette; + if (!(sb->stepEnabled & QStyleOptionSpinBox::StepUpEnabled)) { + pal2.setCurrentColorGroup(QPalette::Disabled); + copy.state &= ~State_Enabled; + } + + copy.palette = pal2; + + if (sb->activeSubControls == SC_SpinBoxUp && (sb->state & State_Sunken)) { + copy.state |= State_On; + copy.state |= State_Sunken; + } else { + copy.state |= State_Raised; + copy.state &= ~State_Sunken; + } + pe = (sb->buttonSymbols == QStyleOptionSpinBox::PlusMinus ? PE_IndicatorSpinPlus + : PE_IndicatorSpinUp); + + copy.rect = proxy()->subControlRect(CC_SpinBox, sb, SC_SpinBoxUp); + proxy()->drawPrimitive(PE_PanelButtonBevel, ©, p); + copy.rect.adjust(3, 0, -4, 0); + proxy()->drawPrimitive(pe, ©, p); + } + + if (sb->subControls & SC_SpinBoxDown) { + copy.subControls = SC_SpinBoxDown; + copy.state = sb->state; + QPalette pal2 = sb->palette; + if (!(sb->stepEnabled & QStyleOptionSpinBox::StepDownEnabled)) { + pal2.setCurrentColorGroup(QPalette::Disabled); + copy.state &= ~State_Enabled; + } + copy.palette = pal2; + + if (sb->activeSubControls == SC_SpinBoxDown && (sb->state & State_Sunken)) { + copy.state |= State_On; + copy.state |= State_Sunken; + } else { + copy.state |= State_Raised; + copy.state &= ~State_Sunken; + } + pe = (sb->buttonSymbols == QStyleOptionSpinBox::PlusMinus ? PE_IndicatorSpinMinus + : PE_IndicatorSpinDown); + + copy.rect = proxy()->subControlRect(CC_SpinBox, sb, SC_SpinBoxDown); + proxy()->drawPrimitive(PE_PanelButtonBevel, ©, p); + copy.rect.adjust(3, 0, -4, 0); + proxy()->drawPrimitive(pe, ©, p); + } + } + break; + case CC_ToolButton: + if (const QStyleOptionToolButton *toolbutton + = qstyleoption_cast<const QStyleOptionToolButton *>(opt)) { + QRect button, menuarea; + button = proxy()->subControlRect(cc, toolbutton, SC_ToolButton); + menuarea = proxy()->subControlRect(cc, toolbutton, SC_ToolButtonMenu); + + State bflags = toolbutton->state & ~State_Sunken; + + if (bflags & State_AutoRaise) { + if (!(bflags & State_MouseOver) || !(bflags & State_Enabled)) { + bflags &= ~State_Raised; + } + } + State mflags = bflags; + if (toolbutton->state & State_Sunken) { + if (toolbutton->activeSubControls & SC_ToolButton) + bflags |= State_Sunken; + mflags |= State_Sunken; + } + + QStyleOption tool = *toolbutton; + if (toolbutton->subControls & SC_ToolButton) { + if (bflags & (State_Sunken | State_On | State_Raised)) { + tool.rect = button; + tool.state = bflags; + proxy()->drawPrimitive(PE_PanelButtonTool, &tool, p); + } + } + + 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), 0); + proxy()->drawPrimitive(PE_FrameFocusRect, &fr, p); + } + QStyleOptionToolButton label = *toolbutton; + label.state = bflags; + int fw = proxy()->pixelMetric(PM_DefaultFrameWidth, opt); + label.rect = button.adjusted(fw, fw, -fw, -fw); + proxy()->drawControl(CE_ToolButtonLabel, &label, p); + + if (toolbutton->subControls & SC_ToolButtonMenu) { + tool.rect = menuarea; + tool.state = mflags; + if (mflags & (State_Sunken | State_On | State_Raised)) + proxy()->drawPrimitive(PE_IndicatorButtonDropDown, &tool, p); + proxy()->drawPrimitive(PE_IndicatorArrowDown, &tool, p); + } else if (toolbutton->features & QStyleOptionToolButton::HasMenu) { + int mbi = proxy()->pixelMetric(PM_MenuButtonIndicator, toolbutton); + QRect ir = toolbutton->rect; + QStyleOptionToolButton newBtn = *toolbutton; + newBtn.rect = QRect(ir.right() + 5 - mbi, ir.y() + ir.height() - mbi + 4, mbi - 6, mbi - 6); + newBtn.rect = visualRect(toolbutton->direction, button, newBtn.rect); + proxy()->drawPrimitive(PE_IndicatorArrowDown, &newBtn, p); + } + } + break; + case CC_TitleBar: + if (const QStyleOptionTitleBar *tb = qstyleoption_cast<const QStyleOptionTitleBar *>(opt)) { + QRect ir; + if (opt->subControls & SC_TitleBarLabel) { + QColor left = tb->palette.highlight().color(); + QColor right = tb->palette.base().color(); + + QBrush fillBrush(left); + if (left != right) { + QPoint p1(tb->rect.x(), tb->rect.top() + tb->rect.height()/2); + QPoint p2(tb->rect.right(), tb->rect.top() + tb->rect.height()/2); + QLinearGradient lg(p1, p2); + lg.setColorAt(0, left); + lg.setColorAt(1, right); + fillBrush = lg; + } + + p->fillRect(opt->rect, fillBrush); + + ir = proxy()->subControlRect(CC_TitleBar, tb, SC_TitleBarLabel); + + p->setPen(tb->palette.highlightedText().color()); + p->drawText(ir.x() + 2, ir.y(), ir.width() - 2, ir.height(), + Qt::AlignLeft | Qt::AlignVCenter | Qt::TextSingleLine, tb->text); + } + + bool down = false; + QPixmap pm; + + QStyleOption tool = *tb; + if (tb->subControls & SC_TitleBarCloseButton && tb->titleBarFlags & Qt::WindowSystemMenuHint) { + ir = proxy()->subControlRect(CC_TitleBar, tb, SC_TitleBarCloseButton); + down = tb->activeSubControls & SC_TitleBarCloseButton && (opt->state & State_Sunken); + if ((tb->titleBarFlags & Qt::WindowType_Mask) == Qt::Tool) + pm = proxy()->standardIcon(SP_DockWidgetCloseButton, &tool).pixmap(opt->window, QSize(10, 10)); + else + pm = proxy()->standardIcon(SP_TitleBarCloseButton, &tool).pixmap(opt->window, QSize(10, 10)); + tool.rect = ir; + tool.state = down ? State_Sunken : State_Raised; + proxy()->drawPrimitive(PE_PanelButtonTool, &tool, p); + + p->save(); + if (down) + p->translate(proxy()->pixelMetric(PM_ButtonShiftHorizontal, tb), + proxy()->pixelMetric(PM_ButtonShiftVertical, tb)); + proxy()->drawItemPixmap(p, ir, Qt::AlignCenter, pm); + p->restore(); + } + + if (tb->subControls & SC_TitleBarMaxButton + && tb->titleBarFlags & Qt::WindowMaximizeButtonHint + && !(tb->titleBarState & Qt::WindowMaximized)) { + ir = proxy()->subControlRect(CC_TitleBar, tb, SC_TitleBarMaxButton); + + down = tb->activeSubControls & SC_TitleBarMaxButton && (opt->state & State_Sunken); + pm = proxy()->standardIcon(SP_TitleBarMaxButton, &tool).pixmap(opt->window, QSize(10, 10)); + tool.rect = ir; + tool.state = down ? State_Sunken : State_Raised; + proxy()->drawPrimitive(PE_PanelButtonTool, &tool, p); + + p->save(); + if (down) + p->translate(proxy()->pixelMetric(PM_ButtonShiftHorizontal, tb), + proxy()->pixelMetric(PM_ButtonShiftVertical, tb)); + proxy()->drawItemPixmap(p, ir, Qt::AlignCenter, pm); + p->restore(); + } + + if (tb->subControls & SC_TitleBarMinButton + && tb->titleBarFlags & Qt::WindowMinimizeButtonHint + && !(tb->titleBarState & Qt::WindowMinimized)) { + ir = proxy()->subControlRect(CC_TitleBar, tb, SC_TitleBarMinButton); + down = tb->activeSubControls & SC_TitleBarMinButton && (opt->state & State_Sunken); + pm = proxy()->standardIcon(SP_TitleBarMinButton, &tool).pixmap(opt->window, QSize(10, 10)); + tool.rect = ir; + tool.state = down ? State_Sunken : State_Raised; + proxy()->drawPrimitive(PE_PanelButtonTool, &tool, p); + + p->save(); + if (down) + p->translate(proxy()->pixelMetric(PM_ButtonShiftHorizontal, tb), + proxy()->pixelMetric(PM_ButtonShiftVertical, tb)); + proxy()->drawItemPixmap(p, ir, Qt::AlignCenter, pm); + p->restore(); + } + + bool drawNormalButton = (tb->subControls & SC_TitleBarNormalButton) + && (((tb->titleBarFlags & Qt::WindowMinimizeButtonHint) + && (tb->titleBarState & Qt::WindowMinimized)) + || ((tb->titleBarFlags & Qt::WindowMaximizeButtonHint) + && (tb->titleBarState & Qt::WindowMaximized))); + + if (drawNormalButton) { + ir = proxy()->subControlRect(CC_TitleBar, tb, SC_TitleBarNormalButton); + down = tb->activeSubControls & SC_TitleBarNormalButton && (opt->state & State_Sunken); + pm = proxy()->standardIcon(SP_TitleBarNormalButton, &tool).pixmap(opt->window, QSize(10, 10)); + tool.rect = ir; + tool.state = down ? State_Sunken : State_Raised; + proxy()->drawPrimitive(PE_PanelButtonTool, &tool, p); + + p->save(); + if (down) + p->translate(proxy()->pixelMetric(PM_ButtonShiftHorizontal, tb), + proxy()->pixelMetric(PM_ButtonShiftVertical, tb)); + proxy()->drawItemPixmap(p, ir, Qt::AlignCenter, pm); + p->restore(); + } + + if (tb->subControls & SC_TitleBarShadeButton + && tb->titleBarFlags & Qt::WindowShadeButtonHint + && !(tb->titleBarState & Qt::WindowMinimized)) { + ir = proxy()->subControlRect(CC_TitleBar, tb, SC_TitleBarShadeButton); + down = (tb->activeSubControls & SC_TitleBarShadeButton && (opt->state & State_Sunken)); + pm = proxy()->standardIcon(SP_TitleBarShadeButton, &tool).pixmap(opt->window, QSize(10, 10)); + tool.rect = ir; + tool.state = down ? State_Sunken : State_Raised; + proxy()->drawPrimitive(PE_PanelButtonTool, &tool, p); + p->save(); + if (down) + p->translate(proxy()->pixelMetric(PM_ButtonShiftHorizontal, tb), + proxy()->pixelMetric(PM_ButtonShiftVertical, tb)); + proxy()->drawItemPixmap(p, ir, Qt::AlignCenter, pm); + p->restore(); + } + + if (tb->subControls & SC_TitleBarUnshadeButton + && tb->titleBarFlags & Qt::WindowShadeButtonHint + && tb->titleBarState & Qt::WindowMinimized) { + ir = proxy()->subControlRect(CC_TitleBar, tb, SC_TitleBarUnshadeButton); + + down = tb->activeSubControls & SC_TitleBarUnshadeButton && (opt->state & State_Sunken); + pm = proxy()->standardIcon(SP_TitleBarUnshadeButton, &tool).pixmap(opt->window, QSize(10, 10)); + tool.rect = ir; + tool.state = down ? State_Sunken : State_Raised; + proxy()->drawPrimitive(PE_PanelButtonTool, &tool, p); + p->save(); + if (down) + p->translate(proxy()->pixelMetric(PM_ButtonShiftHorizontal, tb), + proxy()->pixelMetric(PM_ButtonShiftVertical, tb)); + proxy()->drawItemPixmap(p, ir, Qt::AlignCenter, pm); + p->restore(); + } + if (tb->subControls & SC_TitleBarContextHelpButton + && tb->titleBarFlags & Qt::WindowContextHelpButtonHint) { + ir = proxy()->subControlRect(CC_TitleBar, tb, SC_TitleBarContextHelpButton); + + down = tb->activeSubControls & SC_TitleBarContextHelpButton && (opt->state & State_Sunken); + pm = proxy()->standardIcon(SP_TitleBarContextHelpButton, &tool).pixmap(opt->window, QSize(10, 10)); + tool.rect = ir; + tool.state = down ? State_Sunken : State_Raised; + proxy()->drawPrimitive(PE_PanelButtonTool, &tool, p); + p->save(); + if (down) + p->translate(proxy()->pixelMetric(PM_ButtonShiftHorizontal, tb), + proxy()->pixelMetric(PM_ButtonShiftVertical, tb)); + proxy()->drawItemPixmap(p, ir, Qt::AlignCenter, pm); + p->restore(); + } + if (tb->subControls & SC_TitleBarSysMenu && tb->titleBarFlags & Qt::WindowSystemMenuHint) { + ir = proxy()->subControlRect(CC_TitleBar, tb, SC_TitleBarSysMenu); + if (!tb->icon.isNull()) { + tb->icon.paint(p, ir); + } else { + int iconSize = proxy()->pixelMetric(PM_SmallIconSize, tb); + pm = proxy()->standardIcon(SP_TitleBarMenuButton, &tool).pixmap(opt->window, QSize(iconSize, iconSize)); + tool.rect = ir; + p->save(); + proxy()->drawItemPixmap(p, ir, Qt::AlignCenter, pm); + p->restore(); + } + } + } + break; + case CC_Dial: + if (const QStyleOptionSlider *dial = qstyleoption_cast<const QStyleOptionSlider *>(opt)) { + // OK, this is more a port of things over + p->save(); + + // avoid dithering + if (p->paintEngine()->hasFeature(QPaintEngine::Antialiasing)) + p->setRenderHint(QPainter::Antialiasing); + + int width = dial->rect.width(); + int height = dial->rect.height(); + qreal r = qMin(width, height) / 2; + qreal d_ = r / 6; + qreal dx = dial->rect.x() + d_ + (width - 2 * r) / 2 + 1; + qreal dy = dial->rect.y() + d_ + (height - 2 * r) / 2 + 1; + QRect br = QRect(int(dx), int(dy), int(r * 2 - 2 * d_ - 2), int(r * 2 - 2 * d_ - 2)); + + QPalette pal = opt->palette; + // draw notches + if (dial->subControls & QStyle::SC_DialTickmarks) { + p->setPen(pal.windowText().color()); + p->drawLines(QStyleHelper::calcLines(dial)); + } + + if (dial->state & State_Enabled) { + p->setBrush(pal.brush(QPalette::ColorRole(proxy()->styleHint(SH_Dial_BackgroundRole, dial)))); + p->setPen(Qt::NoPen); + p->drawEllipse(br); + p->setBrush(Qt::NoBrush); + } + p->setPen(QPen(pal.dark().color())); + p->drawArc(br, 60 * 16, 180 * 16); + p->setPen(QPen(pal.light().color())); + p->drawArc(br, 240 * 16, 180 * 16); + + qreal a; + const StaticPolygonF<3> arrow = calcArrow(dial, a); + + p->setPen(Qt::NoPen); + p->setBrush(pal.button()); + p->setRenderHint(QPainter::Qt4CompatiblePainting); + p->drawPolygon(arrow.cbegin(), arrow.size()); + + a = QStyleHelper::angle(QPointF(width / 2, height / 2), arrow[0]); + p->setBrush(Qt::NoBrush); + + if (a <= 0 || a > 200) { + p->setPen(pal.light().color()); + p->drawLine(arrow[2], arrow[0]); + p->drawLine(arrow[1], arrow[2]); + p->setPen(pal.dark().color()); + p->drawLine(arrow[0], arrow[1]); + } else if (a > 0 && a < 45) { + p->setPen(pal.light().color()); + p->drawLine(arrow[2], arrow[0]); + p->setPen(pal.dark().color()); + p->drawLine(arrow[1], arrow[2]); + p->drawLine(arrow[0], arrow[1]); + } else if (a >= 45 && a < 135) { + p->setPen(pal.dark().color()); + p->drawLine(arrow[2], arrow[0]); + p->drawLine(arrow[1], arrow[2]); + p->setPen(pal.light().color()); + p->drawLine(arrow[0], arrow[1]); + } else if (a >= 135 && a < 200) { + p->setPen(pal.dark().color()); + p->drawLine(arrow[2], arrow[0]); + p->setPen(pal.light().color()); + p->drawLine(arrow[0], arrow[1]); + p->drawLine(arrow[1], arrow[2]); + } + + // draw focus rect around the dial + QStyleOptionFocusRect fropt; + fropt.rect = dial->rect; + fropt.state = dial->state; + fropt.palette = dial->palette; + if (fropt.state & QStyle::State_HasFocus) { + br.adjust(0, 0, 2, 2); + if (dial->subControls & SC_DialTickmarks) { + int r = qMin(width, height) / 2; + br.translate(-r / 6, - r / 6); + br.setWidth(br.width() + r / 3); + br.setHeight(br.height() + r / 3); + } + fropt.rect = br.adjusted(-2, -2, 2, 2); + proxy()->drawPrimitive(QStyle::PE_FrameFocusRect, &fropt, p); + } + p->restore(); + } + break; + case CC_GroupBox: + if (const QStyleOptionGroupBox *groupBox = qstyleoption_cast<const QStyleOptionGroupBox *>(opt)) { + // Draw frame + QRect textRect = proxy()->subControlRect(CC_GroupBox, opt, SC_GroupBoxLabel); + QRect checkBoxRect = proxy()->subControlRect(CC_GroupBox, opt, SC_GroupBoxCheckBox); + 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 = proxy()->subControlRect(CC_GroupBox, opt, SC_GroupBoxFrame); + 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); + proxy()->drawPrimitive(PE_FrameGroupBox, &frame, p); + 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 (!proxy()->styleHint(QStyle::SH_UnderlineShortcut, opt)) + alignment |= Qt::TextHideMnemonic; + + proxy()->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; + proxy()->drawPrimitive(PE_FrameFocusRect, &fropt, p); + } + } + + // Draw checkbox + if (groupBox->subControls & SC_GroupBoxCheckBox) { + QStyleOptionButton box; + box.QStyleOption::operator=(*groupBox); + box.rect = checkBoxRect; + proxy()->drawPrimitive(PE_IndicatorCheckBox, &box, p); + } + } + break; + case CC_MdiControls: + { + QStyleOptionButton btnOpt; + btnOpt.QStyleOption::operator=(*opt); + btnOpt.state &= ~State_MouseOver; + int bsx = 0; + int bsy = 0; + const int buttonIconMetric = proxy()->pixelMetric(PM_TitleBarButtonIconSize, &btnOpt); + const QSize buttonIconSize(buttonIconMetric, buttonIconMetric); + if (opt->subControls & QStyle::SC_MdiCloseButton) { + if (opt->activeSubControls & QStyle::SC_MdiCloseButton && (opt->state & State_Sunken)) { + btnOpt.state |= State_Sunken; + btnOpt.state &= ~State_Raised; + bsx = proxy()->pixelMetric(PM_ButtonShiftHorizontal); + bsy = proxy()->pixelMetric(PM_ButtonShiftVertical); + } else { + btnOpt.state |= State_Raised; + btnOpt.state &= ~State_Sunken; + bsx = 0; + bsy = 0; + } + btnOpt.rect = proxy()->subControlRect(CC_MdiControls, opt, SC_MdiCloseButton); + proxy()->drawPrimitive(PE_PanelButtonCommand, &btnOpt, p); + QPixmap pm = proxy()->standardIcon(SP_TitleBarCloseButton).pixmap(opt->window, buttonIconSize); + proxy()->drawItemPixmap(p, btnOpt.rect.translated(bsx, bsy), Qt::AlignCenter, pm); + } + if (opt->subControls & QStyle::SC_MdiNormalButton) { + if (opt->activeSubControls & QStyle::SC_MdiNormalButton && (opt->state & State_Sunken)) { + btnOpt.state |= State_Sunken; + btnOpt.state &= ~State_Raised; + bsx = proxy()->pixelMetric(PM_ButtonShiftHorizontal); + bsy = proxy()->pixelMetric(PM_ButtonShiftVertical); + } else { + btnOpt.state |= State_Raised; + btnOpt.state &= ~State_Sunken; + bsx = 0; + bsy = 0; + } + btnOpt.rect = proxy()->subControlRect(CC_MdiControls, opt, SC_MdiNormalButton); + proxy()->drawPrimitive(PE_PanelButtonCommand, &btnOpt, p); + QPixmap pm = proxy()->standardIcon(SP_TitleBarNormalButton).pixmap(opt->window, buttonIconSize); + proxy()->drawItemPixmap(p, btnOpt.rect.translated(bsx, bsy), Qt::AlignCenter, pm); + } + if (opt->subControls & QStyle::SC_MdiMinButton) { + if (opt->activeSubControls & QStyle::SC_MdiMinButton && (opt->state & State_Sunken)) { + btnOpt.state |= State_Sunken; + btnOpt.state &= ~State_Raised; + bsx = proxy()->pixelMetric(PM_ButtonShiftHorizontal); + bsy = proxy()->pixelMetric(PM_ButtonShiftVertical); + } else { + btnOpt.state |= State_Raised; + btnOpt.state &= ~State_Sunken; + bsx = 0; + bsy = 0; + } + btnOpt.rect = proxy()->subControlRect(CC_MdiControls, opt, SC_MdiMinButton); + proxy()->drawPrimitive(PE_PanelButtonCommand, &btnOpt, p); + QPixmap pm = proxy()->standardIcon(SP_TitleBarMinButton).pixmap(opt->window, buttonIconSize); + proxy()->drawItemPixmap(p, btnOpt.rect.translated(bsx, bsy), Qt::AlignCenter, pm); + } + } + break; + default: + qWarning("QCommonStyle::drawComplexControl: Control %d not handled", cc); + } +} + +/*! + \reimp +*/ +QStyle::SubControl QCommonStyle::hitTestComplexControl(ComplexControl cc, const QStyleOptionComplex *opt, const QPoint &pt) const +{ + SubControl sc = SC_None; + switch (cc) { + case CC_Slider: + if (const QStyleOptionSlider *slider = qstyleoption_cast<const QStyleOptionSlider *>(opt)) { + QRect r = proxy()->subControlRect(cc, slider, SC_SliderHandle); + if (r.isValid() && r.contains(pt)) { + sc = SC_SliderHandle; + } else { + r = proxy()->subControlRect(cc, slider, SC_SliderGroove); + if (r.isValid() && r.contains(pt)) + sc = SC_SliderGroove; + } + } + break; + case CC_ScrollBar: + if (const QStyleOptionSlider *scrollbar = qstyleoption_cast<const QStyleOptionSlider *>(opt)) { + QRect r; + uint ctrl = SC_ScrollBarAddLine; + while (ctrl <= SC_ScrollBarGroove) { + r = proxy()->subControlRect(cc, scrollbar, QStyle::SubControl(ctrl)); + if (r.isValid() && r.contains(pt)) { + sc = QStyle::SubControl(ctrl); + break; + } + ctrl <<= 1; + } + } + break; + case CC_ToolButton: + if (const QStyleOptionToolButton *toolbutton = qstyleoption_cast<const QStyleOptionToolButton *>(opt)) { + QRect r; + uint ctrl = SC_ToolButton; + while (ctrl <= SC_ToolButtonMenu) { + r = proxy()->subControlRect(cc, toolbutton, QStyle::SubControl(ctrl)); + if (r.isValid() && r.contains(pt)) { + sc = QStyle::SubControl(ctrl); + break; + } + ctrl <<= 1; + } + } + break; + case CC_SpinBox: + if (const QStyleOptionSpinBox *spinbox = qstyleoption_cast<const QStyleOptionSpinBox *>(opt)) { + QRect r; + uint ctrl = SC_SpinBoxUp; + while (ctrl <= SC_SpinBoxEditField) { + r = proxy()->subControlRect(cc, spinbox, QStyle::SubControl(ctrl)); + if (r.isValid() && r.contains(pt)) { + sc = QStyle::SubControl(ctrl); + break; + } + ctrl <<= 1; + } + } + break; + case CC_TitleBar: + if (const QStyleOptionTitleBar *tb = qstyleoption_cast<const QStyleOptionTitleBar *>(opt)) { + QRect r; + uint ctrl = SC_TitleBarSysMenu; + + while (ctrl <= SC_TitleBarLabel) { + r = proxy()->subControlRect(cc, tb, QStyle::SubControl(ctrl)); + if (r.isValid() && r.contains(pt)) { + sc = QStyle::SubControl(ctrl); + break; + } + ctrl <<= 1; + } + } + break; + case CC_ComboBox: + if (const QStyleOptionComboBox *cb = qstyleoption_cast<const QStyleOptionComboBox *>(opt)) { + QRect r; + uint ctrl = SC_ComboBoxArrow; // Start here and go down. + while (ctrl > 0) { + r = proxy()->subControlRect(cc, cb, QStyle::SubControl(ctrl)); + if (r.isValid() && r.contains(pt)) { + sc = QStyle::SubControl(ctrl); + break; + } + ctrl >>= 1; + } + } + break; + case CC_GroupBox: + if (const QStyleOptionGroupBox *groupBox = qstyleoption_cast<const QStyleOptionGroupBox *>(opt)) { + QRect r; + uint ctrl = SC_GroupBoxCheckBox; + while (ctrl <= SC_GroupBoxFrame) { + r = proxy()->subControlRect(cc, groupBox, QStyle::SubControl(ctrl)); + if (r.isValid() && r.contains(pt)) { + sc = QStyle::SubControl(ctrl); + break; + } + ctrl <<= 1; + } + } + break; + case CC_MdiControls: + { + QRect r; + uint ctrl = SC_MdiMinButton; + while (ctrl <= SC_MdiCloseButton) { + r = proxy()->subControlRect(CC_MdiControls, opt, QStyle::SubControl(ctrl)); + if (r.isValid() && r.contains(pt) && (opt->subControls & ctrl)) { + sc = QStyle::SubControl(ctrl); + return sc; + } + ctrl <<= 1; + } + } + break; + default: + qWarning("QCommonStyle::hitTestComplexControl: Case %d not handled", cc); + } + return sc; +} + +/*! + \reimp +*/ +QRect QCommonStyle::subControlRect(ComplexControl cc, const QStyleOptionComplex *opt, SubControl sc) const +{ + QRect ret; + switch (cc) { + case CC_Slider: + if (const QStyleOptionSlider *slider = qstyleoption_cast<const QStyleOptionSlider *>(opt)) { + int tickOffset = proxy()->pixelMetric(PM_SliderTickmarkOffset, slider); + int thickness = proxy()->pixelMetric(PM_SliderControlThickness, slider); + + switch (sc) { + case SC_SliderHandle: { + int sliderPos = 0; + int len = proxy()->pixelMetric(PM_SliderLength, slider); + bool horizontal = slider->orientation == Qt::Horizontal; + sliderPos = sliderPositionFromValue(slider->minimum, slider->maximum, + slider->sliderPosition, + (horizontal ? slider->rect.width() + : slider->rect.height()) - len, + slider->upsideDown); + if (horizontal) + ret.setRect(slider->rect.x() + sliderPos, slider->rect.y() + tickOffset, len, thickness); + else + ret.setRect(slider->rect.x() + tickOffset, slider->rect.y() + sliderPos, thickness, len); + break; } + case SC_SliderGroove: + if (slider->orientation == Qt::Horizontal) + ret.setRect(slider->rect.x(), slider->rect.y() + tickOffset, + slider->rect.width(), thickness); + else + ret.setRect(slider->rect.x() + tickOffset, slider->rect.y(), + thickness, slider->rect.height()); + break; + default: + break; + } + ret = visualRect(slider->direction, slider->rect, ret); + } + break; + case CC_ScrollBar: + if (const QStyleOptionSlider *scrollbar = qstyleoption_cast<const QStyleOptionSlider *>(opt)) { + const QRect scrollBarRect = scrollbar->rect; + int sbextent = 0; + if (!proxy()->styleHint(SH_ScrollBar_Transient, scrollbar)) + sbextent = proxy()->pixelMetric(PM_ScrollBarExtent, scrollbar); + int maxlen = ((scrollbar->orientation == Qt::Horizontal) ? + scrollBarRect.width() : scrollBarRect.height()) - (sbextent * 2); + int sliderlen; + + // calculate slider length + if (scrollbar->maximum != scrollbar->minimum) { + uint range = scrollbar->maximum - scrollbar->minimum; + sliderlen = (qint64(scrollbar->pageStep) * maxlen) / (range + scrollbar->pageStep); + + int slidermin = proxy()->pixelMetric(PM_ScrollBarSliderMin, scrollbar); + if (sliderlen < slidermin || range > INT_MAX / 2) + sliderlen = slidermin; + if (sliderlen > maxlen) + sliderlen = maxlen; + } else { + sliderlen = maxlen; + } + + int sliderstart = sbextent + sliderPositionFromValue(scrollbar->minimum, + scrollbar->maximum, + scrollbar->sliderPosition, + maxlen - sliderlen, + scrollbar->upsideDown); + + switch (sc) { + case SC_ScrollBarSubLine: // top/left button + if (scrollbar->orientation == Qt::Horizontal) { + int buttonWidth = qMin(scrollBarRect.width() / 2, sbextent); + ret.setRect(0, 0, buttonWidth, scrollBarRect.height()); + } else { + int buttonHeight = qMin(scrollBarRect.height() / 2, sbextent); + ret.setRect(0, 0, scrollBarRect.width(), buttonHeight); + } + break; + case SC_ScrollBarAddLine: // bottom/right button + if (scrollbar->orientation == Qt::Horizontal) { + int buttonWidth = qMin(scrollBarRect.width()/2, sbextent); + ret.setRect(scrollBarRect.width() - buttonWidth, 0, buttonWidth, scrollBarRect.height()); + } else { + int buttonHeight = qMin(scrollBarRect.height()/2, sbextent); + ret.setRect(0, scrollBarRect.height() - buttonHeight, scrollBarRect.width(), buttonHeight); + } + break; + case SC_ScrollBarSubPage: // between top/left button and slider + if (scrollbar->orientation == Qt::Horizontal) + ret.setRect(sbextent, 0, sliderstart - sbextent, scrollBarRect.height()); + else + ret.setRect(0, sbextent, scrollBarRect.width(), sliderstart - sbextent); + break; + case SC_ScrollBarAddPage: // between bottom/right button and slider + if (scrollbar->orientation == Qt::Horizontal) + ret.setRect(sliderstart + sliderlen, 0, + maxlen - sliderstart - sliderlen + sbextent, scrollBarRect.height()); + else + ret.setRect(0, sliderstart + sliderlen, scrollBarRect.width(), + maxlen - sliderstart - sliderlen + sbextent); + break; + case SC_ScrollBarGroove: + if (scrollbar->orientation == Qt::Horizontal) + ret.setRect(sbextent, 0, scrollBarRect.width() - sbextent * 2, + scrollBarRect.height()); + else + ret.setRect(0, sbextent, scrollBarRect.width(), + scrollBarRect.height() - sbextent * 2); + break; + case SC_ScrollBarSlider: + if (scrollbar->orientation == Qt::Horizontal) + ret.setRect(sliderstart, 0, sliderlen, scrollBarRect.height()); + else + ret.setRect(0, sliderstart, scrollBarRect.width(), sliderlen); + break; + default: + break; + } + ret = visualRect(scrollbar->direction, scrollBarRect, ret); + } + break; + case CC_SpinBox: + if (const QStyleOptionSpinBox *spinbox = qstyleoption_cast<const QStyleOptionSpinBox *>(opt)) { + QSize bs; + int fw = spinbox->frame ? proxy()->pixelMetric(PM_SpinBoxFrameWidth, spinbox) : 0; + bs.setHeight(qMax(8, spinbox->rect.height()/2 - fw)); + // 1.6 -approximate golden mean + bs.setWidth(qMax(16, qMin(bs.height() * 8 / 5, spinbox->rect.width() / 4))); + int y = fw + spinbox->rect.y(); + int x, lx, rx; + x = spinbox->rect.x() + spinbox->rect.width() - fw - bs.width(); + lx = fw; + rx = x - fw; + switch (sc) { + case SC_SpinBoxUp: + if (spinbox->buttonSymbols == QStyleOptionSpinBox::NoButtons) + return QRect(); + ret = QRect(x, y, bs.width(), bs.height()); + break; + case SC_SpinBoxDown: + if (spinbox->buttonSymbols == QStyleOptionSpinBox::NoButtons) + return QRect(); + + ret = QRect(x, y + bs.height(), bs.width(), bs.height()); + break; + case SC_SpinBoxEditField: + if (spinbox->buttonSymbols == QStyleOptionSpinBox::NoButtons) { + ret = QRect(lx, fw, spinbox->rect.width() - 2*fw, spinbox->rect.height() - 2*fw); + } else { + ret = QRect(lx, fw, rx, spinbox->rect.height() - 2*fw); + } + break; + case SC_SpinBoxFrame: + ret = spinbox->rect; + default: + break; + } + ret = visualRect(spinbox->direction, spinbox->rect, ret); + } + break; + case CC_ToolButton: + if (const QStyleOptionToolButton *tb = qstyleoption_cast<const QStyleOptionToolButton *>(opt)) { + int mbi = proxy()->pixelMetric(PM_MenuButtonIndicator, tb); + ret = tb->rect; + switch (sc) { + case SC_ToolButton: + if ((tb->features + & (QStyleOptionToolButton::MenuButtonPopup | QStyleOptionToolButton::PopupDelay)) + == QStyleOptionToolButton::MenuButtonPopup) + ret.adjust(0, 0, -mbi, 0); + break; + case SC_ToolButtonMenu: + if ((tb->features + & (QStyleOptionToolButton::MenuButtonPopup | QStyleOptionToolButton::PopupDelay)) + == QStyleOptionToolButton::MenuButtonPopup) + ret.adjust(ret.width() - mbi, 0, 0, 0); + break; + default: + break; + } + ret = visualRect(tb->direction, tb->rect, ret); + } + break; + case CC_ComboBox: + if (const QStyleOptionComboBox *cb = qstyleoption_cast<const QStyleOptionComboBox *>(opt)) { + const qreal dpi = QStyleHelper::dpi(opt); + const int x = cb->rect.x(), y = cb->rect.y(), wi = cb->rect.width(), he = cb->rect.height(); + const int margin = cb->frame ? qRound(QStyleHelper::dpiScaled(3, dpi)) : 0; + const int bmarg = cb->frame ? qRound(QStyleHelper::dpiScaled(2, dpi)) : 0; + const int xpos = x + wi - bmarg - qRound(QStyleHelper::dpiScaled(16, dpi)); + + + switch (sc) { + case SC_ComboBoxFrame: + ret = cb->rect; + break; + case SC_ComboBoxArrow: + ret.setRect(xpos, y + bmarg, qRound(QStyleHelper::dpiScaled(16, opt)), he - 2*bmarg); + break; + case SC_ComboBoxEditField: + ret.setRect(x + margin, y + margin, wi - 2 * margin - qRound(QStyleHelper::dpiScaled(16, dpi)), he - 2 * margin); + break; + case SC_ComboBoxListBoxPopup: + ret = cb->rect; + break; + default: + break; + } + ret = visualRect(cb->direction, cb->rect, ret); + } + break; + case CC_TitleBar: + if (const QStyleOptionTitleBar *tb = qstyleoption_cast<const QStyleOptionTitleBar *>(opt)) { + const int controlMargin = 2; + const int controlHeight = tb->rect.height() - controlMargin *2; + const int delta = controlHeight + controlMargin; + int offset = 0; + + bool isMinimized = tb->titleBarState & Qt::WindowMinimized; + bool isMaximized = tb->titleBarState & Qt::WindowMaximized; + + switch (sc) { + case SC_TitleBarLabel: + if (tb->titleBarFlags & (Qt::WindowTitleHint | Qt::WindowSystemMenuHint)) { + ret = tb->rect; + if (tb->titleBarFlags & Qt::WindowSystemMenuHint) + ret.adjust(delta, 0, -delta, 0); + if (tb->titleBarFlags & Qt::WindowMinimizeButtonHint) + ret.adjust(0, 0, -delta, 0); + if (tb->titleBarFlags & Qt::WindowMaximizeButtonHint) + ret.adjust(0, 0, -delta, 0); + if (tb->titleBarFlags & Qt::WindowShadeButtonHint) + ret.adjust(0, 0, -delta, 0); + if (tb->titleBarFlags & Qt::WindowContextHelpButtonHint) + ret.adjust(0, 0, -delta, 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 (sc == 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 (sc == SC_TitleBarNormalButton) + break; + Q_FALLTHROUGH(); + case SC_TitleBarMaxButton: + if (!isMaximized && (tb->titleBarFlags & Qt::WindowMaximizeButtonHint)) + offset += delta; + else if (sc == SC_TitleBarMaxButton) + break; + Q_FALLTHROUGH(); + case SC_TitleBarShadeButton: + if (!isMinimized && (tb->titleBarFlags & Qt::WindowShadeButtonHint)) + offset += delta; + else if (sc == SC_TitleBarShadeButton) + break; + Q_FALLTHROUGH(); + case SC_TitleBarUnshadeButton: + if (isMinimized && (tb->titleBarFlags & Qt::WindowShadeButtonHint)) + offset += delta; + else if (sc == SC_TitleBarUnshadeButton) + break; + Q_FALLTHROUGH(); + case SC_TitleBarCloseButton: + if (tb->titleBarFlags & Qt::WindowSystemMenuHint) + offset += delta; + else if (sc == SC_TitleBarCloseButton) + break; + ret.setRect(tb->rect.right() - offset, tb->rect.top() + controlMargin, + controlHeight, controlHeight); + break; + case SC_TitleBarSysMenu: + if (tb->titleBarFlags & Qt::WindowSystemMenuHint) { + ret.setRect(tb->rect.left() + controlMargin, tb->rect.top() + controlMargin, + controlHeight, controlHeight); + } + break; + default: + break; + } + ret = visualRect(tb->direction, tb->rect, ret); + } + break; + case CC_GroupBox: { + if (const QStyleOptionGroupBox *groupBox = qstyleoption_cast<const QStyleOptionGroupBox *>(opt)) { + switch (sc) { + case SC_GroupBoxFrame: + case SC_GroupBoxContents: { + int topMargin = 0; + int topHeight = 0; + int verticalAlignment = proxy()->styleHint(SH_GroupBox_TextLabelVerticalAlignment, groupBox); + bool hasCheckBox = groupBox->subControls & QStyle::SC_GroupBoxCheckBox; + if (groupBox->text.size() || hasCheckBox) { + int checkBoxHeight = hasCheckBox ? proxy()->pixelMetric(PM_IndicatorHeight, groupBox) : 0; + topHeight = qMax(groupBox->fontMetrics.height(), checkBoxHeight); + if (verticalAlignment & Qt::AlignVCenter) + topMargin = topHeight / 2; + else if (verticalAlignment & Qt::AlignTop) + topMargin = topHeight; + } + + QRect frameRect = groupBox->rect; + frameRect.setTop(topMargin); + + if (sc == SC_GroupBoxFrame) { + ret = frameRect; + break; + } + + int frameWidth = 0; + if ((groupBox->features & QStyleOptionFrame::Flat) == 0) + frameWidth = proxy()->pixelMetric(PM_DefaultFrameWidth, groupBox); + ret = frameRect.adjusted(frameWidth, frameWidth + topHeight - topMargin, + -frameWidth, -frameWidth); + break; + } + case SC_GroupBoxCheckBox: + case SC_GroupBoxLabel: { + QFontMetrics fontMetrics = groupBox->fontMetrics; + int th = fontMetrics.height(); + int tw = fontMetrics.size(Qt::TextShowMnemonic, groupBox->text + QLatin1Char(' ')).width(); + int marg = (groupBox->features & QStyleOptionFrame::Flat) ? 0 : 8; + ret = groupBox->rect.adjusted(marg, 0, -marg, 0); + + int indicatorWidth = proxy()->pixelMetric(PM_IndicatorWidth, opt); + int indicatorHeight = proxy()->pixelMetric(PM_IndicatorHeight, opt); + int indicatorSpace = proxy()->pixelMetric(PM_CheckBoxLabelSpacing, opt) - 1; + bool hasCheckBox = groupBox->subControls & QStyle::SC_GroupBoxCheckBox; + int checkBoxWidth = hasCheckBox ? (indicatorWidth + indicatorSpace) : 0; + int checkBoxHeight = hasCheckBox ? indicatorHeight : 0; + + int h = qMax(th, checkBoxHeight); + ret.setHeight(h); + + // Adjusted rect for label + indicatorWidth + indicatorSpace + QRect totalRect = alignedRect(groupBox->direction, groupBox->textAlignment, + QSize(tw + checkBoxWidth, h), ret); + + // Adjust totalRect if checkbox is set + if (hasCheckBox) { + bool ltr = groupBox->direction == Qt::LeftToRight; + int left = 0; + // Adjust for check box + if (sc == SC_GroupBoxCheckBox) { + left = ltr ? totalRect.left() : (totalRect.right() - indicatorWidth); + int top = totalRect.top() + (h - checkBoxHeight) / 2; + totalRect.setRect(left, top, indicatorWidth, indicatorHeight); + // Adjust for label + } else { + left = ltr ? (totalRect.left() + checkBoxWidth - 2) : totalRect.left(); + int top = totalRect.top() + (h - th) / 2; + totalRect.setRect(left, top, totalRect.width() - checkBoxWidth, th); + } + } + ret = totalRect; + break; + } + default: + break; + } + } + break; + } + case CC_MdiControls: + { + int numSubControls = 0; + if (opt->subControls & SC_MdiCloseButton) + ++numSubControls; + if (opt->subControls & SC_MdiMinButton) + ++numSubControls; + if (opt->subControls & SC_MdiNormalButton) + ++numSubControls; + if (numSubControls == 0) + break; + + int buttonWidth = opt->rect.width() / numSubControls - 1; + int offset = 0; + switch (sc) { + case SC_MdiCloseButton: + // Only one sub control, no offset needed. + if (numSubControls == 1) + break; + offset += buttonWidth + 2; + 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 && !(opt->subControls & SC_MdiMinButton))) + break; + if (opt->subControls & SC_MdiNormalButton) + offset += buttonWidth; + break; + default: + break; + } + + // Subtract one pixel if we only have one sub control. At this point + // buttonWidth is the actual width + 1 pixel margin, but we don't want the + // margin when there are no other controllers. + if (numSubControls == 1) + --buttonWidth; + ret = QRect(offset, 0, buttonWidth, opt->rect.height()); + break; } + default: + qWarning("QCommonStyle::subControlRect: Case %d not handled", cc); + } + + return ret; +} + +int QCommonStyle::pixelMetric(PixelMetric m, const QStyleOption *opt) const +{ + int ret; + + switch (m) { + case PM_FocusFrameVMargin: + case PM_FocusFrameHMargin: + ret = 2; + break; + case PM_MenuBarVMargin: + case PM_MenuBarHMargin: + ret = 0; + break; + case PM_DialogButtonsSeparator: + ret = int(QStyleHelper::dpiScaled(5, opt)); + break; + case PM_DialogButtonsButtonWidth: + ret = int(QStyleHelper::dpiScaled(70, opt)); + break; + case PM_DialogButtonsButtonHeight: + ret = int(QStyleHelper::dpiScaled(30, opt)); + break; + case PM_TitleBarHeight: { + if (const QStyleOptionTitleBar *tb = qstyleoption_cast<const QStyleOptionTitleBar *>(opt)) { + if ((tb->titleBarFlags & Qt::WindowType_Mask) == Qt::Tool) { + ret = qMax(opt->fontMetrics.height(), 16); + } else { + ret = qMax(opt->fontMetrics.height(), 18); + } + } else { + ret = int(QStyleHelper::dpiScaled(18., opt)); + } + + break; } + case PM_TitleBarButtonSize: + ret = int(QStyleHelper::dpiScaled(16., opt)); + break; + case PM_TitleBarButtonIconSize: + ret = int(QStyleHelper::dpiScaled(16., opt)); + break; + + case PM_ScrollBarSliderMin: + ret = int(QStyleHelper::dpiScaled(9., opt)); + break; + + case PM_ButtonMargin: + ret = int(QStyleHelper::dpiScaled(6., opt)); + break; + + case PM_DockWidgetTitleBarButtonMargin: + ret = int(QStyleHelper::dpiScaled(2., opt)); + break; + + case PM_ButtonDefaultIndicator: + ret = 0; + break; + + case PM_MenuButtonIndicator: + ret = int(QStyleHelper::dpiScaled(12, opt)); + break; + + case PM_ButtonShiftHorizontal: + case PM_ButtonShiftVertical: + + case PM_DefaultFrameWidth: + ret = 2; + break; + + case PM_ComboBoxFrameWidth: + case PM_SpinBoxFrameWidth: + case PM_MenuPanelWidth: + case PM_TabBarBaseOverlap: + case PM_TabBarBaseHeight: + ret = proxy()->pixelMetric(PM_DefaultFrameWidth, opt); + break; + case PM_MdiSubWindowFrameWidth: + ret = int(QStyleHelper::dpiScaled(4, opt)); + break; + case PM_MdiSubWindowMinimizedWidth: + ret = int(QStyleHelper::dpiScaled(196, opt)); + break; + case PM_ScrollBarExtent: + ret = int(QStyleHelper::dpiScaled(16, opt)); + break; + case PM_MaximumDragDistance: + ret = QGuiApplicationPrivate::platformTheme()->themeHint(QPlatformTheme::MaximumScrollBarDragDistance).toInt(); + break; + case PM_SliderThickness: + ret = int(QStyleHelper::dpiScaled(16, opt)); + break; + case PM_SliderTickmarkOffset: + if (const QStyleOptionSlider *sl = qstyleoption_cast<const QStyleOptionSlider *>(opt)) { + int space = (sl->orientation == Qt::Horizontal) ? sl->rect.height() + : sl->rect.width(); + int thickness = proxy()->pixelMetric(PM_SliderControlThickness, sl); + int ticks = sl->tickPosition; + + if (ticks == QStyleOptionSlider::TicksBothSides) + ret = (space - thickness) / 2; + else if (ticks == QStyleOptionSlider::TicksAbove) + ret = space - thickness; + else + ret = 0; + } else { + ret = 0; + } + break; + case PM_SliderSpaceAvailable: + if (const QStyleOptionSlider *sl = qstyleoption_cast<const QStyleOptionSlider *>(opt)) { + if (sl->orientation == Qt::Horizontal) + ret = sl->rect.width() - proxy()->pixelMetric(PM_SliderLength, sl); + else + ret = sl->rect.height() - proxy()->pixelMetric(PM_SliderLength, sl); + } else { + ret = 0; + } + break; + case PM_DockWidgetSeparatorExtent: + ret = int(QStyleHelper::dpiScaled(6, opt)); + break; + + case PM_DockWidgetHandleExtent: + ret = int(QStyleHelper::dpiScaled(8, opt)); + break; + case PM_DockWidgetTitleMargin: + ret = 0; + break; + case PM_DockWidgetFrameWidth: + ret = 1; + break; + case PM_SpinBoxSliderHeight: + case PM_MenuBarPanelWidth: + ret = 2; + break; + case PM_MenuBarItemSpacing: + ret = 0; + break; + case PM_ToolBarFrameWidth: + ret = 1; + break; + + case PM_ToolBarItemMargin: + ret = 0; + break; + + case PM_ToolBarItemSpacing: + ret = int(QStyleHelper::dpiScaled(4, opt)); + break; + + case PM_ToolBarHandleExtent: + ret = int(QStyleHelper::dpiScaled(8, opt)); + break; + + case PM_ToolBarSeparatorExtent: + ret = int(QStyleHelper::dpiScaled(6, opt)); + break; + + case PM_ToolBarExtensionExtent: + ret = int(QStyleHelper::dpiScaled(12, opt)); + break; + + case PM_TabBarTabOverlap: + ret = 3; + break; + + case PM_TabBarTabHSpace: + ret = int(QStyleHelper::dpiScaled(24, opt)); + break; + + case PM_TabBarTabShiftHorizontal: + ret = 0; + break; + + case PM_TabBarTabShiftVertical: + ret = 2; + break; + + case PM_TabBarTabVSpace: { + const QStyleOptionTab *tb = qstyleoption_cast<const QStyleOptionTab *>(opt); + if (tb && (tb->shape == QStyleOptionTab::RoundedNorth || tb->shape == QStyleOptionTab::RoundedSouth + || tb->shape == QStyleOptionTab::RoundedWest || tb->shape == QStyleOptionTab::RoundedEast)) + ret = 8; + else + if(tb && (tb->shape == QStyleOptionTab::TriangularWest || tb->shape == QStyleOptionTab::TriangularEast)) + ret = 3; + else + ret = 2; + break; } + + case PM_ProgressBarChunkWidth: + ret = 9; + break; + + case PM_IndicatorWidth: + ret = int(QStyleHelper::dpiScaled(13, opt)); + break; + + case PM_IndicatorHeight: + ret = int(QStyleHelper::dpiScaled(13, opt)); + break; + + case PM_ExclusiveIndicatorWidth: + ret = int(QStyleHelper::dpiScaled(12, opt)); + break; + + case PM_ExclusiveIndicatorHeight: + ret = int(QStyleHelper::dpiScaled(12, opt)); + break; + + case PM_MenuTearoffHeight: + ret = int(QStyleHelper::dpiScaled(10, opt)); + break; + + case PM_MenuScrollerHeight: + ret = int(QStyleHelper::dpiScaled(10, opt)); + break; + + case PM_MenuDesktopFrameWidth: + case PM_MenuHMargin: + case PM_MenuVMargin: + ret = 0; + break; + + case PM_HeaderMargin: + ret = int(QStyleHelper::dpiScaled(4, opt)); + break; + case PM_HeaderMarkSize: + ret = int(QStyleHelper::dpiScaled(16, opt)); + break; + case PM_HeaderGripMargin: + ret = int(QStyleHelper::dpiScaled(4, opt)); + break; + case PM_HeaderDefaultSectionSizeHorizontal: + ret = int(QStyleHelper::dpiScaled(100, opt)); + break; + case PM_HeaderDefaultSectionSizeVertical: + ret = int(QStyleHelper::dpiScaled(30, opt)); + break; + case PM_TabBarScrollButtonWidth: + ret = int(QStyleHelper::dpiScaled(16, opt)); + break; + case PM_LayoutLeftMargin: + case PM_LayoutTopMargin: + case PM_LayoutRightMargin: + case PM_LayoutBottomMargin: + { + bool isWindow = opt ? (opt->state & State_Window) : false; + ret = proxy()->pixelMetric(isWindow ? PM_DefaultTopLevelMargin : PM_DefaultChildMargin); + } + break; + case PM_LayoutHorizontalSpacing: + case PM_LayoutVerticalSpacing: + ret = proxy()->pixelMetric(PM_DefaultLayoutSpacing); + break; + + case PM_DefaultTopLevelMargin: + ret = int(QStyleHelper::dpiScaled(11, opt)); + break; + case PM_DefaultChildMargin: + ret = int(QStyleHelper::dpiScaled(9, opt)); + break; + case PM_DefaultLayoutSpacing: + ret = int(QStyleHelper::dpiScaled(6, opt)); + break; + + case PM_ToolBarIconSize: + ret = 0; + if (const QPlatformTheme *theme = QGuiApplicationPrivate::platformTheme()) + ret = theme->themeHint(QPlatformTheme::ToolBarIconSize).toInt(); + if (ret <= 0) + ret = int(QStyleHelper::dpiScaled(24, opt)); + break; + + case PM_TabBarIconSize: + case PM_ListViewIconSize: + ret = proxy()->pixelMetric(PM_SmallIconSize, opt); + break; + + case PM_ButtonIconSize: + case PM_SmallIconSize: + ret = int(QStyleHelper::dpiScaled(16, opt)); + break; + case PM_IconViewIconSize: + ret = proxy()->pixelMetric(PM_LargeIconSize, opt); + break; + + case PM_LargeIconSize: + ret = int(QStyleHelper::dpiScaled(32, opt)); + break; + + case PM_ToolTipLabelFrameWidth: + ret = 1; + break; + case PM_CheckBoxLabelSpacing: + case PM_RadioButtonLabelSpacing: + ret = int(QStyleHelper::dpiScaled(6, opt)); + break; + case PM_SizeGripSize: + ret = int(QStyleHelper::dpiScaled(13, opt)); + break; + case PM_MessageBoxIconSize: +#ifdef Q_OS_MAC + if (QGuiApplication::desktopSettingsAware()) { + ret = 64; // No DPI scaling, it's handled elsewhere. + } else +#endif + { + ret = int(QStyleHelper::dpiScaled(32, opt)); + } + break; + case PM_TextCursorWidth: + ret = QGuiApplicationPrivate::platformTheme()->themeHint(QPlatformTheme::TextCursorWidth).toInt(); + break; + case PM_TabBar_ScrollButtonOverlap: + ret = 1; + break; + case PM_TabCloseIndicatorWidth: + case PM_TabCloseIndicatorHeight: + ret = int(QStyleHelper::dpiScaled(16, opt)); + break; + case PM_ScrollView_ScrollBarSpacing: + ret = 2 * proxy()->pixelMetric(PM_DefaultFrameWidth, opt); + break; + case PM_ScrollView_ScrollBarOverlap: + ret = 0; + break; + case PM_SubMenuOverlap: + ret = -proxy()->pixelMetric(QStyle::PM_MenuPanelWidth, opt); + break; + case PM_TreeViewIndentation: + ret = int(QStyleHelper::dpiScaled(20, opt)); + break; + default: + ret = 0; + break; + } + + return ret; +} + +QSize QCommonStyle::sizeFromContents(ContentsType ct, const QStyleOption *opt, const QSize &csz) const +{ + Q_D(const QCommonStyle); + QSize sz(csz); + switch (ct) { + case CT_PushButton: + if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(opt)) { + int w = csz.width(), + h = csz.height(), + bm = proxy()->pixelMetric(PM_ButtonMargin, btn), + fw = proxy()->pixelMetric(PM_DefaultFrameWidth, btn) * 2; + w += bm + fw; + h += bm + fw; + if (btn->features & QStyleOptionButton::AutoDefaultButton){ + int dbw = proxy()->pixelMetric(PM_ButtonDefaultIndicator, btn) * 2; + w += dbw; + h += dbw; + } + sz = QSize(w, h); + } + break; + case CT_RadioButton: + case CT_CheckBox: + if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(opt)) { + bool isRadio = (ct == CT_RadioButton); + + int w = proxy()->pixelMetric(isRadio ? PM_ExclusiveIndicatorWidth + : PM_IndicatorWidth, btn); + int h = proxy()->pixelMetric(isRadio ? PM_ExclusiveIndicatorHeight + : PM_IndicatorHeight, btn); + + int margins = 0; + // we add 4 pixels for label margins + if (!btn->icon.isNull() || !btn->text.isEmpty()) + margins = 4 + proxy()->pixelMetric(isRadio ? PM_RadioButtonLabelSpacing + : PM_CheckBoxLabelSpacing, opt); + sz += QSize(w + margins, 4); + sz.setHeight(qMax(sz.height(), h)); + } + break; + case CT_MenuItem: + if (const QStyleOptionMenuItem *mi = qstyleoption_cast<const QStyleOptionMenuItem *>(opt)) { + bool checkable = mi->menuHasCheckableItems; + int maxpmw = mi->maxIconWidth; + int w = sz.width(), h = sz.height(); + if (mi->menuItemType == QStyleOptionMenuItem::Separator) { + w = 10; + h = 2; + } else { + h = mi->fontMetrics.height() + 8; + if (!mi->icon.isNull()) { + 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 (maxpmw > 0) + w += maxpmw + 6; + if (checkable && maxpmw < 20) + w += 20 - maxpmw; + if (checkable || maxpmw > 0) + w += 2; + w += 12; + sz = QSize(w, h); + } + break; + case CT_ToolButton: + sz = QSize(sz.width() + 6, sz.height() + 5); + break; + case CT_ComboBox: + if (const QStyleOptionComboBox *cmb = qstyleoption_cast<const QStyleOptionComboBox *>(opt)) { + int fw = cmb->frame ? proxy()->pixelMetric(PM_ComboBoxFrameWidth, opt) * 2 : 0; + const int textMargins = 2*(proxy()->pixelMetric(PM_FocusFrameHMargin) + 1); + // QItemDelegate::sizeHint expands the textMargins two times, thus the 2*textMargins... + int other = qMax(23, 2*textMargins + proxy()->pixelMetric(QStyle::PM_ScrollBarExtent, opt)); + sz = QSize(sz.width() + fw + other, sz.height() + fw); + } + break; + case CT_HeaderSection: + if (const QStyleOptionHeader *hdr = qstyleoption_cast<const QStyleOptionHeader *>(opt)) { + bool nullIcon = hdr->icon.isNull(); + int margin = proxy()->pixelMetric(QStyle::PM_HeaderMargin, hdr); + int iconSize = nullIcon ? 0 : proxy()->pixelMetric(QStyle::PM_SmallIconSize, hdr); + QSize txt = hdr->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 = proxy()->pixelMetric(QStyle::PM_HeaderMargin, hdr); + if (hdr->orientation == Qt::Horizontal) + sz.rwidth() += sz.height() + margin; + else + sz.rheight() += sz.width() + margin; + } + } + break; + case CT_TabWidget: + sz += QSize(4, 4); + break; + case CT_LineEdit: + if (const QStyleOptionFrame *f = qstyleoption_cast<const QStyleOptionFrame *>(opt)) { + const int borderSize = 2 * f->lineWidth; + sz += QSize(borderSize, borderSize); + const int minSize = 10; + if (sz.width() < minSize) + sz.rwidth() = minSize; + if (sz.height() < minSize) + sz.rheight() = minSize; + } + break; + case CT_GroupBox: + if (const QStyleOptionGroupBox *styleOpt = qstyleoption_cast<const QStyleOptionGroupBox *>(opt)) { + if (sz.isEmpty()) + sz = QSize(20, 20); + sz += QSize(styleOpt->features.testFlag(QStyleOptionFrame::Flat) ? 0 : 16, 0); + } + break; + case CT_MdiControls: + if (const QStyleOptionComplex *styleOpt = qstyleoption_cast<const QStyleOptionComplex *>(opt)) { + const int buttonSize = proxy()->pixelMetric(PM_TitleBarButtonSize, styleOpt); + int width = 1; + if (styleOpt->subControls & SC_MdiMinButton) + width += buttonSize + 1; + if (styleOpt->subControls & SC_MdiNormalButton) + width += buttonSize + 1; + if (styleOpt->subControls & SC_MdiCloseButton) + width += buttonSize + 1; + sz = QSize(width, buttonSize); + } else { + const int buttonSize = proxy()->pixelMetric(PM_TitleBarButtonSize, opt); + sz = QSize(1 + 3 * (buttonSize + 1), buttonSize); + } + break; + case CT_ItemViewItem: + if (const QStyleOptionViewItem *vopt = qstyleoption_cast<const QStyleOptionViewItem *>(opt)) { + QRect decorationRect, displayRect, checkRect; + d->viewItemLayout(vopt, &checkRect, &decorationRect, &displayRect, true); + sz = (decorationRect|displayRect|checkRect).size(); + if (decorationRect.isValid() && sz.height() == decorationRect.height()) + sz.rheight() += 2; // Prevent icons from overlapping. + } + break; + case CT_SpinBox: + if (const QStyleOptionSpinBox *vopt = qstyleoption_cast<const QStyleOptionSpinBox *>(opt)) { + // Add button + frame widths + const qreal dpi = QStyleHelper::dpi(opt); + const bool hasButtons = (vopt->buttonSymbols != QStyleOptionSpinBox::NoButtons); + const int buttonWidth = hasButtons ? qRound(QStyleHelper::dpiScaled(16, dpi)) : 0; + const int fw = vopt->frame ? proxy()->pixelMetric(PM_SpinBoxFrameWidth, vopt) : 0; + sz += QSize(buttonWidth + 2*fw, 2*fw); + } + break; + case CT_Slider: + if (const QStyleOptionSlider *option = qstyleoption_cast<const QStyleOptionSlider *>(opt)) + sz = subControlRect(QStyle::CC_Slider, option, QStyle::SC_SliderHandle).size(); + break; + case CT_Dial: + sz = QSize(20, 20); + case CT_Frame: + if (const QStyleOptionFrame *option = qstyleoption_cast<const QStyleOptionFrame *>(opt)) { + const int ninePatchSplit = 1; + int w = qMax(10, (option->lineWidth * 2) + ninePatchSplit); + sz = QSize(w, w); + } + break; + case CT_ProgressBar: + if (sz.isNull()) { + // Special case: return minimum nine patch image size + sz = QSize(10, 10); + } + break; + case CT_ScrollBar: + case CT_MenuBar: + case CT_Menu: + case CT_MenuBarItem: + case CT_TabBarTab: + // just return the contentsSize for now + Q_FALLTHROUGH(); + default: + break; + } + return sz; +} + +QFont QCommonStyle::font(QStyle::ControlElement element, const QStyle::State state) const +{ + Q_UNUSED(element); + Q_UNUSED(state); + return QGuiApplication::font(); +} + +QMargins QCommonStyle::ninePatchMargins(QStyle::ControlElement ce, const QStyleOption *opt, const QSize &imageSize) const +{ + // By default, we just divide the image at the center + int w = imageSize.width() / 2; + int h = imageSize.height() / 2; + return QMargins(w, h, w, h); +} + +QMargins QCommonStyle::ninePatchMargins(QStyle::ComplexControl cc, const QStyleOptionComplex *opt, const QSize &imageSize) const +{ + // By default, we just divide the image at the center + int w = imageSize.width() / 2; + int h = imageSize.height() / 2; + return QMargins(w, h, w, h); +} + +int QCommonStyle::styleHint(StyleHint sh, const QStyleOption *opt, QStyleHintReturn *hret) const +{ + int ret = 0; + + switch (sh) { + case SH_Menu_KeyboardSearch: + ret = false; + break; + case SH_Slider_AbsoluteSetButtons: + ret = Qt::MidButton; + break; + case SH_Slider_PageSetButtons: + ret = Qt::LeftButton; + break; + case SH_ScrollBar_ContextMenu: + ret = true; + break; + case SH_GroupBox_TextLabelVerticalAlignment: + ret = Qt::AlignVCenter; + break; + case SH_GroupBox_TextLabelColor: + ret = opt ? int(opt->palette.color(QPalette::Text).rgba()) : 0; + break; + + case SH_ListViewExpand_SelectMouseType: + case SH_TabBar_SelectMouseType: + ret = QEvent::MouseButtonPress; + break; + + case SH_TabBar_Alignment: + ret = Qt::AlignLeft; + break; + + case SH_Header_ArrowAlignment: + ret = Qt::AlignRight | Qt::AlignVCenter; + break; + + case SH_TitleBar_AutoRaise: + ret = false; + break; + + case SH_Menu_SubMenuPopupDelay: + ret = 256; + break; + + case SH_Menu_SloppySubMenus: + ret = true; + break; + + case SH_Menu_SubMenuUniDirection: + ret = false; + break; + case SH_Menu_SubMenuUniDirectionFailCount: + ret = 1; + break; + case SH_Menu_SubMenuSloppySelectOtherActions: + ret = true; + break; + case SH_Menu_SubMenuSloppyCloseTimeout: + ret = 1000; + break; + case SH_Menu_SubMenuResetWhenReenteringParent: + ret = false; + break; + case SH_Menu_SubMenuDontStartSloppyOnLeave: + ret = false; + break; + + case SH_ProgressDialog_TextLabelAlignment: + ret = Qt::AlignCenter; + break; + + case SH_BlinkCursorWhenTextSelected: +#if defined(Q_OS_DARWIN) + ret = 0; +#else + ret = 1; +#endif + break; + + case SH_Table_GridLineColor: + if (opt) + ret = opt->palette.color(QPalette::Mid).rgba(); + else + ret = -1; + break; + case SH_LineEdit_PasswordCharacter: { + const QPlatformTheme *theme = QGuiApplicationPrivate::platformTheme(); + const QPlatformTheme::ThemeHint hintType = QPlatformTheme::PasswordMaskCharacter; + const QVariant hint = theme ? theme->themeHint(hintType) : QPlatformTheme::defaultThemeHint(hintType); + ret = hint.toChar().unicode(); + break; + } + case SH_LineEdit_PasswordMaskDelay: + ret = QGuiApplicationPrivate::platformTheme()->themeHint(QPlatformTheme::PasswordMaskDelay).toInt(); + break; + case SH_ToolBox_SelectedPageTitleBold: + ret = 1; + break; + + case SH_UnderlineShortcut: + ret = 1; + break; + + case SH_SpinBox_ClickAutoRepeatRate: + ret = 150; + break; + + case SH_SpinBox_ClickAutoRepeatThreshold: + ret = 500; + break; + + case SH_SpinBox_KeyPressAutoRepeatRate: + ret = 75; + break; + + case SH_Menu_SelectionWrap: + ret = true; + break; + + case SH_Menu_FillScreenWithScroll: + ret = true; + break; + + case SH_ToolTipLabel_Opacity: + ret = 255; + break; + + case SH_Button_FocusPolicy: + ret = Qt::StrongFocus; + break; + + case SH_MessageBox_UseBorderForButtonSpacing: + ret = 0; + break; + + case SH_ToolButton_PopupDelay: + ret = 600; + break; + + case SH_FocusFrame_Mask: + ret = 1; + break; + case SH_RubberBand_Mask: + if (const QStyleOptionRubberBand *rbOpt = qstyleoption_cast<const QStyleOptionRubberBand *>(opt)) { + ret = 0; + if (rbOpt->shape == QStyleOptionRubberBand::Rectangle) { + ret = true; + if(QStyleHintReturnMask *mask = qstyleoption_cast<QStyleHintReturnMask*>(hret)) { + mask->region = opt->rect; + int margin = proxy()->pixelMetric(PM_DefaultFrameWidth) * 2; + mask->region -= opt->rect.adjusted(margin, margin, -margin, -margin); + } + } + } + break; + case SH_SpinControls_DisableOnBounds: + ret = 1; + break; + + case SH_Dial_BackgroundRole: + ret = QPalette::Window; + break; + + case SH_ComboBox_LayoutDirection: + ret = opt ? opt->direction : Qt::LeftToRight; + break; + + case SH_ItemView_EllipsisLocation: + ret = Qt::AlignTrailing; + break; + + case SH_ItemView_ShowDecorationSelected: + ret = false; + break; + + case SH_ItemView_ActivateItemOnSingleClick: + ret = 0; + if (const QPlatformTheme *theme = QGuiApplicationPrivate::platformTheme()) + ret = theme->themeHint(QPlatformTheme::ItemViewActivateItemOnSingleClick).toBool() ? 1 : 0; + break; + case SH_TitleBar_ModifyNotification: + ret = true; + break; + case SH_ScrollBar_RollBetweenButtons: + ret = false; + break; + case SH_TabBar_ElideMode: + ret = Qt::ElideNone; + break; + case SH_DialogButtonLayout: + if (const QPlatformTheme *theme = QGuiApplicationPrivate::platformTheme()) + ret = theme->themeHint(QPlatformTheme::DialogButtonBoxLayout).toInt(); + break; + case SH_ComboBox_PopupFrameStyle: + ret = QStyleOptionFrame::StyledPanel | QStyleOptionFrame::Plain; + break; + case SH_MessageBox_TextInteractionFlags: + ret = Qt::LinksAccessibleByMouse; + break; + case SH_DialogButtonBox_ButtonsHaveIcons: + ret = 0; + if (const QPlatformTheme *theme = QGuiApplicationPrivate::platformTheme()) + ret = theme->themeHint(QPlatformTheme::DialogButtonBoxButtonsHaveIcons).toBool() ? 1 : 0; + break; + case SH_SpellCheckUnderlineStyle: + ret = QTextCharFormat::WaveUnderline; + break; + case SH_MessageBox_CenterButtons: + ret = true; + break; + case SH_ItemView_MovementWithoutUpdatingSelection: + ret = true; + break; + case SH_FocusFrame_AboveWidget: + ret = false; + break; + case SH_TabWidget_DefaultTabPosition: + ret = QStyleOptionTabBarBase::North; + break; + case SH_ToolBar_Movable: + ret = true; + break; + case SH_TextControl_FocusIndicatorTextCharFormat: + ret = true; + if (QStyleHintReturnVariant *vret = qstyleoption_cast<QStyleHintReturnVariant*>(hret)) { + QPen outline(opt->palette.color(QPalette::Text), 1, Qt::DotLine); + QTextCharFormat fmt; + fmt.setProperty(QTextFormat::OutlinePen, outline); + vret->variant = fmt; + } + break; + case SH_WizardStyle: + break; + case SH_FormLayoutWrapPolicy: + break; + case SH_FormLayoutFieldGrowthPolicy: + break; + case SH_FormLayoutFormAlignment: + ret = Qt::AlignLeft | Qt::AlignTop; + break; + case SH_FormLayoutLabelAlignment: + ret = Qt::AlignLeft; + break; + case SH_ItemView_ArrowKeysNavigateIntoChildren: + ret = false; + break; + case SH_ItemView_DrawDelegateFrame: + ret = 0; + break; + case SH_TabBar_CloseButtonPosition: + ret = QStyleOptionTabBarBase::RightSide; + break; + case SH_TabBar_ChangeCurrentDelay: + ret = 500; + break; + case SH_DockWidget_ButtonsHaveFrame: + ret = true; + break; + case SH_ToolButtonStyle: + ret = 0; + if (const QPlatformTheme *theme = QGuiApplicationPrivate::platformTheme()) + ret = theme->themeHint(QPlatformTheme::ToolButtonStyle).toInt(); + break; + case SH_RequestSoftwareInputPanel: + ret = RSIP_OnMouseClick; + break; + case SH_ScrollBar_Transient: + ret = false; + break; + case SH_Menu_SupportsSections: + ret = false; + break; + case SH_ToolTip_WakeUpDelay: + ret = 700; + break; + case SH_ToolTip_FallAsleepDelay: + ret = 2000; + break; + case SH_Widget_Animate: + ret = true; + break; + case SH_Splitter_OpaqueResize: + ret = true; + break; + case SH_ItemView_ScrollMode: + ret = QStyleOptionViewItem::ScrollPerItem; + break; + case SH_TitleBar_ShowToolTipsOnButtons: + ret = true; + break; + case SH_Widget_Animation_Duration: + ret = styleHint(SH_Widget_Animate, opt, hret) ? 200 : 0; + break; + case SH_ComboBox_AllowWheelScrolling: + ret = true; + break; + case SH_SpinBox_ButtonsInsideFrame: + ret = true; + break; + case SH_SpinBox_StepModifier: + ret = Qt::ControlModifier; + break; + default: + ret = 0; + break; + } + + return ret; +} + +static QPixmap cachedPixmapFromXPM(const char * const *xpm) +{ + QPixmap result; + const QString tag = QString::asprintf("xpm:0x%p", static_cast<const void*>(xpm)); + if (!QPixmapCache::find(tag, &result)) { + result = QPixmap(xpm); + QPixmapCache::insert(tag, result); + } + return result; +} + +static inline QPixmap titleBarMenuCachedPixmapFromXPM() { return cachedPixmapFromXPM(qt_menu_xpm); } + +static inline QString clearText16IconPath() +{ + return QStringLiteral(":/qt-project.org/styles/commonstyle/images/cleartext-16.png"); +} + +#if defined(Q_OS_WIN) || QT_CONFIG(imageformat_png) +static QIcon clearTextIcon(bool rtl) +{ + const QString directionalThemeName = rtl + ? QStringLiteral("edit-clear-locationbar-ltr") : QStringLiteral("edit-clear-locationbar-rtl"); + if (QIcon::hasThemeIcon(directionalThemeName)) + return QIcon::fromTheme(directionalThemeName); + const QString themeName = QStringLiteral("edit-clear"); + if (QIcon::hasThemeIcon(themeName)) + return QIcon::fromTheme(themeName); + + QIcon icon; +#ifndef QT_NO_IMAGEFORMAT_PNG + QPixmap clearText16(clearText16IconPath()); + Q_ASSERT(!clearText16.size().isEmpty()); + icon.addPixmap(clearText16); + QPixmap clearText32(QStringLiteral(":/qt-project.org/styles/commonstyle/images/cleartext-32.png")); + Q_ASSERT(!clearText32.size().isEmpty()); + icon.addPixmap(clearText32); + clearText32.setDevicePixelRatio(2); // The 32x32 pixmap can also be used for 16x16/devicePixelRatio=2 + icon.addPixmap(clearText32); +#endif // !QT_NO_IMAGEFORMAT_PNG + return icon; +} +#endif + +QPixmap QCommonStyle::standardPixmap(StandardPixmap sp, const QStyleOption *option) const +{ + const bool rtl = (option && option->direction == Qt::RightToLeft) || (!option && QGuiApplication::isRightToLeft()); +#ifdef QT_NO_IMAGEFORMAT_PNG + Q_UNUSED(widget); + Q_UNUSED(sp); +#else + QPixmap pixmap; + + if (QGuiApplication::desktopSettingsAware() && !QIcon::themeName().isEmpty()) { + switch (sp) { + case SP_DialogYesButton: + case SP_DialogOkButton: + pixmap = QIcon::fromTheme(QLatin1String("dialog-ok")).pixmap(16); + break; + case SP_DialogApplyButton: + pixmap = QIcon::fromTheme(QLatin1String("dialog-ok-apply")).pixmap(16); + break; + case SP_DialogDiscardButton: + pixmap = QIcon::fromTheme(QLatin1String("edit-delete")).pixmap(16); + break; + case SP_DialogCloseButton: + pixmap = QIcon::fromTheme(QLatin1String("dialog-close")).pixmap(16); + break; + case SP_DirHomeIcon: + pixmap = QIcon::fromTheme(QLatin1String("user-home")).pixmap(16); + break; + case SP_MessageBoxInformation: + pixmap = QIcon::fromTheme(QLatin1String("messagebox_info")).pixmap(16); + break; + case SP_MessageBoxWarning: + pixmap = QIcon::fromTheme(QLatin1String("messagebox_warning")).pixmap(16); + break; + case SP_MessageBoxCritical: + pixmap = QIcon::fromTheme(QLatin1String("messagebox_critical")).pixmap(16); + break; + case SP_MessageBoxQuestion: + pixmap = QIcon::fromTheme(QLatin1String("help")).pixmap(16); + break; + case SP_DialogOpenButton: + case SP_DirOpenIcon: + pixmap = QIcon::fromTheme(QLatin1String("folder-open")).pixmap(16); + break; + case SP_FileIcon: + pixmap = QIcon::fromTheme(QLatin1String("text-x-generic"), + QIcon::fromTheme(QLatin1String("empty"))).pixmap(16); + break; + case SP_DirClosedIcon: + case SP_DirIcon: + pixmap = QIcon::fromTheme(QLatin1String("folder")).pixmap(16); + break; + case SP_DriveFDIcon: + pixmap = QIcon::fromTheme(QLatin1String("media-floppy"), + QIcon::fromTheme(QLatin1String("3floppy_unmount"))).pixmap(16); + break; + case SP_ComputerIcon: + pixmap = QIcon::fromTheme(QLatin1String("computer"), + QIcon::fromTheme(QLatin1String("system"))).pixmap(16); + break; + case SP_DesktopIcon: + pixmap = QIcon::fromTheme(QLatin1String("user-desktop"), + QIcon::fromTheme(QLatin1String("desktop"))).pixmap(16); + break; + case SP_TrashIcon: + pixmap = QIcon::fromTheme(QLatin1String("user-trash"), + QIcon::fromTheme(QLatin1String("trashcan_empty"))).pixmap(16); + break; + case SP_DriveCDIcon: + case SP_DriveDVDIcon: + pixmap = QIcon::fromTheme(QLatin1String("media-optical"), + QIcon::fromTheme(QLatin1String("cdrom_unmount"))).pixmap(16); + break; + case SP_DriveHDIcon: + pixmap = QIcon::fromTheme(QLatin1String("drive-harddisk"), + QIcon::fromTheme(QLatin1String("hdd_unmount"))).pixmap(16); + break; + case SP_FileDialogToParent: + pixmap = QIcon::fromTheme(QLatin1String("go-up"), + QIcon::fromTheme(QLatin1String("up"))).pixmap(16); + break; + case SP_FileDialogNewFolder: + pixmap = QIcon::fromTheme(QLatin1String("folder_new")).pixmap(16); + break; + case SP_ArrowUp: + pixmap = QIcon::fromTheme(QLatin1String("go-up"), + QIcon::fromTheme(QLatin1String("up"))).pixmap(16); + break; + case SP_ArrowDown: + pixmap = QIcon::fromTheme(QLatin1String("go-down"), + QIcon::fromTheme(QLatin1String("down"))).pixmap(16); + break; + case SP_ArrowRight: + pixmap = QIcon::fromTheme(QLatin1String("go-next"), + QIcon::fromTheme(QLatin1String("forward"))).pixmap(16); + break; + case SP_ArrowLeft: + pixmap = QIcon::fromTheme(QLatin1String("go-previous"), + QIcon::fromTheme(QLatin1String("back"))).pixmap(16); + break; + case SP_FileDialogDetailedView: + pixmap = QIcon::fromTheme(QLatin1String("view_detailed")).pixmap(16); + break; + case SP_FileDialogListView: + pixmap = QIcon::fromTheme(QLatin1String("view_icon")).pixmap(16); + break; + case SP_BrowserReload: + pixmap = QIcon::fromTheme(QLatin1String("reload")).pixmap(16); + break; + case SP_BrowserStop: + pixmap = QIcon::fromTheme(QLatin1String("process-stop")).pixmap(16); + break; + case SP_MediaPlay: + pixmap = QIcon::fromTheme(QLatin1String("media-playback-start")).pixmap(16); + break; + case SP_MediaPause: + pixmap = QIcon::fromTheme(QLatin1String("media-playback-pause")).pixmap(16); + break; + case SP_MediaStop: + pixmap = QIcon::fromTheme(QLatin1String("media-playback-stop")).pixmap(16); + break; + case SP_MediaSeekForward: + pixmap = QIcon::fromTheme(QLatin1String("media-seek-forward")).pixmap(16); + break; + case SP_MediaSeekBackward: + pixmap = QIcon::fromTheme(QLatin1String("media-seek-backward")).pixmap(16); + break; + case SP_MediaSkipForward: + pixmap = QIcon::fromTheme(QLatin1String("media-skip-forward")).pixmap(16); + break; + case SP_MediaSkipBackward: + pixmap = QIcon::fromTheme(QLatin1String("media-skip-backward")).pixmap(16); + break; + case SP_DialogResetButton: + pixmap = QIcon::fromTheme(QLatin1String("edit-clear")).pixmap(24); + break; + case SP_DialogHelpButton: + pixmap = QIcon::fromTheme(QLatin1String("help-contents")).pixmap(24); + break; + case SP_DialogNoButton: + case SP_DialogCancelButton: + pixmap = QIcon::fromTheme(QLatin1String("dialog-cancel"), + QIcon::fromTheme(QLatin1String("process-stop"))).pixmap(24); + break; + case SP_DialogSaveButton: + pixmap = QIcon::fromTheme(QLatin1String("document-save")).pixmap(24); + break; + case SP_FileLinkIcon: + pixmap = QIcon::fromTheme(QLatin1String("emblem-symbolic-link")).pixmap(16); + if (!pixmap.isNull()) { + QPixmap fileIcon = QIcon::fromTheme(QLatin1String("text-x-generic")).pixmap(16); + if (fileIcon.isNull()) + fileIcon = QIcon::fromTheme(QLatin1String("empty")).pixmap(16); + if (!fileIcon.isNull()) { + QPainter painter(&fileIcon); + painter.drawPixmap(0, 0, 16, 16, pixmap); + return fileIcon; + } + } + break; + case SP_DirLinkIcon: + pixmap = QIcon::fromTheme(QLatin1String("emblem-symbolic-link")).pixmap(16); + if (!pixmap.isNull()) { + QPixmap dirIcon = QIcon::fromTheme(QLatin1String("folder")).pixmap(16); + if (!dirIcon.isNull()) { + QPainter painter(&dirIcon); + painter.drawPixmap(0, 0, 16, 16, pixmap); + return dirIcon; + } + } + break; + case SP_LineEditClearButton: + pixmap = clearTextIcon(rtl).pixmap(16); + break; + default: + break; + } + } + + if (!pixmap.isNull()) + return pixmap; +#endif //QT_NO_IMAGEFORMAT_PNG + switch (sp) { +#ifndef QT_NO_IMAGEFORMAT_XPM + case SP_ToolBarHorizontalExtensionButton: + if (rtl) { + QImage im(tb_extension_arrow_h_xpm); + im = im.convertToFormat(QImage::Format_ARGB32).mirrored(true, false); + return QPixmap::fromImage(im); + } + return cachedPixmapFromXPM(tb_extension_arrow_h_xpm); + case SP_ToolBarVerticalExtensionButton: + return cachedPixmapFromXPM(tb_extension_arrow_v_xpm); + case SP_FileDialogStart: + return cachedPixmapFromXPM(filedialog_start_xpm); + case SP_FileDialogEnd: + return cachedPixmapFromXPM(filedialog_end_xpm); +#endif +#ifndef QT_NO_IMAGEFORMAT_PNG + case SP_CommandLink: + case SP_ArrowForward: + if (rtl) + return proxy()->standardPixmap(SP_ArrowLeft, option); + return proxy()->standardPixmap(SP_ArrowRight, option); + case SP_ArrowBack: + if (rtl) + return proxy()->standardPixmap(SP_ArrowRight, option); + return proxy()->standardPixmap(SP_ArrowLeft, option); + case SP_ArrowLeft: + return QPixmap(QLatin1String(":/qt-project.org/styles/commonstyle/images/left-16.png")); + case SP_ArrowRight: + return QPixmap(QLatin1String(":/qt-project.org/styles/commonstyle/images/right-16.png")); + case SP_ArrowUp: + return QPixmap(QLatin1String(":/qt-project.org/styles/commonstyle/images/up-16.png")); + case SP_ArrowDown: + return QPixmap(QLatin1String(":/qt-project.org/styles/commonstyle/images/down-16.png")); + case SP_FileDialogToParent: + return proxy()->standardPixmap(SP_ArrowUp, option); + case SP_FileDialogNewFolder: + return QPixmap(QLatin1String(":/qt-project.org/styles/commonstyle/images/newdirectory-16.png")); + case SP_FileDialogDetailedView: + return QPixmap(QLatin1String(":/qt-project.org/styles/commonstyle/images/viewdetailed-16.png")); + case SP_FileDialogInfoView: + return QPixmap(QLatin1String(":/qt-project.org/styles/commonstyle/images/fileinfo-16.png")); + case SP_FileDialogContentsView: + return QPixmap(QLatin1String(":/qt-project.org/styles/commonstyle/images/filecontents-16.png")); + case SP_FileDialogListView: + return QPixmap(QLatin1String(":/qt-project.org/styles/commonstyle/images/viewlist-16.png")); + case SP_FileDialogBack: + return proxy()->standardPixmap(SP_ArrowBack, option); + case SP_DriveHDIcon: + return QPixmap(QLatin1String(":/qt-project.org/styles/commonstyle/images/harddrive-16.png")); + case SP_TrashIcon: + return QPixmap(QLatin1String(":/qt-project.org/styles/commonstyle/images/trash-16.png")); + case SP_DriveFDIcon: + return QPixmap(QLatin1String(":/qt-project.org/styles/commonstyle/images/floppy-16.png")); + case SP_DriveNetIcon: + return QPixmap(QLatin1String(":/qt-project.org/styles/commonstyle/images/networkdrive-16.png")); + case SP_DesktopIcon: + return QPixmap(QLatin1String(":/qt-project.org/styles/commonstyle/images/desktop-16.png")); + case SP_ComputerIcon: + return QPixmap(QLatin1String(":/qt-project.org/styles/commonstyle/images/computer-16.png")); + case SP_DriveCDIcon: + return QPixmap(QLatin1String(":/qt-project.org/styles/commonstyle/images/cdr-16.png")); + case SP_DriveDVDIcon: + return QPixmap(QLatin1String(":/qt-project.org/styles/commonstyle/images/dvd-16.png")); + case SP_DirHomeIcon: + case SP_DirOpenIcon: + return QPixmap(QLatin1String(":/qt-project.org/styles/commonstyle/images/diropen-16.png")); + case SP_DirIcon: + case SP_DirClosedIcon: + return QPixmap(QLatin1String(":/qt-project.org/styles/commonstyle/images/dirclosed-16.png")); + case SP_DirLinkIcon: + return QPixmap(QLatin1String(":/qt-project.org/styles/commonstyle/images/dirlink-16.png")); + case SP_FileIcon: + return QPixmap(QLatin1String(":/qt-project.org/styles/commonstyle/images/file-16.png")); + case SP_FileLinkIcon: + return QPixmap(QLatin1String(":/qt-project.org/styles/commonstyle/images/filelink-16.png")); + case SP_DialogOkButton: + return QPixmap(QLatin1String(":/qt-project.org/styles/commonstyle/images/standardbutton-ok-16.png")); + case SP_DialogCancelButton: + return QPixmap(QLatin1String(":/qt-project.org/styles/commonstyle/images/standardbutton-cancel-16.png")); + case SP_DialogHelpButton: + return QPixmap(QLatin1String(":/qt-project.org/styles/commonstyle/images/standardbutton-help-16.png")); + case SP_DialogOpenButton: + return QPixmap(QLatin1String(":/qt-project.org/styles/commonstyle/images/standardbutton-open-16.png")); + case SP_DialogSaveButton: + return QPixmap(QLatin1String(":/qt-project.org/styles/commonstyle/images/standardbutton-save-16.png")); + case SP_DialogCloseButton: + return QPixmap(QLatin1String(":/qt-project.org/styles/commonstyle/images/standardbutton-close-16.png")); + case SP_DialogApplyButton: + return QPixmap(QLatin1String(":/qt-project.org/styles/commonstyle/images/standardbutton-apply-16.png")); + case SP_DialogResetButton: + return QPixmap(QLatin1String(":/qt-project.org/styles/commonstyle/images/standardbutton-clear-16.png")); + case SP_DialogDiscardButton: + return QPixmap(QLatin1String(":/qt-project.org/styles/commonstyle/images/standardbutton-delete-16.png")); + case SP_DialogYesButton: + return QPixmap(QLatin1String(":/qt-project.org/styles/commonstyle/images/standardbutton-yes-16.png")); + case SP_DialogNoButton: + return QPixmap(QLatin1String(":/qt-project.org/styles/commonstyle/images/standardbutton-no-16.png")); + case SP_BrowserReload: + return QPixmap(QLatin1String(":/qt-project.org/styles/commonstyle/images/refresh-24.png")); + case SP_BrowserStop: + return QPixmap(QLatin1String(":/qt-project.org/styles/commonstyle/images/stop-24.png")); + case SP_MediaPlay: + return QPixmap(QLatin1String(":/qt-project.org/styles/commonstyle/images/media-play-32.png")); + case SP_MediaPause: + return QPixmap(QLatin1String(":/qt-project.org/styles/commonstyle/images/media-pause-32.png")); + case SP_MediaStop: + return QPixmap(QLatin1String(":/qt-project.org/styles/commonstyle/images/media-stop-32.png")); + case SP_MediaSeekForward: + return QPixmap(QLatin1String(":/qt-project.org/styles/commonstyle/images/media-seek-forward-32.png")); + case SP_MediaSeekBackward: + return QPixmap(QLatin1String(":/qt-project.org/styles/commonstyle/images/media-seek-backward-32.png")); + case SP_MediaSkipForward: + return QPixmap(QLatin1String(":/qt-project.org/styles/commonstyle/images/media-skip-forward-32.png")); + case SP_MediaSkipBackward: + return QPixmap(QLatin1String(":/qt-project.org/styles/commonstyle/images/media-skip-backward-32.png")); + case SP_MediaVolume: + return QPixmap(QLatin1String(":/qt-project.org/styles/commonstyle/images/media-volume-16.png")); + case SP_MediaVolumeMuted: + return QPixmap(QLatin1String(":/qt-project.org/styles/commonstyle/images/media-volume-muted-16.png")); + case SP_LineEditClearButton: + return QPixmap(clearText16IconPath()); +#endif // QT_NO_IMAGEFORMAT_PNG + default: + break; + } + +#ifndef QT_NO_IMAGEFORMAT_XPM + switch (sp) { + case SP_TitleBarMenuButton: + return titleBarMenuCachedPixmapFromXPM(); + case SP_TitleBarShadeButton: + return cachedPixmapFromXPM(qt_shade_xpm); + case SP_TitleBarUnshadeButton: + return cachedPixmapFromXPM(qt_unshade_xpm); + case SP_TitleBarNormalButton: + return cachedPixmapFromXPM(qt_normalizeup_xpm); + case SP_TitleBarMinButton: + return cachedPixmapFromXPM(qt_minimize_xpm); + case SP_TitleBarMaxButton: + return cachedPixmapFromXPM(qt_maximize_xpm); + case SP_TitleBarCloseButton: + return cachedPixmapFromXPM(qt_close_xpm); + case SP_TitleBarContextHelpButton: + return cachedPixmapFromXPM(qt_help_xpm); + case SP_DockWidgetCloseButton: + return cachedPixmapFromXPM(dock_widget_close_xpm); + case SP_MessageBoxInformation: + return cachedPixmapFromXPM(information_xpm); + case SP_MessageBoxWarning: + return cachedPixmapFromXPM(warning_xpm); + case SP_MessageBoxCritical: + return cachedPixmapFromXPM(critical_xpm); + case SP_MessageBoxQuestion: + return cachedPixmapFromXPM(question_xpm); + default: + break; + } +#endif //QT_NO_IMAGEFORMAT_XPM + +#if !QT_CONFIG(imageformat_png) && !QT_CONFIG(imageformat_xpm) && !QT_CONFIG(imageformat_png) + Q_UNUSED(rtl); +#endif + + return QPixmap(); +} + +#if QT_CONFIG(imageformat_png) +static inline QString iconResourcePrefix() { return QStringLiteral(":/qt-project.org/styles/commonstyle/images/"); } +static inline QString iconPngSuffix() { return QStringLiteral(".png"); } + +static void addIconFiles(const QString &prefix, const int sizes[], size_t count, QIcon &icon) +{ + for (size_t i = 0; i < count; ++i) + icon.addFile(prefix + QString::number(sizes[i]) + iconPngSuffix()); +} + +static const int dockTitleIconSizes[] = {10, 16, 20, 32, 48, 64}; +static const int titleBarSizes[] = {16, 32, 48}; +static const int toolBarExtHSizes[] = {8, 16, 32}; +static const int toolBarExtVSizes[] = {5, 10, 20}; +#endif // imageformat_png + +/*! + \internal +*/ +QIcon QCommonStyle::standardIcon(StandardPixmap standardIcon, const QStyleOption *option) const +{ + QIcon icon; + const bool rtl = (option && option->direction == Qt::RightToLeft) || (!option && QGuiApplication::isRightToLeft()); + +#ifdef Q_OS_WIN + switch (standardIcon) { + case SP_DriveCDIcon: + case SP_DriveDVDIcon: + case SP_DriveNetIcon: + case SP_DriveHDIcon: + case SP_DriveFDIcon: + case SP_FileIcon: + case SP_FileLinkIcon: + case SP_DesktopIcon: + case SP_ComputerIcon: + case SP_VistaShield: + case SP_MessageBoxInformation: + case SP_MessageBoxWarning: + case SP_MessageBoxCritical: + case SP_MessageBoxQuestion: + if (const QPlatformTheme *theme = QGuiApplicationPrivate::platformTheme()) { + QPlatformTheme::StandardPixmap sp = static_cast<QPlatformTheme::StandardPixmap>(standardIcon); + for (int size = 16 ; size <= 32 ; size += 16) { + QPixmap pixmap = theme->standardPixmap(sp, QSizeF(size, size)); + icon.addPixmap(pixmap, QIcon::Normal); + } + } + break; + case SP_DirIcon: + case SP_DirLinkIcon: + if (const QPlatformTheme *theme = QGuiApplicationPrivate::platformTheme()) { + QPlatformTheme::StandardPixmap spOff = static_cast<QPlatformTheme::StandardPixmap>(standardIcon); + QPlatformTheme::StandardPixmap spOn = standardIcon == SP_DirIcon ? QPlatformTheme::DirOpenIcon : + QPlatformTheme::DirLinkOpenIcon; + for (int size = 16 ; size <= 32 ; size += 16) { + QSizeF pixSize(size, size); + QPixmap pixmap = theme->standardPixmap(spOff, pixSize); + icon.addPixmap(pixmap, QIcon::Normal, QIcon::Off); + pixmap = theme->standardPixmap(spOn, pixSize); + icon.addPixmap(pixmap, QIcon::Normal, QIcon::On); + } + } + break; + case SP_LineEditClearButton: + icon = clearTextIcon(rtl); + break; + default: + break; + } + if (!icon.isNull()) + return icon; + +#endif + + if (QGuiApplication::desktopSettingsAware() && !QIcon::themeName().isEmpty()) { + switch (standardIcon) { + case SP_DirHomeIcon: + icon = QIcon::fromTheme(QLatin1String("user-home")); + break; + case SP_MessageBoxInformation: + icon = QIcon::fromTheme(QLatin1String("dialog-information")); + break; + case SP_MessageBoxWarning: + icon = QIcon::fromTheme(QLatin1String("dialog-warning")); + break; + case SP_MessageBoxCritical: + icon = QIcon::fromTheme(QLatin1String("dialog-error")); + break; + case SP_MessageBoxQuestion: + icon = QIcon::fromTheme(QLatin1String("dialog-question")); + break; + case SP_DialogOpenButton: + case SP_DirOpenIcon: + icon = QIcon::fromTheme(QLatin1String("folder-open")); + break; + case SP_DialogSaveButton: + icon = QIcon::fromTheme(QLatin1String("document-save")); + break; + case SP_DialogApplyButton: + icon = QIcon::fromTheme(QLatin1String("dialog-ok-apply")); + break; + case SP_DialogYesButton: + case SP_DialogOkButton: + icon = QIcon::fromTheme(QLatin1String("dialog-ok")); + break; + case SP_DialogDiscardButton: + icon = QIcon::fromTheme(QLatin1String("edit-delete")); + break; + case SP_DialogResetButton: + icon = QIcon::fromTheme(QLatin1String("edit-clear")); + break; + case SP_DialogHelpButton: + icon = QIcon::fromTheme(QLatin1String("help-contents")); + break; + case SP_FileIcon: + icon = QIcon::fromTheme(QLatin1String("text-x-generic")); + break; + case SP_DirClosedIcon: + case SP_DirIcon: + icon = QIcon::fromTheme(QLatin1String("folder")); + break; + case SP_DriveFDIcon: + icon = QIcon::fromTheme(QLatin1String("floppy_unmount")); + break; + case SP_ComputerIcon: + icon = QIcon::fromTheme(QLatin1String("computer"), + QIcon::fromTheme(QLatin1String("system"))); + break; + case SP_DesktopIcon: + icon = QIcon::fromTheme(QLatin1String("user-desktop")); + break; + case SP_TrashIcon: + icon = QIcon::fromTheme(QLatin1String("user-trash")); + break; + case SP_DriveCDIcon: + case SP_DriveDVDIcon: + icon = QIcon::fromTheme(QLatin1String("media-optical")); + break; + case SP_DriveHDIcon: + icon = QIcon::fromTheme(QLatin1String("drive-harddisk")); + break; + case SP_FileDialogToParent: + icon = QIcon::fromTheme(QLatin1String("go-up")); + break; + case SP_FileDialogNewFolder: + icon = QIcon::fromTheme(QLatin1String("folder-new")); + break; + case SP_ArrowUp: + icon = QIcon::fromTheme(QLatin1String("go-up")); + break; + case SP_ArrowDown: + icon = QIcon::fromTheme(QLatin1String("go-down")); + break; + case SP_ArrowRight: + icon = QIcon::fromTheme(QLatin1String("go-next")); + break; + case SP_ArrowLeft: + icon = QIcon::fromTheme(QLatin1String("go-previous")); + break; + case SP_DialogNoButton: + case SP_DialogCancelButton: + icon = QIcon::fromTheme(QLatin1String("dialog-cancel"), + QIcon::fromTheme(QLatin1String("process-stop"))); + break; + case SP_DialogCloseButton: + icon = QIcon::fromTheme(QLatin1String("window-close")); + break; + case SP_FileDialogDetailedView: + icon = QIcon::fromTheme(QLatin1String("view-list-details")); + break; + case SP_FileDialogListView: + icon = QIcon::fromTheme(QLatin1String("view-list-icons")); + break; + case SP_BrowserReload: + icon = QIcon::fromTheme(QLatin1String("view-refresh")); + break; + case SP_BrowserStop: + icon = QIcon::fromTheme(QLatin1String("process-stop")); + break; + case SP_MediaPlay: + icon = QIcon::fromTheme(QLatin1String("media-playback-start")); + break; + case SP_MediaPause: + icon = QIcon::fromTheme(QLatin1String("media-playback-pause")); + break; + case SP_MediaStop: + icon = QIcon::fromTheme(QLatin1String("media-playback-stop")); + break; + case SP_MediaSeekForward: + icon = QIcon::fromTheme(QLatin1String("media-seek-forward")); + break; + case SP_MediaSeekBackward: + icon = QIcon::fromTheme(QLatin1String("media-seek-backward")); + break; + case SP_MediaSkipForward: + icon = QIcon::fromTheme(QLatin1String("media-skip-forward")); + break; + case SP_MediaSkipBackward: + icon = QIcon::fromTheme(QLatin1String("media-skip-backward")); + break; + case SP_MediaVolume: + icon = QIcon::fromTheme(QLatin1String("audio-volume-medium")); + break; + case SP_MediaVolumeMuted: + icon = QIcon::fromTheme(QLatin1String("audio-volume-muted")); + break; + case SP_ArrowForward: + if (rtl) + return QCommonStyle::standardIcon(SP_ArrowLeft, option); + return QCommonStyle::standardIcon(SP_ArrowRight, option); + case SP_ArrowBack: + if (rtl) + return QCommonStyle::standardIcon(SP_ArrowRight, option); + return QCommonStyle::standardIcon(SP_ArrowLeft, option); + case SP_FileLinkIcon: + { + QIcon linkIcon = QIcon::fromTheme(QLatin1String("emblem-symbolic-link")); + if (!linkIcon.isNull()) { + QIcon baseIcon = QCommonStyle::standardIcon(SP_FileIcon, option); + const QList<QSize> sizes = baseIcon.availableSizes(QIcon::Normal, QIcon::Off); + for (int i = 0 ; i < sizes.size() ; ++i) { + int size = sizes[i].width(); + QPixmap basePixmap = baseIcon.pixmap(option->window, QSize(size, size)); + QPixmap linkPixmap = linkIcon.pixmap(option->window, QSize(size / 2, size / 2)); + QPainter painter(&basePixmap); + painter.drawPixmap(size/2, size/2, linkPixmap); + icon.addPixmap(basePixmap); + } + } + } + break; + case SP_DirLinkIcon: + { + QIcon linkIcon = QIcon::fromTheme(QLatin1String("emblem-symbolic-link")); + if (!linkIcon.isNull()) { + QIcon baseIcon = QCommonStyle::standardIcon(SP_DirIcon, option); + const QList<QSize> sizes = baseIcon.availableSizes(QIcon::Normal, QIcon::Off); + for (int i = 0 ; i < sizes.size() ; ++i) { + int size = sizes[i].width(); + QPixmap basePixmap = baseIcon.pixmap(option->window, QSize(size, size)); + QPixmap linkPixmap = linkIcon.pixmap(option->window, QSize(size / 2, size / 2)); + QPainter painter(&basePixmap); + painter.drawPixmap(size/2, size/2, linkPixmap); + icon.addPixmap(basePixmap); + } + } + } + break; + default: + break; + } + } // if (QGuiApplication::desktopSettingsAware() && !QIcon::themeName().isEmpty()) + + if (!icon.isNull()) + return icon; + +#if defined(Q_OS_MAC) + if (QGuiApplication::desktopSettingsAware()) { + switch (standardIcon) { + case SP_DirIcon: { + // A rather special case + QIcon closeIcon = QCommonStyle::standardIcon(SP_DirClosedIcon, option); + QIcon openIcon = QCommonStyle::standardIcon(SP_DirOpenIcon, option); + closeIcon.addPixmap(openIcon.pixmap(16, 16), QIcon::Normal, QIcon::On); + closeIcon.addPixmap(openIcon.pixmap(32, 32), QIcon::Normal, QIcon::On); + closeIcon.addPixmap(openIcon.pixmap(64, 64), QIcon::Normal, QIcon::On); + closeIcon.addPixmap(openIcon.pixmap(128, 128), QIcon::Normal, QIcon::On); + return closeIcon; + } + + case SP_TitleBarNormalButton: + case SP_TitleBarCloseButton: { + QIcon titleBarIcon; + if (standardIcon == SP_TitleBarCloseButton) { + titleBarIcon.addFile(QLatin1String(":/qt-project.org/styles/macstyle/images/closedock-16.png")); + titleBarIcon.addFile(QLatin1String(":/qt-project.org/styles/macstyle/images/closedock-down-16.png"), QSize(16, 16), QIcon::Normal, QIcon::On); + } else { + titleBarIcon.addFile(QLatin1String(":/qt-project.org/styles/macstyle/images/dockdock-16.png")); + titleBarIcon.addFile(QLatin1String(":/qt-project.org/styles/macstyle/images/dockdock-down-16.png"), QSize(16, 16), QIcon::Normal, QIcon::On); + } + return titleBarIcon; + } + + case SP_MessageBoxQuestion: + case SP_MessageBoxInformation: + case SP_MessageBoxWarning: + case SP_MessageBoxCritical: + case SP_DesktopIcon: + case SP_TrashIcon: + case SP_ComputerIcon: + case SP_DriveFDIcon: + case SP_DriveHDIcon: + case SP_DriveCDIcon: + case SP_DriveDVDIcon: + case SP_DriveNetIcon: + case SP_DirOpenIcon: + case SP_DirClosedIcon: + case SP_DirLinkIcon: + case SP_FileLinkIcon: + case SP_FileIcon: + if (const QPlatformTheme *theme = QGuiApplicationPrivate::platformTheme()) { + QPlatformTheme::StandardPixmap sp = static_cast<QPlatformTheme::StandardPixmap>(standardIcon); + QIcon retIcon; + const QList<QSize> sizes = theme->themeHint(QPlatformTheme::IconPixmapSizes).value<QList<QSize> >(); + for (const QSize &size : sizes) { + QPixmap mainIcon; + const QString cacheKey = QLatin1String("qt_mac_constructQIconFromIconRef") + QString::number(standardIcon) + QString::number(size.width()); + if (standardIcon >= QStyle::SP_CustomBase) { + mainIcon = theme->standardPixmap(sp, QSizeF(size)); + } else if (QPixmapCache::find(cacheKey, &mainIcon) == false) { + mainIcon = theme->standardPixmap(sp, QSizeF(size)); + QPixmapCache::insert(cacheKey, mainIcon); + } + + retIcon.addPixmap(mainIcon); + } + if (!retIcon.isNull()) + return retIcon; + } + + default: + break; + } + } // if (QGuiApplication::desktopSettingsAware()) +#endif // Q_OS_MAC + + switch (standardIcon) { +#ifndef QT_NO_IMAGEFORMAT_PNG + case SP_TitleBarMinButton: + addIconFiles(iconResourcePrefix() + QStringLiteral("titlebar-min-"), + titleBarSizes, sizeof(titleBarSizes)/sizeof(titleBarSizes[0]), icon); + break; + case SP_TitleBarMaxButton: + addIconFiles(iconResourcePrefix() + QStringLiteral("titlebar-max-"), + titleBarSizes, sizeof(titleBarSizes)/sizeof(titleBarSizes[0]), icon); + break; + case SP_TitleBarShadeButton: + addIconFiles(iconResourcePrefix() + QStringLiteral("titlebar-shade-"), + titleBarSizes, sizeof(titleBarSizes)/sizeof(titleBarSizes[0]), icon); + + break; + case SP_TitleBarUnshadeButton: + addIconFiles(iconResourcePrefix() + QStringLiteral("titlebar-unshade-"), + titleBarSizes, sizeof(titleBarSizes)/sizeof(titleBarSizes[0]), icon); + break; + case SP_TitleBarContextHelpButton: + addIconFiles(iconResourcePrefix() + QStringLiteral("titlebar-contexthelp-"), + titleBarSizes, sizeof(titleBarSizes)/sizeof(titleBarSizes[0]), icon); + break; + case SP_FileDialogNewFolder: + icon.addFile(QLatin1String(":/qt-project.org/styles/commonstyle/images/newdirectory-16.png"), QSize(16, 16)); + icon.addFile(QLatin1String(":/qt-project.org/styles/commonstyle/images/newdirectory-32.png"), QSize(32, 32)); + icon.addFile(QLatin1String(":/qt-project.org/styles/commonstyle/images/newdirectory-128.png"), QSize(128, 128)); + break; + case SP_FileDialogBack: + return QCommonStyle::standardIcon(SP_ArrowBack, option); + case SP_FileDialogToParent: + return QCommonStyle::standardIcon(SP_ArrowUp, option); + case SP_FileDialogDetailedView: + icon.addFile(QLatin1String(":/qt-project.org/styles/commonstyle/images/viewdetailed-16.png"), QSize(16, 16)); + icon.addFile(QLatin1String(":/qt-project.org/styles/commonstyle/images/viewdetailed-32.png"), QSize(32, 32)); + icon.addFile(QLatin1String(":/qt-project.org/styles/commonstyle/images/viewdetailed-128.png"), QSize(128, 128)); + break; + case SP_FileDialogInfoView: + icon.addFile(QLatin1String(":/qt-project.org/styles/commonstyle/images/fileinfo-16.png"), QSize(16, 16)); + icon.addFile(QLatin1String(":/qt-project.org/styles/commonstyle/images/fileinfo-32.png"), QSize(32, 32)); + icon.addFile(QLatin1String(":/qt-project.org/styles/commonstyle/images/fileinfo-128.png"), QSize(128, 128)); + break; + case SP_FileDialogContentsView: + icon.addFile(QLatin1String(":/qt-project.org/styles/commonstyle/images/filecontents-16.png"), QSize(16, 16)); + icon.addFile(QLatin1String(":/qt-project.org/styles/commonstyle/images/filecontents-32.png"), QSize(32, 32)); + icon.addFile(QLatin1String(":/qt-project.org/styles/commonstyle/images/filecontents-128.png"), QSize(128, 128)); + break; + case SP_FileDialogListView: + icon.addFile(QLatin1String(":/qt-project.org/styles/commonstyle/images/viewlist-16.png"), QSize(16, 16)); + icon.addFile(QLatin1String(":/qt-project.org/styles/commonstyle/images/viewlist-32.png"), QSize(32, 32)); + icon.addFile(QLatin1String(":/qt-project.org/styles/commonstyle/images/viewlist-128.png"), QSize(128, 128)); + break; + case SP_DialogOkButton: + icon.addFile(QLatin1String(":/qt-project.org/styles/commonstyle/images/standardbutton-ok-16.png"), QSize(16, 16)); + icon.addFile(QLatin1String(":/qt-project.org/styles/commonstyle/images/standardbutton-ok-32.png"), QSize(32, 32)); + icon.addFile(QLatin1String(":/qt-project.org/styles/commonstyle/images/standardbutton-ok-128.png"), QSize(128, 128)); + break; + case SP_DialogCancelButton: + icon.addFile(QLatin1String(":/qt-project.org/styles/commonstyle/images/standardbutton-cancel-16.png"), QSize(16, 16)); + icon.addFile(QLatin1String(":/qt-project.org/styles/commonstyle/images/standardbutton-cancel-32.png"), QSize(32, 32)); + icon.addFile(QLatin1String(":/qt-project.org/styles/commonstyle/images/standardbutton-cancel-128.png"), QSize(128, 128)); + break; + case SP_DialogHelpButton: + icon.addFile(QLatin1String(":/qt-project.org/styles/commonstyle/images/standardbutton-help-16.png"), QSize(16, 16)); + icon.addFile(QLatin1String(":/qt-project.org/styles/commonstyle/images/standardbutton-help-32.png"), QSize(32, 32)); + icon.addFile(QLatin1String(":/qt-project.org/styles/commonstyle/images/standardbutton-help-128.png"), QSize(128, 128)); + break; + case SP_DialogOpenButton: + icon.addFile(QLatin1String(":/qt-project.org/styles/commonstyle/images/standardbutton-open-16.png"), QSize(16, 16)); + icon.addFile(QLatin1String(":/qt-project.org/styles/commonstyle/images/standardbutton-open-32.png"), QSize(32, 32)); + icon.addFile(QLatin1String(":/qt-project.org/styles/commonstyle/images/standardbutton-open-128.png"), QSize(128, 128)); + break; + case SP_DialogSaveButton: + icon.addFile(QLatin1String(":/qt-project.org/styles/commonstyle/images/standardbutton-save-16.png"), QSize(16, 16)); + icon.addFile(QLatin1String(":/qt-project.org/styles/commonstyle/images/standardbutton-save-32.png"), QSize(32, 32)); + icon.addFile(QLatin1String(":/qt-project.org/styles/commonstyle/images/standardbutton-save-128.png"), QSize(128, 128)); + break; + case SP_DialogCloseButton: + icon.addFile(QLatin1String(":/qt-project.org/styles/commonstyle/images/standardbutton-close-16.png"), QSize(16, 16)); + icon.addFile(QLatin1String(":/qt-project.org/styles/commonstyle/images/standardbutton-close-32.png"), QSize(32, 32)); + icon.addFile(QLatin1String(":/qt-project.org/styles/commonstyle/images/standardbutton-close-128.png"), QSize(128, 128)); + break; + case SP_DialogApplyButton: + icon.addFile(QLatin1String(":/qt-project.org/styles/commonstyle/images/standardbutton-apply-16.png"), QSize(16, 16)); + icon.addFile(QLatin1String(":/qt-project.org/styles/commonstyle/images/standardbutton-apply-32.png"), QSize(32, 32)); + icon.addFile(QLatin1String(":/qt-project.org/styles/commonstyle/images/standardbutton-apply-128.png"), QSize(128, 128)); + break; + case SP_DialogResetButton: + icon.addFile(QLatin1String(":/qt-project.org/styles/commonstyle/images/standardbutton-clear-16.png"), QSize(16, 16)); + icon.addFile(QLatin1String(":/qt-project.org/styles/commonstyle/images/standardbutton-clear-32.png"), QSize(32, 32)); + icon.addFile(QLatin1String(":/qt-project.org/styles/commonstyle/images/standardbutton-clear-128.png"), QSize(128, 128)); + break; + case SP_DialogDiscardButton: + icon.addFile(QLatin1String(":/qt-project.org/styles/commonstyle/images/standardbutton-delete-16.png"), QSize(16, 16)); + icon.addFile(QLatin1String(":/qt-project.org/styles/commonstyle/images/standardbutton-delete-32.png"), QSize(32, 32)); + icon.addFile(QLatin1String(":/qt-project.org/styles/commonstyle/images/standardbutton-delete-128.png"), QSize(128, 128)); + break; + case SP_DialogYesButton: + icon.addFile(QLatin1String(":/qt-project.org/styles/commonstyle/images/standardbutton-yes-16.png"), QSize(16, 16)); + icon.addFile(QLatin1String(":/qt-project.org/styles/commonstyle/images/standardbutton-yes-32.png"), QSize(32, 32)); + icon.addFile(QLatin1String(":/qt-project.org/styles/commonstyle/images/standardbutton-yes-128.png"), QSize(128, 128)); + break; + case SP_DialogNoButton: + icon.addFile(QLatin1String(":/qt-project.org/styles/commonstyle/images/standardbutton-no-16.png"), QSize(16, 16)); + icon.addFile(QLatin1String(":/qt-project.org/styles/commonstyle/images/standardbutton-no-32.png"), QSize(32, 32)); + icon.addFile(QLatin1String(":/qt-project.org/styles/commonstyle/images/standardbutton-no-128.png"), QSize(128, 128)); + break; + case SP_ArrowForward: + if (rtl) + return QCommonStyle::standardIcon(SP_ArrowLeft, option); + return QCommonStyle::standardIcon(SP_ArrowRight, option); + case SP_ArrowBack: + if (rtl) + return QCommonStyle::standardIcon(SP_ArrowRight, option); + return QCommonStyle::standardIcon(SP_ArrowLeft, option); + case SP_ArrowLeft: + icon.addFile(QLatin1String(":/qt-project.org/styles/commonstyle/images/left-16.png"), QSize(16, 16)); + icon.addFile(QLatin1String(":/qt-project.org/styles/commonstyle/images/left-32.png"), QSize(32, 32)); + icon.addFile(QLatin1String(":/qt-project.org/styles/commonstyle/images/left-128.png"), QSize(128, 128)); + break; + case SP_ArrowRight: + icon.addFile(QLatin1String(":/qt-project.org/styles/commonstyle/images/right-16.png"), QSize(16, 16)); + icon.addFile(QLatin1String(":/qt-project.org/styles/commonstyle/images/right-32.png"), QSize(32, 32)); + icon.addFile(QLatin1String(":/qt-project.org/styles/commonstyle/images/right-128.png"), QSize(128, 128)); + break; + case SP_ArrowUp: + icon.addFile(QLatin1String(":/qt-project.org/styles/commonstyle/images/up-16.png"), QSize(16, 16)); + icon.addFile(QLatin1String(":/qt-project.org/styles/commonstyle/images/up-32.png"), QSize(32, 32)); + icon.addFile(QLatin1String(":/qt-project.org/styles/commonstyle/images/up-128.png"), QSize(128, 128)); + break; + case SP_ArrowDown: + icon.addFile(QLatin1String(":/qt-project.org/styles/commonstyle/images/down-16.png"), QSize(16, 16)); + icon.addFile(QLatin1String(":/qt-project.org/styles/commonstyle/images/down-32.png"), QSize(32, 32)); + icon.addFile(QLatin1String(":/qt-project.org/styles/commonstyle/images/down-128.png"), QSize(128, 128)); + break; + case SP_DirHomeIcon: + case SP_DirIcon: + icon.addFile(QLatin1String(":/qt-project.org/styles/commonstyle/images/dirclosed-16.png"), + QSize(), QIcon::Normal, QIcon::Off); + icon.addFile(QLatin1String(":/qt-project.org/styles/commonstyle/images/diropen-16.png"), + QSize(), QIcon::Normal, QIcon::On); + icon.addFile(QLatin1String(":/qt-project.org/styles/commonstyle/images/dirclosed-32.png"), + QSize(32, 32), QIcon::Normal, QIcon::Off); + icon.addFile(QLatin1String(":/qt-project.org/styles/commonstyle/images/diropen-32.png"), + QSize(32, 32), QIcon::Normal, QIcon::On); + icon.addFile(QLatin1String(":/qt-project.org/styles/commonstyle/images/dirclosed-128.png"), + QSize(128, 128), QIcon::Normal, QIcon::Off); + icon.addFile(QLatin1String(":/qt-project.org/styles/commonstyle/images/diropen-128.png"), + QSize(128, 128), QIcon::Normal, QIcon::On); + break; + case SP_DriveCDIcon: + icon.addFile(QLatin1String(":/qt-project.org/styles/commonstyle/images/cdr-16.png"), QSize(16, 16)); + icon.addFile(QLatin1String(":/qt-project.org/styles/commonstyle/images/cdr-32.png"), QSize(32, 32)); + icon.addFile(QLatin1String(":/qt-project.org/styles/commonstyle/images/cdr-128.png"), QSize(128, 128)); + break; + case SP_DriveDVDIcon: + icon.addFile(QLatin1String(":/qt-project.org/styles/commonstyle/images/dvd-16.png"), QSize(16, 16)); + icon.addFile(QLatin1String(":/qt-project.org/styles/commonstyle/images/dvd-32.png"), QSize(32, 32)); + icon.addFile(QLatin1String(":/qt-project.org/styles/commonstyle/images/dvd-128.png"), QSize(128, 128)); + break; + case SP_FileIcon: + icon.addFile(QLatin1String(":/qt-project.org/styles/commonstyle/images/file-16.png"), QSize(16, 16)); + icon.addFile(QLatin1String(":/qt-project.org/styles/commonstyle/images/file-32.png"), QSize(32, 32)); + icon.addFile(QLatin1String(":/qt-project.org/styles/commonstyle/images/file-128.png"), QSize(128, 128)); + break; + case SP_FileLinkIcon: + icon.addFile(QLatin1String(":/qt-project.org/styles/commonstyle/images/filelink-16.png"), QSize(16, 16)); + icon.addFile(QLatin1String(":/qt-project.org/styles/commonstyle/images/filelink-32.png"), QSize(32, 32)); + icon.addFile(QLatin1String(":/qt-project.org/styles/commonstyle/images/filelink-128.png"), QSize(128, 128)); + break; + case SP_TrashIcon: + icon.addFile(QLatin1String(":/qt-project.org/styles/commonstyle/images/trash-16.png"), QSize(16, 16)); + icon.addFile(QLatin1String(":/qt-project.org/styles/commonstyle/images/trash-32.png"), QSize(32, 32)); + icon.addFile(QLatin1String(":/qt-project.org/styles/commonstyle/images/trash-128.png"), QSize(128, 128)); + break; + case SP_BrowserReload: + icon.addFile(QLatin1String(":/qt-project.org/styles/commonstyle/images/refresh-24.png"), QSize(24, 24)); + icon.addFile(QLatin1String(":/qt-project.org/styles/commonstyle/images/refresh-32.png"), QSize(32, 32)); + break; + case SP_BrowserStop: + icon.addFile(QLatin1String(":/qt-project.org/styles/commonstyle/images/stop-24.png"), QSize(24, 24)); + icon.addFile(QLatin1String(":/qt-project.org/styles/commonstyle/images/stop-32.png"), QSize(32, 32)); + break; + case SP_MediaPlay: + icon.addFile(QLatin1String(":/qt-project.org/styles/commonstyle/images/media-play-16.png"), QSize(16, 16)); + icon.addFile(QLatin1String(":/qt-project.org/styles/commonstyle/images/media-play-32.png"), QSize(32, 32)); + break; + case SP_MediaPause: + icon.addFile(QLatin1String(":/qt-project.org/styles/commonstyle/images/media-pause-16.png"), QSize(16, 16)); + icon.addFile(QLatin1String(":/qt-project.org/styles/commonstyle/images/media-pause-32.png"), QSize(32, 32)); + break; + case SP_MediaStop: + icon.addFile(QLatin1String(":/qt-project.org/styles/commonstyle/images/media-stop-16.png"), QSize(16, 16)); + icon.addFile(QLatin1String(":/qt-project.org/styles/commonstyle/images/media-stop-32.png"), QSize(32, 32)); + break; + case SP_MediaSeekForward: + icon.addFile(QLatin1String(":/qt-project.org/styles/commonstyle/images/media-seek-forward-16.png"), QSize(16, 16)); + icon.addFile(QLatin1String(":/qt-project.org/styles/commonstyle/images/media-seek-forward-32.png"), QSize(32, 32)); + break; + case SP_MediaSeekBackward: + icon.addFile(QLatin1String(":/qt-project.org/styles/commonstyle/images/media-seek-backward-16.png"), QSize(16, 16)); + icon.addFile(QLatin1String(":/qt-project.org/styles/commonstyle/images/media-seek-backward-32.png"), QSize(32, 32)); + break; + case SP_MediaSkipForward: + icon.addFile(QLatin1String(":/qt-project.org/styles/commonstyle/images/media-skip-forward-16.png"), QSize(16, 16)); + icon.addFile(QLatin1String(":/qt-project.org/styles/commonstyle/images/media-skip-forward-32.png"), QSize(32, 32)); + break; + case SP_MediaSkipBackward: + icon.addFile(QLatin1String(":/qt-project.org/styles/commonstyle/images/media-skip-backward-16.png"), QSize(16, 16)); + icon.addFile(QLatin1String(":/qt-project.org/styles/commonstyle/images/media-skip-backward-32.png"), QSize(32, 32)); + break; + case SP_MediaVolume: + icon.addFile(QLatin1String(":/qt-project.org/styles/commonstyle/images/media-volume-16.png"), QSize(16, 16)); + break; + case SP_MediaVolumeMuted: + icon.addFile(QLatin1String(":/qt-project.org/styles/commonstyle/images/media-volume-muted-16.png"), QSize(16, 16)); + break; + case SP_TitleBarCloseButton: + addIconFiles(iconResourcePrefix() + QStringLiteral("closedock-"), + dockTitleIconSizes, sizeof(dockTitleIconSizes)/sizeof(dockTitleIconSizes[0]), icon); + break; + case SP_TitleBarMenuButton: +# ifndef QT_NO_IMAGEFORMAT_XPM + icon.addPixmap(titleBarMenuCachedPixmapFromXPM()); +# endif + icon.addFile(QLatin1String(":/qt-project.org/qmessagebox/images/qtlogo-64.png")); + break; + case SP_TitleBarNormalButton: + addIconFiles(iconResourcePrefix() + QStringLiteral("normalizedockup-"), + dockTitleIconSizes, sizeof(dockTitleIconSizes)/sizeof(dockTitleIconSizes[0]), icon); + break; + case SP_ToolBarHorizontalExtensionButton: { + QString prefix = iconResourcePrefix() + QStringLiteral("toolbar-ext-h-"); + if (rtl) + prefix += QStringLiteral("rtl-"); + addIconFiles(prefix, toolBarExtHSizes, sizeof(toolBarExtHSizes)/sizeof(toolBarExtHSizes[0]), icon); + } + break; + case SP_ToolBarVerticalExtensionButton: + addIconFiles(iconResourcePrefix() + QStringLiteral("toolbar-ext-v-"), + toolBarExtVSizes, sizeof(toolBarExtVSizes)/sizeof(toolBarExtVSizes[0]), icon); + break; +#endif // QT_NO_IMAGEFORMAT_PNG + default: + icon.addPixmap(proxy()->standardPixmap(standardIcon, option)); + break; + } + return icon; +} + +static inline uint qt_intensity(uint r, uint g, uint b) +{ + // 30% red, 59% green, 11% blue + return (77 * r + 150 * g + 28 * b) / 255; +} + +/*! \reimp */ +QPixmap QCommonStyle::generatedIconPixmap(QIcon::Mode iconMode, const QPixmap &pixmap, + const QStyleOption *opt) const +{ + switch (iconMode) { + case QIcon::Disabled: { + QImage im = pixmap.toImage().convertToFormat(QImage::Format_ARGB32); + + // Create a colortable based on the background (black -> bg -> white) + QColor bg = opt->palette.color(QPalette::Disabled, QPalette::Window); + int red = bg.red(); + int green = bg.green(); + int blue = bg.blue(); + uchar reds[256], greens[256], blues[256]; + for (int i=0; i<128; ++i) { + reds[i] = uchar((red * (i<<1)) >> 8); + greens[i] = uchar((green * (i<<1)) >> 8); + blues[i] = uchar((blue * (i<<1)) >> 8); + } + for (int i=0; i<128; ++i) { + reds[i+128] = uchar(qMin(red + (i << 1), 255)); + greens[i+128] = uchar(qMin(green + (i << 1), 255)); + blues[i+128] = uchar(qMin(blue + (i << 1), 255)); + } + + int intensity = qt_intensity(red, green, blue); + const int factor = 191; + + // High intensity colors needs dark shifting in the color table, while + // low intensity colors needs light shifting. This is to increase the + // perceived contrast. + if ((red - factor > green && red - factor > blue) + || (green - factor > red && green - factor > blue) + || (blue - factor > red && blue - factor > green)) + intensity = qMin(255, intensity + 91); + else if (intensity <= 128) + intensity -= 51; + + for (int y=0; y<im.height(); ++y) { + QRgb *scanLine = (QRgb*)im.scanLine(y); + for (int x=0; x<im.width(); ++x) { + QRgb pixel = *scanLine; + // Calculate color table index, taking intensity adjustment + // and a magic offset into account. + uint ci = uint(qGray(pixel)/3 + (130 - intensity / 3)); + *scanLine = qRgba(reds[ci], greens[ci], blues[ci], qAlpha(pixel)); + ++scanLine; + } + } + + return QPixmap::fromImage(im); + } + case QIcon::Selected: { + QImage img = pixmap.toImage().convertToFormat(QImage::Format_ARGB32_Premultiplied); + QColor color = opt->palette.color(QPalette::Normal, QPalette::Highlight); + color.setAlphaF(qreal(0.3)); + QPainter painter(&img); + painter.setCompositionMode(QPainter::CompositionMode_SourceAtop); + painter.fillRect(0, 0, img.width(), img.height(), color); + painter.end(); + return QPixmap::fromImage(img); } + case QIcon::Active: + return pixmap; + default: + break; + } + return pixmap; +} + +} // namespace QQC2 + +QT_END_NAMESPACE + +#include "moc_qquickcommonstyle.cpp" diff --git a/src/imports/nativestyle/qstyle/qquickcommonstyle.h b/src/imports/nativestyle/qstyle/qquickcommonstyle.h new file mode 100644 index 00000000..894194a0 --- /dev/null +++ b/src/imports/nativestyle/qstyle/qquickcommonstyle.h @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QCOMMONSTYLE_H +#define QCOMMONSTYLE_H + +#include "qquickstyle.h" + +QT_BEGIN_NAMESPACE + +namespace QQC2 { + +class QCommonStylePrivate; + +class QCommonStyle: public QStyle +{ + Q_OBJECT + +public: + QCommonStyle(); + ~QCommonStyle() override; + + void drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, QPainter *p) const override; + void drawControl(ControlElement element, const QStyleOption *opt, QPainter *p) const override; + void drawComplexControl(ComplexControl cc, const QStyleOptionComplex *opt, QPainter *p) const override; + + QRect subElementRect(SubElement r, const QStyleOption *opt) const override; + QRect subControlRect(ComplexControl cc, const QStyleOptionComplex *opt, SubControl sc) const override; + + QSize sizeFromContents(ContentsType ct, const QStyleOption *opt, const QSize &contentsSize) const override; + QFont font(ControlElement element, const QStyle::State state) const override; + QMargins ninePatchMargins(ControlElement ce, const QStyleOption *opt, const QSize &imageSize) const override; + QMargins ninePatchMargins(ComplexControl cc, const QStyleOptionComplex *opt, const QSize &imageSize) const override; + + int pixelMetric(PixelMetric m, const QStyleOption *opt = nullptr) const override; + int styleHint(StyleHint sh, const QStyleOption *opt = nullptr, QStyleHintReturn *shret = nullptr) const override; + + QIcon standardIcon(StandardPixmap standardIcon, const QStyleOption *opt = nullptr) const override; + QPixmap standardPixmap(StandardPixmap sp, const QStyleOption *opt = nullptr) const override; + QPixmap generatedIconPixmap(QIcon::Mode iconMode, const QPixmap &pixmap, const QStyleOption *opt) const override; + + SubControl hitTestComplexControl(ComplexControl cc, const QStyleOptionComplex *opt, const QPoint &pt) const override; + +protected: + QCommonStyle(QCommonStylePrivate &dd); + +private: + Q_DECLARE_PRIVATE(QCommonStyle) + Q_DISABLE_COPY(QCommonStyle) +}; + +} // namespace QQC2 + +QT_END_NAMESPACE + +#endif // QCOMMONSTYLE_H diff --git a/src/imports/nativestyle/qstyle/qquickcommonstyle_p.h b/src/imports/nativestyle/qstyle/qquickcommonstyle_p.h new file mode 100644 index 00000000..2fed0cad --- /dev/null +++ b/src/imports/nativestyle/qstyle/qquickcommonstyle_p.h @@ -0,0 +1,110 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QCOMMONSTYLE_P_H +#define QCOMMONSTYLE_P_H + +#include "qquickcommonstyle.h" +#include "qquickstyle_p.h" +#include "qquickstyleoption.h" + +QT_BEGIN_NAMESPACE + +// +// 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. +// + +class QStringList; +class QTextOption; + +namespace QQC2 { + +class QCommonStylePrivate : public QStylePrivate +{ + Q_DECLARE_PUBLIC(QCommonStyle) +public: + + ~QCommonStylePrivate() + { + delete cachedOption; + } + + QString calculateElidedText(const QString &text, const QTextOption &textOption, + const QFont &font, const QRect &textRect, const Qt::Alignment valign, + Qt::TextElideMode textElideMode, int flags, + bool lastVisibleLineShouldBeElided, QPointF *paintStartPosition) const; + void viewItemDrawText(QPainter *p, const QStyleOptionViewItem *option, const QRect &rect) const; + void viewItemLayout(const QStyleOptionViewItem *opt, QRect *checkRect, + QRect *pixmapRect, QRect *textRect, bool sizehint) const; + QSize viewItemSize(const QStyleOptionViewItem *option, int role) const; + + mutable QRect decorationRect, displayRect, checkRect; + mutable QStyleOptionViewItem *cachedOption = nullptr; + bool isViewItemCached(const QStyleOptionViewItem &option) const { + return cachedOption + && option.index == cachedOption->index + && option.state == cachedOption->state + && option.rect == cachedOption->rect + && option.text == cachedOption->text + && option.direction == cachedOption->direction + && option.displayAlignment == cachedOption->displayAlignment + && option.decorationAlignment == cachedOption->decorationAlignment + && option.decorationPosition == cachedOption->decorationPosition + && option.decorationSize == cachedOption->decorationSize + && option.features == cachedOption->features + && option.icon.isNull() == cachedOption->icon.isNull() + && option.font == cachedOption->font + && option.viewItemPosition == cachedOption->viewItemPosition; + } + QString toolButtonElideText(const QStyleOptionToolButton *toolbutton, + const QRect &textRect, int flags) const; + + mutable QIcon tabBarcloseButtonIcon; + virtual void tabLayout(const QStyleOptionTab *opt, QRect *textRect, QRect *pixmapRect) const; +}; + +} // namespace QQC2 + +QT_END_NAMESPACE + +#endif //QCOMMONSTYLE_P_H diff --git a/src/imports/nativestyle/qstyle/qquickcommonstylepixmaps_p.h b/src/imports/nativestyle/qstyle/qquickcommonstylepixmaps_p.h new file mode 100644 index 00000000..29815280 --- /dev/null +++ b/src/imports/nativestyle/qstyle/qquickcommonstylepixmaps_p.h @@ -0,0 +1,528 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT_NO_IMAGEFORMAT_XPM + +// +// 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. +// + +namespace QQC2 { + +static const char * const check_list_controller_xpm[] = { +"16 16 4 1", +" c None", +". c #000000000000", +"X c #FFFFFFFF0000", +"o c #C71BC30BC71B", +" ", +" ", +" .......... ", +" .XXXXXXXX. ", +" .XXXXXXXX.oo ", +" .XXXXXXXX.oo ", +" .XXXXXXXX.oo ", +" .XXXXXXXX.oo ", +" .XXXXXXXX.oo ", +" .XXXXXXXX.oo ", +" .XXXXXXXX.oo ", +" ..........oo ", +" oooooooooo ", +" oooooooooo ", +" ", +" "}; + + +static const char * const tb_extension_arrow_v_xpm[] = { + "5 8 3 1", + " c None", + ". c #000000", + "+ c none", + ".+++.", + "..+..", + "+...+", + "++.++", + ".+++.", + "..+..", + "+...+", + "++.++" +}; + +static const char * const tb_extension_arrow_h_xpm[] = { + "8 5 3 1", + " c None", + ". c #000000", + "+ c none", + "..++..++", + "+..++..+", + "++..++..", + "+..++..+", + "..++..++", +}; + +static const char * const filedialog_start_xpm[]={ + "16 15 8 1", + "a c #cec6bd", + "# c #000000", + "e c #ffff00", + "b c #999999", + "f c #cccccc", + "d c #dcdcdc", + "c c #ffffff", + ". c None", + ".....######aaaaa", + "...bb#cccc##aaaa", + "..bcc#cccc#d#aaa", + ".bcef#cccc#dd#aa", + ".bcfe#cccc#####a", + ".bcef#ccccccccc#", + "bbbbbbbbbbbbccc#", + "bccccccccccbbcc#", + "bcefefefefee#bc#", + ".bcefefefefef#c#", + ".bcfefefefefe#c#", + "..bcfefefefeeb##", + "..bbbbbbbbbbbbb#", + "...#############", + "................"}; + +static const char * const filedialog_end_xpm[]={ + "16 15 9 1", + "d c #a0a0a0", + "c c #c3c3c3", + "# c #cec6bd", + ". c #000000", + "f c #ffff00", + "e c #999999", + "g c #cccccc", + "b c #ffffff", + "a c None", + "......####aaaaaa", + ".bbbb..###aaaaaa", + ".bbbb.c.##aaaaaa", + ".bbbb....ddeeeea", + ".bbbbbbb.bbbbbe.", + ".bbbbbbb.bcfgfe.", + "eeeeeeeeeeeeefe.", + "ebbbbbbbbbbeege.", + "ebfgfgfgfgff.ee.", + "aebfgfgfgfgfg.e.", + "aebgfgfgfgfgf.e.", + "aaebgfgfgfgffe..", + "aaeeeeeeeeeeeee.", + "aaa.............", + "aaaaaaaaaaaaaaaa"}; + + +/* XPM */ +static const char * const qt_menu_xpm[] = { +"16 16 72 1", +" c None", +". c #65AF36", +"+ c #66B036", +"@ c #77B94C", +"# c #A7D28C", +"$ c #BADBA4", +"% c #A4D088", +"& c #72B646", +"* c #9ACB7A", +"= c #7FBD56", +"- c #85C05F", +"; c #F4F9F0", +"> c #FFFFFF", +", c #E5F1DC", +"' c #ECF5E7", +") c #7ABA50", +"! c #83BF5C", +"~ c #AED595", +"{ c #D7EACA", +"] c #A9D28D", +"^ c #BCDDA8", +"/ c #C4E0B1", +"( c #81BE59", +"_ c #D0E7C2", +": c #D4E9C6", +"< c #6FB542", +"[ c #6EB440", +"} c #88C162", +"| c #98CA78", +"1 c #F4F9F1", +"2 c #8FC56C", +"3 c #F1F8EC", +"4 c #E8F3E1", +"5 c #D4E9C7", +"6 c #74B748", +"7 c #80BE59", +"8 c #73B747", +"9 c #6DB43F", +"0 c #CBE4BA", +"a c #80BD58", +"b c #6DB33F", +"c c #FEFFFE", +"d c #68B138", +"e c #F9FCF7", +"f c #91C66F", +"g c #E8F3E0", +"h c #DCEDD0", +"i c #91C66E", +"j c #A3CF86", +"k c #C9E3B8", +"l c #B0D697", +"m c #E3F0DA", +"n c #95C873", +"o c #E6F2DE", +"p c #9ECD80", +"q c #BEDEAA", +"r c #C7E2B6", +"s c #79BA4F", +"t c #6EB441", +"u c #BCDCA7", +"v c #FAFCF8", +"w c #F6FAF3", +"x c #84BF5D", +"y c #EDF6E7", +"z c #FAFDF9", +"A c #88C263", +"B c #98CA77", +"C c #CDE5BE", +"D c #67B037", +"E c #D9EBCD", +"F c #6AB23C", +"G c #77B94D", +" .++++++++++++++", +".+++++++++++++++", +"+++@#$%&+++*=+++", +"++-;>,>')+!>~+++", +"++{>]+^>/(_>:~<+", +"+[>>}+|>123>456+", +"+7>>8+->>90>~+++", +"+a>>b+a>c[0>~+++", +"+de>=+f>g+0>~+++", +"++h>i+j>k+0>~+++", +"++l>mno>p+q>rst+", +"++duv>wl++xy>zA+", +"++++B>Cb++++&D++", +"+++++0zE++++++++", +"++++++FG+++++++.", +"++++++++++++++. "}; + +static const char * const qt_close_xpm[] = { +"10 10 2 1", +"# c #000000", +". c None", +"..........", +".##....##.", +"..##..##..", +"...####...", +"....##....", +"...####...", +"..##..##..", +".##....##.", +"..........", +".........."}; + +static const char * const qt_maximize_xpm[]={ +"10 10 2 1", +"# c #000000", +". c None", +"#########.", +"#########.", +"#.......#.", +"#.......#.", +"#.......#.", +"#.......#.", +"#.......#.", +"#.......#.", +"#########.", +".........."}; + +static const char * const qt_minimize_xpm[] = { +"10 10 2 1", +"# c #000000", +". c None", +"..........", +"..........", +"..........", +"..........", +"..........", +"..........", +"..........", +".#######..", +".#######..", +".........."}; + +static const char * const qt_normalizeup_xpm[] = { +"10 10 2 1", +"# c #000000", +". c None", +"...######.", +"...######.", +"...#....#.", +".######.#.", +".######.#.", +".#....###.", +".#....#...", +".#....#...", +".######...", +".........."}; + +static const char * const qt_help_xpm[] = { +"10 10 2 1", +". c None", +"# c #000000", +"..........", +"..######..", +".##....##.", +"......##..", +".....##...", +"....##....", +"....##....", +"..........", +"....##....", +".........."}; + +static const char * const qt_shade_xpm[] = { +"10 10 2 1", +"# c #000000", +". c None", +"..........", +"..........", +"..........", +"..........", +"....#.....", +"...###....", +"..#####...", +".#######..", +"..........", +".........."}; + +static const char * const qt_unshade_xpm[] = { +"10 10 2 1", +"# c #000000", +". c None", +"..........", +"..........", +"..........", +".#######..", +"..#####...", +"...###....", +"....#.....", +"..........", +"..........", +".........."}; + +static const char * const dock_widget_close_xpm[] = { +"8 8 2 1", +"# c #000000", +". c None", +"........", +".##..##.", +"..####..", +"...##...", +"..####..", +".##..##.", +"........", +"........"}; + +/* XPM */ +static const char * const information_xpm[]={ +"32 32 5 1", +". c None", +"c c #000000", +"* c #999999", +"a c #ffffff", +"b c #0000ff", +"...........********.............", +"........***aaaaaaaa***..........", +"......**aaaaaaaaaaaaaa**........", +".....*aaaaaaaaaaaaaaaaaa*.......", +"....*aaaaaaaabbbbaaaaaaaac......", +"...*aaaaaaaabbbbbbaaaaaaaac.....", +"..*aaaaaaaaabbbbbbaaaaaaaaac....", +".*aaaaaaaaaaabbbbaaaaaaaaaaac...", +".*aaaaaaaaaaaaaaaaaaaaaaaaaac*..", +"*aaaaaaaaaaaaaaaaaaaaaaaaaaaac*.", +"*aaaaaaaaaabbbbbbbaaaaaaaaaaac*.", +"*aaaaaaaaaaaabbbbbaaaaaaaaaaac**", +"*aaaaaaaaaaaabbbbbaaaaaaaaaaac**", +"*aaaaaaaaaaaabbbbbaaaaaaaaaaac**", +"*aaaaaaaaaaaabbbbbaaaaaaaaaaac**", +"*aaaaaaaaaaaabbbbbaaaaaaaaaaac**", +".*aaaaaaaaaaabbbbbaaaaaaaaaac***", +".*aaaaaaaaaaabbbbbaaaaaaaaaac***", +"..*aaaaaaaaaabbbbbaaaaaaaaac***.", +"...caaaaaaabbbbbbbbbaaaaaac****.", +"....caaaaaaaaaaaaaaaaaaaac****..", +".....caaaaaaaaaaaaaaaaaac****...", +"......ccaaaaaaaaaaaaaacc****....", +".......*cccaaaaaaaaccc*****.....", +"........***cccaaaac*******......", +"..........****caaac*****........", +".............*caaac**...........", +"...............caac**...........", +"................cac**...........", +".................cc**...........", +"..................***...........", +"...................**..........."}; +/* XPM */ +static const char* const warning_xpm[]={ +"32 32 4 1", +". c None", +"a c #ffff00", +"* c #000000", +"b c #999999", +".............***................", +"............*aaa*...............", +"...........*aaaaa*b.............", +"...........*aaaaa*bb............", +"..........*aaaaaaa*bb...........", +"..........*aaaaaaa*bb...........", +".........*aaaaaaaaa*bb..........", +".........*aaaaaaaaa*bb..........", +"........*aaaaaaaaaaa*bb.........", +"........*aaaa***aaaa*bb.........", +".......*aaaa*****aaaa*bb........", +".......*aaaa*****aaaa*bb........", +"......*aaaaa*****aaaaa*bb.......", +"......*aaaaa*****aaaaa*bb.......", +".....*aaaaaa*****aaaaaa*bb......", +".....*aaaaaa*****aaaaaa*bb......", +"....*aaaaaaaa***aaaaaaaa*bb.....", +"....*aaaaaaaa***aaaaaaaa*bb.....", +"...*aaaaaaaaa***aaaaaaaaa*bb....", +"...*aaaaaaaaaa*aaaaaaaaaa*bb....", +"..*aaaaaaaaaaa*aaaaaaaaaaa*bb...", +"..*aaaaaaaaaaaaaaaaaaaaaaa*bb...", +".*aaaaaaaaaaaa**aaaaaaaaaaa*bb..", +".*aaaaaaaaaaa****aaaaaaaaaa*bb..", +"*aaaaaaaaaaaa****aaaaaaaaaaa*bb.", +"*aaaaaaaaaaaaa**aaaaaaaaaaaa*bb.", +"*aaaaaaaaaaaaaaaaaaaaaaaaaaa*bbb", +"*aaaaaaaaaaaaaaaaaaaaaaaaaaa*bbb", +".*aaaaaaaaaaaaaaaaaaaaaaaaa*bbbb", +"..*************************bbbbb", +"....bbbbbbbbbbbbbbbbbbbbbbbbbbb.", +".....bbbbbbbbbbbbbbbbbbbbbbbbb.."}; +/* XPM */ +static const char* const critical_xpm[]={ +"32 32 4 1", +". c None", +"a c #999999", +"* c #ff0000", +"b c #ffffff", +"...........********.............", +".........************...........", +".......****************.........", +"......******************........", +".....********************a......", +"....**********************a.....", +"...************************a....", +"..*******b**********b*******a...", +"..******bbb********bbb******a...", +".******bbbbb******bbbbb******a..", +".*******bbbbb****bbbbb*******a..", +"*********bbbbb**bbbbb*********a.", +"**********bbbbbbbbbb**********a.", +"***********bbbbbbbb***********aa", +"************bbbbbb************aa", +"************bbbbbb************aa", +"***********bbbbbbbb***********aa", +"**********bbbbbbbbbb**********aa", +"*********bbbbb**bbbbb*********aa", +".*******bbbbb****bbbbb*******aa.", +".******bbbbb******bbbbb******aa.", +"..******bbb********bbb******aaa.", +"..*******b**********b*******aa..", +"...************************aaa..", +"....**********************aaa...", +"....a********************aaa....", +".....a******************aaa.....", +"......a****************aaa......", +".......aa************aaaa.......", +".........aa********aaaaa........", +"...........aaaaaaaaaaa..........", +".............aaaaaaa............"}; +/* XPM */ +static const char *const question_xpm[] = { +"32 32 5 1", +". c None", +"c c #000000", +"* c #999999", +"a c #ffffff", +"b c #0000ff", +"...........********.............", +"........***aaaaaaaa***..........", +"......**aaaaaaaaaaaaaa**........", +".....*aaaaaaaaaaaaaaaaaa*.......", +"....*aaaaaaaaaaaaaaaaaaaac......", +"...*aaaaaaaabbbbbbaaaaaaaac.....", +"..*aaaaaaaabaaabbbbaaaaaaaac....", +".*aaaaaaaabbaaaabbbbaaaaaaaac...", +".*aaaaaaaabbbbaabbbbaaaaaaaac*..", +"*aaaaaaaaabbbbaabbbbaaaaaaaaac*.", +"*aaaaaaaaaabbaabbbbaaaaaaaaaac*.", +"*aaaaaaaaaaaaabbbbaaaaaaaaaaac**", +"*aaaaaaaaaaaaabbbaaaaaaaaaaaac**", +"*aaaaaaaaaaaaabbaaaaaaaaaaaaac**", +"*aaaaaaaaaaaaabbaaaaaaaaaaaaac**", +"*aaaaaaaaaaaaaaaaaaaaaaaaaaaac**", +".*aaaaaaaaaaaabbaaaaaaaaaaaac***", +".*aaaaaaaaaaabbbbaaaaaaaaaaac***", +"..*aaaaaaaaaabbbbaaaaaaaaaac***.", +"...caaaaaaaaaabbaaaaaaaaaac****.", +"....caaaaaaaaaaaaaaaaaaaac****..", +".....caaaaaaaaaaaaaaaaaac****...", +"......ccaaaaaaaaaaaaaacc****....", +".......*cccaaaaaaaaccc*****.....", +"........***cccaaaac*******......", +"..........****caaac*****........", +".............*caaac**...........", +"...............caac**...........", +"................cac**...........", +".................cc**...........", +"..................***...........", +"...................**..........."}; + +} // namespace QQC2 + +#endif //QT_NO_IMAGEFORMAT_XPM diff --git a/src/imports/nativestyle/qstyle/qquickdrawutil.cpp b/src/imports/nativestyle/qstyle/qquickdrawutil.cpp new file mode 100644 index 00000000..d5290a7b --- /dev/null +++ b/src/imports/nativestyle/qstyle/qquickdrawutil.cpp @@ -0,0 +1,1145 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickdrawutil.h" + +#include "qbitmap.h" +#include "qpixmapcache.h" +#include "qpainter.h" +#include "qpalette.h" +#include <private/qpaintengineex_p.h> +#include <qvarlengtharray.h> +#include <qmath.h> +#include <private/qhexstring_p.h> + +QT_BEGIN_NAMESPACE + +namespace QQC2 { + +namespace { +class PainterStateGuard { + Q_DISABLE_COPY_MOVE(PainterStateGuard) +public: + explicit PainterStateGuard(QPainter *p) : m_painter(p) {} + ~PainterStateGuard() + { + for ( ; m_level > 0; --m_level) + m_painter->restore(); + } + + void save() + { + m_painter->save(); + ++m_level; + } + + void restore() + { + m_painter->restore(); + --m_level; + } + +private: + QPainter *m_painter; + int m_level= 0; +}; +} // namespace + +/*! + \headerfile <qdrawutil.h> + \title Drawing Utility Functions + + \sa QPainter +*/ + +/*! + \fn void qDrawShadeLine(QPainter *painter, int x1, int y1, int x2, int y2, + const QPalette &palette, bool sunken, + int lineWidth, int midLineWidth) + \relates <qdrawutil.h> + + Draws a horizontal (\a y1 == \a y2) or vertical (\a x1 == \a x2) + shaded line using the given \a painter. Note that nothing is + drawn if \a y1 != \a y2 and \a x1 != \a x2 (i.e. the line is + neither horizontal nor vertical). + + The provided \a palette specifies the shading colors (\l + {QPalette::light()}{light}, \l {QPalette::dark()}{dark} and \l + {QPalette::mid()}{middle} colors). The given \a lineWidth + specifies the line width for each of the lines; it is not the + total line width. The given \a midLineWidth specifies the width of + a middle line drawn in the QPalette::mid() color. + + The line appears sunken if \a sunken is true, otherwise raised. + + \warning This function does not look at QWidget::style() or + QApplication::style(). Use the drawing functions in QStyle to + make widgets that follow the current GUI style. + + + Alternatively you can use a QFrame widget and apply the + QFrame::setFrameStyle() function to display a shaded line: + + \snippet code/src_gui_painting_qdrawutil.cpp 0 + + \sa qDrawShadeRect(), qDrawShadePanel(), QStyle +*/ + +void qDrawShadeLine(QPainter *p, int x1, int y1, int x2, int y2, + const QPalette &pal, bool sunken, + int lineWidth, int midLineWidth) +{ + if (Q_UNLIKELY(!p || lineWidth < 0 || midLineWidth < 0)) { + qWarning("qDrawShadeLine: Invalid parameters"); + return; + } + int tlw = lineWidth*2 + midLineWidth; // total line width + QPen oldPen = p->pen(); // save pen + if (sunken) + p->setPen(pal.color(QPalette::Dark)); + else + p->setPen(pal.light().color()); + QPolygon a; + int i; + if (y1 == y2) { // horizontal line + int y = y1 - tlw/2; + if (x1 > x2) { // swap x1 and x2 + int t = x1; + x1 = x2; + x2 = t; + } + x2--; + for (i=0; i<lineWidth; i++) { // draw top shadow + a.setPoints(3, x1+i, y+tlw-1-i, + x1+i, y+i, + x2-i, y+i); + p->drawPolyline(a); + } + if (midLineWidth > 0) { + p->setPen(pal.mid().color()); + for (i=0; i<midLineWidth; i++) // draw lines in the middle + p->drawLine(x1+lineWidth, y+lineWidth+i, + x2-lineWidth, y+lineWidth+i); + } + if (sunken) + p->setPen(pal.light().color()); + else + p->setPen(pal.dark().color()); + for (i=0; i<lineWidth; i++) { // draw bottom shadow + a.setPoints(3, x1+i, y+tlw-i-1, + x2-i, y+tlw-i-1, + x2-i, y+i+1); + p->drawPolyline(a); + } + } + else if (x1 == x2) { // vertical line + int x = x1 - tlw/2; + if (y1 > y2) { // swap y1 and y2 + int t = y1; + y1 = y2; + y2 = t; + } + y2--; + for (i=0; i<lineWidth; i++) { // draw left shadow + a.setPoints(3, x+i, y2, + x+i, y1+i, + x+tlw-1, y1+i); + p->drawPolyline(a); + } + if (midLineWidth > 0) { + p->setPen(pal.mid().color()); + for (i=0; i<midLineWidth; i++) // draw lines in the middle + p->drawLine(x+lineWidth+i, y1+lineWidth, x+lineWidth+i, y2); + } + if (sunken) + p->setPen(pal.light().color()); + else + p->setPen(pal.dark().color()); + for (i=0; i<lineWidth; i++) { // draw right shadow + a.setPoints(3, x+lineWidth, y2-i, + x+tlw-i-1, y2-i, + x+tlw-i-1, y1+lineWidth); + p->drawPolyline(a); + } + } + p->setPen(oldPen); +} + +/*! + \fn void qDrawShadeRect(QPainter *painter, int x, int y, int width, int height, + const QPalette &palette, bool sunken, + int lineWidth, int midLineWidth, + const QBrush *fill) + \relates <qdrawutil.h> + + Draws the shaded rectangle beginning at (\a x, \a y) with the + given \a width and \a height using the provided \a painter. + + The provide \a palette specifies the shading colors (\l + {QPalette::light()}{light}, \l {QPalette::dark()}{dark} and \l + {QPalette::mid()}{middle} colors. The given \a lineWidth + specifies the line width for each of the lines; it is not the + total line width. The \a midLineWidth specifies the width of a + middle line drawn in the QPalette::mid() color. The rectangle's + interior is filled with the \a fill brush unless \a fill is 0. + + The rectangle appears sunken if \a sunken is true, otherwise + raised. + + \warning This function does not look at QWidget::style() or + QApplication::style(). Use the drawing functions in QStyle to make + widgets that follow the current GUI style. + + Alternatively you can use a QFrame widget and apply the + QFrame::setFrameStyle() function to display a shaded rectangle: + + \snippet code/src_gui_painting_qdrawutil.cpp 1 + + \sa qDrawShadeLine(), qDrawShadePanel(), qDrawPlainRect(), QStyle +*/ + +void qDrawShadeRect(QPainter *p, int x, int y, int w, int h, + const QPalette &pal, bool sunken, + int lineWidth, int midLineWidth, + const QBrush *fill) +{ + if (w == 0 || h == 0) + return; + if (Q_UNLIKELY(w < 0 || h < 0 || lineWidth < 0 || midLineWidth < 0)) { + qWarning("qDrawShadeRect: Invalid parameters"); + return; + } + + PainterStateGuard painterGuard(p); + const qreal devicePixelRatio = p->device()->devicePixelRatioF(); + if (!qFuzzyCompare(devicePixelRatio, qreal(1))) { + painterGuard.save(); + const qreal inverseScale = qreal(1) / devicePixelRatio; + p->scale(inverseScale, inverseScale); + x = qRound(devicePixelRatio * x); + y = qRound(devicePixelRatio * y); + w = qRound(devicePixelRatio * w); + h = qRound(devicePixelRatio * h); + lineWidth = qRound(devicePixelRatio * lineWidth); + midLineWidth = qRound(devicePixelRatio * midLineWidth); + } + + QPen oldPen = p->pen(); + if (sunken) + p->setPen(pal.dark().color()); + else + p->setPen(pal.light().color()); + int x1=x, y1=y, x2=x+w-1, y2=y+h-1; + + if (lineWidth == 1 && midLineWidth == 0) {// standard shade rectangle + p->drawRect(x1, y1, w-2, h-2); + if (sunken) + p->setPen(pal.light().color()); + else + p->setPen(pal.dark().color()); + QLineF lines[4] = { QLineF(x1+1, y1+1, x2-2, y1+1), + QLineF(x1+1, y1+2, x1+1, y2-2), + QLineF(x1, y2, x2, y2), + QLineF(x2,y1, x2,y2-1) }; + p->drawLines(lines, 4); // draw bottom/right lines + } else { // more complicated + int m = lineWidth+midLineWidth; + int i, j=0, k=m; + for (i=0; i<lineWidth; i++) { // draw top shadow + QLineF lines[4] = { QLineF(x1+i, y2-i, x1+i, y1+i), + QLineF(x1+i, y1+i, x2-i, y1+i), + QLineF(x1+k, y2-k, x2-k, y2-k), + QLineF(x2-k, y2-k, x2-k, y1+k) }; + p->drawLines(lines, 4); + k++; + } + p->setPen(pal.mid().color()); + j = lineWidth*2; + for (i=0; i<midLineWidth; i++) { // draw lines in the middle + p->drawRect(x1+lineWidth+i, y1+lineWidth+i, w-j-1, h-j-1); + j += 2; + } + if (sunken) + p->setPen(pal.light().color()); + else + p->setPen(pal.dark().color()); + k = m; + for (i=0; i<lineWidth; i++) { // draw bottom shadow + QLineF lines[4] = { QLineF(x1+1+i, y2-i, x2-i, y2-i), + QLineF(x2-i, y2-i, x2-i, y1+i+1), + QLineF(x1+k, y2-k, x1+k, y1+k), + QLineF(x1+k, y1+k, x2-k, y1+k) }; + p->drawLines(lines, 4); + k++; + } + } + if (fill) { + QBrush oldBrush = p->brush(); + int tlw = lineWidth + midLineWidth; + p->setPen(Qt::NoPen); + p->setBrush(*fill); + p->drawRect(x+tlw, y+tlw, w-2*tlw, h-2*tlw); + p->setBrush(oldBrush); + } + p->setPen(oldPen); // restore pen +} + + +/*! + \fn void qDrawShadePanel(QPainter *painter, int x, int y, int width, int height, + const QPalette &palette, bool sunken, + int lineWidth, const QBrush *fill) + \relates <qdrawutil.h> + + Draws the shaded panel beginning at (\a x, \a y) with the given \a + width and \a height using the provided \a painter and the given \a + lineWidth. + + The given \a palette specifies the shading colors (\l + {QPalette::light()}{light}, \l {QPalette::dark()}{dark} and \l + {QPalette::mid()}{middle} colors). The panel's interior is filled + with the \a fill brush unless \a fill is 0. + + The panel appears sunken if \a sunken is true, otherwise raised. + + \warning This function does not look at QWidget::style() or + QApplication::style(). Use the drawing functions in QStyle to make + widgets that follow the current GUI style. + + Alternatively you can use a QFrame widget and apply the + QFrame::setFrameStyle() function to display a shaded panel: + + \snippet code/src_gui_painting_qdrawutil.cpp 2 + + \sa qDrawWinPanel(), qDrawShadeLine(), qDrawShadeRect(), QStyle +*/ + +void qDrawShadePanel(QPainter *p, int x, int y, int w, int h, + const QPalette &pal, bool sunken, + int lineWidth, const QBrush *fill) +{ + if (w == 0 || h == 0) + return; + if (Q_UNLIKELY(w < 0 || h < 0 || lineWidth < 0)) { + qWarning("qDrawShadePanel: Invalid parameters"); + } + + PainterStateGuard painterGuard(p); + const qreal devicePixelRatio = p->device()->devicePixelRatioF(); + if (!qFuzzyCompare(devicePixelRatio, qreal(1))) { + painterGuard.save(); + const qreal inverseScale = qreal(1) / devicePixelRatio; + p->scale(inverseScale, inverseScale); + x = qRound(devicePixelRatio * x); + y = qRound(devicePixelRatio * y); + w = qRound(devicePixelRatio * w); + h = qRound(devicePixelRatio * h); + lineWidth = qRound(devicePixelRatio * lineWidth); + } + + QColor shade = pal.dark().color(); + QColor light = pal.light().color(); + if (fill) { + if (fill->color() == shade) + shade = pal.shadow().color(); + if (fill->color() == light) + light = pal.midlight().color(); + } + QPen oldPen = p->pen(); // save pen + QVector<QLineF> lines; + lines.reserve(2*lineWidth); + + if (sunken) + p->setPen(shade); + else + p->setPen(light); + int x1, y1, x2, y2; + int i; + x1 = x; + y1 = y2 = y; + x2 = x+w-2; + for (i=0; i<lineWidth; i++) { // top shadow + lines << QLineF(x1, y1++, x2--, y2++); + } + x2 = x1; + y1 = y+h-2; + for (i=0; i<lineWidth; i++) { // left shado + lines << QLineF(x1++, y1, x2++, y2--); + } + p->drawLines(lines); + lines.clear(); + if (sunken) + p->setPen(light); + else + p->setPen(shade); + x1 = x; + y1 = y2 = y+h-1; + x2 = x+w-1; + for (i=0; i<lineWidth; i++) { // bottom shadow + lines << QLineF(x1++, y1--, x2, y2--); + } + x1 = x2; + y1 = y; + y2 = y+h-lineWidth-1; + for (i=0; i<lineWidth; i++) { // right shadow + lines << QLineF(x1--, y1++, x2--, y2); + } + p->drawLines(lines); + if (fill) // fill with fill color + p->fillRect(x+lineWidth, y+lineWidth, w-lineWidth*2, h-lineWidth*2, *fill); + p->setPen(oldPen); // restore pen +} + + +/*! + \internal + This function draws a rectangle with two pixel line width. + It is called from qDrawWinButton() and qDrawWinPanel(). + + c1..c4 and fill are used: + + 1 1 1 1 1 2 + 1 3 3 3 4 2 + 1 3 F F 4 2 + 1 3 F F 4 2 + 1 4 4 4 4 2 + 2 2 2 2 2 2 +*/ + +static void qDrawWinShades(QPainter *p, + int x, int y, int w, int h, + const QColor &c1, const QColor &c2, + const QColor &c3, const QColor &c4, + const QBrush *fill) +{ + if (w < 2 || h < 2) // can't do anything with that + return; + + PainterStateGuard painterGuard(p); + const qreal devicePixelRatio = p->device()->devicePixelRatioF(); + if (!qFuzzyCompare(devicePixelRatio, qreal(1))) { + painterGuard.save(); + const qreal inverseScale = qreal(1) / devicePixelRatio; + p->scale(inverseScale, inverseScale); + x = qRound(devicePixelRatio * x); + y = qRound(devicePixelRatio * y); + w = qRound(devicePixelRatio * w); + h = qRound(devicePixelRatio * h); + } + + QPen oldPen = p->pen(); + QPoint a[3] = { QPoint(x, y+h-2), QPoint(x, y), QPoint(x+w-2, y) }; + p->setPen(c1); + p->drawPolyline(a, 3); + QPoint b[3] = { QPoint(x, y+h-1), QPoint(x+w-1, y+h-1), QPoint(x+w-1, y) }; + p->setPen(c2); + p->drawPolyline(b, 3); + if (w > 4 && h > 4) { + QPoint c[3] = { QPoint(x+1, y+h-3), QPoint(x+1, y+1), QPoint(x+w-3, y+1) }; + p->setPen(c3); + p->drawPolyline(c, 3); + QPoint d[3] = { QPoint(x+1, y+h-2), QPoint(x+w-2, y+h-2), QPoint(x+w-2, y+1) }; + p->setPen(c4); + p->drawPolyline(d, 3); + if (fill) + p->fillRect(QRect(x+2, y+2, w-4, h-4), *fill); + } + p->setPen(oldPen); +} + + +/*! + \fn void qDrawWinButton(QPainter *painter, int x, int y, int width, int height, + const QPalette &palette, bool sunken, + const QBrush *fill) + \relates <qdrawutil.h> + + Draws the Windows-style button specified by the given point (\a x, + \a y}, \a width and \a height using the provided \a painter with a + line width of 2 pixels. The button's interior is filled with the + \a{fill} brush unless \a fill is 0. + + The given \a palette specifies the shading colors (\l + {QPalette::light()}{light}, \l {QPalette::dark()}{dark} and \l + {QPalette::mid()}{middle} colors). + + The button appears sunken if \a sunken is true, otherwise raised. + + \warning This function does not look at QWidget::style() or + QApplication::style()-> Use the drawing functions in QStyle to make + widgets that follow the current GUI style. + + \sa qDrawWinPanel(), QStyle +*/ + +void qDrawWinButton(QPainter *p, int x, int y, int w, int h, + const QPalette &pal, bool sunken, + const QBrush *fill) +{ + if (sunken) + qDrawWinShades(p, x, y, w, h, + pal.shadow().color(), pal.light().color(), pal.dark().color(), + pal.button().color(), fill); + else + qDrawWinShades(p, x, y, w, h, + pal.light().color(), pal.shadow().color(), pal.button().color(), + pal.dark().color(), fill); +} + +/*! + \fn void qDrawWinPanel(QPainter *painter, int x, int y, int width, int height, + const QPalette &palette, bool sunken, + const QBrush *fill) + \relates <qdrawutil.h> + + Draws the Windows-style panel specified by the given point(\a x, + \a y), \a width and \a height using the provided \a painter with a + line width of 2 pixels. The button's interior is filled with the + \a fill brush unless \a fill is 0. + + The given \a palette specifies the shading colors. The panel + appears sunken if \a sunken is true, otherwise raised. + + \warning This function does not look at QWidget::style() or + QApplication::style(). Use the drawing functions in QStyle to make + widgets that follow the current GUI style. + + Alternatively you can use a QFrame widget and apply the + QFrame::setFrameStyle() function to display a shaded panel: + + \snippet code/src_gui_painting_qdrawutil.cpp 3 + + \sa qDrawShadePanel(), qDrawWinButton(), QStyle +*/ + +void qDrawWinPanel(QPainter *p, int x, int y, int w, int h, + const QPalette &pal, bool sunken, + const QBrush *fill) +{ + if (sunken) + qDrawWinShades(p, x, y, w, h, + pal.dark().color(), pal.light().color(), pal.shadow().color(), + pal.midlight().color(), fill); + else + qDrawWinShades(p, x, y, w, h, + pal.light().color(), pal.shadow().color(), pal.midlight().color(), + pal.dark().color(), fill); +} + +/*! + \fn void qDrawPlainRect(QPainter *painter, int x, int y, int width, int height, const QColor &lineColor, + int lineWidth, const QBrush *fill) + \relates <qdrawutil.h> + + Draws the plain rectangle beginning at (\a x, \a y) with the given + \a width and \a height, using the specified \a painter, \a lineColor + and \a lineWidth. The rectangle's interior is filled with the \a + fill brush unless \a fill is 0. + + \warning This function does not look at QWidget::style() or + QApplication::style(). Use the drawing functions in QStyle to make + widgets that follow the current GUI style. + + Alternatively you can use a QFrame widget and apply the + QFrame::setFrameStyle() function to display a plain rectangle: + + \snippet code/src_gui_painting_qdrawutil.cpp 4 + + \sa qDrawShadeRect(), QStyle +*/ + +void qDrawPlainRect(QPainter *p, int x, int y, int w, int h, const QColor &c, + int lineWidth, const QBrush *fill) +{ + if (w == 0 || h == 0) + return; + if (Q_UNLIKELY(w < 0 || h < 0 || lineWidth < 0)) { + qWarning("qDrawPlainRect: Invalid parameters"); + } + + PainterStateGuard painterGuard(p); + const qreal devicePixelRatio = p->device()->devicePixelRatioF(); + if (!qFuzzyCompare(devicePixelRatio, qreal(1))) { + painterGuard.save(); + const qreal inverseScale = qreal(1) / devicePixelRatio; + p->scale(inverseScale, inverseScale); + x = qRound(devicePixelRatio * x); + y = qRound(devicePixelRatio * y); + w = qRound(devicePixelRatio * w); + h = qRound(devicePixelRatio * h); + lineWidth = qRound(devicePixelRatio * lineWidth); + } + + QPen oldPen = p->pen(); + QBrush oldBrush = p->brush(); + p->setPen(c); + p->setBrush(Qt::NoBrush); + for (int i=0; i<lineWidth; i++) + p->drawRect(x+i, y+i, w-i*2 - 1, h-i*2 - 1); + if (fill) { // fill with fill color + p->setPen(Qt::NoPen); + p->setBrush(*fill); + p->drawRect(x+lineWidth, y+lineWidth, w-lineWidth*2, h-lineWidth*2); + } + p->setPen(oldPen); + p->setBrush(oldBrush); +} + +/***************************************************************************** + Overloaded functions. + *****************************************************************************/ + +/*! + \fn void qDrawShadeLine(QPainter *painter, const QPoint &p1, const QPoint &p2, + const QPalette &palette, bool sunken, int lineWidth, int midLineWidth) + \relates <qdrawutil.h> + \overload + + Draws a horizontal or vertical shaded line between \a p1 and \a p2 + using the given \a painter. Note that nothing is drawn if the line + between the points would be neither horizontal nor vertical. + + The provided \a palette specifies the shading colors (\l + {QPalette::light()}{light}, \l {QPalette::dark()}{dark} and \l + {QPalette::mid()}{middle} colors). The given \a lineWidth + specifies the line width for each of the lines; it is not the + total line width. The given \a midLineWidth specifies the width of + a middle line drawn in the QPalette::mid() color. + + The line appears sunken if \a sunken is true, otherwise raised. + + \warning This function does not look at QWidget::style() or + QApplication::style(). Use the drawing functions in QStyle to + make widgets that follow the current GUI style. + + + Alternatively you can use a QFrame widget and apply the + QFrame::setFrameStyle() function to display a shaded line: + + \snippet code/src_gui_painting_qdrawutil.cpp 5 + + \sa qDrawShadeRect(), qDrawShadePanel(), QStyle +*/ + +void qDrawShadeLine(QPainter *p, const QPoint &p1, const QPoint &p2, + const QPalette &pal, bool sunken, + int lineWidth, int midLineWidth) +{ + qDrawShadeLine(p, p1.x(), p1.y(), p2.x(), p2.y(), pal, sunken, + lineWidth, midLineWidth); +} + +/*! + \fn void qDrawShadeRect(QPainter *painter, const QRect &rect, const QPalette &palette, + bool sunken, int lineWidth, int midLineWidth, const QBrush *fill) + \relates <qdrawutil.h> + \overload + + Draws the shaded rectangle specified by \a rect using the given \a painter. + + The provide \a palette specifies the shading colors (\l + {QPalette::light()}{light}, \l {QPalette::dark()}{dark} and \l + {QPalette::mid()}{middle} colors. The given \a lineWidth + specifies the line width for each of the lines; it is not the + total line width. The \a midLineWidth specifies the width of a + middle line drawn in the QPalette::mid() color. The rectangle's + interior is filled with the \a fill brush unless \a fill is 0. + + The rectangle appears sunken if \a sunken is true, otherwise + raised. + + \warning This function does not look at QWidget::style() or + QApplication::style(). Use the drawing functions in QStyle to make + widgets that follow the current GUI style. + + Alternatively you can use a QFrame widget and apply the + QFrame::setFrameStyle() function to display a shaded rectangle: + + \snippet code/src_gui_painting_qdrawutil.cpp 6 + + \sa qDrawShadeLine(), qDrawShadePanel(), qDrawPlainRect(), QStyle +*/ + +void qDrawShadeRect(QPainter *p, const QRect &r, + const QPalette &pal, bool sunken, + int lineWidth, int midLineWidth, + const QBrush *fill) +{ + qDrawShadeRect(p, r.x(), r.y(), r.width(), r.height(), pal, sunken, + lineWidth, midLineWidth, fill); +} + +/*! + \fn void qDrawShadePanel(QPainter *painter, const QRect &rect, const QPalette &palette, + bool sunken, int lineWidth, const QBrush *fill) + \relates <qdrawutil.h> + \overload + + Draws the shaded panel at the rectangle specified by \a rect using the + given \a painter and the given \a lineWidth. + + The given \a palette specifies the shading colors (\l + {QPalette::light()}{light}, \l {QPalette::dark()}{dark} and \l + {QPalette::mid()}{middle} colors). The panel's interior is filled + with the \a fill brush unless \a fill is 0. + + The panel appears sunken if \a sunken is true, otherwise raised. + + \warning This function does not look at QWidget::style() or + QApplication::style(). Use the drawing functions in QStyle to make + widgets that follow the current GUI style. + + Alternatively you can use a QFrame widget and apply the + QFrame::setFrameStyle() function to display a shaded panel: + + \snippet code/src_gui_painting_qdrawutil.cpp 7 + + \sa qDrawWinPanel(), qDrawShadeLine(), qDrawShadeRect(), QStyle +*/ + +void qDrawShadePanel(QPainter *p, const QRect &r, + const QPalette &pal, bool sunken, + int lineWidth, const QBrush *fill) +{ + qDrawShadePanel(p, r.x(), r.y(), r.width(), r.height(), pal, sunken, + lineWidth, fill); +} + +/*! + \fn void qDrawWinButton(QPainter *painter, const QRect &rect, const QPalette &palette, + bool sunken, const QBrush *fill) + \relates <qdrawutil.h> + \overload + + Draws the Windows-style button at the rectangle specified by \a rect using + the given \a painter with a line width of 2 pixels. The button's interior + is filled with the \a{fill} brush unless \a fill is 0. + + The given \a palette specifies the shading colors (\l + {QPalette::light()}{light}, \l {QPalette::dark()}{dark} and \l + {QPalette::mid()}{middle} colors). + + The button appears sunken if \a sunken is true, otherwise raised. + + \warning This function does not look at QWidget::style() or + QApplication::style()-> Use the drawing functions in QStyle to make + widgets that follow the current GUI style. + + \sa qDrawWinPanel(), QStyle +*/ + +void qDrawWinButton(QPainter *p, const QRect &r, + const QPalette &pal, bool sunken, const QBrush *fill) +{ + qDrawWinButton(p, r.x(), r.y(), r.width(), r.height(), pal, sunken, fill); +} + +/*! + \fn void qDrawWinPanel(QPainter *painter, const QRect &rect, const QPalette &palette, + bool sunken, const QBrush *fill) + \overload + + Draws the Windows-style panel at the rectangle specified by \a rect using + the given \a painter with a line width of 2 pixels. The button's interior + is filled with the \a fill brush unless \a fill is 0. + + The given \a palette specifies the shading colors. The panel + appears sunken if \a sunken is true, otherwise raised. + + \warning This function does not look at QWidget::style() or + QApplication::style(). Use the drawing functions in QStyle to make + widgets that follow the current GUI style. + + Alternatively you can use a QFrame widget and apply the + QFrame::setFrameStyle() function to display a shaded panel: + + \snippet code/src_gui_painting_qdrawutil.cpp 8 + + \sa qDrawShadePanel(), qDrawWinButton(), QStyle +*/ + +void qDrawWinPanel(QPainter *p, const QRect &r, + const QPalette &pal, bool sunken, const QBrush *fill) +{ + qDrawWinPanel(p, r.x(), r.y(), r.width(), r.height(), pal, sunken, fill); +} + +/*! + \fn void qDrawPlainRect(QPainter *painter, const QRect &rect, const QColor &lineColor, int lineWidth, const QBrush *fill) + \relates <qdrawutil.h> + \overload + + Draws the plain rectangle specified by \a rect using the given \a painter, + \a lineColor and \a lineWidth. The rectangle's interior is filled with the + \a fill brush unless \a fill is 0. + + \warning This function does not look at QWidget::style() or + QApplication::style(). Use the drawing functions in QStyle to make + widgets that follow the current GUI style. + + Alternatively you can use a QFrame widget and apply the + QFrame::setFrameStyle() function to display a plain rectangle: + + \snippet code/src_gui_painting_qdrawutil.cpp 9 + + \sa qDrawShadeRect(), QStyle +*/ + +void qDrawPlainRect(QPainter *p, const QRect &r, const QColor &c, + int lineWidth, const QBrush *fill) +{ + qDrawPlainRect(p, r.x(), r.y(), r.width(), r.height(), c, + lineWidth, fill); +} + + +/*! + \class QTileRules + \since 4.6 + + \inmodule QtWidgets + + \brief The QTileRules class provides the rules used to draw a + pixmap or image split into nine segments. + + Spliiting is similar to \l{http://www.w3.org/TR/css3-background/}{CSS3 border-images}. + + \sa Qt::TileRule, QMargins +*/ + +/*! \fn QTileRules::QTileRules(Qt::TileRule horizontalRule, Qt::TileRule verticalRule) + Constructs a QTileRules with the given \a horizontalRule and + \a verticalRule. + */ + +/*! \fn QTileRules::QTileRules(Qt::TileRule rule) + Constructs a QTileRules with the given \a rule used for both + the horizontal rule and the vertical rule. + */ + +/*! + \fn void qDrawBorderPixmap(QPainter *painter, const QRect &target, const QMargins &margins, const QPixmap &pixmap) + \relates <qdrawutil.h> + \since 4.6 + + \brief The qDrawBorderPixmap function is for drawing a pixmap into + the margins of a rectangle. + + Draws the given \a pixmap into the given \a target rectangle, using the + given \a painter. The pixmap will be split into nine segments and drawn + according to the \a margins structure. +*/ + +typedef QVarLengthArray<QPainter::PixmapFragment, 16> QPixmapFragmentsArray; + +/*! + \since 4.6 + + Draws the indicated \a sourceRect rectangle from the given \a pixmap into + the given \a targetRect rectangle, using the given \a painter. The pixmap + will be split into nine segments according to the given \a targetMargins + and \a sourceMargins structures. Finally, the pixmap will be drawn + according to the given \a rules. + + This function is used to draw a scaled pixmap, similar to + \l{http://www.w3.org/TR/css3-background/}{CSS3 border-images} + + \sa Qt::TileRule, QTileRules, QMargins +*/ + +void qDrawBorderPixmap(QPainter *painter, const QRect &targetRect, const QMargins &targetMargins, + const QPixmap &pixmap, const QRect &sourceRect,const QMargins &sourceMargins, + const QTileRules &rules +#ifndef Q_CLANG_QDOC + , QDrawBorderPixmap::DrawingHints hints +#endif + ) +{ + QPainter::PixmapFragment d; + d.opacity = 1.0; + d.rotation = 0.0; + + QPixmapFragmentsArray opaqueData; + QPixmapFragmentsArray translucentData; + + // source center + const int sourceCenterTop = sourceRect.top() + sourceMargins.top(); + const int sourceCenterLeft = sourceRect.left() + sourceMargins.left(); + const int sourceCenterBottom = sourceRect.bottom() - sourceMargins.bottom() + 1; + const int sourceCenterRight = sourceRect.right() - sourceMargins.right() + 1; + const int sourceCenterWidth = sourceCenterRight - sourceCenterLeft; + const int sourceCenterHeight = sourceCenterBottom - sourceCenterTop; + // target center + const int targetCenterTop = targetRect.top() + targetMargins.top(); + const int targetCenterLeft = targetRect.left() + targetMargins.left(); + const int targetCenterBottom = targetRect.bottom() - targetMargins.bottom() + 1; + const int targetCenterRight = targetRect.right() - targetMargins.right() + 1; + const int targetCenterWidth = targetCenterRight - targetCenterLeft; + const int targetCenterHeight = targetCenterBottom - targetCenterTop; + + QVarLengthArray<qreal, 16> xTarget; // x-coordinates of target rectangles + QVarLengthArray<qreal, 16> yTarget; // y-coordinates of target rectangles + + int columns = 3; + int rows = 3; + if (rules.horizontal != Qt::StretchTile && sourceCenterWidth != 0) + columns = qMax(3, 2 + qCeil(targetCenterWidth / qreal(sourceCenterWidth))); + if (rules.vertical != Qt::StretchTile && sourceCenterHeight != 0) + rows = qMax(3, 2 + qCeil(targetCenterHeight / qreal(sourceCenterHeight))); + + xTarget.resize(columns + 1); + yTarget.resize(rows + 1); + + bool oldAA = painter->testRenderHint(QPainter::Antialiasing); + if (painter->paintEngine()->type() != QPaintEngine::OpenGL + && painter->paintEngine()->type() != QPaintEngine::OpenGL2 + && oldAA && painter->combinedTransform().type() != QTransform::TxNone) { + painter->setRenderHint(QPainter::Antialiasing, false); + } + + xTarget[0] = targetRect.left(); + xTarget[1] = targetCenterLeft; + xTarget[columns - 1] = targetCenterRight; + xTarget[columns] = targetRect.left() + targetRect.width(); + + yTarget[0] = targetRect.top(); + yTarget[1] = targetCenterTop; + yTarget[rows - 1] = targetCenterBottom; + yTarget[rows] = targetRect.top() + targetRect.height(); + + qreal dx = targetCenterWidth; + qreal dy = targetCenterHeight; + + switch (rules.horizontal) { + case Qt::StretchTile: + dx = targetCenterWidth; + break; + case Qt::RepeatTile: + dx = sourceCenterWidth; + break; + case Qt::RoundTile: + dx = targetCenterWidth / qreal(columns - 2); + break; + } + + for (int i = 2; i < columns - 1; ++i) + xTarget[i] = xTarget[i - 1] + dx; + + switch (rules.vertical) { + case Qt::StretchTile: + dy = targetCenterHeight; + break; + case Qt::RepeatTile: + dy = sourceCenterHeight; + break; + case Qt::RoundTile: + dy = targetCenterHeight / qreal(rows - 2); + break; + } + + for (int i = 2; i < rows - 1; ++i) + yTarget[i] = yTarget[i - 1] + dy; + + // corners + if (targetMargins.top() > 0 && targetMargins.left() > 0 && sourceMargins.top() > 0 && sourceMargins.left() > 0) { // top left + d.x = (0.5 * (xTarget[1] + xTarget[0])); + d.y = (0.5 * (yTarget[1] + yTarget[0])); + d.sourceLeft = sourceRect.left(); + d.sourceTop = sourceRect.top(); + d.width = sourceMargins.left(); + d.height = sourceMargins.top(); + d.scaleX = qreal(xTarget[1] - xTarget[0]) / d.width; + d.scaleY = qreal(yTarget[1] - yTarget[0]) / d.height; + if (hints & QDrawBorderPixmap::OpaqueTopLeft) + opaqueData.append(d); + else + translucentData.append(d); + } + if (targetMargins.top() > 0 && targetMargins.right() > 0 && sourceMargins.top() > 0 && sourceMargins.right() > 0) { // top right + d.x = (0.5 * (xTarget[columns] + xTarget[columns - 1])); + d.y = (0.5 * (yTarget[1] + yTarget[0])); + d.sourceLeft = sourceCenterRight; + d.sourceTop = sourceRect.top(); + d.width = sourceMargins.right(); + d.height = sourceMargins.top(); + d.scaleX = qreal(xTarget[columns] - xTarget[columns - 1]) / d.width; + d.scaleY = qreal(yTarget[1] - yTarget[0]) / d.height; + if (hints & QDrawBorderPixmap::OpaqueTopRight) + opaqueData.append(d); + else + translucentData.append(d); + } + if (targetMargins.bottom() > 0 && targetMargins.left() > 0 && sourceMargins.bottom() > 0 && sourceMargins.left() > 0) { // bottom left + d.x = (0.5 * (xTarget[1] + xTarget[0])); + d.y =(0.5 * (yTarget[rows] + yTarget[rows - 1])); + d.sourceLeft = sourceRect.left(); + d.sourceTop = sourceCenterBottom; + d.width = sourceMargins.left(); + d.height = sourceMargins.bottom(); + d.scaleX = qreal(xTarget[1] - xTarget[0]) / d.width; + d.scaleY = qreal(yTarget[rows] - yTarget[rows - 1]) / d.height; + if (hints & QDrawBorderPixmap::OpaqueBottomLeft) + opaqueData.append(d); + else + translucentData.append(d); + } + if (targetMargins.bottom() > 0 && targetMargins.right() > 0 && sourceMargins.bottom() > 0 && sourceMargins.right() > 0) { // bottom right + d.x = (0.5 * (xTarget[columns] + xTarget[columns - 1])); + d.y = (0.5 * (yTarget[rows] + yTarget[rows - 1])); + d.sourceLeft = sourceCenterRight; + d.sourceTop = sourceCenterBottom; + d.width = sourceMargins.right(); + d.height = sourceMargins.bottom(); + d.scaleX = qreal(xTarget[columns] - xTarget[columns - 1]) / d.width; + d.scaleY = qreal(yTarget[rows] - yTarget[rows - 1]) / d.height; + if (hints & QDrawBorderPixmap::OpaqueBottomRight) + opaqueData.append(d); + else + translucentData.append(d); + } + + // horizontal edges + if (targetCenterWidth > 0 && sourceCenterWidth > 0) { + if (targetMargins.top() > 0 && sourceMargins.top() > 0) { // top + QPixmapFragmentsArray &data = hints & QDrawBorderPixmap::OpaqueTop ? opaqueData : translucentData; + d.sourceLeft = sourceCenterLeft; + d.sourceTop = sourceRect.top(); + d.width = sourceCenterWidth; + d.height = sourceMargins.top(); + d.y = (0.5 * (yTarget[1] + yTarget[0])); + d.scaleX = dx / d.width; + d.scaleY = qreal(yTarget[1] - yTarget[0]) / d.height; + for (int i = 1; i < columns - 1; ++i) { + d.x = (0.5 * (xTarget[i + 1] + xTarget[i])); + data.append(d); + } + if (rules.horizontal == Qt::RepeatTile) + data[data.size() - 1].width = ((xTarget[columns - 1] - xTarget[columns - 2]) / d.scaleX); + } + if (targetMargins.bottom() > 0 && sourceMargins.bottom() > 0) { // bottom + QPixmapFragmentsArray &data = hints & QDrawBorderPixmap::OpaqueBottom ? opaqueData : translucentData; + d.sourceLeft = sourceCenterLeft; + d.sourceTop = sourceCenterBottom; + d.width = sourceCenterWidth; + d.height = sourceMargins.bottom(); + d.y = (0.5 * (yTarget[rows] + yTarget[rows - 1])); + d.scaleX = dx / d.width; + d.scaleY = qreal(yTarget[rows] - yTarget[rows - 1]) / d.height; + for (int i = 1; i < columns - 1; ++i) { + d.x = (0.5 * (xTarget[i + 1] + xTarget[i])); + data.append(d); + } + if (rules.horizontal == Qt::RepeatTile) + data[data.size() - 1].width = ((xTarget[columns - 1] - xTarget[columns - 2]) / d.scaleX); + } + } + + // vertical edges + if (targetCenterHeight > 0 && sourceCenterHeight > 0) { + if (targetMargins.left() > 0 && sourceMargins.left() > 0) { // left + QPixmapFragmentsArray &data = hints & QDrawBorderPixmap::OpaqueLeft ? opaqueData : translucentData; + d.sourceLeft = sourceRect.left(); + d.sourceTop = sourceCenterTop; + d.width = sourceMargins.left(); + d.height = sourceCenterHeight; + d.x = (0.5 * (xTarget[1] + xTarget[0])); + d.scaleX = qreal(xTarget[1] - xTarget[0]) / d.width; + d.scaleY = dy / d.height; + for (int i = 1; i < rows - 1; ++i) { + d.y = (0.5 * (yTarget[i + 1] + yTarget[i])); + data.append(d); + } + if (rules.vertical == Qt::RepeatTile) + data[data.size() - 1].height = ((yTarget[rows - 1] - yTarget[rows - 2]) / d.scaleY); + } + if (targetMargins.right() > 0 && sourceMargins.right() > 0) { // right + QPixmapFragmentsArray &data = hints & QDrawBorderPixmap::OpaqueRight ? opaqueData : translucentData; + d.sourceLeft = sourceCenterRight; + d.sourceTop = sourceCenterTop; + d.width = sourceMargins.right(); + d.height = sourceCenterHeight; + d.x = (0.5 * (xTarget[columns] + xTarget[columns - 1])); + d.scaleX = qreal(xTarget[columns] - xTarget[columns - 1]) / d.width; + d.scaleY = dy / d.height; + for (int i = 1; i < rows - 1; ++i) { + d.y = (0.5 * (yTarget[i + 1] + yTarget[i])); + data.append(d); + } + if (rules.vertical == Qt::RepeatTile) + data[data.size() - 1].height = ((yTarget[rows - 1] - yTarget[rows - 2]) / d.scaleY); + } + } + + // center + if (targetCenterWidth > 0 && targetCenterHeight > 0 && sourceCenterWidth > 0 && sourceCenterHeight > 0) { + QPixmapFragmentsArray &data = hints & QDrawBorderPixmap::OpaqueCenter ? opaqueData : translucentData; + d.sourceLeft = sourceCenterLeft; + d.sourceTop = sourceCenterTop; + d.width = sourceCenterWidth; + d.height = sourceCenterHeight; + d.scaleX = dx / d.width; + d.scaleY = dy / d.height; + + qreal repeatWidth = (xTarget[columns - 1] - xTarget[columns - 2]) / d.scaleX; + qreal repeatHeight = (yTarget[rows - 1] - yTarget[rows - 2]) / d.scaleY; + + for (int j = 1; j < rows - 1; ++j) { + d.y = (0.5 * (yTarget[j + 1] + yTarget[j])); + for (int i = 1; i < columns - 1; ++i) { + d.x = (0.5 * (xTarget[i + 1] + xTarget[i])); + data.append(d); + } + if (rules.horizontal == Qt::RepeatTile) + data[data.size() - 1].width = repeatWidth; + } + if (rules.vertical == Qt::RepeatTile) { + for (int i = 1; i < columns - 1; ++i) + data[data.size() - i].height = repeatHeight; + } + } + + if (opaqueData.size()) + painter->drawPixmapFragments(opaqueData.data(), opaqueData.size(), pixmap, QPainter::OpaqueHint); + if (translucentData.size()) + painter->drawPixmapFragments(translucentData.data(), translucentData.size(), pixmap); + + if (oldAA) + painter->setRenderHint(QPainter::Antialiasing, true); +} + +} // namespace QQC2 + +QT_END_NAMESPACE diff --git a/src/imports/nativestyle/qstyle/qquickdrawutil.h b/src/imports/nativestyle/qstyle/qquickdrawutil.h new file mode 100644 index 00000000..4f7a6c2e --- /dev/null +++ b/src/imports/nativestyle/qstyle/qquickdrawutil.h @@ -0,0 +1,170 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDRAWUTIL_H +#define QDRAWUTIL_H + +#include <QtCore/qnamespace.h> +#include <QtCore/qstring.h> // char*->QString conversion +#include <QtCore/qmargins.h> +#include <QtGui/qpixmap.h> + +QT_BEGIN_NAMESPACE + + +class QPainter; +class QPalette; +class QPoint; +class QColor; +class QBrush; +class QRect; + +namespace QQC2 { + +// +// Standard shade drawing +// + +void qDrawShadeLine(QPainter *p, int x1, int y1, int x2, int y2, + const QPalette &pal, bool sunken = true, + int lineWidth = 1, int midLineWidth = 0); + +void qDrawShadeLine(QPainter *p, const QPoint &p1, const QPoint &p2, + const QPalette &pal, bool sunken = true, + int lineWidth = 1, int midLineWidth = 0); + +void qDrawShadeRect(QPainter *p, int x, int y, int w, int h, + const QPalette &pal, bool sunken = false, + int lineWidth = 1, int midLineWidth = 0, + const QBrush *fill = nullptr); + +void qDrawShadeRect(QPainter *p, const QRect &r, + const QPalette &pal, bool sunken = false, + int lineWidth = 1, int midLineWidth = 0, + const QBrush *fill = nullptr); + +void qDrawShadePanel(QPainter *p, int x, int y, int w, int h, + const QPalette &pal, bool sunken = false, + int lineWidth = 1, const QBrush *fill = nullptr); + +void qDrawShadePanel(QPainter *p, const QRect &r, + const QPalette &pal, bool sunken = false, + int lineWidth = 1, const QBrush *fill = nullptr); + +void qDrawWinButton(QPainter *p, int x, int y, int w, int h, + const QPalette &pal, bool sunken = false, + const QBrush *fill = nullptr); + +void qDrawWinButton(QPainter *p, const QRect &r, + const QPalette &pal, bool sunken = false, + const QBrush *fill = nullptr); + +void qDrawWinPanel(QPainter *p, int x, int y, int w, int h, + const QPalette &pal, bool sunken = false, + const QBrush *fill = nullptr); + +void qDrawWinPanel(QPainter *p, const QRect &r, + const QPalette &pal, bool sunken = false, + const QBrush *fill = nullptr); + +void qDrawPlainRect(QPainter *p, int x, int y, int w, int h, const QColor &, + int lineWidth = 1, const QBrush *fill = nullptr); + +void qDrawPlainRect(QPainter *p, const QRect &r, const QColor &, + int lineWidth = 1, const QBrush *fill = nullptr); + + + +struct QTileRules +{ + inline QTileRules(Qt::TileRule horizontalRule, Qt::TileRule verticalRule) + : horizontal(horizontalRule), vertical(verticalRule) {} + inline QTileRules(Qt::TileRule rule = Qt::StretchTile) + : horizontal(rule), vertical(rule) {} + Qt::TileRule horizontal; + Qt::TileRule vertical; +}; + +#ifndef Q_CLANG_QDOC +// For internal use only. +namespace QDrawBorderPixmap +{ + enum DrawingHint + { + OpaqueTopLeft = 0x0001, + OpaqueTop = 0x0002, + OpaqueTopRight = 0x0004, + OpaqueLeft = 0x0008, + OpaqueCenter = 0x0010, + OpaqueRight = 0x0020, + OpaqueBottomLeft = 0x0040, + OpaqueBottom = 0x0080, + OpaqueBottomRight = 0x0100, + OpaqueCorners = OpaqueTopLeft | OpaqueTopRight | OpaqueBottomLeft | OpaqueBottomRight, + OpaqueEdges = OpaqueTop | OpaqueLeft | OpaqueRight | OpaqueBottom, + OpaqueFrame = OpaqueCorners | OpaqueEdges, + OpaqueAll = OpaqueCenter | OpaqueFrame + }; + + Q_DECLARE_FLAGS(DrawingHints, DrawingHint) +} +#endif + +void qDrawBorderPixmap(QPainter *painter, + const QRect &targetRect, + const QMargins &targetMargins, + const QPixmap &pixmap, + const QRect &sourceRect, + const QMargins &sourceMargins, + const QTileRules &rules = QTileRules() +#ifndef Q_CLANG_QDOC + , QDrawBorderPixmap::DrawingHints hints = QDrawBorderPixmap::DrawingHints() +#endif + ); + +inline void qDrawBorderPixmap(QPainter *painter, + const QRect &target, + const QMargins &margins, + const QPixmap &pixmap) +{ + qDrawBorderPixmap(painter, target, margins, pixmap, pixmap.rect(), margins); +} + +} // namespace QQC2 + +QT_END_NAMESPACE + +#endif // QDRAWUTIL_H diff --git a/src/imports/nativestyle/qstyle/qquicknativestyle.cpp b/src/imports/nativestyle/qstyle/qquicknativestyle.cpp new file mode 100644 index 00000000..7a2030e0 --- /dev/null +++ b/src/imports/nativestyle/qstyle/qquicknativestyle.cpp @@ -0,0 +1,47 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquicknativestyle.h" + +QT_BEGIN_NAMESPACE + +namespace QQC2 { + +QStyle *QQuickNativeStyle::s_style = nullptr; + +} // namespace QQC2 + +QT_END_NAMESPACE diff --git a/src/imports/nativestyle/qstyle/qquicknativestyle.h b/src/imports/nativestyle/qstyle/qquicknativestyle.h new file mode 100644 index 00000000..13c313f0 --- /dev/null +++ b/src/imports/nativestyle/qstyle/qquicknativestyle.h @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKNATIVESTYLE_H +#define QQUICKNATIVESTYLE_H + +#include "qquickstyle.h" + +QT_BEGIN_NAMESPACE + +namespace QQC2 { + +class QQuickNativeStyle +{ +public: + static void setStyle(QStyle *style) + { + if (s_style) + delete s_style; + s_style = style; + } + + inline static QStyle *style() + { + return s_style; + } + +private: + static QStyle *s_style; +}; + +} // namespace QQC2 + +QT_END_NAMESPACE + +#endif // QQUICKNATIVESTYLE_H diff --git a/src/imports/nativestyle/qstyle/qquickstyle.cpp b/src/imports/nativestyle/qstyle/qquickstyle.cpp new file mode 100644 index 00000000..2278fb65 --- /dev/null +++ b/src/imports/nativestyle/qstyle/qquickstyle.cpp @@ -0,0 +1,412 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickstyle.h" +#include "qquickstyle_p.h" +#include "qquickstyleoption.h" + +#include <QtGui/qpainter.h> +#include <QtGui/qbitmap.h> +#include <QtGui/qpixmapcache.h> +#include <QtGui/qpa/qplatformtheme.h> + +#include <QtGui/private/qguiapplication_p.h> + +#ifndef QT_NO_DEBUG +# include <QtCore/qdebug.h> +#endif + +#include <limits.h> +#include <algorithm> + +QT_BEGIN_NAMESPACE + +namespace QQC2 { + +/*! + Constructs a style object. +*/ +QStyle::QStyle() + : QObject(*new QStylePrivate) +{ + Q_D(QStyle); + d->proxyStyle = this; +} + +/*! + \internal + + Constructs a style object. +*/ +QStyle::QStyle(QStylePrivate &dd) + : QObject(dd) +{ + Q_D(QStyle); + d->proxyStyle = this; +} + +/*! + Destroys the style object. +*/ +QStyle::~QStyle() +{ +} + +/*! + \fn QRect QStyle::itemTextRect(const QFontMetrics &metrics, const QRect &rectangle, int alignment, bool enabled, const QString &text) const + + Returns the area within the given \a rectangle in which to draw + the provided \a text according to the specified font \a metrics + and \a alignment. The \a enabled parameter indicates whether or + not the associated item is enabled. + + If the given \a rectangle is larger than the area needed to render + the \a text, the rectangle that is returned will be offset within + \a rectangle according to the specified \a alignment. For + example, if \a alignment is Qt::AlignCenter, the returned + rectangle will be centered within \a rectangle. If the given \a + rectangle is smaller than the area needed, the returned rectangle + will be the smallest rectangle large enough to render the \a text. + + \sa Qt::Alignment +*/ +QRect QStyle::itemTextRect(const QFontMetrics &metrics, const QRect &rect, int alignment, bool enabled, + const QString &text) const +{ + QRect result; + int x, y, w, h; + rect.getRect(&x, &y, &w, &h); + if (!text.isEmpty()) { + result = metrics.boundingRect(x, y, w, h, alignment, text); + if (!enabled && proxy()->styleHint(SH_EtchDisabledText)) { + result.setWidth(result.width()+1); + result.setHeight(result.height()+1); + } + } else { + result = QRect(x, y, w, h); + } + return result; +} + +/*! + \fn QRect QStyle::itemPixmapRect(const QRect &rectangle, int alignment, const QPixmap &pixmap) const + + Returns the area within the given \a rectangle in which to draw + the specified \a pixmap according to the defined \a alignment. +*/ +QRect QStyle::itemPixmapRect(const QRect &rect, int alignment, const QPixmap &pixmap) const +{ + QRect result; + int x, y, w, h; + rect.getRect(&x, &y, &w, &h); + + const int pixmapWidth = pixmap.width()/pixmap.devicePixelRatio(); + const int pixmapHeight = pixmap.height()/pixmap.devicePixelRatio(); + + if ((alignment & Qt::AlignVCenter) == Qt::AlignVCenter) + y += h/2 - pixmapHeight/2; + else if ((alignment & Qt::AlignBottom) == Qt::AlignBottom) + y += h - pixmapHeight; + if ((alignment & Qt::AlignRight) == Qt::AlignRight) + x += w - pixmapWidth; + else if ((alignment & Qt::AlignHCenter) == Qt::AlignHCenter) + x += w/2 - pixmapWidth/2; + else if ((alignment & Qt::AlignLeft) != Qt::AlignLeft && QGuiApplication::isRightToLeft()) + x += w - pixmapWidth; + result = QRect(x, y, pixmapWidth, pixmapHeight); + return result; +} + +/*! + \fn void QStyle::drawItemText(QPainter *painter, const QRect &rectangle, int alignment, const QPalette &palette, bool enabled, const QString& text, QPalette::ColorRole textRole) const + + Draws the given \a text in the specified \a rectangle using the + provided \a painter and \a palette. + + The text is drawn using the painter's pen, and aligned and wrapped + according to the specified \a alignment. If an explicit \a + textRole is specified, the text is drawn using the \a palette's + color for the given role. The \a enabled parameter indicates + whether or not the item is enabled; when reimplementing this + function, the \a enabled parameter should influence how the item is + drawn. + + \sa Qt::Alignment, drawItemPixmap() +*/ +void QStyle::drawItemText(QPainter *painter, const QRect &rect, int alignment, const QPalette &pal, + bool enabled, const QString& text, QPalette::ColorRole textRole) const +{ + if (text.isEmpty()) + return; + QPen savedPen; + if (textRole != QPalette::NoRole) { + savedPen = painter->pen(); + painter->setPen(QPen(pal.brush(textRole), savedPen.widthF())); + } + if (!enabled) { + if (proxy()->styleHint(SH_DitherDisabledText)) { + QRect br; + painter->drawText(rect, alignment, text, &br); + painter->fillRect(br, QBrush(painter->background().color(), Qt::Dense5Pattern)); + return; + } else if (proxy()->styleHint(SH_EtchDisabledText)) { + QPen pen = painter->pen(); + painter->setPen(pal.light().color()); + painter->drawText(rect.adjusted(1, 1, 1, 1), alignment, text); + painter->setPen(pen); + } + } + painter->drawText(rect, alignment, text); + if (textRole != QPalette::NoRole) + painter->setPen(savedPen); +} + +/*! + \fn void QStyle::drawItemPixmap(QPainter *painter, const QRect &rectangle, int alignment, + const QPixmap &pixmap) const + + Draws the given \a pixmap in the specified \a rectangle, according + to the specified \a alignment, using the provided \a painter. + + \sa drawItemText() +*/ + +void QStyle::drawItemPixmap(QPainter *painter, const QRect &rect, int alignment, + const QPixmap &pixmap) const +{ + qreal scale = pixmap.devicePixelRatio(); + QRect aligned = alignedRect(QGuiApplication::layoutDirection(), QFlag(alignment), pixmap.size() / scale, rect); + QRect inter = aligned.intersected(rect); + + painter->drawPixmap(inter.x(), inter.y(), pixmap, inter.x() - aligned.x(), inter.y() - aligned.y(), inter.width() * scale, inter.height() *scale); +} + +/*! + \fn QRect QStyle::visualRect(Qt::LayoutDirection direction, const QRect &boundingRectangle, const QRect &logicalRectangle) + + Returns the given \a logicalRectangle converted to screen + coordinates based on the specified \a direction. The \a + boundingRectangle is used when performing the translation. + + This function is provided to support right-to-left desktops, and + is typically used in implementations of the subControlRect() + function. + + \sa QWidget::layoutDirection +*/ +QRect QStyle::visualRect(Qt::LayoutDirection direction, const QRect &boundingRect, const QRect &logicalRect) +{ + if (direction == Qt::LeftToRight) + return logicalRect; + QRect rect = logicalRect; + rect.translate(2 * (boundingRect.right() - logicalRect.right()) + + logicalRect.width() - boundingRect.width(), 0); + return rect; +} + +/*! + \fn QPoint QStyle::visualPos(Qt::LayoutDirection direction, const QRect &boundingRectangle, const QPoint &logicalPosition) + + Returns the given \a logicalPosition converted to screen + coordinates based on the specified \a direction. The \a + boundingRectangle is used when performing the translation. + + \sa QWidget::layoutDirection +*/ +QPoint QStyle::visualPos(Qt::LayoutDirection direction, const QRect &boundingRect, const QPoint &logicalPos) +{ + if (direction == Qt::LeftToRight) + return logicalPos; + return QPoint(boundingRect.right() - logicalPos.x(), logicalPos.y()); +} + +/*! + Returns a new rectangle of the specified \a size that is aligned to the given \a + rectangle according to the specified \a alignment and \a direction. + */ +QRect QStyle::alignedRect(Qt::LayoutDirection direction, Qt::Alignment alignment, const QSize &size, const QRect &rectangle) +{ + alignment = visualAlignment(direction, alignment); + int x = rectangle.x(); + int y = rectangle.y(); + int w = size.width(); + int h = size.height(); + if ((alignment & Qt::AlignVCenter) == Qt::AlignVCenter) + y += rectangle.size().height()/2 - h/2; + else if ((alignment & Qt::AlignBottom) == Qt::AlignBottom) + y += rectangle.size().height() - h; + if ((alignment & Qt::AlignRight) == Qt::AlignRight) + x += rectangle.size().width() - w; + else if ((alignment & Qt::AlignHCenter) == Qt::AlignHCenter) + x += rectangle.size().width()/2 - w/2; + return QRect(x, y, w, h); +} + +/*! + Transforms an \a alignment of Qt::AlignLeft or Qt::AlignRight + without Qt::AlignAbsolute into Qt::AlignLeft or Qt::AlignRight with + Qt::AlignAbsolute according to the layout \a direction. The other + alignment flags are left untouched. + + If no horizontal alignment was specified, the function returns the + default alignment for the given layout \a direction. + + QWidget::layoutDirection +*/ +Qt::Alignment QStyle::visualAlignment(Qt::LayoutDirection direction, Qt::Alignment alignment) +{ + return QGuiApplicationPrivate::visualAlignment(direction, alignment); +} + +/*! + Converts the given \a logicalValue to a pixel position. The \a min + parameter maps to 0, \a max maps to \a span and other values are + distributed evenly in-between. + + This function can handle the entire integer range without + overflow, providing that \a span is less than 4096. + + By default, this function assumes that the maximum value is on the + right for horizontal items and on the bottom for vertical items. + Set the \a upsideDown parameter to true to reverse this behavior. + + \sa sliderValueFromPosition() +*/ + +int QStyle::sliderPositionFromValue(int min, int max, int logicalValue, int span, bool upsideDown) +{ + if (span <= 0 || logicalValue < min || max <= min) + return 0; + if (logicalValue > max) + return upsideDown ? span : min; + + uint range = max - min; + uint p = upsideDown ? max - logicalValue : logicalValue - min; + + if (range > (uint)INT_MAX/4096) { + double dpos = (double(p))/(double(range)/span); + return int(dpos); + } else if (range > (uint)span) { + return (2 * p * span + range) / (2*range); + } else { + uint div = span / range; + uint mod = span % range; + return p * div + (2 * p * mod + range) / (2 * range); + } + // equiv. to (p * span) / range + 0.5 + // no overflow because of this implicit assumption: + // span <= 4096 +} + +/*! + \fn int QStyle::sliderValueFromPosition(int min, int max, int position, int span, bool upsideDown) + + Converts the given pixel \a position to a logical value. 0 maps to + the \a min parameter, \a span maps to \a max and other values are + distributed evenly in-between. + + This function can handle the entire integer range without + overflow. + + By default, this function assumes that the maximum value is on the + right for horizontal items and on the bottom for vertical + items. Set the \a upsideDown parameter to true to reverse this + behavior. + + \sa sliderPositionFromValue() +*/ + +int QStyle::sliderValueFromPosition(int min, int max, int pos, int span, bool upsideDown) +{ + if (span <= 0 || pos <= 0) + return upsideDown ? max : min; + if (pos >= span) + return upsideDown ? min : max; + + uint range = max - min; + + if ((uint)span > range) { + int tmp = (2 * pos * range + span) / (2 * span); + return upsideDown ? max - tmp : tmp + min; + } else { + uint div = range / span; + uint mod = range % span; + int tmp = pos * div + (2 * pos * mod + span) / (2 * span); + return upsideDown ? max - tmp : tmp + min; + } + // equiv. to min + (pos*range)/span + 0.5 + // no overflow because of this implicit assumption: + // pos <= span < sqrt(INT_MAX+0.0625)+0.25 ~ sqrt(INT_MAX) +} + +/*! + Returns the style's standard palette. + + Note that on systems that support system colors, the style's + standard palette is not used. In particular, the Windows + Vista and Mac styles do not use the standard palette, but make + use of native theme engines. With these styles, you should not set + the palette with QApplication::setPalette(). + + \sa QApplication::setPalette() + */ +QPalette QStyle::standardPalette() const +{ + QColor background = QColor(0xd4, 0xd0, 0xc8); // win 2000 grey + + QColor light(background.lighter()); + QColor dark(background.darker()); + QColor mid(Qt::gray); + QPalette palette(Qt::black, background, light, dark, mid, Qt::black, Qt::white); + palette.setBrush(QPalette::Disabled, QPalette::WindowText, dark); + palette.setBrush(QPalette::Disabled, QPalette::Text, dark); + palette.setBrush(QPalette::Disabled, QPalette::ButtonText, dark); + palette.setBrush(QPalette::Disabled, QPalette::Base, background); + return palette; +} + +//Windows and KDE allow menus to cover the taskbar, while GNOME and macOS don't +bool QStylePrivate::useFullScreenForPopup() +{ + auto theme = QGuiApplicationPrivate::platformTheme(); + return theme && theme->themeHint(QPlatformTheme::UseFullScreenForPopupMenu).toBool(); +} + +} // namespace QQC2 + +QT_END_NAMESPACE + +#include "moc_qquickstyle.cpp" diff --git a/src/imports/nativestyle/qstyle/qquickstyle.h b/src/imports/nativestyle/qstyle/qquickstyle.h new file mode 100644 index 00000000..4a2e5287 --- /dev/null +++ b/src/imports/nativestyle/qstyle/qquickstyle.h @@ -0,0 +1,829 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSTYLE_H +#define QSTYLE_H + +#include <QtCore/qobject.h> +#include <QtCore/qrect.h> +#include <QtCore/qsize.h> +#include <QtGui/qwindow.h> +#include <QtGui/qicon.h> +#include <QtGui/qpixmap.h> +#include <QtGui/qpalette.h> + +QT_BEGIN_NAMESPACE + +class QAction; +class QDebug; +class QFontMetrics; + +namespace QQC2 { + +class QStyleHintReturn; +class QStyleOption; +class QStyleOptionComplex; +class QStylePrivate; + +class QStyle : public QObject +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QStyle) + +protected: + QStyle(QStylePrivate &dd); + +public: + QStyle(); + virtual ~QStyle(); + + enum StateFlag { + State_None = 0x00000000, + State_Enabled = 0x00000001, + State_Raised = 0x00000002, + State_Sunken = 0x00000004, + State_Off = 0x00000008, + State_NoChange = 0x00000010, + State_On = 0x00000020, + State_DownArrow = 0x00000040, + State_Horizontal = 0x00000080, + State_HasFocus = 0x00000100, + State_Top = 0x00000200, + State_Bottom = 0x00000400, + State_FocusAtBorder = 0x00000800, + State_AutoRaise = 0x00001000, + State_MouseOver = 0x00002000, + State_UpArrow = 0x00004000, + State_Selected = 0x00008000, + State_Active = 0x00010000, + State_Window = 0x00020000, + State_Open = 0x00040000, + State_Children = 0x00080000, + State_Item = 0x00100000, + State_Sibling = 0x00200000, + State_Editing = 0x00400000, + State_KeyboardFocusChange = 0x00800000, +#ifdef QT_KEYPAD_NAVIGATION + State_HasEditFocus = 0x01000000, +#endif + State_ReadOnly = 0x02000000, + State_Small = 0x04000000, + State_Mini = 0x08000000 + }; + Q_ENUM(StateFlag) + Q_DECLARE_FLAGS(State, StateFlag) + + enum PrimitiveElement { + PE_Frame, + PE_FrameDefaultButton, + PE_FrameDockWidget, + PE_FrameFocusRect, + PE_FrameGroupBox, + PE_FrameLineEdit, + PE_FrameMenu, + PE_FrameStatusBarItem, + PE_FrameTabWidget, + PE_FrameWindow, + PE_FrameButtonBevel, + PE_FrameButtonTool, + PE_FrameTabBarBase, + + PE_PanelButtonCommand, + PE_PanelButtonBevel, + PE_PanelButtonTool, + PE_PanelMenuBar, + PE_PanelToolBar, + PE_PanelLineEdit, + + PE_IndicatorArrowDown, + PE_IndicatorArrowLeft, + PE_IndicatorArrowRight, + PE_IndicatorArrowUp, + PE_IndicatorBranch, + PE_IndicatorButtonDropDown, + PE_IndicatorItemViewItemCheck, + PE_IndicatorCheckBox, + PE_IndicatorDockWidgetResizeHandle, + PE_IndicatorHeaderArrow, + PE_IndicatorMenuCheckMark, + PE_IndicatorProgressChunk, + PE_IndicatorRadioButton, + PE_IndicatorSpinDown, + PE_IndicatorSpinMinus, + PE_IndicatorSpinPlus, + PE_IndicatorSpinUp, + PE_IndicatorToolBarHandle, + PE_IndicatorToolBarSeparator, + PE_PanelTipLabel, + PE_IndicatorTabTear, + PE_IndicatorTabTearLeft = PE_IndicatorTabTear, + PE_PanelScrollAreaCorner, + + PE_Widget, + + PE_IndicatorColumnViewArrow, + PE_IndicatorItemViewItemDrop, + + PE_PanelItemViewItem, + PE_PanelItemViewRow, // ### Qt 6: remove + + PE_PanelStatusBar, + + PE_IndicatorTabClose, + PE_PanelMenu, + + PE_IndicatorTabTearRight, + + // do not add any values below/greater this + PE_CustomBase = 0xf000000 + }; + Q_ENUM(PrimitiveElement) + + enum ControlElement { + CE_PushButton, + CE_PushButtonBevel, + CE_PushButtonLabel, + + CE_CheckBox, + CE_CheckBoxLabel, + + CE_RadioButton, + CE_RadioButtonLabel, + + CE_TabBarTab, + CE_TabBarTabShape, + CE_TabBarTabLabel, + + CE_ProgressBar, + CE_ProgressBarGroove, + CE_ProgressBarContents, + CE_ProgressBarLabel, + + CE_MenuItem, + CE_MenuScroller, + CE_MenuVMargin, + CE_MenuHMargin, + CE_MenuTearoff, + CE_MenuEmptyArea, + + CE_MenuBarItem, + CE_MenuBarEmptyArea, + + CE_ToolButtonLabel, + + CE_Header, + CE_HeaderSection, + CE_HeaderLabel, + + CE_ToolBoxTab, + CE_SizeGrip, + CE_Splitter, + CE_RubberBand, + CE_DockWidgetTitle, + + CE_ScrollBarAddLine, + CE_ScrollBarSubLine, + CE_ScrollBarAddPage, + CE_ScrollBarSubPage, + CE_ScrollBarSlider, + CE_ScrollBarFirst, + CE_ScrollBarLast, + + CE_FocusFrame, + CE_ComboBoxLabel, + + CE_ToolBar, + CE_ToolBoxTabShape, + CE_ToolBoxTabLabel, + CE_HeaderEmptyArea, + + CE_ColumnViewGrip, + + CE_ItemViewItem, + + CE_ShapedFrame, + + // do not add any values below/greater than this + CE_CustomBase = 0xf0000000 + }; + Q_ENUM(ControlElement) + + enum SubElement { + SE_PushButtonContents, + SE_PushButtonFocusRect, + + SE_CheckBoxIndicator, + SE_CheckBoxContents, + SE_CheckBoxFocusRect, + SE_CheckBoxClickRect, + + SE_RadioButtonIndicator, + SE_RadioButtonContents, + SE_RadioButtonFocusRect, + SE_RadioButtonClickRect, + + SE_ComboBoxFocusRect, + + SE_SliderFocusRect, + + SE_ProgressBarGroove, + SE_ProgressBarContents, + SE_ProgressBarLabel, + + SE_ToolBoxTabContents, + + SE_HeaderLabel, + SE_HeaderArrow, + + SE_TabWidgetTabBar, + SE_TabWidgetTabPane, + SE_TabWidgetTabContents, + SE_TabWidgetLeftCorner, + SE_TabWidgetRightCorner, + + SE_ItemViewItemCheckIndicator, + SE_TabBarTearIndicator, + SE_TabBarTearIndicatorLeft = SE_TabBarTearIndicator, + + SE_TreeViewDisclosureItem, + + SE_LineEditContents, + SE_FrameContents, + + SE_DockWidgetCloseButton, + SE_DockWidgetFloatButton, + SE_DockWidgetTitleBarText, + SE_DockWidgetIcon, + + SE_CheckBoxLayoutItem, + SE_ComboBoxLayoutItem, + SE_DateTimeEditLayoutItem, + SE_LabelLayoutItem, + SE_ProgressBarLayoutItem, + SE_PushButtonLayoutItem, + SE_RadioButtonLayoutItem, + SE_SliderLayoutItem, + SE_ScrollBarLayoutItem, + SE_SpinBoxLayoutItem, + SE_ToolButtonLayoutItem, + + SE_FrameLayoutItem, + SE_GroupBoxLayoutItem, + SE_TabWidgetLayoutItem, + + SE_ItemViewItemDecoration, + SE_ItemViewItemText, + SE_ItemViewItemFocusRect, + + SE_TabBarTabLeftButton, + SE_TabBarTabRightButton, + SE_TabBarTabText, + + SE_ShapedFrameContents, + + SE_ToolBarHandle, + + SE_TabBarScrollLeftButton, + SE_TabBarScrollRightButton, + SE_TabBarTearIndicatorRight, + + // do not add any values below/greater than this + SE_CustomBase = 0xf0000000 + }; + Q_ENUM(SubElement) + + enum ComplexControl { + CC_SpinBox, + CC_ComboBox, + CC_ScrollBar, + CC_Slider, + CC_ToolButton, + CC_TitleBar, + CC_Dial, + CC_GroupBox, + CC_MdiControls, + + // do not add any values below/greater than this + CC_CustomBase = 0xf0000000 + }; + Q_ENUM(ComplexControl) + + enum SubControl { + SC_None = 0x00000000, + + SC_ScrollBarAddLine = 0x00000001, + SC_ScrollBarSubLine = 0x00000002, + SC_ScrollBarAddPage = 0x00000004, + SC_ScrollBarSubPage = 0x00000008, + SC_ScrollBarFirst = 0x00000010, + SC_ScrollBarLast = 0x00000020, + SC_ScrollBarSlider = 0x00000040, + SC_ScrollBarGroove = 0x00000080, + + SC_SpinBoxUp = 0x00000001, + SC_SpinBoxDown = 0x00000002, + SC_SpinBoxFrame = 0x00000004, + SC_SpinBoxEditField = 0x00000008, + + SC_ComboBoxFrame = 0x00000001, + SC_ComboBoxEditField = 0x00000002, + SC_ComboBoxArrow = 0x00000004, + SC_ComboBoxListBoxPopup = 0x00000008, + + SC_SliderGroove = 0x00000001, + SC_SliderHandle = 0x00000002, + SC_SliderTickmarks = 0x00000004, + + SC_ToolButton = 0x00000001, + SC_ToolButtonMenu = 0x00000002, + + SC_TitleBarSysMenu = 0x00000001, + SC_TitleBarMinButton = 0x00000002, + SC_TitleBarMaxButton = 0x00000004, + SC_TitleBarCloseButton = 0x00000008, + SC_TitleBarNormalButton = 0x00000010, + SC_TitleBarShadeButton = 0x00000020, + SC_TitleBarUnshadeButton = 0x00000040, + SC_TitleBarContextHelpButton = 0x00000080, + SC_TitleBarLabel = 0x00000100, + + SC_DialGroove = 0x00000001, + SC_DialHandle = 0x00000002, + SC_DialTickmarks = 0x00000004, + + SC_GroupBoxCheckBox = 0x00000001, + SC_GroupBoxLabel = 0x00000002, + SC_GroupBoxContents = 0x00000004, + SC_GroupBoxFrame = 0x00000008, + + SC_MdiMinButton = 0x00000001, + SC_MdiNormalButton = 0x00000002, + SC_MdiCloseButton = 0x00000004, + + SC_CustomBase = 0xf0000000, + SC_All = 0xffffffff + }; + Q_ENUM(SubControl) + Q_DECLARE_FLAGS(SubControls, SubControl) + + enum PixelMetric { + PM_ButtonMargin, + PM_ButtonDefaultIndicator, + PM_MenuButtonIndicator, + PM_ButtonShiftHorizontal, + PM_ButtonShiftVertical, + + PM_DefaultFrameWidth, + PM_SpinBoxFrameWidth, + PM_ComboBoxFrameWidth, + + PM_MaximumDragDistance, + + PM_ScrollBarExtent, + PM_ScrollBarSliderMin, + + PM_SliderThickness, // total slider thickness + PM_SliderControlThickness, // thickness of the business part + PM_SliderLength, // total length of slider + PM_SliderTickmarkOffset, // + PM_SliderSpaceAvailable, // available space for slider to move + + PM_DockWidgetSeparatorExtent, + PM_DockWidgetHandleExtent, + PM_DockWidgetFrameWidth, + + PM_TabBarTabOverlap, + PM_TabBarTabHSpace, + PM_TabBarTabVSpace, + PM_TabBarBaseHeight, + PM_TabBarBaseOverlap, + + PM_ProgressBarChunkWidth, + + PM_SplitterWidth, + PM_TitleBarHeight, + + PM_MenuScrollerHeight, + PM_MenuHMargin, + PM_MenuVMargin, + PM_MenuPanelWidth, + PM_MenuTearoffHeight, + PM_MenuDesktopFrameWidth, + + PM_MenuBarPanelWidth, + PM_MenuBarItemSpacing, + PM_MenuBarVMargin, + PM_MenuBarHMargin, + + PM_IndicatorWidth, + PM_IndicatorHeight, + PM_ExclusiveIndicatorWidth, + PM_ExclusiveIndicatorHeight, + + PM_DialogButtonsSeparator, + PM_DialogButtonsButtonWidth, + PM_DialogButtonsButtonHeight, + + PM_MdiSubWindowFrameWidth, + PM_MdiSubWindowMinimizedWidth, + + PM_HeaderMargin, + PM_HeaderMarkSize, + PM_HeaderGripMargin, + PM_TabBarTabShiftHorizontal, + PM_TabBarTabShiftVertical, + PM_TabBarScrollButtonWidth, + + PM_ToolBarFrameWidth, + PM_ToolBarHandleExtent, + PM_ToolBarItemSpacing, + PM_ToolBarItemMargin, + PM_ToolBarSeparatorExtent, + PM_ToolBarExtensionExtent, + + PM_SpinBoxSliderHeight, + + PM_DefaultTopLevelMargin, + PM_DefaultChildMargin, + PM_DefaultLayoutSpacing, + + PM_ToolBarIconSize, + PM_ListViewIconSize, + PM_IconViewIconSize, + PM_SmallIconSize, + PM_LargeIconSize, + + PM_FocusFrameVMargin, + PM_FocusFrameHMargin, + + PM_ToolTipLabelFrameWidth, + PM_CheckBoxLabelSpacing, + PM_TabBarIconSize, + PM_SizeGripSize, + PM_DockWidgetTitleMargin, + PM_MessageBoxIconSize, + PM_ButtonIconSize, + + PM_DockWidgetTitleBarButtonMargin, + + PM_RadioButtonLabelSpacing, + PM_LayoutLeftMargin, + PM_LayoutTopMargin, + PM_LayoutRightMargin, + PM_LayoutBottomMargin, + PM_LayoutHorizontalSpacing, + PM_LayoutVerticalSpacing, + PM_TabBar_ScrollButtonOverlap, + + PM_TextCursorWidth, + + PM_TabCloseIndicatorWidth, + PM_TabCloseIndicatorHeight, + + PM_ScrollView_ScrollBarSpacing, + PM_ScrollView_ScrollBarOverlap, + PM_SubMenuOverlap, + PM_TreeViewIndentation, + + PM_HeaderDefaultSectionSizeHorizontal, + PM_HeaderDefaultSectionSizeVertical, + + PM_TitleBarButtonIconSize, + PM_TitleBarButtonSize, + + // do not add any values below/greater than this + PM_CustomBase = 0xf0000000 + }; + Q_ENUM(PixelMetric) + + enum ContentsType { + CT_PushButton, + CT_CheckBox, + CT_RadioButton, + CT_ToolButton, + CT_ComboBox, + CT_Splitter, + CT_ProgressBar, + CT_MenuItem, + CT_MenuBarItem, + CT_MenuBar, + CT_Menu, + CT_TabBarTab, + CT_Slider, + CT_Dial, + CT_ScrollBar, + CT_LineEdit, + CT_SpinBox, + CT_SizeGrip, + CT_TabWidget, + CT_DialogButtons, + CT_HeaderSection, + CT_GroupBox, + CT_MdiControls, + CT_ItemViewItem, + CT_Frame, + // do not add any values below/greater than this + CT_CustomBase = 0xf0000000 + }; + Q_ENUM(ContentsType) + + enum RequestSoftwareInputPanel { + RSIP_OnMouseClickAndAlreadyFocused, + RSIP_OnMouseClick + }; + Q_ENUM(RequestSoftwareInputPanel) + + enum StyleHint { + SH_EtchDisabledText, + SH_DitherDisabledText, + SH_ScrollBar_MiddleClickAbsolutePosition, + SH_ScrollBar_ScrollWhenPointerLeavesControl, + SH_TabBar_SelectMouseType, + SH_TabBar_Alignment, + SH_Header_ArrowAlignment, + SH_Slider_SnapToValue, + SH_Slider_SloppyKeyEvents, + SH_ProgressDialog_CenterCancelButton, + SH_ProgressDialog_TextLabelAlignment, + SH_PrintDialog_RightAlignButtons, + SH_MainWindow_SpaceBelowMenuBar, + SH_FontDialog_SelectAssociatedText, + SH_Menu_AllowActiveAndDisabled, + SH_Menu_SpaceActivatesItem, + SH_Menu_SubMenuPopupDelay, + SH_ScrollView_FrameOnlyAroundContents, + SH_MenuBar_AltKeyNavigation, + SH_ComboBox_ListMouseTracking, + SH_Menu_MouseTracking, + SH_MenuBar_MouseTracking, + SH_ItemView_ChangeHighlightOnFocus, + SH_Widget_ShareActivation, + SH_Workspace_FillSpaceOnMaximize, + SH_ComboBox_Popup, + SH_TitleBar_NoBorder, + SH_Slider_StopMouseOverSlider, + SH_BlinkCursorWhenTextSelected, + SH_RichText_FullWidthSelection, + SH_Menu_Scrollable, + SH_GroupBox_TextLabelVerticalAlignment, + SH_GroupBox_TextLabelColor, + SH_Menu_SloppySubMenus, + SH_Table_GridLineColor, + SH_LineEdit_PasswordCharacter, + SH_DialogButtons_DefaultButton, + SH_ToolBox_SelectedPageTitleBold, + SH_TabBar_PreferNoArrows, + SH_ScrollBar_LeftClickAbsolutePosition, + SH_ListViewExpand_SelectMouseType, + SH_UnderlineShortcut, + SH_SpinBox_AnimateButton, + SH_SpinBox_KeyPressAutoRepeatRate, + SH_SpinBox_ClickAutoRepeatRate, + SH_Menu_FillScreenWithScroll, + SH_ToolTipLabel_Opacity, + SH_DrawMenuBarSeparator, + SH_TitleBar_ModifyNotification, + SH_Button_FocusPolicy, + SH_MessageBox_UseBorderForButtonSpacing, + SH_TitleBar_AutoRaise, + SH_ToolButton_PopupDelay, + SH_FocusFrame_Mask, + SH_RubberBand_Mask, + SH_WindowFrame_Mask, + SH_SpinControls_DisableOnBounds, + SH_Dial_BackgroundRole, + SH_ComboBox_LayoutDirection, + SH_ItemView_EllipsisLocation, + SH_ItemView_ShowDecorationSelected, + SH_ItemView_ActivateItemOnSingleClick, + SH_ScrollBar_ContextMenu, + SH_ScrollBar_RollBetweenButtons, + SH_Slider_AbsoluteSetButtons, + SH_Slider_PageSetButtons, + SH_Menu_KeyboardSearch, + SH_TabBar_ElideMode, + SH_DialogButtonLayout, + SH_ComboBox_PopupFrameStyle, + SH_MessageBox_TextInteractionFlags, + SH_DialogButtonBox_ButtonsHaveIcons, + SH_SpellCheckUnderlineStyle, + SH_MessageBox_CenterButtons, + SH_Menu_SelectionWrap, + SH_ItemView_MovementWithoutUpdatingSelection, + SH_ToolTip_Mask, + SH_FocusFrame_AboveWidget, + SH_TextControl_FocusIndicatorTextCharFormat, + SH_WizardStyle, + SH_ItemView_ArrowKeysNavigateIntoChildren, + SH_Menu_Mask, + SH_Menu_FlashTriggeredItem, + SH_Menu_FadeOutOnHide, + SH_SpinBox_ClickAutoRepeatThreshold, + SH_ItemView_PaintAlternatingRowColorsForEmptyArea, + SH_FormLayoutWrapPolicy, + SH_TabWidget_DefaultTabPosition, + SH_ToolBar_Movable, + SH_FormLayoutFieldGrowthPolicy, + SH_FormLayoutFormAlignment, + SH_FormLayoutLabelAlignment, + SH_ItemView_DrawDelegateFrame, + SH_TabBar_CloseButtonPosition, + SH_DockWidget_ButtonsHaveFrame, + SH_ToolButtonStyle, + SH_RequestSoftwareInputPanel, + SH_ScrollBar_Transient, + SH_Menu_SupportsSections, + SH_ToolTip_WakeUpDelay, + SH_ToolTip_FallAsleepDelay, + SH_Widget_Animate, + SH_Splitter_OpaqueResize, + // Whether we should use a native popup. + // Only supported for non-editable combo boxes on Mac OS X so far. + SH_ComboBox_UseNativePopup, + SH_LineEdit_PasswordMaskDelay, + SH_TabBar_ChangeCurrentDelay, + SH_Menu_SubMenuUniDirection, + SH_Menu_SubMenuUniDirectionFailCount, + SH_Menu_SubMenuSloppySelectOtherActions, + SH_Menu_SubMenuSloppyCloseTimeout, + SH_Menu_SubMenuResetWhenReenteringParent, + SH_Menu_SubMenuDontStartSloppyOnLeave, + SH_ItemView_ScrollMode, + SH_TitleBar_ShowToolTipsOnButtons, + SH_Widget_Animation_Duration, + SH_ComboBox_AllowWheelScrolling, + SH_SpinBox_ButtonsInsideFrame, + SH_SpinBox_StepModifier, + // Add new style hint values here + + SH_CustomBase = 0xf0000000 + }; + Q_ENUM(StyleHint) + + enum StandardPixmap { + SP_TitleBarMenuButton, + SP_TitleBarMinButton, + SP_TitleBarMaxButton, + SP_TitleBarCloseButton, + SP_TitleBarNormalButton, + SP_TitleBarShadeButton, + SP_TitleBarUnshadeButton, + SP_TitleBarContextHelpButton, + SP_DockWidgetCloseButton, + SP_MessageBoxInformation, + SP_MessageBoxWarning, + SP_MessageBoxCritical, + SP_MessageBoxQuestion, + SP_DesktopIcon, + SP_TrashIcon, + SP_ComputerIcon, + SP_DriveFDIcon, + SP_DriveHDIcon, + SP_DriveCDIcon, + SP_DriveDVDIcon, + SP_DriveNetIcon, + SP_DirOpenIcon, + SP_DirClosedIcon, + SP_DirLinkIcon, + SP_DirLinkOpenIcon, + SP_FileIcon, + SP_FileLinkIcon, + SP_ToolBarHorizontalExtensionButton, + SP_ToolBarVerticalExtensionButton, + SP_FileDialogStart, + SP_FileDialogEnd, + SP_FileDialogToParent, + SP_FileDialogNewFolder, + SP_FileDialogDetailedView, + SP_FileDialogInfoView, + SP_FileDialogContentsView, + SP_FileDialogListView, + SP_FileDialogBack, + SP_DirIcon, + SP_DialogOkButton, + SP_DialogCancelButton, + SP_DialogHelpButton, + SP_DialogOpenButton, + SP_DialogSaveButton, + SP_DialogCloseButton, + SP_DialogApplyButton, + SP_DialogResetButton, + SP_DialogDiscardButton, + SP_DialogYesButton, + SP_DialogNoButton, + SP_ArrowUp, + SP_ArrowDown, + SP_ArrowLeft, + SP_ArrowRight, + SP_ArrowBack, + SP_ArrowForward, + SP_DirHomeIcon, + SP_CommandLink, + SP_VistaShield, + SP_BrowserReload, + SP_BrowserStop, + SP_MediaPlay, + SP_MediaStop, + SP_MediaPause, + SP_MediaSkipForward, + SP_MediaSkipBackward, + SP_MediaSeekForward, + SP_MediaSeekBackward, + SP_MediaVolume, + SP_MediaVolumeMuted, + SP_LineEditClearButton, + SP_DialogYesToAllButton, + SP_DialogNoToAllButton, + SP_DialogSaveAllButton, + SP_DialogAbortButton, + SP_DialogRetryButton, + SP_DialogIgnoreButton, + SP_RestoreDefaultsButton, + // do not add any values below/greater than this + SP_CustomBase = 0xf0000000 + }; + Q_ENUM(StandardPixmap) + + virtual QRect itemTextRect(const QFontMetrics &fm, const QRect &r, int flags, bool enabled, const QString &text) const; + virtual QRect itemPixmapRect(const QRect &r, int flags, const QPixmap &pixmap) const; + virtual QRect subElementRect(SubElement subElement, const QStyleOption *option) const = 0; + virtual QRect subControlRect(ComplexControl cc, const QStyleOptionComplex *opt, SubControl sc) const = 0; + + virtual QSize sizeFromContents(ContentsType ct, const QStyleOption *opt, const QSize &contentsSize) const = 0; + virtual QFont font(ControlElement element, const QStyle::State state) const = 0; + virtual QMargins ninePatchMargins(ControlElement ce, const QStyleOption *opt, const QSize &imageSize) const = 0; + virtual QMargins ninePatchMargins(ComplexControl cc, const QStyleOptionComplex *opt, const QSize &imageSize) const = 0; + + virtual SubControl hitTestComplexControl(ComplexControl cc, const QStyleOptionComplex *opt, const QPoint &pt) const = 0; + + virtual int pixelMetric(PixelMetric metric, const QStyleOption *option = nullptr) const = 0; + virtual int styleHint(StyleHint stylehint, const QStyleOption *opt = nullptr, QStyleHintReturn* returnData = nullptr) const = 0; + + virtual void drawItemText(QPainter *painter, const QRect &rect, + int flags, const QPalette &pal, bool enabled, + const QString &text, QPalette::ColorRole textRole = QPalette::NoRole) const; + virtual void drawItemPixmap(QPainter *painter, const QRect &rect, int alignment, const QPixmap &pixmap) const; + virtual void drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, QPainter *p) const = 0; + virtual void drawControl(ControlElement element, const QStyleOption *opt, QPainter *p) const = 0; + virtual void drawComplexControl(ComplexControl cc, const QStyleOptionComplex *opt, QPainter *p) const = 0; + + virtual QPalette standardPalette() const; + virtual QPixmap standardPixmap(StandardPixmap standardPixmap, const QStyleOption *opt = nullptr) const = 0; + virtual QIcon standardIcon(StandardPixmap standardIcon, const QStyleOption *option = nullptr) const = 0; + virtual QPixmap generatedIconPixmap(QIcon::Mode iconMode, const QPixmap &pixmap, const QStyleOption *opt) const = 0; + + static QRect visualRect(Qt::LayoutDirection direction, const QRect &boundingRect, const QRect &logicalRect); + static QPoint visualPos(Qt::LayoutDirection direction, const QRect &boundingRect, const QPoint &logicalPos); + static int sliderPositionFromValue(int min, int max, int val, int space, bool upsideDown = false); + static int sliderValueFromPosition(int min, int max, int pos, int space, bool upsideDown = false); + static Qt::Alignment visualAlignment(Qt::LayoutDirection direction, Qt::Alignment alignment); + static QRect alignedRect(Qt::LayoutDirection direction, Qt::Alignment alignment, const QSize &size, const QRect &rectangle); + + // TODO: Remove the concept of proxy (but keep it for now until everything builds) + const QStyle *proxy() const { return this; } + +private: + Q_DISABLE_COPY(QStyle) +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QStyle::State) +Q_DECLARE_OPERATORS_FOR_FLAGS(QStyle::SubControls) + +} // namespace QQC2 + +QT_END_NAMESPACE + +#endif // QSTYLE_H diff --git a/src/imports/nativestyle/qstyle/qquickstyle_p.h b/src/imports/nativestyle/qstyle/qquickstyle_p.h new file mode 100644 index 00000000..46f53740 --- /dev/null +++ b/src/imports/nativestyle/qstyle/qquickstyle_p.h @@ -0,0 +1,121 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSTYLE_P_H +#define QSTYLE_P_H + +#include "qquickstyle.h" + +#include <QtCore/private/qobject_p.h> +#include <QtGui/qguiapplication.h> + +QT_BEGIN_NAMESPACE + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of qstyle_*.cpp. This header file may change from version to version +// without notice, or even be removed. +// +// We mean it. +// + +// Private class + +namespace QQC2 { + +class QStylePrivate: public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QStyle) + +public: + static bool useFullScreenForPopup(); + mutable int layoutSpacingIndex = -1; + QStyle *proxyStyle; +}; + +inline QImage styleCacheImage(const QSize &size) +{ + const qreal pixelRatio = qApp->devicePixelRatio(); + QImage cacheImage = QImage(size * pixelRatio, QImage::Format_ARGB32_Premultiplied); + cacheImage.setDevicePixelRatio(pixelRatio); + return cacheImage; +} + +inline QPixmap styleCachePixmap(const QSize &size) +{ + const qreal pixelRatio = qApp->devicePixelRatio(); + QPixmap cachePixmap = QPixmap(size * pixelRatio); + cachePixmap.setDevicePixelRatio(pixelRatio); + return cachePixmap; +} + +#define BEGIN_STYLE_PIXMAPCACHE(a) \ + QRect rect = option->rect; \ + QPixmap internalPixmapCache; \ + QImage imageCache; \ + QPainter *p = painter; \ + QString unique = QStyleHelper::uniqueName((a), option, option->rect.size()); \ + int txType = painter->deviceTransform().type() | painter->worldTransform().type(); \ + bool doPixmapCache = (!option->rect.isEmpty()) \ + && ((txType <= QTransform::TxTranslate) || (painter->deviceTransform().type() == QTransform::TxScale)); \ + if (doPixmapCache && QPixmapCache::find(unique, &internalPixmapCache)) { \ + painter->drawPixmap(option->rect.topLeft(), internalPixmapCache); \ + } else { \ + if (doPixmapCache) { \ + rect.setRect(0, 0, option->rect.width(), option->rect.height()); \ + imageCache = styleCacheImage(option->rect.size()); \ + imageCache.fill(0); \ + p = new QPainter(&imageCache); \ + } + +#define END_STYLE_PIXMAPCACHE \ + if (doPixmapCache) { \ + p->end(); \ + delete p; \ + internalPixmapCache = QPixmap::fromImage(imageCache); \ + painter->drawPixmap(option->rect.topLeft(), internalPixmapCache); \ + QPixmapCache::insert(unique, internalPixmapCache); \ + } \ + } + +} // namespace QQC2 + +QT_END_NAMESPACE + +#endif //QSTYLE_P_H diff --git a/src/imports/nativestyle/qstyle/qquickstylehelper.cpp b/src/imports/nativestyle/qstyle/qquickstylehelper.cpp new file mode 100644 index 00000000..346408a4 --- /dev/null +++ b/src/imports/nativestyle/qstyle/qquickstylehelper.cpp @@ -0,0 +1,438 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickstylehelper_p.h" +#include "qquickstyleoption.h" +#include "qquickstyle_p.h" + +#include <QtCore/qmath.h> +#include <QtGui/qpainter.h> +#include <QtGui/qpixmapcache.h> +#include <QtGui/qwindow.h> +#include <QtGui/private/qhighdpiscaling_p.h> +#include <QtGui/private/qguiapplication_p.h> +#include <QtGui/private/qmath_p.h> +#include <QtGui/private/qhexstring_p.h> + +#include <qmetaobject.h> +#include <qstringbuilder.h> + +QT_BEGIN_NAMESPACE + +Q_GUI_EXPORT int qt_defaultDpiX(); + +namespace QQC2 { + +namespace QStyleHelper { + +QString uniqueName(const QString &key, const QStyleOption *option, const QSize &size) +{ + const QStyleOptionComplex *complexOption = qstyleoption_cast<const QStyleOptionComplex *>(option); + QString tmp = key % HexString<uint>(option->state) + % HexString<uint>(option->direction) + % HexString<uint>(complexOption ? uint(complexOption->activeSubControls) : 0u) + % HexString<quint64>(option->palette.cacheKey()) + % HexString<uint>(size.width()) + % HexString<uint>(size.height()); + + if (const QStyleOptionSpinBox *spinBox = qstyleoption_cast<const QStyleOptionSpinBox *>(option)) { + tmp = tmp % HexString<uint>(spinBox->buttonSymbols) + % HexString<uint>(spinBox->stepEnabled) + % QLatin1Char(spinBox->frame ? '1' : '0'); ; + } + + return tmp; +} + +#ifdef Q_OS_DARWIN +static const qreal qstyleBaseDpi = 72; +#else +static const qreal qstyleBaseDpi = 96; +#endif + +qreal dpi(const QStyleOption *option) +{ +#ifndef Q_OS_DARWIN + // Prioritize the application override, except for on macOS where + // we have historically not supported the AA_Use96Dpi flag. + if (QCoreApplication::testAttribute(Qt::AA_Use96Dpi)) + return 96; +#endif + + // Expect that QStyleOption::QFontMetrics::QFont has the correct DPI set + if (option) + return option->fontMetrics.fontDpi(); + + return qstyleBaseDpi; +} + +qreal dpiScaled(qreal value, qreal dpi) +{ + return value * dpi / qstyleBaseDpi; +} + +qreal dpiScaled(qreal value, const QPaintDevice *device) +{ + return dpiScaled(value, device->logicalDpiX()); +} + +qreal dpiScaled(qreal value, const QStyleOption *option) +{ + return dpiScaled(value, dpi(option)); +} + +bool isInstanceOf(QObject *obj, QAccessible::Role role) +{ + bool match = false; + QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(obj); + match = iface && iface->role() == role; + return match; +} + +// Searches for an ancestor of a particular accessible role +bool hasAncestor(QObject *obj, QAccessible::Role role) +{ + bool found = false; + QObject *parent = obj ? obj->parent() : nullptr; + while (parent && !found) { + if (isInstanceOf(parent, role)) + found = true; + parent = parent->parent(); + } + return found; +} + + +int calcBigLineSize(int radius) +{ + int bigLineSize = radius / 6; + if (bigLineSize < 4) + bigLineSize = 4; + if (bigLineSize > radius / 2) + bigLineSize = radius / 2; + return bigLineSize; +} + +static QPointF calcRadialPos(const QStyleOptionSlider *dial, qreal offset) +{ + const int width = dial->rect.width(); + const int height = dial->rect.height(); + const int r = qMin(width, height) / 2; + const int currentSliderPosition = dial->upsideDown ? dial->sliderPosition : (dial->maximum - dial->sliderPosition); + qreal a = 0; + if (dial->maximum == dial->minimum) + a = Q_PI / 2; + else if (dial->dialWrapping) + a = Q_PI * 3 / 2 - (currentSliderPosition - dial->minimum) * 2 * Q_PI + / (dial->maximum - dial->minimum); + else + a = (Q_PI * 8 - (currentSliderPosition - dial->minimum) * 10 * Q_PI + / (dial->maximum - dial->minimum)) / 6; + qreal xc = width / 2.0; + qreal yc = height / 2.0; + qreal len = r - QStyleHelper::calcBigLineSize(r) - 3; + qreal back = offset * len; + QPointF pos(QPointF(xc + back * qCos(a), yc - back * qSin(a))); + return pos; +} + +qreal angle(const QPointF &p1, const QPointF &p2) +{ + static const qreal rad_factor = 180 / Q_PI; + qreal _angle = 0; + + if (p1.x() == p2.x()) { + if (p1.y() < p2.y()) + _angle = 270; + else + _angle = 90; + } else { + qreal x1, x2, y1, y2; + + if (p1.x() <= p2.x()) { + x1 = p1.x(); y1 = p1.y(); + x2 = p2.x(); y2 = p2.y(); + } else { + x2 = p1.x(); y2 = p1.y(); + x1 = p2.x(); y1 = p2.y(); + } + + qreal m = -(y2 - y1) / (x2 - x1); + _angle = qAtan(m) * rad_factor; + + if (p1.x() < p2.x()) + _angle = 180 - _angle; + else + _angle = -_angle; + } + return _angle; +} + +QPolygonF calcLines(const QStyleOptionSlider *dial) +{ + QPolygonF poly; + int width = dial->rect.width(); + int height = dial->rect.height(); + qreal r = qMin(width, height) / 2; + int bigLineSize = calcBigLineSize(int(r)); + + qreal xc = width / 2 + 0.5; + qreal yc = height / 2 + 0.5; + const int ns = dial->tickInterval; + if (!ns) // Invalid values may be set by Qt Designer. + return poly; + int notches = (dial->maximum + ns - 1 - dial->minimum) / ns; + if (notches <= 0) + return poly; + if (dial->maximum < dial->minimum || dial->maximum - dial->minimum > 1000) { + int maximum = dial->minimum + 1000; + notches = (maximum + ns - 1 - dial->minimum) / ns; + } + + poly.resize(2 + 2 * notches); + int smallLineSize = bigLineSize / 2; + for (int i = 0; i <= notches; ++i) { + qreal angle = dial->dialWrapping ? Q_PI * 3 / 2 - i * 2 * Q_PI / notches + : (Q_PI * 8 - i * 10 * Q_PI / notches) / 6; + qreal s = qSin(angle); + qreal c = qCos(angle); + if (i == 0 || (((ns * i) % (dial->pageStep ? dial->pageStep : 1)) == 0)) { + poly[2 * i] = QPointF(xc + (r - bigLineSize) * c, + yc - (r - bigLineSize) * s); + poly[2 * i + 1] = QPointF(xc + r * c, yc - r * s); + } else { + poly[2 * i] = QPointF(xc + (r - 1 - smallLineSize) * c, + yc - (r - 1 - smallLineSize) * s); + poly[2 * i + 1] = QPointF(xc + (r - 1) * c, yc -(r - 1) * s); + } + } + return poly; +} + +// This will draw a nice and shiny QDial for us. We don't want +// all the shinyness in QWindowsStyle, hence we place it here + +void drawDial(const QStyleOptionSlider *option, QPainter *painter) +{ + QPalette pal = option->palette; + QColor buttonColor = pal.button().color(); + const int width = option->rect.width(); + const int height = option->rect.height(); + const bool enabled = option->state & QStyle::State_Enabled; + qreal r = qMin(width, height) / 2; + r -= r/50; + const qreal penSize = r/20.0; + + painter->save(); + painter->setRenderHint(QPainter::Antialiasing); + + // Draw notches + if (option->subControls & QStyle::SC_DialTickmarks) { + painter->setPen(option->palette.dark().color().darker(120)); + painter->drawLines(QStyleHelper::calcLines(option)); + } + + // setting color before BEGIN_STYLE_PIXMAPCACHE since + // otherwise it is not set when the image is in the cache + buttonColor.setHsv(buttonColor .hue(), + qMin(140, buttonColor .saturation()), + qMax(180, buttonColor.value())); + + // Cache dial background + BEGIN_STYLE_PIXMAPCACHE(QString::fromLatin1("qdial")) + p->setRenderHint(QPainter::Antialiasing); + + const qreal d_ = r / 6; + const qreal dx = option->rect.x() + d_ + (width - 2 * r) / 2 + 1; + const qreal dy = option->rect.y() + d_ + (height - 2 * r) / 2 + 1; + + QRectF br = QRectF(dx + 0.5, dy + 0.5, + int(r * 2 - 2 * d_ - 2), + int(r * 2 - 2 * d_ - 2)); + + if (enabled) { + // Drop shadow + qreal shadowSize = qMax(1.0, penSize/2.0); + QRectF shadowRect= br.adjusted(-2*shadowSize, -2*shadowSize, + 2*shadowSize, 2*shadowSize); + QRadialGradient shadowGradient(shadowRect.center().x(), + shadowRect.center().y(), shadowRect.width()/2.0, + shadowRect.center().x(), shadowRect.center().y()); + shadowGradient.setColorAt(qreal(0.91), QColor(0, 0, 0, 40)); + shadowGradient.setColorAt(qreal(1.0), Qt::transparent); + p->setBrush(shadowGradient); + p->setPen(Qt::NoPen); + p->translate(shadowSize, shadowSize); + p->drawEllipse(shadowRect); + p->translate(-shadowSize, -shadowSize); + + // Main gradient + QRadialGradient gradient(br.center().x() - br.width()/3, dy, + br.width()*1.3, br.center().x(), + br.center().y() - br.height()/2); + gradient.setColorAt(0, buttonColor.lighter(110)); + gradient.setColorAt(qreal(0.5), buttonColor); + gradient.setColorAt(qreal(0.501), buttonColor.darker(102)); + gradient.setColorAt(1, buttonColor.darker(115)); + p->setBrush(gradient); + } else { + p->setBrush(Qt::NoBrush); + } + + p->setPen(QPen(buttonColor.darker(280))); + p->drawEllipse(br); + p->setBrush(Qt::NoBrush); + p->setPen(buttonColor.lighter(110)); + p->drawEllipse(br.adjusted(1, 1, -1, -1)); + + if (option->state & QStyle::State_HasFocus) { + QColor highlight = pal.highlight().color(); + highlight.setHsv(highlight.hue(), + qMin(160, highlight.saturation()), + qMax(230, highlight.value())); + highlight.setAlpha(127); + p->setPen(QPen(highlight, 2.0)); + p->setBrush(Qt::NoBrush); + p->drawEllipse(br.adjusted(-1, -1, 1, 1)); + } + + END_STYLE_PIXMAPCACHE + + QPointF dp = calcRadialPos(option, qreal(0.70)); + buttonColor = buttonColor.lighter(104); + buttonColor.setAlphaF(qreal(0.8)); + const qreal ds = r/qreal(7.0); + QRectF dialRect(dp.x() - ds, dp.y() - ds, 2*ds, 2*ds); + QRadialGradient dialGradient(dialRect.center().x() + dialRect.width()/2, + dialRect.center().y() + dialRect.width(), + dialRect.width()*2, + dialRect.center().x(), dialRect.center().y()); + dialGradient.setColorAt(1, buttonColor.darker(140)); + dialGradient.setColorAt(qreal(0.4), buttonColor.darker(120)); + dialGradient.setColorAt(0, buttonColor.darker(110)); + if (penSize > 3.0) { + painter->setPen(QPen(QColor(0, 0, 0, 25), penSize)); + painter->drawLine(calcRadialPos(option, qreal(0.90)), calcRadialPos(option, qreal(0.96))); + } + + painter->setBrush(dialGradient); + painter->setPen(QColor(255, 255, 255, 150)); + painter->drawEllipse(dialRect.adjusted(-1, -1, 1, 1)); + painter->setPen(QColor(0, 0, 0, 80)); + painter->drawEllipse(dialRect); + painter->restore(); +} + +void drawBorderPixmap(const QPixmap &pixmap, QPainter *painter, const QRect &rect, + int left, int top, int right, + int bottom) +{ + QSize size = pixmap.size(); + //painter->setRenderHint(QPainter::SmoothPixmapTransform); + + //top + if (top > 0) { + painter->drawPixmap(QRect(rect.left() + left, rect.top(), rect.width() -right - left, top), pixmap, + QRect(left, 0, size.width() -right - left, top)); + + //top-left + if(left > 0) + painter->drawPixmap(QRect(rect.left(), rect.top(), left, top), pixmap, + QRect(0, 0, left, top)); + + //top-right + if (right > 0) + painter->drawPixmap(QRect(rect.left() + rect.width() - right, rect.top(), right, top), pixmap, + QRect(size.width() - right, 0, right, top)); + } + + //left + if (left > 0) + painter->drawPixmap(QRect(rect.left(), rect.top()+top, left, rect.height() - top - bottom), pixmap, + QRect(0, top, left, size.height() - bottom - top)); + + //center + painter->drawPixmap(QRect(rect.left() + left, rect.top()+top, rect.width() -right - left, + rect.height() - bottom - top), pixmap, + QRect(left, top, size.width() -right -left, + size.height() - bottom - top)); + //right + if (right > 0) + painter->drawPixmap(QRect(rect.left() +rect.width() - right, rect.top()+top, right, rect.height() - top - bottom), pixmap, + QRect(size.width() - right, top, right, size.height() - bottom - top)); + + //bottom + if (bottom > 0) { + painter->drawPixmap(QRect(rect.left() +left, rect.top() + rect.height() - bottom, + rect.width() - right - left, bottom), pixmap, + QRect(left, size.height() - bottom, + size.width() - right - left, bottom)); + //bottom-left + if (left > 0) + painter->drawPixmap(QRect(rect.left(), rect.top() + rect.height() - bottom, left, bottom), pixmap, + QRect(0, size.height() - bottom, left, bottom)); + + //bottom-right + if (right > 0) + painter->drawPixmap(QRect(rect.left() + rect.width() - right, rect.top() + rect.height() - bottom, right, bottom), pixmap, + QRect(size.width() - right, size.height() - bottom, right, bottom)); + + } +} + +WidgetSizePolicy widgetSizePolicy(const QStyleOption *opt) +{ + if (opt && opt->state & QStyle::State_Mini) + return SizeMini; + else if (opt && opt->state & QStyle::State_Small) + return SizeSmall; + + return SizeDefault; +} + +QColor backgroundColor(const QPalette &pal) +{ +// if (qobject_cast<const QScrollBar *>(widget) && widget->parent() && +// qobject_cast<const QAbstractScrollArea *>(widget->parent()->parent())) +// return widget->parentWidget()->parentWidget()->palette().color(QPalette::Base); + return pal.color(QPalette::Base); +} + +} + +} // namespace QQC2 + +QT_END_NAMESPACE diff --git a/src/imports/nativestyle/qstyle/qquickstylehelper_p.h b/src/imports/nativestyle/qstyle/qquickstylehelper_p.h new file mode 100644 index 00000000..757d647d --- /dev/null +++ b/src/imports/nativestyle/qstyle/qquickstylehelper_p.h @@ -0,0 +1,99 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtCore/qpoint.h> +#include <QtCore/qstring.h> +#include <QtGui/qpaintdevice.h> +#include <QtGui/qpolygon.h> +#include <QtCore/qstringbuilder.h> +#include <QtGui/qaccessible.h> + +#ifndef QSTYLEHELPER_P_H +#define QSTYLEHELPER_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. +// + +QT_BEGIN_NAMESPACE + +class QObject; +class QPainter; +class QPixmap; + +namespace QQC2 { + +class QStyleOptionSlider; +class QStyleOption; + +namespace QStyleHelper +{ + QString uniqueName(const QString &key, const QStyleOption *option, const QSize &size); + + qreal dpi(const QStyleOption *option); + qreal dpiScaled(qreal value, qreal dpi); + qreal dpiScaled(qreal value, const QPaintDevice *device); + qreal dpiScaled(qreal value, const QStyleOption *option); + + qreal angle(const QPointF &p1, const QPointF &p2); + QPolygonF calcLines(const QStyleOptionSlider *dial); + int calcBigLineSize(int radius); + void drawDial(const QStyleOptionSlider *dial, QPainter *painter); + + void drawBorderPixmap(const QPixmap &pixmap, QPainter *painter, const QRect &rect, + int left = 0, int top = 0, int right = 0, + int bottom = 0); + + bool isInstanceOf(QObject *obj, QAccessible::Role role); + bool hasAncestor(QObject *obj, QAccessible::Role role); + QColor backgroundColor(const QPalette &pal); + + enum WidgetSizePolicy { SizeLarge = 0, SizeSmall = 1, SizeMini = 2, SizeDefault = -1 }; + WidgetSizePolicy widgetSizePolicy(const QStyleOption *opt); +} + +} // namespace QQC2 + +QT_END_NAMESPACE + +#endif // QSTYLEHELPER_P_H diff --git a/src/imports/nativestyle/qstyle/qquickstyleoption.cpp b/src/imports/nativestyle/qstyle/qquickstyleoption.cpp new file mode 100644 index 00000000..c34dddd7 --- /dev/null +++ b/src/imports/nativestyle/qstyle/qquickstyleoption.cpp @@ -0,0 +1,652 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickstyleoption.h" + +#include <QtGui/private/qguiapplication_p.h> +#include <QtCore/qdebug.h> +#include <QtCore/qmath.h> + +QT_BEGIN_NAMESPACE + +namespace QQC2 { + +QStyleOption::QStyleOption(int version, int type) + : version(version), type(type), state(QStyle::State_None), + direction(QGuiApplication::layoutDirection()), fontMetrics(QFont()), + styleObject(nullptr), control(nullptr), window(nullptr) +{ +} + +/*! + Destroys this style option object. +*/ +QStyleOption::~QStyleOption() +{ +} + +/*! + Constructs a copy of \a other. +*/ +QStyleOption::QStyleOption(const QStyleOption &other) + : version(Version), type(Type), state(other.state), + direction(other.direction), rect(other.rect), fontMetrics(other.fontMetrics), + palette(other.palette), styleObject(other.styleObject), + control(other.control), window(other.window) +{ +} + +/*! + Assign \a other to this QStyleOption. +*/ +QStyleOption &QStyleOption::operator=(const QStyleOption &other) +{ + control = other.control; + window = other.window; + state = other.state; + direction = other.direction; + rect = other.rect; + fontMetrics = other.fontMetrics; + palette = other.palette; + styleObject = other.styleObject; + return *this; +} + +/*! + Constructs a QStyleOptionFocusRect, initializing the members + variables to their default values. +*/ +QStyleOptionFocusRect::QStyleOptionFocusRect() + : QStyleOption(Version, SO_FocusRect) +{ + state |= QStyle::State_KeyboardFocusChange; // assume we had one, will be corrected in initFrom() +} + +/*! + \internal +*/ +QStyleOptionFocusRect::QStyleOptionFocusRect(int versionIn) + : QStyleOption(versionIn, SO_FocusRect) +{ + state |= QStyle::State_KeyboardFocusChange; // assume we had one, will be corrected in initFrom() +} + +/*! + Constructs a QStyleOptionFrame, initializing the members + variables to their default values. +*/ +QStyleOptionFrame::QStyleOptionFrame() + : QStyleOption(Version, SO_Frame), lineWidth(0), midLineWidth(0), + features(None), frameShape(NoFrame) +{ +} + +/*! + \internal +*/ +QStyleOptionFrame::QStyleOptionFrame(int versionIn) + : QStyleOption(versionIn, SO_Frame), lineWidth(0), midLineWidth(0), + features(None) +{ +} + +/*! + Constructs a QStyleOptionGroupBox, initializing the members + variables to their default values. +*/ +QStyleOptionGroupBox::QStyleOptionGroupBox() + : QStyleOptionComplex(Version, Type), features(QStyleOptionFrame::None), + textAlignment(Qt::AlignLeft), lineWidth(0), midLineWidth(0) +{ +} + +QStyleOptionGroupBox::QStyleOptionGroupBox(int versionIn) + : QStyleOptionComplex(versionIn, Type), features(QStyleOptionFrame::None), + textAlignment(Qt::AlignLeft), lineWidth(0), midLineWidth(0) +{ +} + +/*! + Constructs a QStyleOptionHeader, initializing the members + variables to their default values. +*/ +QStyleOptionHeader::QStyleOptionHeader() + : QStyleOption(QStyleOptionHeader::Version, SO_Header), + section(0), textAlignment(Qt::AlignLeft), iconAlignment(Qt::AlignLeft), + position(QStyleOptionHeader::Beginning), + selectedPosition(QStyleOptionHeader::NotAdjacent), sortIndicator(None), + orientation(Qt::Horizontal) +{ +} + +/*! + \internal +*/ +QStyleOptionHeader::QStyleOptionHeader(int versionIn) + : QStyleOption(versionIn, SO_Header), + section(0), textAlignment(Qt::AlignLeft), iconAlignment(Qt::AlignLeft), + position(QStyleOptionHeader::Beginning), + selectedPosition(QStyleOptionHeader::NotAdjacent), sortIndicator(None), + orientation(Qt::Horizontal) +{ +} + +/*! + Constructs a QStyleOptionButton, initializing the members + variables to their default values. +*/ +QStyleOptionButton::QStyleOptionButton() + : QStyleOption(QStyleOptionButton::Version, SO_Button), features(None) +{ +} + +/*! + \internal +*/ +QStyleOptionButton::QStyleOptionButton(int versionIn) + : QStyleOption(versionIn, SO_Button), features(None) +{ +} + +/*! + Constructs a QStyleOptionToolBar, initializing the members + variables to their default values. +*/ +QStyleOptionToolBar::QStyleOptionToolBar() + : QStyleOption(Version, SO_ToolBar), positionOfLine(OnlyOne), positionWithinLine(OnlyOne), + toolBarArea(Qt::TopToolBarArea), features(None), lineWidth(0), midLineWidth(0) +{ +} + +/*! + \fn QStyleOptionToolBar::QStyleOptionToolBar(const QStyleOptionToolBar &other) + + Constructs a copy of the \a other style option. +*/ +QStyleOptionToolBar::QStyleOptionToolBar(int versionIn) +: QStyleOption(versionIn, SO_ToolBar), positionOfLine(OnlyOne), positionWithinLine(OnlyOne), + toolBarArea(Qt::TopToolBarArea), features(None), lineWidth(0), midLineWidth(0) +{ + +} + +/*! + Constructs a QStyleOptionTab object, initializing the members + variables to their default values. +*/ +QStyleOptionTab::QStyleOptionTab() + : QStyleOption(QStyleOptionTab::Version, SO_Tab), + row(0), + position(Beginning), + selectedPosition(NotAdjacent), cornerWidgets(QStyleOptionTab::NoCornerWidgets), + documentMode(false), + features(QStyleOptionTab::None) +{ +} + +QStyleOptionTab::QStyleOptionTab(int versionIn) + : QStyleOption(versionIn, SO_Tab), + row(0), + position(Beginning), + selectedPosition(NotAdjacent), cornerWidgets(QStyleOptionTab::NoCornerWidgets), + documentMode(false), + features(QStyleOptionTab::None) +{ +} + +/*! + Constructs a QStyleOptionTabV4 object, initializing the members + variables to their default values. + */ +QStyleOptionTabV4::QStyleOptionTabV4() : QStyleOptionTab(QStyleOptionTabV4::Version) +{ +} + +/*! + Constructs a QStyleOptionProgressBar, initializing the members + variables to their default values. +*/ +QStyleOptionProgressBar::QStyleOptionProgressBar() + : QStyleOption(QStyleOptionProgressBar::Version, SO_ProgressBar), + minimum(0), maximum(0), progress(0), textAlignment(Qt::AlignLeft), textVisible(false), + invertedAppearance(false), bottomToTop(false) +{ +} + +QStyleOptionProgressBar::QStyleOptionProgressBar(int versionIn) + : QStyleOption(versionIn, SO_ProgressBar), + minimum(0), maximum(0), progress(0), textAlignment(Qt::AlignLeft), textVisible(false), + invertedAppearance(false), bottomToTop(false) +{ +} + +/*! + Constructs a QStyleOptionMenuItem, initializing the members + variables to their default values. +*/ +QStyleOptionMenuItem::QStyleOptionMenuItem() + : QStyleOption(QStyleOptionMenuItem::Version, SO_MenuItem), menuItemType(Normal), + checkType(NotCheckable), checked(false), menuHasCheckableItems(true), maxIconWidth(0), tabWidth(0) +{ +} + +/*! + \internal +*/ +QStyleOptionMenuItem::QStyleOptionMenuItem(int versionIn) + : QStyleOption(versionIn, SO_MenuItem), menuItemType(Normal), + checkType(NotCheckable), checked(false), menuHasCheckableItems(true), maxIconWidth(0), tabWidth(0) +{ +} + +/*! + Constructs a QStyleOptionComplex of the specified \a type and \a + version, initializing the member variables to their default + values. This constructor is usually called by subclasses. +*/ +QStyleOptionComplex::QStyleOptionComplex(int versionIn, int typeIn) + : QStyleOption(versionIn, typeIn), subControls(QStyle::SC_All), activeSubControls(QStyle::SC_None) +{ +} + + +/*! + Constructs a QStyleOptionSlider, initializing the members + variables to their default values. +*/ +QStyleOptionSlider::QStyleOptionSlider() + : QStyleOptionComplex(Version, SO_Slider), orientation(Qt::Horizontal), minimum(0), maximum(0), + tickPosition(NoTicks), tickInterval(0), upsideDown(false), + sliderPosition(0), sliderValue(0), singleStep(0), pageStep(0), notchTarget(0.0), + dialWrapping(false) +{ +} + +/*! + \internal +*/ +QStyleOptionSlider::QStyleOptionSlider(int versionIn) + : QStyleOptionComplex(versionIn, SO_Slider), orientation(Qt::Horizontal), minimum(0), maximum(0), + tickPosition(NoTicks), tickInterval(0), upsideDown(false), + sliderPosition(0), sliderValue(0), singleStep(0), pageStep(0), notchTarget(0.0), + dialWrapping(false) +{ +} + +/*! + Constructs a QStyleOptionSpinBox, initializing the members + variables to their default values. +*/ +QStyleOptionSpinBox::QStyleOptionSpinBox() + : QStyleOptionComplex(Version, SO_SpinBox), buttonSymbols(UpDownArrows), + stepEnabled(StepNone), frame(false) +{ +} + +/*! + \internal +*/ +QStyleOptionSpinBox::QStyleOptionSpinBox(int versionIn) + : QStyleOptionComplex(versionIn, SO_SpinBox), buttonSymbols(UpDownArrows), + stepEnabled(StepNone), frame(false) +{ +} + +/*! + Constructs a QStyleOptionDockWidget, initializing the member + variables to their default values. +*/ +QStyleOptionDockWidget::QStyleOptionDockWidget() + : QStyleOption(Version, SO_DockWidget), closable(false), + movable(false), floatable(false), verticalTitleBar(false) +{ +} + +/*! + \internal +*/ +QStyleOptionDockWidget::QStyleOptionDockWidget(int versionIn) + : QStyleOption(versionIn, SO_DockWidget), closable(false), + movable(false), floatable(false), verticalTitleBar(false) +{ +} + +/*! + Constructs a QStyleOptionToolButton, initializing the members + variables to their default values. +*/ +QStyleOptionToolButton::QStyleOptionToolButton() + : QStyleOptionComplex(Version, SO_ToolButton), features(None), arrowType(Qt::DownArrow) + , toolButtonStyle(Qt::ToolButtonIconOnly) +{ +} + +QStyleOptionToolButton::QStyleOptionToolButton(int versionIn) + : QStyleOptionComplex(versionIn, SO_ToolButton), features(None), arrowType(Qt::DownArrow) + , toolButtonStyle(Qt::ToolButtonIconOnly) + +{ +} + +/*! + Creates a QStyleOptionComboBox, initializing the members variables + to their default values. +*/ +QStyleOptionComboBox::QStyleOptionComboBox() + : QStyleOptionComplex(Version, SO_ComboBox), editable(false), frame(true) +{ +} + +QStyleOptionComboBox::QStyleOptionComboBox(int versionIn) + : QStyleOptionComplex(versionIn, SO_ComboBox), editable(false), frame(true) +{ +} + +/*! + Creates a QStyleOptionToolBox, initializing the members variables + to their default values. +*/ +QStyleOptionToolBox::QStyleOptionToolBox() + : QStyleOption(Version, SO_ToolBox), position(Beginning), selectedPosition(NotAdjacent) +{ +} + +QStyleOptionToolBox::QStyleOptionToolBox(int versionIn) + : QStyleOption(versionIn, SO_ToolBox), position(Beginning), selectedPosition(NotAdjacent) +{ +} + + +/*! + Creates a QStyleOptionRubberBand, initializing the members + variables to their default values. +*/ +QStyleOptionRubberBand::QStyleOptionRubberBand() + : QStyleOption(Version, SO_RubberBand), opaque(false), shape(Line) +{ +} + +QStyleOptionRubberBand::QStyleOptionRubberBand(int versionIn) + : QStyleOption(versionIn, SO_RubberBand), opaque(false) +{ +} + +/*! + Constructs a QStyleOptionTitleBar, initializing the members + variables to their default values. +*/ +QStyleOptionTitleBar::QStyleOptionTitleBar() + : QStyleOptionComplex(Version, SO_TitleBar), titleBarState(0) +{ +} + +QStyleOptionTitleBar::QStyleOptionTitleBar(int versionIn) + : QStyleOptionComplex(versionIn, SO_TitleBar), titleBarState(0) +{ +} + +/*! + Constructs a QStyleOptionViewItem, initializing the members + variables to their default values. +*/ +QStyleOptionViewItem::QStyleOptionViewItem() + : QStyleOption(Version, SO_ViewItem), + displayAlignment(Qt::AlignLeft), decorationAlignment(Qt::AlignLeft), + textElideMode(Qt::ElideMiddle), decorationPosition(Left), + showDecorationSelected(false), features(None), + checkState(Qt::Unchecked), viewItemPosition(QStyleOptionViewItem::Invalid) +{ +} + +QStyleOptionViewItem::QStyleOptionViewItem(int versionIn) + : QStyleOption(versionIn, SO_ViewItem), + displayAlignment(Qt::AlignLeft), decorationAlignment(Qt::AlignLeft), + textElideMode(Qt::ElideMiddle), decorationPosition(Left), + showDecorationSelected(false), features(None), + checkState(Qt::Unchecked), viewItemPosition(QStyleOptionViewItem::Invalid) +{ +} + +/*! + Constructs a QStyleOptionTabWidgetFrame, initializing the members + variables to their default values. +*/ +QStyleOptionTabWidgetFrame::QStyleOptionTabWidgetFrame() + : QStyleOption(Version, SO_TabWidgetFrame), lineWidth(0), midLineWidth(0), shape(QStyleOptionTab::RoundedNorth) +{ +} + +QStyleOptionTabWidgetFrame::QStyleOptionTabWidgetFrame(int versionIn) + : QStyleOption(versionIn, SO_TabWidgetFrame), lineWidth(0), midLineWidth(0) +{ +} + +/*! + Construct a QStyleOptionTabBarBase, initializing the members + vaiables to their default values. +*/ +QStyleOptionTabBarBase::QStyleOptionTabBarBase() + : QStyleOption(Version, SO_TabBarBase), documentMode(false), shape(QStyleOptionTab::RoundedNorth) +{ +} + +QStyleOptionTabBarBase::QStyleOptionTabBarBase(int versionIn) + : QStyleOption(versionIn, SO_TabBarBase), documentMode(false) +{ +} + +/*! + Constructs a QStyleOptionSizeGrip. +*/ +QStyleOptionSizeGrip::QStyleOptionSizeGrip() + : QStyleOptionComplex(Version, Type), corner(Qt::BottomRightCorner) +{ +} + +QStyleOptionSizeGrip::QStyleOptionSizeGrip(int versionIn) + : QStyleOptionComplex(versionIn, Type), corner(Qt::BottomRightCorner) +{ +} + +/*! + Constructs a QStyleOptionGraphicsItem. +*/ +QStyleOptionGraphicsItem::QStyleOptionGraphicsItem() + : QStyleOption(Version, Type), levelOfDetail(1) +{ +} + +QStyleOptionGraphicsItem::QStyleOptionGraphicsItem(int versionIn) + : QStyleOption(versionIn, Type), levelOfDetail(1) +{ +} + +/*! + \since 4.6 + + Returns the level of detail from the \a worldTransform. + + Its value represents the maximum value of the height and + width of a unity rectangle, mapped using the \a worldTransform + of the painter used to draw the item. By default, if no + transformations are applied, its value is 1. If zoomed out 1:2, the level + of detail will be 0.5, and if zoomed in 2:1, its value is 2. + + \sa QGraphicsScene::minimumRenderSize() +*/ +qreal QStyleOptionGraphicsItem::levelOfDetailFromTransform(const QTransform &worldTransform) +{ + if (worldTransform.type() <= QTransform::TxTranslate) + return 1; // Translation only? The LOD is 1. + + // Two unit vectors. + QLineF v1(0, 0, 1, 0); + QLineF v2(0, 0, 0, 1); + // LOD is the transformed area of a 1x1 rectangle. + return qSqrt(worldTransform.map(v1).length() * worldTransform.map(v2).length()); +} + +/*! + Constructs a QStyleHintReturn with version \a version and type \a + type. + + The version has no special meaning for QStyleHintReturn; it can be + used by subclasses to distinguish between different version of + the same hint type. + + \sa QStyleOption::version, QStyleOption::type +*/ +QStyleHintReturn::QStyleHintReturn(int versionIn, int type) + : version(versionIn), type(type) +{ +} + +/*! + \internal +*/ + +QStyleHintReturn::~QStyleHintReturn() +{ +} + +/*! + Constructs a QStyleHintReturnMask. The member variables are + initialized to default values. +*/ +QStyleHintReturnMask::QStyleHintReturnMask() : QStyleHintReturn(Version, Type) +{ +} + +QStyleHintReturnMask::~QStyleHintReturnMask() +{ +} + +/*! + Constructs a QStyleHintReturnVariant. The member variables are + initialized to default values. +*/ +QStyleHintReturnVariant::QStyleHintReturnVariant() : QStyleHintReturn(Version, Type) +{ +} + +QStyleHintReturnVariant::~QStyleHintReturnVariant() +{ +} + +#if !defined(QT_NO_DEBUG_STREAM) +QDebug operator<<(QDebug debug, const QStyleOption::OptionType &optionType) +{ +#if !defined(QT_NO_DEBUG) + switch (optionType) { + case QStyleOption::SO_Default: + debug << "SO_Default"; break; + case QStyleOption::SO_FocusRect: + debug << "SO_FocusRect"; break; + case QStyleOption::SO_Button: + debug << "SO_Button"; break; + case QStyleOption::SO_Tab: + debug << "SO_Tab"; break; + case QStyleOption::SO_MenuItem: + debug << "SO_MenuItem"; break; + case QStyleOption::SO_Frame: + debug << "SO_Frame"; break; + case QStyleOption::SO_ProgressBar: + debug << "SO_ProgressBar"; break; + case QStyleOption::SO_ToolBox: + debug << "SO_ToolBox"; break; + case QStyleOption::SO_Header: + debug << "SO_Header"; break; + case QStyleOption::SO_DockWidget: + debug << "SO_DockWidget"; break; + case QStyleOption::SO_ViewItem: + debug << "SO_ViewItem"; break; + case QStyleOption::SO_TabWidgetFrame: + debug << "SO_TabWidgetFrame"; break; + case QStyleOption::SO_TabBarBase: + debug << "SO_TabBarBase"; break; + case QStyleOption::SO_RubberBand: + debug << "SO_RubberBand"; break; + case QStyleOption::SO_Complex: + debug << "SO_Complex"; break; + case QStyleOption::SO_Slider: + debug << "SO_Slider"; break; + case QStyleOption::SO_SpinBox: + debug << "SO_SpinBox"; break; + case QStyleOption::SO_ToolButton: + debug << "SO_ToolButton"; break; + case QStyleOption::SO_ComboBox: + debug << "SO_ComboBox"; break; + case QStyleOption::SO_TitleBar: + debug << "SO_TitleBar"; break; + case QStyleOption::SO_CustomBase: + debug << "SO_CustomBase"; break; + case QStyleOption::SO_GroupBox: + debug << "SO_GroupBox"; break; + case QStyleOption::SO_ToolBar: + debug << "SO_ToolBar"; break; + case QStyleOption::SO_ComplexCustomBase: + debug << "SO_ComplexCustomBase"; break; + case QStyleOption::SO_SizeGrip: + debug << "SO_SizeGrip"; break; + case QStyleOption::SO_GraphicsItem: + debug << "SO_GraphicsItem"; break; + } +#else + Q_UNUSED(optionType); +#endif + return debug; +} + +QDebug operator<<(QDebug debug, const QStyleOption &option) +{ +#if !defined(QT_NO_DEBUG) + debug << "QStyleOption("; + debug << QStyleOption::OptionType(option.type); + debug << ',' << (option.direction == Qt::RightToLeft ? "RightToLeft" : "LeftToRight"); + debug << ',' << option.state; + debug << ',' << option.rect; + debug << ',' << option.styleObject; + debug << ')'; +#else + Q_UNUSED(option); +#endif + return debug; +} +#endif + +} // namespace QQC2 + +QT_END_NAMESPACE diff --git a/src/imports/nativestyle/qstyle/qquickstyleoption.h b/src/imports/nativestyle/qstyle/qquickstyleoption.h new file mode 100644 index 00000000..c41f557c --- /dev/null +++ b/src/imports/nativestyle/qstyle/qquickstyleoption.h @@ -0,0 +1,795 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSTYLEOPTION_H +#define QSTYLEOPTION_H + +#include "qquickstyle.h" + +#include <QtCore/qlocale.h> +#include <QtCore/qvariant.h> +#include <QtCore/qdebug.h> +#include <QtCore/qabstractitemmodel.h> + +#include <QtGui/qicon.h> +#include <QtGui/qfontmetrics.h> + +QT_BEGIN_NAMESPACE + +class QQuickItem; + +namespace QQC2 { + +class QStyleOption +{ +public: + enum OptionType { + SO_Default, SO_FocusRect, SO_Button, SO_Tab, SO_MenuItem, + SO_Frame, SO_ProgressBar, SO_ToolBox, SO_Header, + SO_DockWidget, SO_ViewItem, SO_TabWidgetFrame, + SO_TabBarBase, SO_RubberBand, SO_ToolBar, SO_GraphicsItem, + + SO_Complex = 0xf0000, SO_Slider, SO_SpinBox, SO_ToolButton, SO_ComboBox, + SO_TitleBar, SO_GroupBox, SO_SizeGrip, + + SO_CustomBase = 0xf00, + SO_ComplexCustomBase = 0xf000000 + }; + Q_ENUMS(OptionType) + + enum StyleOptionType { Type = SO_Default }; + enum StyleOptionVersion { Version = 1 }; + + int version; // TODO: Remove version information + int type; + QStyle::State state; + Qt::LayoutDirection direction; + QRect rect; + QFontMetrics fontMetrics; + QPalette palette; + QObject *styleObject; + + // QQC2 additions. Remember to also update copy + // constructor and assignment operator when adding + // new variables here. + QQuickItem *control; + QWindow *window; + + QStyleOption(int version = QStyleOption::Version, int type = SO_Default); + QStyleOption(const QStyleOption &other); + ~QStyleOption(); + + QStyleOption &operator=(const QStyleOption &other); +}; + +class QStyleOptionFocusRect : public QStyleOption +{ +public: + enum StyleOptionType { Type = SO_FocusRect }; + enum StyleOptionVersion { Version = 1 }; + + QColor backgroundColor; + + QStyleOptionFocusRect(); + QStyleOptionFocusRect(const QStyleOptionFocusRect &other) : QStyleOption(Version, Type) { *this = other; } + QStyleOptionFocusRect &operator=(const QStyleOptionFocusRect &) = default; + +protected: + QStyleOptionFocusRect(int version); +}; + +class QStyleOptionFrame : public QStyleOption +{ +public: + enum StyleOptionType { Type = SO_Frame }; + enum StyleOptionVersion { Version = 3 }; + enum FrameFeature { + None = 0x00, + Flat = 0x01, + Rounded = 0x02 + }; + Q_DECLARE_FLAGS(FrameFeatures, FrameFeature) + enum Shape { + NoFrame = 0, // no frame + Box = 0x0001, // rectangular box + Panel = 0x0002, // rectangular panel + WinPanel = 0x0003, // rectangular panel (Windows) + HLine = 0x0004, // horizontal line + VLine = 0x0005, // vertical line + StyledPanel = 0x0006 // rectangular panel depending on the GUI style + }; + enum Shadow { + Plain = 0x0010, // plain line + Raised = 0x0020, // raised shadow effect + Sunken = 0x0030 // sunken shadow effect + }; + + int lineWidth; + int midLineWidth; + FrameFeatures features; + Shape frameShape; + + QStyleOptionFrame(); + QStyleOptionFrame(const QStyleOptionFrame &other) : QStyleOption(Version, Type) { *this = other; } + QStyleOptionFrame &operator=(const QStyleOptionFrame &) = default; + +protected: + QStyleOptionFrame(int version); +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QStyleOptionFrame::FrameFeatures) + +typedef Q_DECL_DEPRECATED QStyleOptionFrame QStyleOptionFrameV2; +typedef Q_DECL_DEPRECATED QStyleOptionFrame QStyleOptionFrameV3; + +class QStyleOptionTab : public QStyleOption +{ +public: + enum StyleOptionType { Type = SO_Tab }; + enum StyleOptionVersion { Version = 3 }; + + enum TabPosition { Beginning, Middle, End, OnlyOneTab }; + enum SelectedPosition { NotAdjacent, NextIsSelected, PreviousIsSelected }; + enum CornerWidget { NoCornerWidgets = 0x00, LeftCornerWidget = 0x01, + RightCornerWidget = 0x02 }; + enum TabFeature { None = 0x00, HasFrame = 0x01 }; + enum Shape { RoundedNorth, RoundedSouth, RoundedWest, RoundedEast, + TriangularNorth, TriangularSouth, TriangularWest, TriangularEast + }; + Q_DECLARE_FLAGS(CornerWidgets, CornerWidget) + Q_DECLARE_FLAGS(TabFeatures, TabFeature) + + QString text; + QIcon icon; + int row; + TabPosition position; + Shape shape = RoundedNorth; + SelectedPosition selectedPosition; + CornerWidgets cornerWidgets; + QSize iconSize; + bool documentMode; + QSize leftButtonSize; + QSize rightButtonSize; + TabFeatures features; + + QStyleOptionTab(); + QStyleOptionTab(const QStyleOptionTab &other) : QStyleOption(Version, Type) { *this = other; } + QStyleOptionTab &operator=(const QStyleOptionTab &) = default; + +protected: + QStyleOptionTab(int version); +}; + +class QStyleOptionTabWidgetFrame : public QStyleOption +{ +public: + enum StyleOptionType { Type = SO_TabWidgetFrame }; + enum StyleOptionVersion { Version = 2 }; + + int lineWidth; + int midLineWidth; + QStyleOptionTab::Shape shape; + QSize tabBarSize; + QSize rightCornerWidgetSize; + QSize leftCornerWidgetSize; + QRect tabBarRect; + QRect selectedTabRect; + + QStyleOptionTabWidgetFrame(); + inline QStyleOptionTabWidgetFrame(const QStyleOptionTabWidgetFrame &other) + : QStyleOption(Version, Type) { *this = other; } + QStyleOptionTabWidgetFrame &operator=(const QStyleOptionTabWidgetFrame &) = default; + +protected: + QStyleOptionTabWidgetFrame(int version); +}; + +typedef Q_DECL_DEPRECATED QStyleOptionTabWidgetFrame QStyleOptionTabWidgetFrameV2; + + +class QStyleOptionTabBarBase : public QStyleOption +{ +public: + enum StyleOptionType { Type = SO_TabBarBase }; + + enum TabBarPosition { North, South, West, East }; + enum ButtonPosition { LeftSide, RightSide }; + + QRect tabBarRect; + QRect selectedTabRect; + bool documentMode; + QStyleOptionTab::Shape shape; + + QStyleOptionTabBarBase(); + QStyleOptionTabBarBase(const QStyleOptionTabBarBase &other) : QStyleOption(Version, Type) { *this = other; } + QStyleOptionTabBarBase &operator=(const QStyleOptionTabBarBase &) = default; + +protected: + QStyleOptionTabBarBase(int version); +}; + +class QStyleOptionHeader : public QStyleOption +{ +public: + enum StyleOptionType { Type = SO_Header }; + enum StyleOptionVersion { Version = 1 }; + enum SectionPosition { Beginning, Middle, End, OnlyOneSection }; + enum SelectedPosition { NotAdjacent, NextIsSelected, PreviousIsSelected, + NextAndPreviousAreSelected }; + enum SortIndicator { None, SortUp, SortDown }; + + int section; + QString text; + Qt::Alignment textAlignment; + QIcon icon; + Qt::Alignment iconAlignment; + SectionPosition position; + SelectedPosition selectedPosition; + SortIndicator sortIndicator; + Qt::Orientation orientation; + + QStyleOptionHeader(); + QStyleOptionHeader(const QStyleOptionHeader &other) : QStyleOption(Version, Type) { *this = other; } + QStyleOptionHeader &operator=(const QStyleOptionHeader &) = default; + +protected: + QStyleOptionHeader(int version); +}; + +class QStyleOptionButton : public QStyleOption +{ +public: + enum StyleOptionType { Type = SO_Button }; + enum StyleOptionVersion { Version = 1 }; + + enum ButtonFeature { None = 0x00, Flat = 0x01, HasMenu = 0x02, DefaultButton = 0x04, + AutoDefaultButton = 0x08, CommandLinkButton = 0x10 }; + Q_DECLARE_FLAGS(ButtonFeatures, ButtonFeature) + + ButtonFeatures features; + QString text; + QIcon icon; + QSize iconSize; + + QStyleOptionButton(); + QStyleOptionButton(const QStyleOptionButton &other) : QStyleOption(Version, Type) { *this = other; } + QStyleOptionButton &operator=(const QStyleOptionButton &) = default; + +protected: + QStyleOptionButton(int version); +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QStyleOptionButton::ButtonFeatures) + +class QStyleOptionTabV4 : public QStyleOptionTab +{ +public: + enum StyleOptionVersion { Version = 4 }; + QStyleOptionTabV4(); + int tabIndex = -1; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QStyleOptionTab::CornerWidgets) + +typedef Q_DECL_DEPRECATED QStyleOptionTab QStyleOptionTabV2; +typedef Q_DECL_DEPRECATED QStyleOptionTab QStyleOptionTabV3; + + +class QStyleOptionToolBar : public QStyleOption +{ +public: + enum StyleOptionType { Type = SO_ToolBar }; + enum StyleOptionVersion { Version = 1 }; + enum ToolBarPosition { Beginning, Middle, End, OnlyOne }; + enum ToolBarFeature { None = 0x0, Movable = 0x1 }; + Q_DECLARE_FLAGS(ToolBarFeatures, ToolBarFeature) + + ToolBarPosition positionOfLine; // The toolbar line position + ToolBarPosition positionWithinLine; // The position within a toolbar + Qt::ToolBarArea toolBarArea; // The toolbar docking area + ToolBarFeatures features; + int lineWidth; + int midLineWidth; + + QStyleOptionToolBar(); + QStyleOptionToolBar(const QStyleOptionToolBar &other) : QStyleOption(Version, Type) { *this = other; } + QStyleOptionToolBar &operator=(const QStyleOptionToolBar &) = default; + +protected: + QStyleOptionToolBar(int version); +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QStyleOptionToolBar::ToolBarFeatures) + +class QStyleOptionProgressBar : public QStyleOption +{ +public: + enum StyleOptionType { Type = SO_ProgressBar }; + enum StyleOptionVersion { Version = 2 }; + + int minimum; + int maximum; + int progress; + QString text; + Qt::Alignment textAlignment; + bool textVisible; + bool invertedAppearance; + bool bottomToTop; + + QStyleOptionProgressBar(); + QStyleOptionProgressBar(const QStyleOptionProgressBar &other) : QStyleOption(Version, Type) { *this = other; } + QStyleOptionProgressBar &operator=(const QStyleOptionProgressBar &) = default; + +protected: + QStyleOptionProgressBar(int version); +}; + +class QStyleOptionMenuItem : public QStyleOption +{ +public: + enum StyleOptionType { Type = SO_MenuItem }; + enum StyleOptionVersion { Version = 1 }; + + enum MenuItemType { Normal, DefaultItem, Separator, SubMenu, Scroller, TearOff, Margin, + EmptyArea }; + enum CheckType { NotCheckable, Exclusive, NonExclusive }; + + MenuItemType menuItemType; + CheckType checkType; + bool checked; + bool menuHasCheckableItems; + QRect menuRect; + QString text; + QIcon icon; + int maxIconWidth; + int tabWidth; // ### Qt 6: rename to reservedShortcutWidth + QFont font; + + QStyleOptionMenuItem(); + QStyleOptionMenuItem(const QStyleOptionMenuItem &other) : QStyleOption(Version, Type) { *this = other; } + QStyleOptionMenuItem &operator=(const QStyleOptionMenuItem &) = default; + +protected: + QStyleOptionMenuItem(int version); +}; + +class QStyleOptionDockWidget : public QStyleOption +{ +public: + enum StyleOptionType { Type = SO_DockWidget }; + enum StyleOptionVersion { Version = 2 }; + + QString title; + bool closable; + bool movable; + bool floatable; + bool verticalTitleBar; + + QStyleOptionDockWidget(); + QStyleOptionDockWidget(const QStyleOptionDockWidget &other) : QStyleOption(Version, Type) { *this = other; } + QStyleOptionDockWidget &operator=(const QStyleOptionDockWidget &) = default; + +protected: + QStyleOptionDockWidget(int version); +}; + +typedef Q_DECL_DEPRECATED QStyleOptionDockWidget QStyleOptionDockWidgetV2; + +class QStyleOptionViewItem : public QStyleOption +{ +public: + enum StyleOptionType { Type = SO_ViewItem }; + enum StyleOptionVersion { Version = 4 }; + + enum Position { Left, Right, Top, Bottom }; + enum ScrollMode { ScrollPerItem, ScrollPerPixel }; // Doesn't really belong in this class. + + Qt::Alignment displayAlignment; + Qt::Alignment decorationAlignment; + Qt::TextElideMode textElideMode; + Position decorationPosition; + QSize decorationSize; + QFont font; + bool showDecorationSelected; + + enum ViewItemFeature { + None = 0x00, + WrapText = 0x01, + Alternate = 0x02, + HasCheckIndicator = 0x04, + HasDisplay = 0x08, + HasDecoration = 0x10 + }; + Q_DECLARE_FLAGS(ViewItemFeatures, ViewItemFeature) + + ViewItemFeatures features; + + QLocale locale; + + enum ViewItemPosition { Invalid, Beginning, Middle, End, OnlyOne }; + + QModelIndex index; + Qt::CheckState checkState; + QIcon icon; + QString text; + ViewItemPosition viewItemPosition; + QBrush backgroundBrush; + + QStyleOptionViewItem(); + QStyleOptionViewItem(const QStyleOptionViewItem &other) : QStyleOption(Version, Type) { *this = other; } + QStyleOptionViewItem &operator=(const QStyleOptionViewItem &) = default; + +protected: + QStyleOptionViewItem(int version); +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QStyleOptionViewItem::ViewItemFeatures) + +typedef Q_DECL_DEPRECATED QStyleOptionViewItem QStyleOptionViewItemV2; +typedef Q_DECL_DEPRECATED QStyleOptionViewItem QStyleOptionViewItemV3; +typedef Q_DECL_DEPRECATED QStyleOptionViewItem QStyleOptionViewItemV4; + +class QStyleOptionToolBox : public QStyleOption +{ +public: + enum StyleOptionType { Type = SO_ToolBox }; + enum StyleOptionVersion { Version = 2 }; + + QString text; + QIcon icon; + + enum TabPosition { Beginning, Middle, End, OnlyOneTab }; + enum SelectedPosition { NotAdjacent, NextIsSelected, PreviousIsSelected }; + + TabPosition position; + SelectedPosition selectedPosition; + + QStyleOptionToolBox(); + QStyleOptionToolBox(const QStyleOptionToolBox &other) : QStyleOption(Version, Type) { *this = other; } + QStyleOptionToolBox &operator=(const QStyleOptionToolBox &) = default; + +protected: + QStyleOptionToolBox(int version); +}; + +typedef Q_DECL_DEPRECATED QStyleOptionToolBox QStyleOptionToolBoxV2; + +class QStyleOptionRubberBand : public QStyleOption +{ +public: + enum StyleOptionType { Type = SO_RubberBand }; + enum StyleOptionVersion { Version = 1 }; + enum Shape { Line, Rectangle }; + + bool opaque; + Shape shape; + + QStyleOptionRubberBand(); + QStyleOptionRubberBand(const QStyleOptionRubberBand &other) : QStyleOption(Version, Type) { *this = other; } + QStyleOptionRubberBand &operator=(const QStyleOptionRubberBand &) = default; + +protected: + QStyleOptionRubberBand(int version); +}; + +// -------------------------- Complex style options ------------------------------- +class QStyleOptionComplex : public QStyleOption +{ +public: + enum StyleOptionType { Type = SO_Complex }; + enum StyleOptionVersion { Version = 1 }; + + QStyle::SubControls subControls; + QStyle::SubControls activeSubControls; + + QStyleOptionComplex(int version = QStyleOptionComplex::Version, int type = SO_Complex); + QStyleOptionComplex(const QStyleOptionComplex &other) : QStyleOption(Version, Type) { *this = other; } + QStyleOptionComplex &operator=(const QStyleOptionComplex &) = default; +}; + +class QStyleOptionSlider : public QStyleOptionComplex +{ +public: + enum StyleOptionType { Type = SO_Slider }; + enum StyleOptionVersion { Version = 1 }; + enum TickPosition { + NoTicks = 0, + TicksAbove = 1, + TicksLeft = TicksAbove, + TicksBelow = 2, + TicksRight = TicksBelow, + TicksBothSides = 3 + }; + + Qt::Orientation orientation; + int minimum; + int maximum; + TickPosition tickPosition; + int tickInterval; + bool upsideDown; + int sliderPosition; + int sliderValue; + int singleStep; + int pageStep; + qreal notchTarget; + bool dialWrapping; + + QStyleOptionSlider(); + QStyleOptionSlider(const QStyleOptionSlider &other) : QStyleOptionComplex(Version, Type) { *this = other; } + QStyleOptionSlider &operator=(const QStyleOptionSlider &) = default; + +protected: + QStyleOptionSlider(int version); +}; + +class QStyleOptionSpinBox : public QStyleOptionComplex +{ +public: + enum StyleOptionType { Type = SO_SpinBox }; + enum StyleOptionVersion { Version = 1 }; + enum StepEnabledFlag { StepNone = 0x00, StepUpEnabled = 0x01, StepDownEnabled = 0x02, StepEnabled = 0xFF }; + enum ButtonSymbols { UpDownArrows, PlusMinus, NoButtons }; + + ButtonSymbols buttonSymbols; + StepEnabledFlag stepEnabled; + bool frame; + + QStyleOptionSpinBox(); + QStyleOptionSpinBox(const QStyleOptionSpinBox &other) : QStyleOptionComplex(Version, Type) { *this = other; } + QStyleOptionSpinBox &operator=(const QStyleOptionSpinBox &) = default; + +protected: + QStyleOptionSpinBox(int version); +}; + +class QStyleOptionToolButton : public QStyleOptionComplex +{ +public: + enum StyleOptionType { Type = SO_ToolButton }; + enum StyleOptionVersion { Version = 1 }; + + enum ToolButtonFeature { None = 0x00, Arrow = 0x01, Menu = 0x04, MenuButtonPopup = Menu, PopupDelay = 0x08, + HasMenu = 0x10 }; + Q_DECLARE_FLAGS(ToolButtonFeatures, ToolButtonFeature) + + ToolButtonFeatures features; + QIcon icon; + QSize iconSize; + QString text; + Qt::ArrowType arrowType; + Qt::ToolButtonStyle toolButtonStyle; + QPoint pos; + QFont font; + + QStyleOptionToolButton(); + QStyleOptionToolButton(const QStyleOptionToolButton &other) : QStyleOptionComplex(Version, Type) { *this = other; } + QStyleOptionToolButton &operator=(const QStyleOptionToolButton &) = default; + +protected: + QStyleOptionToolButton(int version); +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QStyleOptionToolButton::ToolButtonFeatures) + +class QStyleOptionComboBox : public QStyleOptionComplex +{ +public: + enum StyleOptionType { Type = SO_ComboBox }; + enum StyleOptionVersion { Version = 1 }; + + bool editable; + QRect popupRect; + bool frame; + QString currentText; + QIcon currentIcon; + QSize iconSize; + + QStyleOptionComboBox(); + QStyleOptionComboBox(const QStyleOptionComboBox &other) : QStyleOptionComplex(Version, Type) { *this = other; } + QStyleOptionComboBox &operator=(const QStyleOptionComboBox &) = default; + +protected: + QStyleOptionComboBox(int version); +}; + +class QStyleOptionTitleBar : public QStyleOptionComplex +{ +public: + enum StyleOptionType { Type = SO_TitleBar }; + enum StyleOptionVersion { Version = 1 }; + + QString text; + QIcon icon; + int titleBarState; + Qt::WindowFlags titleBarFlags; + + QStyleOptionTitleBar(); + QStyleOptionTitleBar(const QStyleOptionTitleBar &other) : QStyleOptionComplex(Version, Type) { *this = other; } + QStyleOptionTitleBar &operator=(const QStyleOptionTitleBar &) = default; + +protected: + QStyleOptionTitleBar(int version); +}; + +class QStyleOptionGroupBox : public QStyleOptionComplex +{ +public: + enum StyleOptionType { Type = SO_GroupBox }; + enum StyleOptionVersion { Version = 1 }; + + QStyleOptionFrame::FrameFeatures features; + QString text; + Qt::Alignment textAlignment; + QColor textColor; + int lineWidth; + int midLineWidth; + + QStyleOptionGroupBox(); + QStyleOptionGroupBox(const QStyleOptionGroupBox &other) : QStyleOptionComplex(Version, Type) { *this = other; } + QStyleOptionGroupBox &operator=(const QStyleOptionGroupBox &) = default; +protected: + QStyleOptionGroupBox(int version); +}; + +class QStyleOptionSizeGrip : public QStyleOptionComplex +{ +public: + enum StyleOptionType { Type = SO_SizeGrip }; + enum StyleOptionVersion { Version = 1 }; + + Qt::Corner corner; + + QStyleOptionSizeGrip(); + QStyleOptionSizeGrip(const QStyleOptionSizeGrip &other) : QStyleOptionComplex(Version, Type) { *this = other; } + QStyleOptionSizeGrip &operator=(const QStyleOptionSizeGrip &) = default; +protected: + QStyleOptionSizeGrip(int version); +}; + +class QStyleOptionGraphicsItem : public QStyleOption +{ +public: + enum StyleOptionType { Type = SO_GraphicsItem }; + enum StyleOptionVersion { Version = 1 }; + + QRectF exposedRect; + qreal levelOfDetail; + + QStyleOptionGraphicsItem(); + QStyleOptionGraphicsItem(const QStyleOptionGraphicsItem &other) : QStyleOption(Version, Type) { *this = other; } + QStyleOptionGraphicsItem &operator=(const QStyleOptionGraphicsItem &) = default; + static qreal levelOfDetailFromTransform(const QTransform &worldTransform); +protected: + QStyleOptionGraphicsItem(int version); +}; + +template <typename T> +T qstyleoption_cast(const QStyleOption *opt) +{ + typedef typename std::remove_cv<typename std::remove_pointer<T>::type>::type Opt; + if (opt && opt->version >= Opt::Version && (opt->type == Opt::Type + || int(Opt::Type) == QStyleOption::SO_Default + || (int(Opt::Type) == QStyleOption::SO_Complex + && opt->type > QStyleOption::SO_Complex))) + return static_cast<T>(opt); + return nullptr; +} + +template <typename T> +T qstyleoption_cast(QStyleOption *opt) +{ + typedef typename std::remove_cv<typename std::remove_pointer<T>::type>::type Opt; + if (opt && opt->version >= Opt::Version && (opt->type == Opt::Type + || int(Opt::Type) == QStyleOption::SO_Default + || (int(Opt::Type) == QStyleOption::SO_Complex + && opt->type > QStyleOption::SO_Complex))) + return static_cast<T>(opt); + return nullptr; +} + +// -------------------------- QStyleHintReturn ------------------------------- + +class QStyleHintReturn +{ +public: + enum HintReturnType { + SH_Default=0xf000, SH_Mask, SH_Variant + }; + + enum StyleOptionType { Type = SH_Default }; + enum StyleOptionVersion { Version = 1 }; + + QStyleHintReturn(int version = QStyleOption::Version, int type = SH_Default); + ~QStyleHintReturn(); + + int version; + int type; +}; + +class QStyleHintReturnMask : public QStyleHintReturn +{ +public: + enum StyleOptionType { Type = SH_Mask }; + enum StyleOptionVersion { Version = 1 }; + + QStyleHintReturnMask(); + ~QStyleHintReturnMask(); + + QRegion region; +}; + +class QStyleHintReturnVariant : public QStyleHintReturn +{ +public: + enum StyleOptionType { Type = SH_Variant }; + enum StyleOptionVersion { Version = 1 }; + + QStyleHintReturnVariant(); + ~QStyleHintReturnVariant(); + + QVariant variant; +}; + +template <typename T> +T qstyleoption_cast(const QStyleHintReturn *hint) +{ + typedef typename std::remove_cv<typename std::remove_pointer<T>::type>::type Opt; + if (hint && hint->version <= Opt::Version && + (hint->type == Opt::Type || int(Opt::Type) == QStyleHintReturn::SH_Default)) + return static_cast<T>(hint); + return nullptr; +} + +template <typename T> +T qstyleoption_cast(QStyleHintReturn *hint) +{ + typedef typename std::remove_cv<typename std::remove_pointer<T>::type>::type Opt; + if (hint && hint->version <= Opt::Version && + (hint->type == Opt::Type || int(Opt::Type) == QStyleHintReturn::SH_Default)) + return static_cast<T>(hint); + return nullptr; +} + +#if !defined(QT_NO_DEBUG_STREAM) +QDebug operator<<(QDebug debug, const QStyleOption::OptionType &optionType); +QDebug operator<<(QDebug debug, const QStyleOption &option); +#endif + +} // namespace QQC2 + +QT_END_NAMESPACE + +#endif // QSTYLEOPTION_H diff --git a/src/imports/nativestyle/qstyle/qstyle.pri b/src/imports/nativestyle/qstyle/qstyle.pri new file mode 100644 index 00000000..4d025498 --- /dev/null +++ b/src/imports/nativestyle/qstyle/qstyle.pri @@ -0,0 +1,23 @@ +INCLUDEPATH += $$PWD + +HEADERS += \ + $$PWD/qquicknativestyle.h \ + $$PWD/qquickcommonstyle.h \ + $$PWD/qquickcommonstyle_p.h \ + $$PWD/qquickcommonstylepixmaps_p.h \ + $$PWD/qquickdrawutil.h \ + $$PWD/qquickstyle.h \ + $$PWD/qquickstyle_p.h \ + $$PWD/qquickstylehelper_p.h \ + $$PWD/qquickstyleoption.h + +SOURCES += \ + $$PWD/qquicknativestyle.cpp \ + $$PWD/qquickcommonstyle.cpp \ + $$PWD/qquickdrawutil.cpp \ + $$PWD/qquickstyle.cpp \ + $$PWD/qquickstylehelper.cpp \ + $$PWD/qquickstyleoption.cpp + +macos: include(mac/mac.pri) +win32: include(windows/windows.pri) diff --git a/src/imports/nativestyle/qstyle/windows/qquickwindowsstyle.cpp b/src/imports/nativestyle/qstyle/windows/qquickwindowsstyle.cpp new file mode 100644 index 00000000..9d312868 --- /dev/null +++ b/src/imports/nativestyle/qstyle/windows/qquickwindowsstyle.cpp @@ -0,0 +1,2386 @@ +/**************************************************************************** +** +** 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 "qquickwindowsstyle_p.h" +#include "qquickwindowsstyle_p_p.h" +#include "qquickstyleoption.h" +#include "qquickstylehelper_p.h" +#include "qquickdrawutil.h" + +#include <QtGui/qbitmap.h> +#include <QtGui/qevent.h> +#include <QtGui/qpaintengine.h> +#include <QtGui/qpainter.h> +#include <QtCore/qdebug.h> +#include <QtCore/qfile.h> +#include <QtCore/qtextstream.h> +#include <QtGui/qpixmapcache.h> +#include <private/qmath_p.h> +#include <qmath.h> +#include <QtGui/qpainterpath.h> +#include <QtGui/qscreen.h> +#include <QtGui/qwindow.h> +#include <qpa/qplatformtheme.h> +#include <qpa/qplatformscreen.h> +#include <private/qguiapplication_p.h> +#include <private/qhighdpiscaling_p.h> +#include <qpa/qplatformnativeinterface.h> + +#if 0 && QT_CONFIG(animation) +//#include <private/qstyleanimation_p.h> +#endif + +#include <algorithm> + +QT_BEGIN_NAMESPACE + +namespace QQC2 { + +#if defined(Q_OS_WIN) + +QT_BEGIN_INCLUDE_NAMESPACE +#include "qt_windows.h" +QT_END_INCLUDE_NAMESPACE +# ifndef COLOR_GRADIENTACTIVECAPTION +# define COLOR_GRADIENTACTIVECAPTION 27 +# endif +# ifndef COLOR_GRADIENTINACTIVECAPTION +# define COLOR_GRADIENTINACTIVECAPTION 28 +# endif + +Q_GUI_EXPORT HICON qt_pixmapToWinHICON(const QPixmap &); +#endif //Q_OS_WIN + +QT_BEGIN_INCLUDE_NAMESPACE +#include <limits.h> +QT_END_INCLUDE_NAMESPACE + +enum QSliderDirection { SlUp, SlDown, SlLeft, SlRight }; + +/* + \internal +*/ + +QWindowsStylePrivate::QWindowsStylePrivate() = default; + +qreal QWindowsStylePrivate::appDevicePixelRatio() +{ + return qApp->devicePixelRatio(); +} + +bool QWindowsStylePrivate::isDarkMode() +{ + bool result = false; +#ifdef Q_OS_WIN + // Windows only: Return whether dark mode style support is desired and + // dark mode is in effect. + if (auto ni = QGuiApplication::platformNativeInterface()) { + const QVariant darkModeStyleP = ni->property("darkModeStyle"); + result = darkModeStyleP.type() == QVariant::Bool + && darkModeStyleP.value<bool>() + && ni->property("darkMode").value<bool>(); + } +#endif + return result; +} + +// ###TODO SH_UnderlineShortcut +#if 0 +// Returns \c true if the toplevel parent of \a widget has seen the Alt-key +bool QWindowsStylePrivate::hasSeenAlt(const QWidget *widget) const +{ + widget = widget->window(); + return seenAlt.contains(widget); +} + +/*! + \reimp +*/ +bool QWindowsStyle::eventFilter(QObject *o, QEvent *e) +{ + // Records Alt- and Focus events +// if (!o->isWidgetType()) + return QObject::eventFilter(o, e); + QWidget *widget = qobject_cast<QWidget*>(o); + Q_D(QWindowsStyle); + switch (e->type()) { + case QEvent::KeyPress: + if (static_cast<QKeyEvent *>(e)->key() == Qt::Key_Alt) { + widget = widget->window(); + + // Alt has been pressed - find all widgets that care + QList<QWidget *> l = widget->findChildren<QWidget *>(); + auto ignorable = [](QWidget *w) { + return w->isWindow() || !w->isVisible() + || w->style()->styleHint(SH_UnderlineShortcut, nullptr, w); + }; + l.erase(std::remove_if (l.begin(), l.end(), ignorable), l.end()); + // Update states before repainting + d->seenAlt.append(widget); + d->alt_down = true; + + // Repaint all relevant widgets + for (int pos = 0; pos < l.size(); ++pos) + l.at(pos)->update(); + } + break; + case QEvent::KeyRelease: + if (static_cast<QKeyEvent*>(e)->key() == Qt::Key_Alt) { + widget = widget->window(); + + // Update state and repaint the menu bars. + d->alt_down = false; +#if 0 && QT_CONFIG(menubar) + QList<QMenuBar *> l = widget->findChildren<QMenuBar *>(); + for (int i = 0; i < l.size(); ++i) + l.at(i)->update(); +#endif + } + break; + case QEvent::Close: + // Reset widget when closing + d->seenAlt.removeAll(widget); + d->seenAlt.removeAll(widget->window()); + break; + default: + break; + } + return QCommonStyle::eventFilter(o, e); +} +#endif + +/*! + \class QWindowsStyle + \brief The QWindowsStyle class provides a Microsoft Windows-like look and feel. + + \ingroup appearance + \inmodule QtWidgets + \internal + + This style is Qt's default GUI style on Windows. + + \image qwindowsstyle.png + \sa QWindowsVistaStyle, QMacStyle, QFusionStyle +*/ + +/*! + Constructs a QWindowsStyle object. +*/ +QWindowsStyle::QWindowsStyle() : QCommonStyle(*new QWindowsStylePrivate) +{ +} + +/*! + \internal + + Constructs a QWindowsStyle object. +*/ +QWindowsStyle::QWindowsStyle(QWindowsStylePrivate &dd) : QCommonStyle(dd) +{ +} + + +/*! Destroys the QWindowsStyle object. */ +QWindowsStyle::~QWindowsStyle() +{ +} + +#ifdef Q_OS_WIN +static inline QRgb colorref2qrgb(COLORREF col) +{ + return qRgb(GetRValue(col), GetGValue(col), GetBValue(col)); +} +#endif +#if 0 +/*! \reimp */ +void QWindowsStyle::polish(QApplication *app) +{ + QCommonStyle::polish(app); + QWindowsStylePrivate *d = const_cast<QWindowsStylePrivate*>(d_func()); + // We only need the overhead when shortcuts are sometimes hidden + if (!proxy()->styleHint(SH_UnderlineShortcut, nullptr) && app) + app->installEventFilter(this); + + const auto &palette = QGuiApplication::palette(); + d->activeGradientCaptionColor = palette.highlight().color(); + d->activeCaptionColor = d->activeGradientCaptionColor; + d->inactiveGradientCaptionColor = palette.dark().color(); + d->inactiveCaptionColor = d->inactiveGradientCaptionColor; + d->inactiveCaptionText = palette.window().color(); + +#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT) //fetch native title bar colors + if (app->desktopSettingsAware()){ + DWORD activeCaption = GetSysColor(COLOR_ACTIVECAPTION); + DWORD gradientActiveCaption = GetSysColor(COLOR_GRADIENTACTIVECAPTION); + DWORD inactiveCaption = GetSysColor(COLOR_INACTIVECAPTION); + DWORD gradientInactiveCaption = GetSysColor(COLOR_GRADIENTINACTIVECAPTION); + DWORD inactiveCaptionText = GetSysColor(COLOR_INACTIVECAPTIONTEXT); + d->activeCaptionColor = colorref2qrgb(activeCaption); + d->activeGradientCaptionColor = colorref2qrgb(gradientActiveCaption); + d->inactiveCaptionColor = colorref2qrgb(inactiveCaption); + d->inactiveGradientCaptionColor = colorref2qrgb(gradientInactiveCaption); + d->inactiveCaptionText = colorref2qrgb(inactiveCaptionText); + } +#endif +} + +/*! \reimp */ +void QWindowsStyle::unpolish(QApplication *app) +{ + QCommonStyle::unpolish(app); + app->removeEventFilter(this); +} + +/*! \reimp */ +void QWindowsStyle::polish(QWidget *widget) +{ + QCommonStyle::polish(widget); +} + +/*! \reimp */ +void QWindowsStyle::unpolish(QWidget *widget) +{ + QCommonStyle::unpolish(widget); +} + +/*! + \reimp +*/ +void QWindowsStyle::polish(QPalette &pal) +{ + QCommonStyle::polish(pal); +} +#endif + +int QWindowsStylePrivate::pixelMetricFromSystemDp(QStyle::PixelMetric pm, const QStyleOption *opt) +{ +#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT) + switch (pm) { + case QStyle::PM_DockWidgetFrameWidth: + return GetSystemMetrics(SM_CXFRAME); + + case QStyle::PM_TitleBarHeight: + Q_ASSERT(opt); + if (const QStyleOptionTitleBar *tb = qstyleoption_cast<const QStyleOptionTitleBar *>(opt)) { + if ((tb->titleBarFlags & Qt::WindowType_Mask) == Qt::Tool) { + // MS always use one less than they say + return GetSystemMetrics(SM_CYSMCAPTION) - 1; + } + } + return GetSystemMetrics(SM_CYCAPTION) - 1; + + case QStyle::PM_ScrollBarExtent: + { + NONCLIENTMETRICS ncm; + ncm.cbSize = FIELD_OFFSET(NONCLIENTMETRICS, lfMessageFont) + sizeof(LOGFONT); + if (SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICS), &ncm, 0)) + return qMax(ncm.iScrollHeight, ncm.iScrollWidth); + } + break; + + case QStyle::PM_MdiSubWindowFrameWidth: + return GetSystemMetrics(SM_CYFRAME); + + default: + break; + } +#else // Q_OS_WIN && !Q_OS_WINRT + Q_UNUSED(pm); + Q_UNUSED(widget); +#endif + return QWindowsStylePrivate::InvalidMetric; +} + +int QWindowsStylePrivate::fixedPixelMetric(QStyle::PixelMetric pm) +{ + switch (pm) { + case QStyle::PM_ToolBarItemSpacing: + return 0; + case QStyle::PM_ButtonDefaultIndicator: + case QStyle::PM_ButtonShiftHorizontal: + case QStyle::PM_ButtonShiftVertical: + case QStyle::PM_MenuHMargin: + case QStyle::PM_MenuVMargin: + case QStyle::PM_ToolBarItemMargin: + return 1; + case QStyle::PM_DockWidgetSeparatorExtent: + return 4; +#if 0 && QT_CONFIG(tabbar) + case QStyle::PM_TabBarTabShiftHorizontal: + return 0; + case QStyle::PM_TabBarTabShiftVertical: + return 2; +#endif + +#if 0 && QT_CONFIG(slider) + case QStyle::PM_SliderLength: + return 11; +#endif // QT_CONFIG(slider) + +#if 0 && QT_CONFIG(menu) + case QStyle::PM_MenuBarHMargin: + case QStyle::PM_MenuBarVMargin: + case QStyle::PM_MenuBarPanelWidth: + return 0; + case QStyle::PM_SmallIconSize: + return 16; + case QStyle::PM_LargeIconSize: + return 32; + case QStyle::PM_DockWidgetTitleMargin: + return 2; + case QStyle::PM_DockWidgetTitleBarButtonMargin: + case QStyle::PM_DockWidgetFrameWidth: + return 4; + +#endif // QT_CONFIG(menu) + case QStyle::PM_ToolBarHandleExtent: + return 10; + default: + break; + } + return QWindowsStylePrivate::InvalidMetric; +} + +static QScreen *screenOf(const QWindow *w) +{ + if (w) { + if (auto screen = w->screen()) + return screen; + } + return QGuiApplication::primaryScreen(); +} + +// Calculate the overall scale factor to obtain Qt Device Independent +// Pixels from a native Windows size. Divide by devicePixelRatio +// and account for secondary screens with differing logical DPI. +qreal QWindowsStylePrivate::nativeMetricScaleFactor(const QStyleOption *opt) +{ + QWindow *win = opt->window; + qreal result = qreal(1) / QWindowsStylePrivate::devicePixelRatio(opt); + if (QGuiApplicationPrivate::screen_list.size() > 1) { + const QScreen *primaryScreen = QGuiApplication::primaryScreen(); + const QScreen *screen = screenOf(win); + if (screen != primaryScreen) { + const qreal primaryLogicalDpi = primaryScreen->handle()->logicalDpi().first; + const qreal logicalDpi = screen->handle()->logicalDpi().first; + if (!qFuzzyCompare(primaryLogicalDpi, logicalDpi)) + result *= logicalDpi / primaryLogicalDpi; + } + } + return result; +} + +/*! + \reimp +*/ +int QWindowsStyle::pixelMetric(PixelMetric pm, const QStyleOption *opt) const +{ + int ret = QWindowsStylePrivate::pixelMetricFromSystemDp(pm, opt); + if (ret != QWindowsStylePrivate::InvalidMetric) + return qRound(qreal(ret) * QWindowsStylePrivate::nativeMetricScaleFactor(opt)); + + ret = QWindowsStylePrivate::fixedPixelMetric(pm); + if (ret != QWindowsStylePrivate::InvalidMetric) + return int(QStyleHelper::dpiScaled(ret, opt)); + + ret = 0; + + switch (pm) { + case PM_MaximumDragDistance: + ret = QCommonStyle::pixelMetric(PM_MaximumDragDistance); + if (ret == -1) + ret = 60; + break; + +#if 0 && QT_CONFIG(slider) + // 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; + } + break; +#endif // QT_CONFIG(slider) + + case PM_IconViewIconSize: + ret = proxy()->pixelMetric(PM_LargeIconSize, opt); + break; + + case PM_SplitterWidth: + ret = int(QStyleHelper::dpiScaled(4, opt)); + break; + + default: + ret = QCommonStyle::pixelMetric(pm, opt); + break; + } + + return ret; +} + +/*! + \reimp + */ +QPixmap QWindowsStyle::standardPixmap(StandardPixmap standardPixmap, const QStyleOption *opt) const +{ +#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT) + QPixmap desktopIcon; + switch (standardPixmap) { + case SP_DriveCDIcon: + case SP_DriveDVDIcon: + case SP_DriveNetIcon: + case SP_DriveHDIcon: + case SP_DriveFDIcon: + case SP_FileIcon: + case SP_FileLinkIcon: + case SP_DirLinkIcon: + case SP_DirClosedIcon: + case SP_DesktopIcon: + case SP_ComputerIcon: + case SP_DirOpenIcon: + case SP_FileDialogNewFolder: + case SP_DirHomeIcon: + case SP_TrashIcon: + case SP_VistaShield: + if (const QPlatformTheme *theme = QGuiApplicationPrivate::platformTheme()) { + QPlatformTheme::StandardPixmap sp = static_cast<QPlatformTheme::StandardPixmap>(standardPixmap); + desktopIcon = theme->standardPixmap(sp, QSizeF(16, 16)); + } + break; + case SP_MessageBoxInformation: + case SP_MessageBoxWarning: + case SP_MessageBoxCritical: + case SP_MessageBoxQuestion: + if (const QPlatformTheme *theme = QGuiApplicationPrivate::platformTheme()) { + QPlatformTheme::StandardPixmap sp = static_cast<QPlatformTheme::StandardPixmap>(standardPixmap); + desktopIcon = theme->standardPixmap(sp, QSizeF()); + } + break; + default: + break; + } + if (!desktopIcon.isNull()) { + return desktopIcon; + } +#endif // Q_OS_WIN && !Q_OS_WINRT + return QCommonStyle::standardPixmap(standardPixmap, opt); +} + +/*! \reimp */ +int QWindowsStyle::styleHint(StyleHint hint, const QStyleOption *opt, + QStyleHintReturn *returnData) const +{ + int ret = 0; + + switch (hint) { + case SH_EtchDisabledText: + ret = d_func()->isDarkMode() ? 0 : 1; + break; + case SH_Slider_SnapToValue: + case SH_PrintDialog_RightAlignButtons: + case SH_FontDialog_SelectAssociatedText: + case SH_Menu_AllowActiveAndDisabled: + case SH_MenuBar_AltKeyNavigation: + case SH_MenuBar_MouseTracking: + case SH_Menu_MouseTracking: + case SH_ComboBox_ListMouseTracking: + case SH_Slider_StopMouseOverSlider: + case SH_MainWindow_SpaceBelowMenuBar: + ret = 1; + + break; + case SH_ItemView_ShowDecorationSelected: +#if 0 && QT_CONFIG(listview) + if (qobject_cast<const QListView*>(widget)) + ret = 1; +#endif + break; + case SH_ItemView_ChangeHighlightOnFocus: + ret = 1; + break; + case SH_ToolBox_SelectedPageTitleBold: + ret = 0; + break; + +#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT) // Option not used on WinRT -> common style + case SH_UnderlineShortcut: + { + ret = 1; + BOOL cues = false; + SystemParametersInfo(SPI_GETKEYBOARDCUES, 0, &cues, 0); + ret = int(cues); + // Do nothing if we always paint underlines + Q_D(const QWindowsStyle); + if (!ret && d) { +#if 0 && QT_CONFIG(menubar) + const QMenuBar *menuBar = qobject_cast<const QMenuBar *>(widget); + if (!menuBar && qobject_cast<const QMenu *>(widget)) { + QWidget *w = QApplication::activeWindow(); + if (w && w != widget) + menuBar = w->findChild<QMenuBar *>(); + } + // If we paint a menu bar draw underlines if is in the keyboardState + if (menuBar) { + if (menuBar->d_func()->keyboardState || d->altDown()) + ret = 1; + // Otherwise draw underlines if the toplevel widget has seen an alt-press + } else +#endif // QT_CONFIG(menubar) +// if (d->hasSeenAlt(widget)) { +// ret = 1; +// } + } +#ifndef QT_NO_ACCESSIBILITY + if (!ret && opt && opt->type == QStyleOption::SO_MenuItem + && QStyleHelper::isInstanceOf(opt->styleObject, QAccessible::MenuItem) + && opt->styleObject->property("_q_showUnderlined").toBool()) + ret = 1; +#endif // QT_NO_ACCESSIBILITY + break; + } +#endif // Q_OS_WIN && !Q_OS_WINRT + case SH_Menu_SubMenuSloppyCloseTimeout: + case SH_Menu_SubMenuPopupDelay: { +#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT) + DWORD delay; + if (SystemParametersInfo(SPI_GETMENUSHOWDELAY, 0, &delay, 0)) + ret = delay; + else +#endif // Q_OS_WIN && !Q_OS_WINRT + ret = 400; + break; + } +#if 0 && QT_CONFIG(rubberband) + case SH_RubberBand_Mask: + if (const QStyleOptionRubberBand *rbOpt = qstyleoption_cast<const QStyleOptionRubberBand *>(opt)) { + ret = 0; + if (rbOpt->shape == QRubberBand::Rectangle) { + ret = true; + if (QStyleHintReturnMask *mask = qstyleoption_cast<QStyleHintReturnMask*>(returnData)) { + mask->region = opt->rect; + int size = 1; + if (widget && widget->isWindow()) + size = 4; + mask->region -= opt->rect.adjusted(size, size, -size, -size); + } + } + } + break; +#endif // QT_CONFIG(rubberband) +#if 0 && QT_CONFIG(wizard) + case SH_WizardStyle: + ret = QWizard::ModernStyle; + break; +#endif + case SH_ItemView_ArrowKeysNavigateIntoChildren: + ret = true; + break; + case SH_DialogButtonBox_ButtonsHaveIcons: + ret = 0; + break; + default: + ret = QCommonStyle::styleHint(hint, opt, returnData); + break; + } + return ret; +} + +/*! \reimp */ +void QWindowsStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, QPainter *p) const +{ + // Used to restore across fallthrough cases. Currently only used in PE_IndicatorCheckBox + bool doRestore = false; + + switch (pe) { +#if 0 && QT_CONFIG(toolbar) + case PE_IndicatorToolBarSeparator: + { + QRect rect = opt->rect; + const int margin = 2; + QPen oldPen = p->pen(); + if (opt->state & State_Horizontal){ + const int offset = rect.width()/2; + p->setPen(QPen(opt->palette.dark().color())); + p->drawLine(rect.bottomLeft().x() + offset, + rect.bottomLeft().y() - margin, + rect.topLeft().x() + offset, + rect.topLeft().y() + margin); + p->setPen(QPen(opt->palette.light().color())); + p->drawLine(rect.bottomLeft().x() + offset + 1, + rect.bottomLeft().y() - margin, + rect.topLeft().x() + offset + 1, + rect.topLeft().y() + margin); + } + else{ //Draw vertical separator + const int offset = rect.height()/2; + p->setPen(QPen(opt->palette.dark().color())); + p->drawLine(rect.topLeft().x() + margin , + rect.topLeft().y() + offset, + rect.topRight().x() - margin, + rect.topRight().y() + offset); + p->setPen(QPen(opt->palette.light().color())); + p->drawLine(rect.topLeft().x() + margin , + rect.topLeft().y() + offset + 1, + rect.topRight().x() - margin, + rect.topRight().y() + offset + 1); + } + p->setPen(oldPen); + } + break; + case PE_IndicatorToolBarHandle: + p->save(); + p->translate(opt->rect.x(), opt->rect.y()); + if (opt->state & State_Horizontal) { + int x = opt->rect.width() / 2 - 4; + if (opt->direction == Qt::RightToLeft) + x -= 2; + if (opt->rect.height() > 4) { + qDrawShadePanel(p, x, 2, 3, opt->rect.height() - 4, + opt->palette, false, 1, nullptr); + qDrawShadePanel(p, x + 3, 2, 3, opt->rect.height() - 4, + opt->palette, false, 1, nullptr); + } + } else { + if (opt->rect.width() > 4) { + int y = opt->rect.height() / 2 - 4; + qDrawShadePanel(p, 2, y, opt->rect.width() - 4, 3, + opt->palette, false, 1, nullptr); + qDrawShadePanel(p, 2, y + 3, opt->rect.width() - 4, 3, + opt->palette, false, 1, nullptr); + } + } + p->restore(); + break; + +#endif // QT_CONFIG(toolbar) + case PE_FrameButtonTool: + case PE_PanelButtonTool: { + QPen oldPen = p->pen(); +#if 0 && QT_CONFIG(dockwidget) + if (w && w->inherits("QDockWidgetTitleButton")) { + if (const QWidget *dw = w->parentWidget()) + if (dw->isWindow()){ + qDrawWinButton(p, opt->rect.adjusted(1, 1, 0, 0), opt->palette, opt->state & (State_Sunken | State_On), + &opt->palette.button()); + + return; + } + } +#endif // QT_CONFIG(dockwidget) + QBrush fill; + bool stippled; + bool panel = (pe == PE_PanelButtonTool); + if ((!(opt->state & State_Sunken )) + && (!(opt->state & State_Enabled) + || !(opt->state & State_MouseOver && opt->state & State_AutoRaise)) + && (opt->state & State_On)) { + fill = QBrush(opt->palette.light().color(), Qt::Dense4Pattern); + stippled = true; + } else { + fill = opt->palette.brush(QPalette::Button); + stippled = false; + } + + if (opt->state & (State_Raised | State_Sunken | State_On)) { + if (opt->state & State_AutoRaise) { + if (opt->state & (State_Enabled | State_Sunken | State_On)){ + if (panel) + qDrawShadePanel(p, opt->rect, opt->palette, + opt->state & (State_Sunken | State_On), 1, &fill); + else + qDrawShadeRect(p, opt->rect, opt->palette, + opt->state & (State_Sunken | State_On), 1); + } + if (stippled) { + p->setPen(opt->palette.button().color()); + p->drawRect(opt->rect.adjusted(1,1,-2,-2)); + } + } else { + qDrawWinButton(p, opt->rect, opt->palette, + opt->state & (State_Sunken | State_On), panel ? &fill : nullptr); + } + } else { + p->fillRect(opt->rect, fill); + } + p->setPen(oldPen); + break; } + case PE_PanelButtonCommand: + if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(opt)) { + QBrush fill; + State flags = opt->state; + QPalette pal = opt->palette; + QRect r = opt->rect; + if (! (flags & State_Sunken) && (flags & State_On)) + fill = QBrush(pal.light().color(), Qt::Dense4Pattern); + else + fill = pal.brush(QPalette::Button); + + if (btn->features & QStyleOptionButton::DefaultButton && flags & State_Sunken) { + p->setPen(pal.dark().color()); + p->setBrush(fill); + p->drawRect(r.adjusted(0, 0, -1, -1)); + } else if (flags & (State_Raised | State_On | State_Sunken)) { + qDrawWinButton(p, r, pal, flags & (State_Sunken | State_On), + &fill); + } else { + p->fillRect(r, fill); + } + } + break; + case PE_FrameDefaultButton: { + QPen oldPen = p->pen(); + p->setPen(QPen(opt->palette.shadow().color(), 0)); + QRectF rect = opt->rect; + const qreal dpi = QStyleHelper::dpi(opt); + const qreal topLevelAdjustment = QStyleHelper::dpiScaled(0.5, dpi); + const qreal bottomRightAdjustment = QStyleHelper::dpiScaled(-1.5, dpi); + rect.adjust(topLevelAdjustment, topLevelAdjustment, + bottomRightAdjustment, bottomRightAdjustment); + p->drawRect(rect); + p->setPen(oldPen); + break; + } + case PE_IndicatorCheckBox: { + QBrush fill; + if (opt->state & State_NoChange) + fill = QBrush(opt->palette.base().color(), Qt::Dense4Pattern); + else if (opt->state & State_Sunken) + fill = opt->palette.button(); + else if (opt->state & State_Enabled) + fill = opt->palette.base(); + else + fill = opt->palette.window(); + p->save(); + doRestore = true; + qDrawWinPanel(p, opt->rect, opt->palette, true, &fill); + if (opt->state & State_NoChange) + p->setPen(opt->palette.dark().color()); + else + p->setPen(opt->palette.text().color()); + } + Q_FALLTHROUGH(); + case PE_IndicatorItemViewItemCheck: + if (!doRestore) { + p->save(); + doRestore = true; + } +#if 0 && QT_CONFIG(itemviews) + if (pe == PE_IndicatorItemViewItemCheck) { + const QStyleOptionViewItem *itemViewOpt = qstyleoption_cast<const QStyleOptionViewItem *>(opt); + p->setPen(itemViewOpt + && itemViewOpt->showDecorationSelected + && opt->state & State_Selected + ? opt->palette.highlightedText().color() + : opt->palette.text().color()); + if (opt->state & State_NoChange) + p->setBrush(opt->palette.brush(QPalette::Button)); + p->drawRect(opt->rect.x() + 1, opt->rect.y() + 1, opt->rect.width() - 2, opt->rect.height() - 2); + } +#endif // QT_CONFIG(itemviews) + if (!(opt->state & State_Off)) { + QPointF points[6]; + qreal scaleh = opt->rect.width() / 12.0; + qreal scalev = opt->rect.height() / 12.0; + points[0] = { opt->rect.x() + 3.5 * scaleh, opt->rect.y() + 5.5 * scalev }; + points[1] = { points[0].x(), points[0].y() + 2 * scalev }; + points[2] = { points[1].x() + 2 * scaleh, points[1].y() + 2 * scalev }; + points[3] = { points[2].x() + 4 * scaleh, points[2].y() - 4 * scalev }; + points[4] = { points[3].x(), points[3].y() - 2 * scalev }; + points[5] = { points[4].x() - 4 * scaleh, points[4].y() + 4 * scalev }; + p->setPen(QPen(opt->palette.text().color(), 0)); + p->setBrush(opt->palette.text().color()); + p->drawPolygon(points, 6); + } + if (doRestore) + p->restore(); + break; + case PE_FrameFocusRect: + if (const QStyleOptionFocusRect *fropt = qstyleoption_cast<const QStyleOptionFocusRect *>(opt)) { + //### check for d->alt_down + if (!(fropt->state & State_KeyboardFocusChange) && !proxy()->styleHint(SH_UnderlineShortcut, opt)) + return; + QRect r = opt->rect; + p->save(); + p->setBackgroundMode(Qt::TransparentMode); + QColor bg_col = fropt->backgroundColor; + if (!bg_col.isValid()) + bg_col = p->background().color(); + // Create an "XOR" color. + QColor patternCol((bg_col.red() ^ 0xff) & 0xff, + (bg_col.green() ^ 0xff) & 0xff, + (bg_col.blue() ^ 0xff) & 0xff); + p->setBrush(QBrush(patternCol, Qt::Dense4Pattern)); + p->setBrushOrigin(r.topLeft()); + p->setPen(Qt::NoPen); + p->drawRect(r.left(), r.top(), r.width(), 1); // Top + p->drawRect(r.left(), r.bottom(), r.width(), 1); // Bottom + p->drawRect(r.left(), r.top(), 1, r.height()); // Left + p->drawRect(r.right(), r.top(), 1, r.height()); // Right + p->restore(); + } + break; + case PE_IndicatorRadioButton: + { + QRect r = opt->rect; + p->save(); + p->setRenderHint(QPainter::Antialiasing, true); + + QPointF circleCenter = r.center() + QPoint(1, 1); + qreal radius = (r.width() + (r.width() + 1) % 2) / 2.0 - 1; + + QPainterPath path1; + path1.addEllipse(circleCenter, radius, radius); + radius *= 0.85; + QPainterPath path2; + path2.addEllipse(circleCenter, radius, radius); + radius *= 0.85; + QPainterPath path3; + path3.addEllipse(circleCenter, radius, radius); + radius *= 0.5; + QPainterPath path4; + path4.addEllipse(circleCenter, radius, radius); + + QPolygon topLeftPol, bottomRightPol; + topLeftPol.setPoints(3, r.x(), r.y(), r.x(), r.y() + r.height(), r.x() + r.width(), r.y()); + bottomRightPol.setPoints(3, r.x(), r.y() + r.height(), r.x() + r.width(), r.y() + r.height(), r.x() + r.width(), r.y()); + + p->setClipRegion(QRegion(topLeftPol)); + p->setPen(opt->palette.dark().color()); + p->setBrush(opt->palette.dark().color()); + p->drawPath(path1); + p->setPen(opt->palette.shadow().color()); + p->setBrush(opt->palette.shadow().color()); + p->drawPath(path2); + + p->setClipRegion(QRegion(bottomRightPol)); + p->setPen(opt->palette.light().color()); + p->setBrush(opt->palette.light().color()); + p->drawPath(path1); + p->setPen(opt->palette.midlight().color()); + p->setBrush(opt->palette.midlight().color()); + p->drawPath(path2); + + QColor fillColor = ((opt->state & State_Sunken) || !(opt->state & State_Enabled)) ? + opt->palette.button().color() : opt->palette.base().color(); + + p->setClipping(false); + p->setPen(fillColor); + p->setBrush(fillColor); + p->drawPath(path3); + + if (opt->state & State_On) { + p->setPen(opt->palette.text().color()); + p->setBrush(opt->palette.text()); + p->drawPath(path4); + } + p->restore(); + break; + } +#ifndef QT_NO_FRAME + case PE_Frame: + case PE_FrameMenu: + if (const QStyleOptionFrame *frame = qstyleoption_cast<const QStyleOptionFrame *>(opt)) { + if (frame->lineWidth == 2 || pe == PE_Frame) { + QPalette popupPal = frame->palette; + if (pe == PE_FrameMenu) { + popupPal.setColor(QPalette::Light, frame->palette.window().color()); + popupPal.setColor(QPalette::Midlight, frame->palette.light().color()); + } + if (pe == PE_Frame && (frame->state & State_Raised)) + qDrawWinButton(p, frame->rect, popupPal, frame->state & State_Sunken); + else if (pe == PE_Frame && (frame->state & State_Sunken)) + { + popupPal.setColor(QPalette::Midlight, frame->palette.window().color()); + qDrawWinPanel(p, frame->rect, popupPal, frame->state & State_Sunken); + } + else + qDrawWinPanel(p, frame->rect, popupPal, frame->state & State_Sunken); + } else { + QCommonStyle::drawPrimitive(pe, opt, p); + } + } else { + QPalette popupPal = opt->palette; + popupPal.setColor(QPalette::Light, opt->palette.window().color()); + popupPal.setColor(QPalette::Midlight, opt->palette.light().color()); + qDrawWinPanel(p, opt->rect, popupPal, opt->state & State_Sunken); + } + break; +#endif // QT_NO_FRAME + case PE_FrameButtonBevel: + case PE_PanelButtonBevel: { + QBrush fill; + bool panel = pe != PE_FrameButtonBevel; + p->setBrushOrigin(opt->rect.topLeft()); + if (!(opt->state & State_Sunken) && (opt->state & State_On)) + fill = QBrush(opt->palette.light().color(), Qt::Dense4Pattern); + else + fill = opt->palette.brush(QPalette::Button); + + if (opt->state & (State_Raised | State_On | State_Sunken)) { + qDrawWinButton(p, opt->rect, opt->palette, opt->state & (State_Sunken | State_On), + panel ? &fill : nullptr); + } else { + if (panel) + p->fillRect(opt->rect, fill); + else + p->drawRect(opt->rect); + } + break; } + case PE_FrameWindow: { + QPalette popupPal = opt->palette; + popupPal.setColor(QPalette::Light, opt->palette.window().color()); + popupPal.setColor(QPalette::Midlight, opt->palette.light().color()); + qDrawWinPanel(p, opt->rect, popupPal, opt->state & State_Sunken); + break; } +#if 0 && QT_CONFIG(dockwidget) + case PE_IndicatorDockWidgetResizeHandle: + break; + case PE_FrameDockWidget: + if (qstyleoption_cast<const QStyleOptionFrame *>(opt)) { + proxy()->drawPrimitive(QStyle::PE_FrameWindow, opt, p, w); + } + break; +#endif // QT_CONFIG(dockwidget) + + case PE_FrameStatusBarItem: + qDrawShadePanel(p, opt->rect, opt->palette, true, 1, nullptr); + break; + + case PE_IndicatorProgressChunk: + { + bool vertical = false, inverted = false; + if (const QStyleOptionProgressBar *pb = qstyleoption_cast<const QStyleOptionProgressBar *>(opt)) { + vertical = !(pb->state & QStyle::State_Horizontal); + inverted = pb->invertedAppearance; + } + + int space = 2; + int chunksize = proxy()->pixelMetric(PM_ProgressBarChunkWidth, opt) - space; + if (!vertical) { + if (opt->rect.width() <= chunksize) + space = 0; + + if (inverted) + p->fillRect(opt->rect.x() + space, opt->rect.y(), opt->rect.width() - space, opt->rect.height(), + opt->palette.brush(QPalette::Highlight)); + else + p->fillRect(opt->rect.x(), opt->rect.y(), opt->rect.width() - space, opt->rect.height(), + opt->palette.brush(QPalette::Highlight)); + } else { + if (opt->rect.height() <= chunksize) + space = 0; + + if (inverted) + p->fillRect(opt->rect.x(), opt->rect.y(), opt->rect.width(), opt->rect.height() - space, + opt->palette.brush(QPalette::Highlight)); + else + p->fillRect(opt->rect.x(), opt->rect.y() + space, opt->rect.width(), opt->rect.height() - space, + opt->palette.brush(QPalette::Highlight)); + } + } + break; + + case PE_FrameTabWidget: { + qDrawWinButton(p, opt->rect, opt->palette, false, nullptr); + break; + } + default: + QCommonStyle::drawPrimitive(pe, opt, p); + } +} + +/*! \reimp */ +void QWindowsStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter *p) const +{ + switch (ce) { +#if 0 && QT_CONFIG(rubberband) + case CE_RubberBand: + if (qstyleoption_cast<const QStyleOptionRubberBand *>(opt)) { + // ### workaround for slow general painter path + QPixmap tiledPixmap(16, 16); + QPainter pixmapPainter(&tiledPixmap); + pixmapPainter.setPen(Qt::NoPen); + pixmapPainter.setBrush(Qt::Dense4Pattern); + pixmapPainter.setBackground(Qt::white); + pixmapPainter.setBackgroundMode(Qt::OpaqueMode); + pixmapPainter.drawRect(0, 0, tiledPixmap.width(), tiledPixmap.height()); + pixmapPainter.end(); + tiledPixmap = QPixmap::fromImage(tiledPixmap.toImage()); + p->save(); + QRect r = opt->rect; + QStyleHintReturnMask mask; + if (proxy()->styleHint(QStyle::SH_RubberBand_Mask, opt, widget, &mask)) + p->setClipRegion(mask.region); + p->drawTiledPixmap(r.x(), r.y(), r.width(), r.height(), tiledPixmap); + p->restore(); + return; + } + break; +#endif // QT_CONFIG(rubberband) + +#if 0 && QT_CONFIG(menu) && QT_CONFIG(mainwindow) + case CE_MenuBarEmptyArea: + if (widget && qobject_cast<const QMainWindow *>(widget->parentWidget())) { + p->fillRect(opt->rect, opt->palette.button()); + QPen oldPen = p->pen(); + p->setPen(QPen(opt->palette.dark().color())); + p->drawLine(opt->rect.bottomLeft(), opt->rect.bottomRight()); + p->setPen(oldPen); + } + break; +#endif +#if 0 && QT_CONFIG(menu) + case CE_MenuItem: + if (const QStyleOptionMenuItem *menuitem = qstyleoption_cast<const QStyleOptionMenuItem *>(opt)) { + 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; + + // windows always has a check column, regardless whether we have an icon or not + int checkcol = qMax<int>(menuitem->maxIconWidth, QWindowsStylePrivate::windowsCheckMarkWidth); + + QBrush fill = menuitem->palette.brush(act ? QPalette::Highlight : QPalette::Button); + p->fillRect(menuitem->rect.adjusted(0, 0, -1, 0), fill); + + if (menuitem->menuItemType == QStyleOptionMenuItem::Separator){ + int yoff = y-1 + h / 2; + p->setPen(menuitem->palette.dark().color()); + p->drawLine(x + 2, yoff, x + w - 4, yoff); + p->setPen(menuitem->palette.light().color()); + p->drawLine(x + 2, yoff + 1, x + w - 4, yoff + 1); + return; + } + + QRect vCheckRect = visualRect(opt->direction, menuitem->rect, QRect(menuitem->rect.x(), menuitem->rect.y(), checkcol, menuitem->rect.height())); + if (!menuitem->icon.isNull() && checked) { + if (act) { + qDrawShadePanel(p, vCheckRect, + menuitem->palette, true, 1, + &menuitem->palette.brush(QPalette::Button)); + } else { + QBrush fill(menuitem->palette.light().color(), Qt::Dense4Pattern); + qDrawShadePanel(p, vCheckRect, menuitem->palette, true, 1, &fill); + } + } else if (!act) { + p->fillRect(vCheckRect, menuitem->palette.brush(QPalette::Button)); + } + + // On Windows Style, if we have a checkable item and an icon we + // draw the icon recessed to indicate an item is checked. If we + // have no icon, we draw a checkmark instead. + if (!menuitem->icon.isNull()) { + QIcon::Mode mode = dis ? QIcon::Disabled : QIcon::Normal; + if (act && !dis) + mode = QIcon::Active; + QPixmap pixmap; + if (checked) + pixmap = menuitem->icon.pixmap(proxy()->pixelMetric(PM_SmallIconSize, opt, widget), mode, QIcon::On); + else + pixmap = menuitem->icon.pixmap(proxy()->pixelMetric(PM_SmallIconSize, opt, 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()); + p->setPen(menuitem->palette.text().color()); + p->drawPixmap(pmr.topLeft(), pixmap); + } else if (checked) { + QStyleOptionMenuItem newMi = *menuitem; + newMi.state = State_None; + if (!dis) + newMi.state |= State_Enabled; + if (act) + newMi.state |= State_On; + newMi.rect = visualRect(opt->direction, menuitem->rect, QRect(menuitem->rect.x() + QWindowsStylePrivate::windowsItemFrame, + menuitem->rect.y() + QWindowsStylePrivate::windowsItemFrame, + checkcol - 2 * QWindowsStylePrivate::windowsItemFrame, + menuitem->rect.height() - 2 * QWindowsStylePrivate::windowsItemFrame)); + proxy()->drawPrimitive(PE_IndicatorMenuCheckMark, &newMi, p, widget); + } + p->setPen(act ? menuitem->palette.highlightedText().color() : menuitem->palette.buttonText().color()); + + QColor discol; + if (dis) { + discol = menuitem->palette.text().color(); + p->setPen(discol); + } + + int xm = int(QWindowsStylePrivate::windowsItemFrame) + checkcol + int(QWindowsStylePrivate::windowsItemHMargin); + int xpos = menuitem->rect.x() + xm; + QRect textRect(xpos, y + QWindowsStylePrivate::windowsItemVMargin, + w - xm - QWindowsStylePrivate::windowsRightBorder - tab + 1, h - 2 * QWindowsStylePrivate::windowsItemVMargin); + QRect vTextRect = visualRect(opt->direction, menuitem->rect, textRect); + QStringRef s(&menuitem->text); + if (!s.isEmpty()) { // draw text + p->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(opt->direction, menuitem->rect, + QRect(textRect.topRight(), QPoint(menuitem->rect.right(), textRect.bottom()))); + const QString textToDraw = s.mid(t + 1).toString(); + if (dis && !act && proxy()->styleHint(SH_EtchDisabledText, opt, widget)) { + p->setPen(menuitem->palette.light().color()); + p->drawText(vShortcutRect.adjusted(1, 1, 1, 1), text_flags, textToDraw); + p->setPen(discol); + } + p->drawText(vShortcutRect, text_flags, textToDraw); + s = s.left(t); + } + QFont font = menuitem->font; + if (menuitem->menuItemType == QStyleOptionMenuItem::DefaultItem) + font.setBold(true); + p->setFont(font); + const QString textToDraw = s.left(t).toString(); + if (dis && !act && proxy()->styleHint(SH_EtchDisabledText, opt, widget)) { + p->setPen(menuitem->palette.light().color()); + p->drawText(vTextRect.adjusted(1, 1, 1, 1), text_flags, textToDraw); + p->setPen(discol); + } + p->drawText(vTextRect, text_flags, textToDraw); + p->restore(); + } + if (menuitem->menuItemType == QStyleOptionMenuItem::SubMenu) {// draw sub menu arrow + int dim = (h - 2 * QWindowsStylePrivate::windowsItemFrame) / 2; + PrimitiveElement arrow; + arrow = (opt->direction == Qt::RightToLeft) ? PE_IndicatorArrowLeft : PE_IndicatorArrowRight; + xpos = x + w - QWindowsStylePrivate::windowsArrowHMargin - QWindowsStylePrivate::windowsItemFrame - dim; + QRect vSubMenuRect = visualRect(opt->direction, menuitem->rect, QRect(xpos, y + h / 2 - dim / 2, dim, dim)); + QStyleOptionMenuItem newMI = *menuitem; + newMI.rect = vSubMenuRect; + newMI.state = dis ? State_None : State_Enabled; + if (act) + newMI.palette.setColor(QPalette::ButtonText, + newMI.palette.highlightedText().color()); + proxy()->drawPrimitive(arrow, &newMI, p, widget); + } + + } + break; +#endif // QT_CONFIG(menu) +#if 0 && QT_CONFIG(menubar) + case CE_MenuBarItem: + if (const QStyleOptionMenuItem *mbi = qstyleoption_cast<const QStyleOptionMenuItem *>(opt)) { + bool active = mbi->state & State_Selected; + bool hasFocus = mbi->state & State_HasFocus; + bool down = mbi->state & State_Sunken; + QStyleOptionMenuItem newMbi = *mbi; + p->fillRect(mbi->rect, mbi->palette.brush(QPalette::Button)); + if (active || hasFocus) { + QBrush b = mbi->palette.brush(QPalette::Button); + if (active && down) + p->setBrushOrigin(p->brushOrigin() + QPoint(1, 1)); + if (active && hasFocus) + qDrawShadeRect(p, mbi->rect.x(), mbi->rect.y(), mbi->rect.width(), + mbi->rect.height(), mbi->palette, active && down, 1, 0, &b); + if (active && down) { + newMbi.rect.translate(proxy()->pixelMetric(PM_ButtonShiftHorizontal, mbi, widget), + proxy()->pixelMetric(PM_ButtonShiftVertical, mbi, widget)); + p->setBrushOrigin(p->brushOrigin() - QPoint(1, 1)); + } + } + QCommonStyle::drawControl(ce, &newMbi, p, widget); + } + break; +#endif // QT_CONFIG(menubar) +#if 0 && QT_CONFIG(tabbar) + case CE_TabBarTabShape: + if (const QStyleOptionTab *tab = qstyleoption_cast<const QStyleOptionTab *>(opt)) { + bool rtlHorTabs = (tab->direction == Qt::RightToLeft + && (tab->shape == QTabBar::RoundedNorth + || tab->shape == QTabBar::RoundedSouth)); + bool selected = tab->state & State_Selected; + bool lastTab = ((!rtlHorTabs && tab->position == QStyleOptionTab::End) + || (rtlHorTabs + && tab->position == QStyleOptionTab::Beginning)); + bool firstTab = ((!rtlHorTabs + && tab->position == QStyleOptionTab::Beginning) + || (rtlHorTabs + && tab->position == QStyleOptionTab::End)); + bool onlyOne = tab->position == QStyleOptionTab::OnlyOneTab; + bool previousSelected = + ((!rtlHorTabs + && tab->selectedPosition == QStyleOptionTab::PreviousIsSelected) + || (rtlHorTabs + && tab->selectedPosition == QStyleOptionTab::NextIsSelected)); + bool nextSelected = + ((!rtlHorTabs + && tab->selectedPosition == QStyleOptionTab::NextIsSelected) + || (rtlHorTabs + && tab->selectedPosition + == QStyleOptionTab::PreviousIsSelected)); + int tabBarAlignment = proxy()->styleHint(SH_TabBar_Alignment, tab, widget); + bool leftAligned = (!rtlHorTabs && tabBarAlignment == Qt::AlignLeft) + || (rtlHorTabs + && tabBarAlignment == Qt::AlignRight); + + bool rightAligned = (!rtlHorTabs && tabBarAlignment == Qt::AlignRight) + || (rtlHorTabs + && tabBarAlignment == Qt::AlignLeft); + + QColor light = tab->palette.light().color(); + QColor dark = tab->palette.dark().color(); + QColor shadow = tab->palette.shadow().color(); + int borderThinkness = proxy()->pixelMetric(PM_TabBarBaseOverlap, tab, widget); + if (selected) + borderThinkness /= 2; + QRect r2(opt->rect); + int x1 = r2.left(); + int x2 = r2.right(); + int y1 = r2.top(); + int y2 = r2.bottom(); + switch (tab->shape) { + default: + QCommonStyle::drawControl(ce, tab, p, widget); + break; + case QTabBar::RoundedNorth: { + if (!selected) { + y1 += 2; + x1 += onlyOne || firstTab ? borderThinkness : 0; + x2 -= onlyOne || lastTab ? borderThinkness : 0; + } + + p->fillRect(QRect(x1 + 1, y1 + 1, (x2 - x1) - 1, (y2 - y1) - 2), tab->palette.window()); + + // Delete border + if (selected) { + p->fillRect(QRect(x1,y2-1,x2-x1,1), tab->palette.window()); + p->fillRect(QRect(x1,y2,x2-x1,1), tab->palette.window()); + } + // Left + if (firstTab || selected || onlyOne || !previousSelected) { + p->setPen(light); + p->drawLine(x1, y1 + 2, x1, y2 - ((onlyOne || firstTab) && selected && leftAligned ? 0 : borderThinkness)); + p->drawPoint(x1 + 1, y1 + 1); + } + // Top + { + int beg = x1 + (previousSelected ? 0 : 2); + int end = x2 - (nextSelected ? 0 : 2); + p->setPen(light); + p->drawLine(beg, y1, end, y1); + } + // Right + if (lastTab || selected || onlyOne || !nextSelected) { + p->setPen(shadow); + p->drawLine(x2, y1 + 2, x2, y2 - ((onlyOne || lastTab) && selected && rightAligned ? 0 : borderThinkness)); + p->drawPoint(x2 - 1, y1 + 1); + p->setPen(dark); + p->drawLine(x2 - 1, y1 + 2, x2 - 1, y2 - ((onlyOne || lastTab) && selected && rightAligned ? 0 : borderThinkness)); + } + break; } + case QTabBar::RoundedSouth: { + if (!selected) { + y2 -= 2; + x1 += firstTab ? borderThinkness : 0; + x2 -= lastTab ? borderThinkness : 0; + } + + p->fillRect(QRect(x1 + 1, y1 + 2, (x2 - x1) - 1, (y2 - y1) - 1), tab->palette.window()); + + // Delete border + if (selected) { + p->fillRect(QRect(x1, y1 + 1, (x2 - 1)-x1, 1), tab->palette.window()); + p->fillRect(QRect(x1, y1, (x2 - 1)-x1, 1), tab->palette.window()); + } + // Left + if (firstTab || selected || onlyOne || !previousSelected) { + p->setPen(light); + p->drawLine(x1, y2 - 2, x1, y1 + ((onlyOne || firstTab) && selected && leftAligned ? 0 : borderThinkness)); + p->drawPoint(x1 + 1, y2 - 1); + } + // Bottom + { + int beg = x1 + (previousSelected ? 0 : 2); + int end = x2 - (nextSelected ? 0 : 2); + p->setPen(shadow); + p->drawLine(beg, y2, end, y2); + p->setPen(dark); + p->drawLine(beg, y2 - 1, end, y2 - 1); + } + // Right + if (lastTab || selected || onlyOne || !nextSelected) { + p->setPen(shadow); + p->drawLine(x2, y2 - 2, x2, y1 + ((onlyOne || lastTab) && selected && rightAligned ? 0 : borderThinkness)); + p->drawPoint(x2 - 1, y2 - 1); + p->setPen(dark); + p->drawLine(x2 - 1, y2 - 2, x2 - 1, y1 + ((onlyOne || lastTab) && selected && rightAligned ? 0 : borderThinkness)); + } + break; } + case QTabBar::RoundedWest: { + if (!selected) { + x1 += 2; + y1 += firstTab ? borderThinkness : 0; + y2 -= lastTab ? borderThinkness : 0; + } + + p->fillRect(QRect(x1 + 1, y1 + 1, (x2 - x1) - 2, (y2 - y1) - 1), tab->palette.window()); + + // Delete border + if (selected) { + p->fillRect(QRect(x2 - 1, y1, 1, y2-y1), tab->palette.window()); + p->fillRect(QRect(x2, y1, 1, y2-y1), tab->palette.window()); + } + // Top + if (firstTab || selected || onlyOne || !previousSelected) { + p->setPen(light); + p->drawLine(x1 + 2, y1, x2 - ((onlyOne || firstTab) && selected && leftAligned ? 0 : borderThinkness), y1); + p->drawPoint(x1 + 1, y1 + 1); + } + // Left + { + int beg = y1 + (previousSelected ? 0 : 2); + int end = y2 - (nextSelected ? 0 : 2); + p->setPen(light); + p->drawLine(x1, beg, x1, end); + } + // Bottom + if (lastTab || selected || onlyOne || !nextSelected) { + p->setPen(shadow); + p->drawLine(x1 + 3, y2, x2 - ((onlyOne || lastTab) && selected && rightAligned ? 0 : borderThinkness), y2); + p->drawPoint(x1 + 2, y2 - 1); + p->setPen(dark); + p->drawLine(x1 + 3, y2 - 1, x2 - ((onlyOne || lastTab) && selected && rightAligned ? 0 : borderThinkness), y2 - 1); + p->drawPoint(x1 + 1, y2 - 1); + p->drawPoint(x1 + 2, y2); + } + break; } + case QTabBar::RoundedEast: { + if (!selected) { + x2 -= 2; + y1 += firstTab ? borderThinkness : 0; + y2 -= lastTab ? borderThinkness : 0; + } + + p->fillRect(QRect(x1 + 2, y1 + 1, (x2 - x1) - 1, (y2 - y1) - 1), tab->palette.window()); + + // Delete border + if (selected) { + p->fillRect(QRect(x1 + 1, y1, 1, (y2 - 1)-y1),tab->palette.window()); + p->fillRect(QRect(x1, y1, 1, (y2-1)-y1), tab->palette.window()); + } + // Top + if (firstTab || selected || onlyOne || !previousSelected) { + p->setPen(light); + p->drawLine(x2 - 2, y1, x1 + ((onlyOne || firstTab) && selected && leftAligned ? 0 : borderThinkness), y1); + p->drawPoint(x2 - 1, y1 + 1); + } + // Right + { + int beg = y1 + (previousSelected ? 0 : 2); + int end = y2 - (nextSelected ? 0 : 2); + p->setPen(shadow); + p->drawLine(x2, beg, x2, end); + p->setPen(dark); + p->drawLine(x2 - 1, beg, x2 - 1, end); + } + // Bottom + if (lastTab || selected || onlyOne || !nextSelected) { + p->setPen(shadow); + p->drawLine(x2 - 2, y2, x1 + ((onlyOne || lastTab) && selected && rightAligned ? 0 : borderThinkness), y2); + p->drawPoint(x2 - 1, y2 - 1); + p->setPen(dark); + p->drawLine(x2 - 2, y2 - 1, x1 + ((onlyOne || lastTab) && selected && rightAligned ? 0 : borderThinkness), y2 - 1); + } + break; } + } + } + break; +#endif // QT_CONFIG(tabbar) + case CE_ToolBoxTabShape: + qDrawShadePanel(p, opt->rect, opt->palette, + opt->state & (State_Sunken | State_On), 1, + &opt->palette.brush(QPalette::Button)); + break; +#if 0 && QT_CONFIG(splitter) + case CE_Splitter: + p->eraseRect(opt->rect); + break; +#endif // QT_CONFIG(splitter) +#if 0 && QT_CONFIG(scrollbar) + case CE_ScrollBarSubLine: + case CE_ScrollBarAddLine: { + if ((opt->state & State_Sunken)) { + p->setPen(opt->palette.dark().color()); + p->setBrush(opt->palette.brush(QPalette::Button)); + p->drawRect(opt->rect.adjusted(0, 0, -1, -1)); + } else { + QStyleOption buttonOpt = *opt; + if (!(buttonOpt.state & State_Sunken)) + buttonOpt.state |= State_Raised; + QPalette pal(opt->palette); + pal.setColor(QPalette::Button, opt->palette.light().color()); + pal.setColor(QPalette::Light, opt->palette.button().color()); + qDrawWinButton(p, opt->rect, pal, opt->state & (State_Sunken | State_On), + &opt->palette.brush(QPalette::Button)); + } + PrimitiveElement arrow; + if (opt->state & State_Horizontal) { + if (ce == CE_ScrollBarAddLine) + arrow = opt->direction == Qt::LeftToRight ? PE_IndicatorArrowRight : PE_IndicatorArrowLeft; + else + arrow = opt->direction == Qt::LeftToRight ? PE_IndicatorArrowLeft : PE_IndicatorArrowRight; + } else { + if (ce == CE_ScrollBarAddLine) + arrow = PE_IndicatorArrowDown; + else + arrow = PE_IndicatorArrowUp; + } + QStyleOption arrowOpt = *opt; + arrowOpt.rect = opt->rect.adjusted(4, 4, -4, -4); + proxy()->drawPrimitive(arrow, &arrowOpt, p, widget); + break; } + case CE_ScrollBarAddPage: + case CE_ScrollBarSubPage: { + QBrush br; + QBrush bg = p->background(); + Qt::BGMode bg_mode = p->backgroundMode(); + p->setPen(Qt::NoPen); + p->setBackgroundMode(Qt::OpaqueMode); + + if (opt->state & State_Sunken) { + br = QBrush(opt->palette.shadow().color(), Qt::Dense4Pattern); + p->setBackground(opt->palette.dark().color()); + p->setBrush(br); + } else { + const QBrush paletteBrush = opt->palette.brush(QPalette::Light); + if (paletteBrush.style() == Qt::TexturePattern) { + if (qHasPixmapTexture(paletteBrush)) + br = QBrush(paletteBrush.texture()); + else + br = QBrush(paletteBrush.textureImage()); + } else + br = QBrush(opt->palette.light().color(), Qt::Dense4Pattern); + p->setBackground(opt->palette.window().color()); + p->setBrush(br); + } + p->drawRect(opt->rect); + p->setBackground(bg); + p->setBackgroundMode(bg_mode); + break; } + case CE_ScrollBarSlider: + if (!(opt->state & State_Enabled)) { + QBrush br; + const QBrush paletteBrush = opt->palette.brush(QPalette::Light); + if (paletteBrush.style() == Qt::TexturePattern) { + if (qHasPixmapTexture(paletteBrush)) + br = QBrush(paletteBrush.texture()); + else + br = QBrush(paletteBrush.textureImage()); + } else + br = QBrush(opt->palette.light().color(), Qt::Dense4Pattern); + p->setPen(Qt::NoPen); + p->setBrush(br); + p->setBackgroundMode(Qt::OpaqueMode); + p->drawRect(opt->rect); + } else { + QStyleOptionButton buttonOpt; + buttonOpt.QStyleOption::operator=(*opt); + buttonOpt.state = State_Enabled | State_Raised; + + QPalette pal(opt->palette); + pal.setColor(QPalette::Button, opt->palette.light().color()); + pal.setColor(QPalette::Light, opt->palette.button().color()); + qDrawWinButton(p, opt->rect, pal, false, &opt->palette.brush(QPalette::Button)); + } + break; +#endif // QT_CONFIG(scrollbar) + case CE_HeaderSection: { + QBrush fill; + if (opt->state & State_On) + fill = QBrush(opt->palette.light().color(), Qt::Dense4Pattern); + else + fill = opt->palette.brush(QPalette::Button); + + if (opt->state & (State_Raised | State_Sunken)) { + qDrawWinButton(p, opt->rect, opt->palette, opt->state & State_Sunken, &fill); + } else { + p->fillRect(opt->rect, fill); + } + break; } +#if 0 && QT_CONFIG(toolbar) + case CE_ToolBar: + if (const QStyleOptionToolBar *toolbar = qstyleoption_cast<const QStyleOptionToolBar *>(opt)) { + // Reserve the beveled appearance only for mainwindow toolbars + if (!(widget && qobject_cast<const QMainWindow*> (widget->parentWidget()))) + break; + + QRect rect = opt->rect; + bool paintLeftBorder = true; + bool paintRightBorder = true; + bool paintBottomBorder = true; + + switch (toolbar->toolBarArea){ + case Qt::BottomToolBarArea : + switch (toolbar->positionOfLine){ + case QStyleOptionToolBar::Beginning: + case QStyleOptionToolBar::OnlyOne: + paintBottomBorder = false; + break; + default: + break; + } + Q_FALLTHROUGH(); // It continues in the end of the next case + case Qt::TopToolBarArea : + switch (toolbar->positionWithinLine){ + case QStyleOptionToolBar::Beginning: + paintLeftBorder = false; + break; + case QStyleOptionToolBar::End: + paintRightBorder = false; + break; + case QStyleOptionToolBar::OnlyOne: + paintRightBorder = false; + paintLeftBorder = false; + break; + default: + break; + } + if (opt->direction == Qt::RightToLeft){ //reverse layout changes the order of Beginning/end + bool tmp = paintLeftBorder; + paintRightBorder=paintLeftBorder; + paintLeftBorder=tmp; + } + break; + case Qt::RightToolBarArea : + switch (toolbar->positionOfLine){ + case QStyleOptionToolBar::Beginning: + case QStyleOptionToolBar::OnlyOne: + paintRightBorder = false; + break; + default: + break; + } + break; + case Qt::LeftToolBarArea : + switch (toolbar->positionOfLine){ + case QStyleOptionToolBar::Beginning: + case QStyleOptionToolBar::OnlyOne: + paintLeftBorder = false; + break; + default: + break; + } + break; + default: + break; + } + + + //draw top border + p->setPen(QPen(opt->palette.light().color())); + p->drawLine(rect.topLeft().x(), + rect.topLeft().y(), + rect.topRight().x(), + rect.topRight().y()); + + if (paintLeftBorder){ + p->setPen(QPen(opt->palette.light().color())); + p->drawLine(rect.topLeft().x(), + rect.topLeft().y(), + rect.bottomLeft().x(), + rect.bottomLeft().y()); + } + + if (paintRightBorder){ + p->setPen(QPen(opt->palette.dark().color())); + p->drawLine(rect.topRight().x(), + rect.topRight().y(), + rect.bottomRight().x(), + rect.bottomRight().y()); + } + + if (paintBottomBorder){ + p->setPen(QPen(opt->palette.dark().color())); + p->drawLine(rect.bottomLeft().x(), + rect.bottomLeft().y(), + rect.bottomRight().x(), + rect.bottomRight().y()); + } + } + break; + + +#endif // QT_CONFIG(toolbar) + + case CE_ProgressBarContents: + if (const QStyleOptionProgressBar *pb = qstyleoption_cast<const QStyleOptionProgressBar *>(opt)) { + QRect rect = pb->rect; + if (!rect.isValid()) + return; + + const bool vertical = !(pb->state & QStyle::State_Horizontal); + const bool inverted = pb->invertedAppearance; + + QTransform m; + if (vertical) { + rect = QRect(rect.y(), rect.x(), rect.height(), rect.width()); // flip width and height + m.rotate(90); + m.translate(0, -(rect.height() + rect.y()*2)); + } + QPalette pal2 = pb->palette; + // Correct the highlight color if it is the same as the background + if (pal2.highlight() == pal2.window()) + pal2.setColor(QPalette::Highlight, pb->palette.color(QPalette::Active, + QPalette::Highlight)); + bool reverse = ((!vertical && (pb->direction == Qt::RightToLeft)) || vertical); + if (inverted) + reverse = !reverse; + int w = rect.width(); + Q_D(const QWindowsStyle); + if (pb->minimum == 0 && pb->maximum == 0) { + const int unit_width = proxy()->pixelMetric(PM_ProgressBarChunkWidth, pb); + QStyleOptionProgressBar pbBits = *pb; + Q_ASSERT(unit_width >0); + + pbBits.rect = rect; + pbBits.palette = pal2; + + int step = 0; + int chunkCount = w / unit_width + 1; +#if 0 && QT_CONFIG(animation) + if (QProgressStyleAnimation *animation = qobject_cast<QProgressStyleAnimation*>(d->animation(opt->styleObject))) + step = (animation->animationStep() / 3) % chunkCount; + else + d->startAnimation(new QProgressStyleAnimation(d->animationFps, opt->styleObject)); +#else + Q_UNUSED(d); +#endif + int chunksInRow = 5; + int myY = pbBits.rect.y(); + int myHeight = pbBits.rect.height(); + int chunksToDraw = chunksInRow; + + if (step > chunkCount - 5)chunksToDraw = (chunkCount - step); + p->save(); + p->setClipRect(m.mapRect(QRectF(rect)).toRect()); + + int x0 = reverse ? rect.left() + rect.width() - unit_width*(step) - unit_width : rect.left() + unit_width * step; + int x = 0; + + for (int i = 0; i < chunksToDraw ; ++i) { + pbBits.rect.setRect(x0 + x, myY, unit_width, myHeight); + pbBits.rect = m.mapRect(QRectF(pbBits.rect)).toRect(); + proxy()->drawPrimitive(PE_IndicatorProgressChunk, &pbBits, p); + x += reverse ? -unit_width : unit_width; + } + //Draw wrap-around chunks + if ( step > chunkCount-5){ + x0 = reverse ? rect.left() + rect.width() - unit_width : rect.left() ; + x = 0; + int chunksToDraw = step - (chunkCount - chunksInRow); + for (int i = 0; i < chunksToDraw ; ++i) { + pbBits.rect.setRect(x0 + x, myY, unit_width, myHeight); + pbBits.rect = m.mapRect(QRectF(pbBits.rect)).toRect(); + proxy()->drawPrimitive(PE_IndicatorProgressChunk, &pbBits, p); + x += reverse ? -unit_width : unit_width; + } + } + p->restore(); //restore state + } + else { +#if 0 && QT_CONFIG(animation) + d->stopAnimation(opt->styleObject); +#endif + QCommonStyle::drawControl(ce, opt, p); + } + } + break; + +#if 0 && QT_CONFIG(dockwidget) + case CE_DockWidgetTitle: + + if (const QStyleOptionDockWidget *dwOpt = qstyleoption_cast<const QStyleOptionDockWidget *>(opt)) { + Q_D(const QWindowsStyle); + + const bool verticalTitleBar = dwOpt->verticalTitleBar; + + QRect rect = dwOpt->rect; + QRect r = rect; + + if (verticalTitleBar) { + r = r.transposed(); + + p->save(); + p->translate(r.left(), r.top() + r.width()); + p->rotate(-90); + p->translate(-r.left(), -r.top()); + } + + bool floating = false; + bool active = dwOpt->state & State_Active; + QColor inactiveCaptionTextColor = d->inactiveCaptionText; + if (dwOpt->movable) { + QColor left, right; + + //Titlebar gradient + if (opt->state & QStyle::State_Window) { + floating = true; + if (active) { + left = d->activeCaptionColor; + right = d->activeGradientCaptionColor; + } else { + left = d->inactiveCaptionColor; + right = d->inactiveGradientCaptionColor; + } + QBrush fillBrush(left); + if (left != right) { + QPoint p1(r.x(), r.top() + r.height()/2); + QPoint p2(rect.right(), r.top() + r.height()/2); + QLinearGradient lg(p1, p2); + lg.setColorAt(0, left); + lg.setColorAt(1, right); + fillBrush = lg; + } + p->fillRect(r.adjusted(0, 0, 0, -3), fillBrush); + } + } + if (!dwOpt->title.isEmpty()) { + QFont oldFont = p->font(); + if (floating) { + QFont font = oldFont; + font.setBold(true); + p->setFont(font); + } + QPalette palette = dwOpt->palette; + palette.setColor(QPalette::Window, inactiveCaptionTextColor); + QRect titleRect = subElementRect(SE_DockWidgetTitleBarText, opt, widget); + if (verticalTitleBar) { + titleRect = QRect(r.left() + rect.bottom() + - titleRect.bottom(), + r.top() + titleRect.left() - rect.left(), + titleRect.height(), titleRect.width()); + } + proxy()->drawItemText(p, titleRect, + Qt::AlignLeft | Qt::AlignVCenter, palette, + dwOpt->state & State_Enabled, dwOpt->title, + floating ? (active ? QPalette::BrightText : QPalette::Window) : QPalette::WindowText); + p->setFont(oldFont); + } + if (verticalTitleBar) + p->restore(); + } + return; +#endif // QT_CONFIG(dockwidget) +#if 0 && QT_CONFIG(combobox) + case CE_ComboBoxLabel: + if (const QStyleOptionComboBox *cb = qstyleoption_cast<const QStyleOptionComboBox *>(opt)) { + if (cb->state & State_HasFocus) { + p->setPen(cb->palette.highlightedText().color()); + p->setBackground(cb->palette.highlight()); + } else { + p->setPen(cb->palette.text().color()); + p->setBackground(cb->palette.window()); + } + } + QCommonStyle::drawControl(ce, opt, p, widget); + break; +#endif // QT_CONFIG(combobox) + default: + QCommonStyle::drawControl(ce, opt, p); + } +} + +/*! \reimp */ +QRect QWindowsStyle::subElementRect(SubElement sr, const QStyleOption *opt) const +{ + QRect r; + switch (sr) { + case SE_SliderFocusRect: + case SE_ToolBoxTabContents: + r = visualRect(opt->direction, opt->rect, opt->rect); + break; + case SE_DockWidgetTitleBarText: { + r = QCommonStyle::subElementRect(sr, opt); + const QStyleOptionDockWidget *dwOpt + = qstyleoption_cast<const QStyleOptionDockWidget*>(opt); + const bool verticalTitleBar = dwOpt && dwOpt->verticalTitleBar; + int m = proxy()->pixelMetric(PM_DockWidgetTitleMargin, opt); + if (verticalTitleBar) { + r.adjust(0, 0, 0, -m); + } else { + if (opt->direction == Qt::LeftToRight) + r.adjust(m, 0, 0, 0); + else + r.adjust(0, 0, -m, 0); + } + break; + } + case SE_ProgressBarContents: + r = QCommonStyle::subElementRect(SE_ProgressBarGroove, opt); + r.adjust(3, 3, -3, -3); + break; + default: + r = QCommonStyle::subElementRect(sr, opt); + } + return r; +} + + +/*! \reimp */ +void QWindowsStyle::drawComplexControl(ComplexControl cc, const QStyleOptionComplex *opt, + QPainter *p) const +{ + switch (cc) { +#if 0 && QT_CONFIG(slider) + case CC_Slider: + if (const QStyleOptionSlider *slider = qstyleoption_cast<const QStyleOptionSlider *>(opt)) { + int thickness = proxy()->pixelMetric(PM_SliderControlThickness, slider, widget); + int len = proxy()->pixelMetric(PM_SliderLength, slider, widget); + int ticks = slider->tickPosition; + QRect groove = proxy()->subControlRect(CC_Slider, slider, SC_SliderGroove, widget); + QRect handle = proxy()->subControlRect(CC_Slider, slider, SC_SliderHandle, widget); + + if ((slider->subControls & SC_SliderGroove) && groove.isValid()) { + int mid = thickness / 2; + + if (ticks & QSlider::TicksAbove) + mid += len / 8; + if (ticks & QSlider::TicksBelow) + mid -= len / 8; + + p->setPen(slider->palette.shadow().color()); + if (slider->orientation == Qt::Horizontal) { + qDrawWinPanel(p, groove.x(), groove.y() + mid - 2, + groove.width(), 4, slider->palette, true); + p->drawLine(groove.x() + 1, groove.y() + mid - 1, + groove.x() + groove.width() - 3, groove.y() + mid - 1); + } else { + qDrawWinPanel(p, groove.x() + mid - 2, groove.y(), + 4, groove.height(), slider->palette, true); + p->drawLine(groove.x() + mid - 1, groove.y() + 1, + groove.x() + mid - 1, groove.y() + groove.height() - 3); + } + } + + if (slider->subControls & SC_SliderTickmarks) { + QStyleOptionSlider tmpSlider = *slider; + tmpSlider.subControls = SC_SliderTickmarks; + QCommonStyle::drawComplexControl(cc, &tmpSlider, p, widget); + } + + if (slider->subControls & SC_SliderHandle) { + // 4444440 + // 4333310 + // 4322210 + // 4322210 + // 4322210 + // 4322210 + // *43210* + // **410** + // ***0*** + const QColor c0 = slider->palette.shadow().color(); + const QColor c1 = slider->palette.dark().color(); + // const QColor c2 = g.button(); + const QColor c3 = slider->palette.midlight().color(); + const QColor c4 = slider->palette.light().color(); + QBrush handleBrush; + + if (slider->state & State_Enabled) { + handleBrush = slider->palette.color(QPalette::Button); + } else { + handleBrush = QBrush(slider->palette.color(QPalette::Button), + Qt::Dense4Pattern); + } + + + int x = handle.x(), y = handle.y(), + wi = handle.width(), he = handle.height(); + + int x1 = x; + int x2 = x+wi-1; + int y1 = y; + int y2 = y+he-1; + + Qt::Orientation orient = slider->orientation; + bool tickAbove = slider->tickPosition == QSlider::TicksAbove; + bool tickBelow = slider->tickPosition == QSlider::TicksBelow; + + 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); + } + + if ((tickAbove && tickBelow) || (!tickAbove && !tickBelow)) { + Qt::BGMode oldMode = p->backgroundMode(); + p->setBackgroundMode(Qt::OpaqueMode); + qDrawWinButton(p, QRect(x, y, wi, he), slider->palette, false, + &handleBrush); + p->setBackgroundMode(oldMode); + return; + } + + QSliderDirection dir; + + if (orient == Qt::Horizontal) + if (tickAbove) + dir = SlUp; + else + dir = SlDown; + else + if (tickAbove) + dir = SlLeft; + else + dir = SlRight; + + QPolygon a; + + int d = 0; + switch (dir) { + case SlUp: + y1 = y1 + wi/2; + d = (wi + 1) / 2 - 1; + a.setPoints(5, x1,y1, x1,y2, x2,y2, x2,y1, x1+d,y1-d); + break; + case SlDown: + y2 = y2 - wi/2; + d = (wi + 1) / 2 - 1; + a.setPoints(5, x1,y1, x1,y2, x1+d,y2+d, x2,y2, x2,y1); + break; + case SlLeft: + d = (he + 1) / 2 - 1; + x1 = x1 + he/2; + a.setPoints(5, x1,y1, x1-d,y1+d, x1,y2, x2,y2, x2,y1); + break; + case SlRight: + d = (he + 1) / 2 - 1; + x2 = x2 - he/2; + a.setPoints(5, x1,y1, x1,y2, x2,y2, x2+d,y1+d, x2,y1); + break; + } + + QBrush oldBrush = p->brush(); + bool oldQt4CompatiblePainting = p->testRenderHint(QPainter::Qt4CompatiblePainting); + p->setPen(Qt::NoPen); + p->setBrush(handleBrush); + p->setRenderHint(QPainter::Qt4CompatiblePainting); + Qt::BGMode oldMode = p->backgroundMode(); + p->setBackgroundMode(Qt::OpaqueMode); + p->drawRect(x1, y1, x2-x1+1, y2-y1+1); + p->drawPolygon(a); + p->setBrush(oldBrush); + p->setBackgroundMode(oldMode); + + if (dir != SlUp) { + p->setPen(c4); + p->drawLine(x1, y1, x2, y1); + p->setPen(c3); + p->drawLine(x1, y1+1, x2, y1+1); + } + if (dir != SlLeft) { + p->setPen(c3); + p->drawLine(x1+1, y1+1, x1+1, y2); + p->setPen(c4); + p->drawLine(x1, y1, x1, y2); + } + if (dir != SlRight) { + p->setPen(c0); + p->drawLine(x2, y1, x2, y2); + p->setPen(c1); + p->drawLine(x2-1, y1+1, x2-1, y2-1); + } + if (dir != SlDown) { + p->setPen(c0); + p->drawLine(x1, y2, x2, y2); + p->setPen(c1); + p->drawLine(x1+1, y2-1, x2-1, y2-1); + } + + switch (dir) { + case SlUp: + p->setPen(c4); + p->drawLine(x1, y1, x1+d, y1-d); + p->setPen(c0); + d = wi - d - 1; + p->drawLine(x2, y1, x2-d, y1-d); + d--; + p->setPen(c3); + p->drawLine(x1+1, y1, x1+1+d, y1-d); + p->setPen(c1); + p->drawLine(x2-1, y1, x2-1-d, y1-d); + break; + case SlDown: + p->setPen(c4); + p->drawLine(x1, y2, x1+d, y2+d); + p->setPen(c0); + d = wi - d - 1; + p->drawLine(x2, y2, x2-d, y2+d); + d--; + p->setPen(c3); + p->drawLine(x1+1, y2, x1+1+d, y2+d); + p->setPen(c1); + p->drawLine(x2-1, y2, x2-1-d, y2+d); + break; + case SlLeft: + p->setPen(c4); + p->drawLine(x1, y1, x1-d, y1+d); + p->setPen(c0); + d = he - d - 1; + p->drawLine(x1, y2, x1-d, y2-d); + d--; + p->setPen(c3); + p->drawLine(x1, y1+1, x1-d, y1+1+d); + p->setPen(c1); + p->drawLine(x1, y2-1, x1-d, y2-1-d); + break; + case SlRight: + p->setPen(c4); + p->drawLine(x2, y1, x2+d, y1+d); + p->setPen(c0); + d = he - d - 1; + p->drawLine(x2, y2, x2+d, y2-d); + d--; + p->setPen(c3); + p->drawLine(x2, y1+1, x2+d, y1+1+d); + p->setPen(c1); + p->drawLine(x2, y2-1, x2+d, y2-1-d); + break; + } + p->setRenderHint(QPainter::Qt4CompatiblePainting, oldQt4CompatiblePainting); + } + } + break; +#endif // QT_CONFIG(slider) +#if 0 && QT_CONFIG(scrollbar) + case CC_ScrollBar: + if (const QStyleOptionSlider *scrollbar = qstyleoption_cast<const QStyleOptionSlider *>(opt)) { + QStyleOptionSlider newScrollbar = *scrollbar; + if (scrollbar->minimum == scrollbar->maximum) + newScrollbar.state &= ~State_Enabled; //do not draw the slider. + QCommonStyle::drawComplexControl(cc, &newScrollbar, p, widget); + } + break; +#endif // QT_CONFIG(scrollbar) +#if 0 && QT_CONFIG(combobox) + case CC_ComboBox: + if (const QStyleOptionComboBox *cmb = qstyleoption_cast<const QStyleOptionComboBox *>(opt)) { + QBrush editBrush = cmb->palette.brush(QPalette::Button); + if ((cmb->subControls & SC_ComboBoxFrame)) { + if (cmb->frame) { + QPalette shadePal = opt->palette; + shadePal.setColor(QPalette::Midlight, shadePal.button().color()); + qDrawWinPanel(p, opt->rect, shadePal, true, &editBrush); + } + else { + p->fillRect(opt->rect, editBrush); + } + } + if (cmb->subControls & SC_ComboBoxArrow) { + State flags = State_None; + + QRect ar = proxy()->subControlRect(CC_ComboBox, cmb, SC_ComboBoxArrow, widget); + bool sunkenArrow = cmb->activeSubControls == SC_ComboBoxArrow + && cmb->state & State_Sunken; + if (sunkenArrow) { + p->setPen(cmb->palette.dark().color()); + p->setBrush(cmb->palette.brush(QPalette::Button)); + p->drawRect(ar.adjusted(0,0,-1,-1)); + } else { + // Make qDrawWinButton use the right colors for drawing the shade of the button + QPalette pal(cmb->palette); + pal.setColor(QPalette::Button, cmb->palette.light().color()); + pal.setColor(QPalette::Light, cmb->palette.button().color()); + qDrawWinButton(p, ar, pal, false, + &cmb->palette.brush(QPalette::Button)); + } + + ar.adjust(2, 2, -2, -2); + if (opt->state & State_Enabled) + flags |= State_Enabled; + if (opt->state & State_HasFocus) + flags |= State_HasFocus; + + if (sunkenArrow) + flags |= State_Sunken; + QStyleOption arrowOpt = *cmb; + arrowOpt.rect = ar.adjusted(1, 1, -1, -1); + arrowOpt.state = flags; + proxy()->drawPrimitive(PE_IndicatorArrowDown, &arrowOpt, p, widget); + } + + if (cmb->subControls & SC_ComboBoxEditField) { + QRect re = proxy()->subControlRect(CC_ComboBox, cmb, SC_ComboBoxEditField, widget); + if (cmb->state & State_HasFocus && !cmb->editable) + p->fillRect(re.x(), re.y(), re.width(), re.height(), + cmb->palette.brush(QPalette::Highlight)); + + if (cmb->state & State_HasFocus) { + p->setPen(cmb->palette.highlightedText().color()); + p->setBackground(cmb->palette.highlight()); + + } else { + p->setPen(cmb->palette.text().color()); + p->setBackground(cmb->palette.window()); + } + + if (cmb->state & State_HasFocus && !cmb->editable) { + QStyleOptionFocusRect focus; + focus.QStyleOption::operator=(*cmb); + focus.rect = subElementRect(SE_ComboBoxFocusRect, cmb, widget); + focus.state |= State_FocusAtBorder; + focus.backgroundColor = cmb->palette.highlight().color(); + proxy()->drawPrimitive(PE_FrameFocusRect, &focus, p, widget); + } + } + } + break; +#endif // QT_CONFIG(combobox) +#if 0 && QT_CONFIG(spinbox) + case CC_SpinBox: + if (const QStyleOptionSpinBox *sb = qstyleoption_cast<const QStyleOptionSpinBox *>(opt)) { + QStyleOptionSpinBox copy = *sb; + PrimitiveElement pe; + bool enabled = opt->state & State_Enabled; + if (sb->frame && (sb->subControls & SC_SpinBoxFrame)) { + QBrush editBrush = sb->palette.brush(QPalette::Base); + QRect r = proxy()->subControlRect(CC_SpinBox, sb, SC_SpinBoxFrame, widget); + QPalette shadePal = sb->palette; + shadePal.setColor(QPalette::Midlight, shadePal.button().color()); + qDrawWinPanel(p, r, shadePal, true, &editBrush); + } + + QPalette shadePal(opt->palette); + shadePal.setColor(QPalette::Button, opt->palette.light().color()); + shadePal.setColor(QPalette::Light, opt->palette.button().color()); + + if (sb->subControls & SC_SpinBoxUp) { + copy.subControls = SC_SpinBoxUp; + QPalette pal2 = sb->palette; + if (!(sb->stepEnabled & QAbstractSpinBox::StepUpEnabled)) { + pal2.setCurrentColorGroup(QPalette::Disabled); + copy.state &= ~State_Enabled; + } + + copy.palette = pal2; + + if (sb->activeSubControls == SC_SpinBoxUp && (sb->state & State_Sunken)) { + copy.state |= State_On; + copy.state |= State_Sunken; + } else { + copy.state |= State_Raised; + copy.state &= ~State_Sunken; + } + pe = (sb->buttonSymbols == QAbstractSpinBox::PlusMinus ? PE_IndicatorSpinPlus + : PE_IndicatorSpinUp); + + copy.rect = proxy()->subControlRect(CC_SpinBox, sb, SC_SpinBoxUp, widget); + qDrawWinButton(p, copy.rect, shadePal, copy.state & (State_Sunken | State_On), + ©.palette.brush(QPalette::Button)); + copy.rect.adjust(4, 1, -5, -1); + if ((!enabled || !(sb->stepEnabled & QAbstractSpinBox::StepUpEnabled)) + && proxy()->styleHint(SH_EtchDisabledText, opt, widget) ) + { + QStyleOptionSpinBox lightCopy = copy; + lightCopy.rect.adjust(1, 1, 1, 1); + lightCopy.palette.setBrush(QPalette::ButtonText, copy.palette.light()); + proxy()->drawPrimitive(pe, &lightCopy, p, widget); + } + proxy()->drawPrimitive(pe, ©, p, widget); + } + + if (sb->subControls & SC_SpinBoxDown) { + copy.subControls = SC_SpinBoxDown; + copy.state = sb->state; + QPalette pal2 = sb->palette; + if (!(sb->stepEnabled & QAbstractSpinBox::StepDownEnabled)) { + pal2.setCurrentColorGroup(QPalette::Disabled); + copy.state &= ~State_Enabled; + } + copy.palette = pal2; + + if (sb->activeSubControls == SC_SpinBoxDown && (sb->state & State_Sunken)) { + copy.state |= State_On; + copy.state |= State_Sunken; + } else { + copy.state |= State_Raised; + copy.state &= ~State_Sunken; + } + pe = (sb->buttonSymbols == QAbstractSpinBox::PlusMinus ? PE_IndicatorSpinMinus + : PE_IndicatorSpinDown); + + copy.rect = proxy()->subControlRect(CC_SpinBox, sb, SC_SpinBoxDown, widget); + qDrawWinButton(p, copy.rect, shadePal, copy.state & (State_Sunken | State_On), + ©.palette.brush(QPalette::Button)); + copy.rect.adjust(4, 0, -5, -1); + if ((!enabled || !(sb->stepEnabled & QAbstractSpinBox::StepDownEnabled)) + && proxy()->styleHint(SH_EtchDisabledText, opt, widget) ) + { + QStyleOptionSpinBox lightCopy = copy; + lightCopy.rect.adjust(1, 1, 1, 1); + lightCopy.palette.setBrush(QPalette::ButtonText, copy.palette.light()); + proxy()->drawPrimitive(pe, &lightCopy, p, widget); + } + proxy()->drawPrimitive(pe, ©, p, widget); + } + } + break; +#endif // QT_CONFIG(spinbox) + + default: + QCommonStyle::drawComplexControl(cc, opt, p); + } +} + +/*! \reimp */ +QSize QWindowsStyle::sizeFromContents(ContentsType ct, const QStyleOption *opt, const QSize &csz) const +{ + QSize sz(csz); + switch (ct) { + case CT_PushButton: + if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(opt)) { + sz = QCommonStyle::sizeFromContents(ct, opt, csz); + int w = sz.width(), + h = sz.height(); + int defwidth = 0; + if (btn->features & QStyleOptionButton::AutoDefaultButton) + defwidth = 2 * proxy()->pixelMetric(PM_ButtonDefaultIndicator, btn); + const qreal dpi = QStyleHelper::dpi(opt); + int minwidth = int(QStyleHelper::dpiScaled(75, dpi)); + int minheight = int(QStyleHelper::dpiScaled(23, dpi)); + +#ifndef QT_QWS_SMALL_PUSHBUTTON + if (w < minwidth + defwidth && !btn->text.isEmpty()) + w = minwidth + defwidth; + if (h < minheight + defwidth) + h = minheight + defwidth; +#endif + sz = QSize(w, h); + } + break; +#if 0 && QT_CONFIG(menu) + case CT_MenuItem: + if (const QStyleOptionMenuItem *mi = qstyleoption_cast<const QStyleOptionMenuItem *>(opt)) { + int w = sz.width(); + sz = QCommonStyle::sizeFromContents(ct, opt, csz, widget); + + if (mi->menuItemType == QStyleOptionMenuItem::Separator) { + sz = QSize(10, QWindowsStylePrivate::windowsSepHeight); + } + else if (mi->icon.isNull()) { + sz.setHeight(sz.height() - 2); + w -= 6; + } + + if (mi->menuItemType != QStyleOptionMenuItem::Separator && !mi->icon.isNull()) { + int iconExtent = proxy()->pixelMetric(PM_SmallIconSize, opt, widget); + sz.setHeight(qMax(sz.height(), + mi->icon.actualSize(QSize(iconExtent, iconExtent)).height() + + 2 * QWindowsStylePrivate::windowsItemFrame)); + } + int maxpmw = mi->maxIconWidth; + int tabSpacing = 20; + if (mi->text.contains(QLatin1Char('\t'))) + w += tabSpacing; + else if (mi->menuItemType == QStyleOptionMenuItem::SubMenu) + w += 2 * QWindowsStylePrivate::windowsArrowHMargin; + else if (mi->menuItemType == QStyleOptionMenuItem::DefaultItem) { + // adjust the font and add the difference in size. + // it would be better if the font could be adjusted in the initStyleOption qmenu func!! + QFontMetrics fm(mi->font); + QFont fontBold = mi->font; + fontBold.setBold(true); + QFontMetrics fmBold(fontBold); + w += fmBold.horizontalAdvance(mi->text) - fm.horizontalAdvance(mi->text); + } + + int checkcol = qMax<int>(maxpmw, QWindowsStylePrivate::windowsCheckMarkWidth); // Windows always shows a check column + w += checkcol; + w += int(QWindowsStylePrivate::windowsRightBorder) + 10; + sz.setWidth(w); + } + break; +#endif // QT_CONFIG(menu) +#if 0 && QT_CONFIG(menubar) + case CT_MenuBarItem: + if (!sz.isEmpty()) + sz += QSize(QWindowsStylePrivate::windowsItemHMargin * 4, QWindowsStylePrivate::windowsItemVMargin * 2); + break; +#endif + case CT_ToolButton: + if (qstyleoption_cast<const QStyleOptionToolButton *>(opt)) + return sz += QSize(7, 6); + Q_FALLTHROUGH(); + + default: + sz = QCommonStyle::sizeFromContents(ct, opt, csz); + } + return sz; +} + +/*! + \reimp +*/ +QIcon QWindowsStyle::standardIcon(StandardPixmap standardIcon, const QStyleOption *option) const +{ + return QCommonStyle::standardIcon(standardIcon, option); +} + +} //namespace QQC2 + +QT_END_NAMESPACE + +#include "moc_qquickwindowsstyle_p.cpp" diff --git a/src/imports/nativestyle/qstyle/windows/qquickwindowsstyle_p.h b/src/imports/nativestyle/qstyle/windows/qquickwindowsstyle_p.h new file mode 100644 index 00000000..92926b71 --- /dev/null +++ b/src/imports/nativestyle/qstyle/windows/qquickwindowsstyle_p.h @@ -0,0 +1,111 @@ +/**************************************************************************** +** +** 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 QQUICKWINDOWSSTYLE_P_H +#define QQUICKWINDOWSSTYLE_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 "qquickcommonstyle.h" + +QT_BEGIN_NAMESPACE + +class QPalette; + +namespace QQC2 { + +class QStyleOptionButton; +class QWindowsStylePrivate; + +class QWindowsStylePrivate; + +class QWindowsStyle : public QCommonStyle +{ + Q_OBJECT +public: + QWindowsStyle(); + ~QWindowsStyle() override; +/* + void polish(QApplication*) override; + void unpolish(QApplication*) override; + + void polish(QWidget*) override; + void unpolish(QWidget*) override; + + void polish(QPalette &) override; +*/ + void drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, QPainter *p) const override; + void drawControl(ControlElement element, const QStyleOption *opt, QPainter *p) const override; + void drawComplexControl(ComplexControl cc, const QStyleOptionComplex *opt, QPainter *p) const override; + + QRect subElementRect(SubElement r, const QStyleOption *opt) const override; + QSize sizeFromContents(ContentsType ct, const QStyleOption *opt, const QSize &contentsSize) const override; + + int pixelMetric(PixelMetric pm, const QStyleOption *option = nullptr) const override; + + int styleHint(StyleHint hint, const QStyleOption *opt = nullptr, + QStyleHintReturn *returnData = nullptr) const override; + + QPixmap standardPixmap(StandardPixmap standardPixmap, const QStyleOption *opt) const override; + + QIcon standardIcon(StandardPixmap standardIcon, const QStyleOption *option = nullptr) const override; + +protected: +// bool eventFilter(QObject *o, QEvent *e) override; + QWindowsStyle(QWindowsStylePrivate &dd); + +private: + Q_DISABLE_COPY_MOVE(QWindowsStyle) + Q_DECLARE_PRIVATE(QWindowsStyle) +}; + +} // namespace QQC2 + +QT_END_NAMESPACE + +#endif // QQUICKWINDOWSSTYLE_P_H diff --git a/src/imports/nativestyle/qstyle/windows/qquickwindowsstyle_p_p.h b/src/imports/nativestyle/qstyle/windows/qquickwindowsstyle_p_p.h new file mode 100644 index 00000000..1cd06fed --- /dev/null +++ b/src/imports/nativestyle/qstyle/windows/qquickwindowsstyle_p_p.h @@ -0,0 +1,116 @@ +/**************************************************************************** +** +** 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 QQUICKWINDOWSSTYLE_P_P_H +#define QQUICKWINDOWSSTYLE_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 XX. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#include "qquickwindowsstyle_p.h" +#include "qquickcommonstyle_p.h" +#include "qquickstylehelper_p.h" + +QT_BEGIN_NAMESPACE + +namespace QQC2 { + +class QStyleOptionButton; +class QWindowsStylePrivate; + +#include <qlist.h> + +class QTime; + +class QWindowsStylePrivate : public QCommonStylePrivate +{ + Q_DECLARE_PUBLIC(QWindowsStyle) +public: + enum { InvalidMetric = -23576 }; + + QWindowsStylePrivate(); + static int pixelMetricFromSystemDp(QStyle::PixelMetric pm, const QStyleOption *option = nullptr); + static int fixedPixelMetric(QStyle::PixelMetric pm); + static qreal devicePixelRatio(const QStyleOption *option = nullptr) + { return (option && option->window) ? option->window->devicePixelRatio() : QWindowsStylePrivate::appDevicePixelRatio(); } + static qreal nativeMetricScaleFactor(const QStyleOption *option = nullptr); + static bool isDarkMode(); + +#if 0 + bool hasSeenAlt(const QWidget *widget) const; + bool altDown() const { return alt_down; } + bool alt_down = false; +#endif + QList<const QWidget *> seenAlt; + int menuBarTimer = 0; + + QColor inactiveCaptionText; + QColor activeCaptionColor; + QColor activeGradientCaptionColor; + QColor inactiveCaptionColor; + QColor inactiveGradientCaptionColor; + + enum { + windowsItemFrame = 2, // menu item frame width + windowsSepHeight = 9, // separator item height + windowsItemHMargin = 3, // menu item hor text margin + windowsItemVMargin = 2, // menu item ver text margin + windowsArrowHMargin = 6, // arrow horizontal margin + windowsRightBorder = 15, // right border on windows + windowsCheckMarkWidth = 12 // checkmarks width on windows + }; + +private: + static qreal appDevicePixelRatio(); +}; + +} // namespace QQC2 + +QT_END_NAMESPACE + +#endif //QQUICKWINDOWSSTYLE_P_P_H + diff --git a/src/imports/nativestyle/qstyle/windows/qquickwindowsvistastyle.cpp b/src/imports/nativestyle/qstyle/windows/qquickwindowsvistastyle.cpp new file mode 100644 index 00000000..c3a567bd --- /dev/null +++ b/src/imports/nativestyle/qstyle/windows/qquickwindowsvistastyle.cpp @@ -0,0 +1,2509 @@ +/**************************************************************************** +** +** 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 "qquickwindowsvistastyle_p.h" +#include "qquickwindowsvistastyle_p_p.h" +#include <qoperatingsystemversion.h> +#include <qscreen.h> +#include <qwindow.h> +#include <private/qstyleanimation_p.h> +#include <private/qstylehelper_p.h> +#include <qpa/qplatformnativeinterface.h> + +QT_BEGIN_NAMESPACE + +static const int windowsItemFrame = 2; // menu item frame width +static const int windowsItemHMargin = 3; // menu item hor text margin +static const int windowsItemVMargin = 4; // menu item ver text margin +static const int windowsArrowHMargin = 6; // arrow horizontal margin +static const int windowsRightBorder = 15; // right border on windows + +#ifndef TMT_CONTENTMARGINS +# define TMT_CONTENTMARGINS 3602 +#endif +#ifndef TMT_SIZINGMARGINS +# define TMT_SIZINGMARGINS 3601 +#endif +#ifndef LISS_NORMAL +# define LISS_NORMAL 1 +# define LISS_HOT 2 +# define LISS_SELECTED 3 +# define LISS_DISABLED 4 +# define LISS_SELECTEDNOTFOCUS 5 +# define LISS_HOTSELECTED 6 +#endif +#ifndef BP_COMMANDLINK +# define BP_COMMANDLINK 6 +# define BP_COMMANDLINKGLYPH 7 +# define CMDLGS_NORMAL 1 +# define CMDLGS_HOT 2 +# define CMDLGS_PRESSED 3 +# define CMDLGS_DISABLED 4 +#endif + +/* \internal + Checks if we should use Vista style , or if we should + fall back to Windows style. +*/ +bool QWindowsVistaStylePrivate::useVista() +{ + return QWindowsVistaStylePrivate::useXP(); +} + +/* \internal + Checks and returns the style object +*/ +inline QObject *styleObject(const QStyleOption *option) { + return option ? option->styleObject : nullptr; +} + +/* \internal + Checks if we can animate on a style option +*/ +bool canAnimate(const QStyleOption *option) { + return option + && option->styleObject + && !option->styleObject->property("_q_no_animation").toBool(); +} + +static inline QImage createAnimationBuffer(const QStyleOption *option, const QWidget *widget) +{ + const 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 = nullptr; + if (const QStyleOptionSlider *slider = qstyleoption_cast<const QStyleOptionSlider*>(option)) + styleOption = new QStyleOptionSlider(*slider); + else if (const QStyleOptionSpinBox *spinbox = qstyleoption_cast<const QStyleOptionSpinBox*>(option)) + styleOption = new QStyleOptionSpinBox(*spinbox); + else if (const QStyleOptionGroupBox *groupBox = qstyleoption_cast<const QStyleOptionGroupBox*>(option)) + styleOption = new QStyleOptionGroupBox(*groupBox); + else if (const QStyleOptionComboBox *combo = qstyleoption_cast<const QStyleOptionComboBox*>(option)) + styleOption = new QStyleOptionComboBox(*combo); + else if (const QStyleOptionButton *button = qstyleoption_cast<const QStyleOptionButton*>(option)) + styleOption = new QStyleOptionButton(*button); + else + styleOption = new QStyleOption(*option); + styleOption->rect = QRect(QPoint(0,0), option->rect.size()); + return styleOption; +} + +/* \internal + Used by animations to delete cloned styleoption +*/ +void deleteClonedAnimationStyleOption(const QStyleOption *option) +{ + if (const QStyleOptionSlider *slider = qstyleoption_cast<const QStyleOptionSlider*>(option)) + delete slider; + else if (const QStyleOptionSpinBox *spinbox = qstyleoption_cast<const QStyleOptionSpinBox*>(option)) + delete spinbox; + else if (const QStyleOptionGroupBox *groupBox = qstyleoption_cast<const QStyleOptionGroupBox*>(option)) + delete groupBox; + else if (const QStyleOptionComboBox *combo = qstyleoption_cast<const QStyleOptionComboBox*>(option)) + delete combo; + else if (const QStyleOptionButton *button = qstyleoption_cast<const QStyleOptionButton*>(option)) + delete button; + else + delete option; +} + +/*! + \class QWindowsVistaStyle + \brief The QWindowsVistaStyle class provides a look and feel suitable for applications on Microsoft Windows Vista. + \since 4.3 + \ingroup appearance + \inmodule QtWidgets + \internal + + \warning This style is only available on the Windows Vista platform + because it makes use of Windows Vista's style engine. + + \sa QMacStyle, QWindowsXPStyle, QFusionStyle +*/ + +/*! + Constructs a QWindowsVistaStyle object. +*/ +QWindowsVistaStyle::QWindowsVistaStyle() + : QWindowsXPStyle(*new QWindowsVistaStylePrivate) +{ +} + +/*! + Destructor. +*/ +QWindowsVistaStyle::~QWindowsVistaStyle() = default; + +//convert Qt state flags to uxtheme button states +static int buttonStateId(int flags, int partId) +{ + int stateId = 0; + if (partId == BP_RADIOBUTTON || partId == BP_CHECKBOX) { + if (!(flags & QStyle::State_Enabled)) + stateId = RBS_UNCHECKEDDISABLED; + else if (flags & QStyle::State_Sunken) + stateId = RBS_UNCHECKEDPRESSED; + else if (flags & QStyle::State_MouseOver) + stateId = RBS_UNCHECKEDHOT; + else + stateId = RBS_UNCHECKEDNORMAL; + + if (flags & QStyle::State_On) + stateId += RBS_CHECKEDNORMAL-1; + + } else if (partId == BP_PUSHBUTTON) { + if (!(flags & QStyle::State_Enabled)) + stateId = PBS_DISABLED; + else if (flags & (QStyle::State_Sunken | QStyle::State_On)) + stateId = PBS_PRESSED; + else if (flags & QStyle::State_MouseOver) + stateId = PBS_HOT; + else + stateId = PBS_NORMAL; + } else { + Q_ASSERT(1); + } + return stateId; +} + +bool QWindowsVistaAnimation::isUpdateNeeded() const +{ + return QWindowsVistaStylePrivate::useVista(); +} + +void QWindowsVistaAnimation::paint(QPainter *painter, const QStyleOption *option) +{ + painter->drawImage(option->rect, currentImage()); +} + +static inline bool supportsStateTransition(QStyle::PrimitiveElement element, + const QStyleOption *option, + const QWidget *widget) +{ + bool result = false; + switch (element) { + case QStyle::PE_IndicatorRadioButton: + case QStyle::PE_IndicatorCheckBox: + result = true; + break; + // QTBUG-40634, do not animate when color is set in palette for PE_PanelLineEdit. + case QStyle::PE_FrameLineEdit: + result = !QWindowsXPStylePrivate::isLineEditBaseColorSet(option, widget); + break; + default: + break; + } + return result; +} + +/*! + \internal + + Animations are used for some state transitions on specific widgets. + + Only one running animation can exist for a widget at any specific + time. Animations can be added through + QWindowsVistaStylePrivate::startAnimation(Animation *) and any + existing animation on a widget can be retrieved with + QWindowsVistaStylePrivate::widgetAnimation(Widget *). + + Once an animation has been started, + QWindowsVistaStylePrivate::timerEvent(QTimerEvent *) will + continuously call update() on the widget until it is stopped, + meaning that drawPrimitive will be called many times until the + transition has completed. During this time, the result will be + retrieved by the Animation::paint(...) function and not by the style + itself. + + To determine if a transition should occur, the style needs to know + the previous state of the widget as well as the current one. This is + solved by updating dynamic properties on the widget every time the + function is called. + + Transitions interrupting existing transitions should always be + smooth, so whenever a hover-transition is started on a pulsating + button, it uses the current frame of the pulse-animation as the + starting image for the hover transition. + + */ + +void QWindowsVistaStyle::drawPrimitive(PrimitiveElement element, const QStyleOption *option, + QPainter *painter, const QWidget *widget) const +{ + QWindowsVistaStylePrivate *d = const_cast<QWindowsVistaStylePrivate*>(d_func()); + + int state = option->state; + if (!QWindowsVistaStylePrivate::useVista()) { + QWindowsStyle::drawPrimitive(element, option, painter, widget); + return; + } + + if ((option->state & State_Enabled) && d->transitionsEnabled() && canAnimate(option)) { + { + QRect oldRect; + QRect newRect; + + if (supportsStateTransition(element, option, widget)) { + // Retrieve and update the dynamic properties tracking + // the previous state of the widget: + QObject *styleObject = option->styleObject; + styleObject->setProperty("_q_no_animation", true); + + int oldState = styleObject->property("_q_stylestate").toInt(); + oldRect = styleObject->property("_q_stylerect").toRect(); + newRect = option->rect; + styleObject->setProperty("_q_stylestate", int(option->state)); + styleObject->setProperty("_q_stylerect", option->rect); + + bool doTransition = oldState && + ((state & State_Sunken) != (oldState & State_Sunken) || + (state & State_On) != (oldState & State_On) || + (state & State_MouseOver) != (oldState & State_MouseOver)); + + if (oldRect != newRect || + (state & State_Enabled) != (oldState & State_Enabled) || + (state & State_Active) != (oldState & State_Active)) + d->stopAnimation(styleObject); + + if (option->state & State_ReadOnly && element == PE_FrameLineEdit) // Do not animate read only line edits + doTransition = false; + + if (doTransition) { + QStyleOption *styleOption = clonedAnimationStyleOption(option); + styleOption->state = QStyle::State(oldState); + + QWindowsVistaAnimation *anim = qobject_cast<QWindowsVistaAnimation *>(d->animation(styleObject)); + QWindowsVistaTransition *t = new QWindowsVistaTransition(styleObject); + + // We create separate images for the initial and final transition states and store them in the + // Transition object. + QImage startImage = createAnimationBuffer(option, widget); + QPainter startPainter(&startImage); + + QImage endImage = createAnimationBuffer(option, widget); + QPainter endPainter(&endImage); + + // If we have a running animation on the widget already, we will use that to paint the initial + // state of the new transition, this ensures a smooth transition from a current animation such as a + // pulsating default button into the intended target state. + if (!anim) + proxy()->drawPrimitive(element, styleOption, &startPainter, widget); + else + anim->paint(&startPainter, styleOption); + + t->setStartImage(startImage); + + // The end state of the transition is simply the result we would have painted + // if the style was not animated. + styleOption->styleObject = nullptr; + styleOption->state = option->state; + proxy()->drawPrimitive(element, styleOption, &endPainter, widget); + + + t->setEndImage(endImage); + + HTHEME theme; + int partId; + DWORD duration; + int fromState = 0; + int toState = 0; + + //translate state flags to UXTHEME states : + if (element == PE_FrameLineEdit) { + theme = OpenThemeData(nullptr, L"Edit"); + partId = EP_EDITBORDER_NOSCROLL; + + if (oldState & State_MouseOver) + fromState = ETS_HOT; + else if (oldState & State_HasFocus) + fromState = ETS_FOCUSED; + else + fromState = ETS_NORMAL; + + if (state & State_MouseOver) + toState = ETS_HOT; + else if (state & State_HasFocus) + toState = ETS_FOCUSED; + else + toState = ETS_NORMAL; + + } else { + theme = OpenThemeData(nullptr, L"Button"); + if (element == PE_IndicatorRadioButton) + partId = BP_RADIOBUTTON; + else if (element == PE_IndicatorCheckBox) + partId = BP_CHECKBOX; + else + partId = BP_PUSHBUTTON; + + fromState = buttonStateId(oldState, partId); + toState = buttonStateId(option->state, partId); + } + + // Retrieve the transition time between the states from the system. + if (theme + && SUCCEEDED(GetThemeTransitionDuration(theme, partId, fromState, toState, + TMT_TRANSITIONDURATIONS, &duration))) { + t->setDuration(int(duration)); + } + t->setStartTime(QTime::currentTime()); + + deleteClonedAnimationStyleOption(styleOption); + d->startAnimation(t); + } + styleObject->setProperty("_q_no_animation", false); + } + + } // End of animation part + } + + QRect rect = option->rect; + + switch (element) { + case PE_IndicatorHeaderArrow: + if (const QStyleOptionHeader *header = qstyleoption_cast<const QStyleOptionHeader *>(option)) { + int stateId = HSAS_SORTEDDOWN; + if (header->sortIndicator & QStyleOptionHeader::SortDown) + stateId = HSAS_SORTEDUP; //note that the uxtheme sort down indicator is the inverse of ours + XPThemeData theme(widget, painter, + QWindowsXPStylePrivate::HeaderTheme, + HP_HEADERSORTARROW, stateId, option->rect); + d->drawBackground(theme); + } + break; + + case PE_IndicatorBranch: + { + XPThemeData theme(widget, painter, QWindowsXPStylePrivate::VistaTreeViewTheme); + static int decoration_size = 0; + if (!decoration_size && theme.isValid()) { + XPThemeData themeSize = theme; + themeSize.partId = TVP_HOTGLYPH; + themeSize.stateId = GLPS_OPENED; + const QSizeF size = themeSize.size() * QWindowsStylePrivate::nativeMetricScaleFactor(widget); + decoration_size = qRound(qMax(size.width(), size.height())); + } + int mid_h = option->rect.x() + option->rect.width() / 2; + int mid_v = option->rect.y() + option->rect.height() / 2; + int bef_h = mid_h; + int bef_v = mid_v; + int aft_h = mid_h; + int aft_v = mid_v; + if (option->state & State_Children) { + int delta = decoration_size / 2; + theme.rect = QRect(bef_h - delta, bef_v - delta, decoration_size, decoration_size); + theme.partId = option->state & State_MouseOver ? TVP_HOTGLYPH : TVP_GLYPH; + theme.stateId = option->state & QStyle::State_Open ? GLPS_OPENED : GLPS_CLOSED; + if (option->direction == Qt::RightToLeft) + theme.mirrorHorizontally = true; + d->drawBackground(theme); + bef_h -= delta + 2; + bef_v -= delta + 2; + aft_h += delta - 2; + aft_v += delta - 2; + } +#if 0 + QBrush brush(option->palette.dark().color(), Qt::Dense4Pattern); + if (option->state & State_Item) { + if (option->direction == Qt::RightToLeft) + painter->fillRect(option->rect.left(), mid_v, bef_h - option->rect.left(), 1, brush); + else + painter->fillRect(aft_h, mid_v, option->rect.right() - aft_h + 1, 1, brush); + } + if (option->state & State_Sibling && option->rect.bottom() > aft_v) + painter->fillRect(mid_h, aft_v, 1, option->rect.bottom() - aft_v + 1, brush); + if (option->state & (State_Open | State_Children | State_Item | State_Sibling) && (bef_v > option->rect.y())) + painter->fillRect(mid_h, option->rect.y(), 1, bef_v - option->rect.y(), brush); +#endif + } + break; + + case PE_PanelButtonBevel: + case PE_IndicatorCheckBox: + case PE_IndicatorRadioButton: + { + if (QWindowsVistaAnimation *a = + qobject_cast<QWindowsVistaAnimation *>(d->animation(styleObject(option)))){ + a->paint(painter, option); + } else { + QWindowsXPStyle::drawPrimitive(element, option, painter, widget); + } + } + break; + + case PE_FrameMenu: + { + int stateId = option->state & State_Active ? MB_ACTIVE : MB_INACTIVE; + XPThemeData theme(widget, painter, + QWindowsXPStylePrivate::MenuTheme, + MENU_POPUPBORDERS, stateId, option->rect); + d->drawBackground(theme); + } + break; + case PE_Frame: { +#ifndef QT_NO_ACCESSIBILITY + if (QStyleHelper::isInstanceOf(option->styleObject, QAccessible::EditableText) + || QStyleHelper::isInstanceOf(option->styleObject, QAccessible::StaticText) || +#else + if ( +#endif + (widget && widget->inherits("QTextEdit"))) { + painter->save(); + int stateId = ETS_NORMAL; + if (!(state & State_Enabled)) + stateId = ETS_DISABLED; + else if (state & State_ReadOnly) + stateId = ETS_READONLY; + else if (state & State_HasFocus) + stateId = ETS_SELECTED; + XPThemeData theme(widget, painter, + QWindowsXPStylePrivate::EditTheme, + EP_EDITBORDER_HVSCROLL, stateId, option->rect); + // Since EP_EDITBORDER_HVSCROLL does not us borderfill, theme.noContent cannot be used for clipping + int borderSize = 1; + GetThemeInt(theme.handle(), theme.partId, theme.stateId, TMT_BORDERSIZE, &borderSize); + QRegion clipRegion = option->rect; + QRegion content = option->rect.adjusted(borderSize, borderSize, -borderSize, -borderSize); + clipRegion ^= content; + painter->setClipRegion(clipRegion); + d->drawBackground(theme); + painter->restore(); + } else { + QWindowsXPStyle::drawPrimitive(element, option, painter, widget); + } + } + break; + + case PE_PanelLineEdit: + if (const QStyleOptionFrame *panel = qstyleoption_cast<const QStyleOptionFrame *>(option)) { + bool isEnabled = option->state & State_Enabled; + if (QWindowsXPStylePrivate::isLineEditBaseColorSet(option, widget)) { + painter->fillRect(panel->rect, panel->palette.brush(QPalette::Base)); + } else { + int partId = EP_BACKGROUND; + int stateId = EBS_NORMAL; + if (!isEnabled) + stateId = EBS_DISABLED; + else if (state & State_ReadOnly) + stateId = EBS_READONLY; + else if (state & State_MouseOver) + stateId = EBS_HOT; + + XPThemeData theme(nullptr, painter, QWindowsXPStylePrivate::EditTheme, + partId, stateId, rect); + if (!theme.isValid()) { + QWindowsStyle::drawPrimitive(element, option, painter, widget); + return; + } + int bgType; + GetThemeEnumValue(theme.handle(), partId, stateId, TMT_BGTYPE, &bgType); + if ( bgType == BT_IMAGEFILE ) { + d->drawBackground(theme); + } else { + QBrush fillColor = option->palette.brush(QPalette::Base); + if (!isEnabled) { + PROPERTYORIGIN origin = PO_NOTFOUND; + GetThemePropertyOrigin(theme.handle(), theme.partId, theme.stateId, TMT_FILLCOLOR, &origin); + // Use only if the fill property comes from our part + if ((origin == PO_PART || origin == PO_STATE)) { + COLORREF bgRef; + GetThemeColor(theme.handle(), partId, stateId, TMT_FILLCOLOR, &bgRef); + fillColor = QBrush(qRgb(GetRValue(bgRef), GetGValue(bgRef), GetBValue(bgRef))); + } + } + painter->fillRect(option->rect, fillColor); + } + } + if (panel->lineWidth > 0) + proxy()->drawPrimitive(PE_FrameLineEdit, panel, painter, widget); + return; + } + break; + + case PE_FrameLineEdit: + if (QWindowsVistaAnimation *anim = qobject_cast<QWindowsVistaAnimation *>(d->animation(styleObject(option)))) { + anim->paint(painter, option); + } else { + QPainter *p = painter; + if (QWindowsXPStylePrivate::isItemViewDelegateLineEdit(widget)) { + // we try to check if this lineedit is a delegate on a QAbstractItemView-derived class. + QPen oldPen = p->pen(); + // Inner white border + p->setPen(QPen(option->palette.base().color(), 1)); + p->drawRect(option->rect.adjusted(1, 1, -2, -2)); + // Outer dark border + p->setPen(QPen(option->palette.shadow().color(), 1)); + p->drawRect(option->rect.adjusted(0, 0, -1, -1)); + p->setPen(oldPen); + return; + } + int stateId = ETS_NORMAL; + if (!(state & State_Enabled)) + stateId = ETS_DISABLED; + else if (state & State_ReadOnly) + stateId = ETS_READONLY; + else if (state & State_MouseOver) + stateId = ETS_HOT; + else if (state & State_HasFocus) + stateId = ETS_SELECTED; + XPThemeData theme(widget, painter, + QWindowsXPStylePrivate::EditTheme, + EP_EDITBORDER_NOSCROLL, stateId, option->rect); + theme.noContent = true; + painter->save(); + QRegion clipRegion = option->rect; + clipRegion -= option->rect.adjusted(2, 2, -2, -2); + painter->setClipRegion(clipRegion); + d->drawBackground(theme); + painter->restore(); + } + break; + + case PE_IndicatorToolBarHandle: + { + XPThemeData theme; + QRect rect; + if (option->state & State_Horizontal) { + theme = XPThemeData(widget, painter, + QWindowsXPStylePrivate::RebarTheme, + RP_GRIPPER, ETS_NORMAL, option->rect.adjusted(0, 1, -2, -2)); + rect = option->rect.adjusted(0, 1, 0, -2); + rect.setWidth(4); + } else { + theme = XPThemeData(widget, painter, QWindowsXPStylePrivate::RebarTheme, + RP_GRIPPERVERT, ETS_NORMAL, option->rect.adjusted(0, 1, -2, -2)); + rect = option->rect.adjusted(1, 0, -1, 0); + rect.setHeight(4); + } + theme.rect = rect; + d->drawBackground(theme); + } + break; + + case PE_IndicatorToolBarSeparator: + { + QPen pen = painter->pen(); + int margin = 3; + painter->setPen(option->palette.window().color().darker(114)); + if (option->state & State_Horizontal) { + int x1 = option->rect.center().x(); + painter->drawLine(QPoint(x1, option->rect.top() + margin), QPoint(x1, option->rect.bottom() - margin)); + } else { + int y1 = option->rect.center().y(); + painter->drawLine(QPoint(option->rect.left() + margin, y1), QPoint(option->rect.right() - margin, y1)); + } + painter->setPen(pen); + } + break; + + case PE_PanelTipLabel: { + XPThemeData theme(widget, painter, + QWindowsXPStylePrivate::ToolTipTheme, + TTP_STANDARD, TTSS_NORMAL, option->rect); + d->drawBackground(theme); + break; + } + + case PE_PanelItemViewItem: + { + const QStyleOptionViewItem *vopt; + bool newStyle = true; + QAbstractItemView::SelectionBehavior selectionBehavior = QAbstractItemView::SelectRows; + QAbstractItemView::SelectionMode selectionMode = QAbstractItemView::NoSelection; + if (const QAbstractItemView *view = qobject_cast<const QAbstractItemView *>(widget)) { + newStyle = !qobject_cast<const QTableView*>(view); + selectionBehavior = view->selectionBehavior(); + selectionMode = view->selectionMode(); +#ifndef QT_NO_ACCESSIBILITY + } else if (!widget) { + newStyle = !QStyleHelper::hasAncestor(option->styleObject, QAccessible::MenuItem) ; +#endif + } + + if (newStyle && (vopt = qstyleoption_cast<const QStyleOptionViewItem *>(option))) { + bool selected = vopt->state & QStyle::State_Selected; + const bool hover = selectionMode != QAbstractItemView::NoSelection && (vopt->state & QStyle::State_MouseOver); + bool active = vopt->state & QStyle::State_Active; + + if (vopt->features & QStyleOptionViewItem::Alternate) + painter->fillRect(vopt->rect, vopt->palette.alternateBase()); + + QPalette::ColorGroup cg = vopt->state & QStyle::State_Enabled + ? QPalette::Normal : QPalette::Disabled; + if (cg == QPalette::Normal && !(vopt->state & QStyle::State_Active)) + cg = QPalette::Inactive; + + QRect itemRect = subElementRect(QStyle::SE_ItemViewItemFocusRect, option, widget).adjusted(-1, 0, 1, 0); + itemRect.setTop(vopt->rect.top()); + itemRect.setBottom(vopt->rect.bottom()); + + QSize sectionSize = itemRect.size(); + if (vopt->showDecorationSelected) + sectionSize = vopt->rect.size(); + + if (selectionBehavior == QAbstractItemView::SelectRows) + sectionSize.setWidth(vopt->rect.width()); + QPixmap pixmap; + + if (vopt->backgroundBrush.style() != Qt::NoBrush) { + const QPointF oldBrushOrigin = painter->brushOrigin(); + painter->setBrushOrigin(vopt->rect.topLeft()); + painter->fillRect(vopt->rect, vopt->backgroundBrush); + painter->setBrushOrigin(oldBrushOrigin); + } + + if (hover || selected) { + if (sectionSize.width() > 0 && sectionSize.height() > 0) { + QString key = QString::fromLatin1("qvdelegate-%1-%2-%3-%4-%5").arg(sectionSize.width()) + .arg(sectionSize.height()).arg(selected).arg(active).arg(hover); + if (!QPixmapCache::find(key, &pixmap)) { + pixmap = QPixmap(sectionSize); + pixmap.fill(Qt::transparent); + + int state; + if (selected && hover) + state = LISS_HOTSELECTED; + else if (selected && !active) + state = LISS_SELECTEDNOTFOCUS; + else if (selected) + state = LISS_SELECTED; + else + state = LISS_HOT; + + QPainter pixmapPainter(&pixmap); + XPThemeData theme(widget, &pixmapPainter, + QWindowsXPStylePrivate::VistaTreeViewTheme, + LVP_LISTITEM, state, QRect(0, 0, sectionSize.width(), sectionSize.height())); + if (theme.isValid()) { + d->drawBackground(theme); + } else { + QWindowsXPStyle::drawPrimitive(PE_PanelItemViewItem, option, painter, widget); + break; + } + QPixmapCache::insert(key, pixmap); + } + } + + if (vopt->showDecorationSelected) { + const int frame = 2; //Assumes a 2 pixel pixmap border + QRect srcRect = QRect(0, 0, sectionSize.width(), sectionSize.height()); + QRect pixmapRect = vopt->rect; + bool reverse = vopt->direction == Qt::RightToLeft; + bool leftSection = vopt->viewItemPosition == QStyleOptionViewItem::Beginning; + bool rightSection = vopt->viewItemPosition == QStyleOptionViewItem::End; + if (vopt->viewItemPosition == QStyleOptionViewItem::OnlyOne + || vopt->viewItemPosition == QStyleOptionViewItem::Invalid) + painter->drawPixmap(pixmapRect.topLeft(), pixmap); + else if (reverse ? rightSection : leftSection){ + painter->drawPixmap(QRect(pixmapRect.topLeft(), + QSize(frame, pixmapRect.height())), pixmap, + QRect(QPoint(0, 0), QSize(frame, pixmapRect.height()))); + painter->drawPixmap(pixmapRect.adjusted(frame, 0, 0, 0), + pixmap, srcRect.adjusted(frame, 0, -frame, 0)); + } else if (reverse ? leftSection : rightSection) { + painter->drawPixmap(QRect(pixmapRect.topRight() - QPoint(frame - 1, 0), + QSize(frame, pixmapRect.height())), pixmap, + QRect(QPoint(pixmapRect.width() - frame, 0), + QSize(frame, pixmapRect.height()))); + painter->drawPixmap(pixmapRect.adjusted(0, 0, -frame, 0), + pixmap, srcRect.adjusted(frame, 0, -frame, 0)); + } else if (vopt->viewItemPosition == QStyleOptionViewItem::Middle) + painter->drawPixmap(pixmapRect, pixmap, + srcRect.adjusted(frame, 0, -frame, 0)); + } else { + if (vopt->text.isEmpty() && vopt->icon.isNull()) + break; + painter->drawPixmap(itemRect.topLeft(), pixmap); + } + } + } else { + QWindowsXPStyle::drawPrimitive(element, option, painter, widget); + } + break; + } + case PE_Widget: + { +#if QT_CONFIG(dialogbuttonbox) + const QDialogButtonBox *buttonBox = nullptr; + + if (qobject_cast<const QMessageBox *> (widget)) + buttonBox = widget->findChild<const QDialogButtonBox *>(QLatin1String("qt_msgbox_buttonbox")); +#if QT_CONFIG(inputdialog) + else if (qobject_cast<const QInputDialog *> (widget)) + buttonBox = widget->findChild<const QDialogButtonBox *>(QLatin1String("qt_inputdlg_buttonbox")); +#endif // QT_CONFIG(inputdialog) + + if (buttonBox) { + //draw white panel part + XPThemeData theme(widget, painter, + QWindowsXPStylePrivate::TaskDialogTheme, + TDLG_PRIMARYPANEL, 0, option->rect); + QRect toprect = option->rect; + toprect.setBottom(buttonBox->geometry().top()); + theme.rect = toprect; + d->drawBackground(theme); + + //draw bottom panel part + QRect buttonRect = option->rect; + buttonRect.setTop(buttonBox->geometry().top()); + theme.rect = buttonRect; + theme.partId = TDLG_SECONDARYPANEL; + d->drawBackground(theme); + } +#endif + } + break; + default: + QWindowsXPStyle::drawPrimitive(element, option, painter, widget); + break; + } +} + + +/*! + \internal + + see drawPrimitive for comments on the animation support + */ +void QWindowsVistaStyle::drawControl(ControlElement element, const QStyleOption *option, + QPainter *painter, const QWidget *widget) const +{ + QWindowsVistaStylePrivate *d = const_cast<QWindowsVistaStylePrivate*>(d_func()); + + if (!QWindowsVistaStylePrivate::useVista()) { + QWindowsStyle::drawControl(element, option, painter, widget); + return; + } + + bool selected = option->state & State_Selected; + bool pressed = option->state & State_Sunken; + bool disabled = !(option->state & State_Enabled); + + int state = option->state; + int themeNumber = -1; + + QRect rect(option->rect); + State flags = option->state; + int partId = 0; + int stateId = 0; + + if (d->transitionsEnabled() && canAnimate(option)) + { + if (element == CE_PushButtonBevel) { + QRect oldRect; + QRect newRect; + + QObject *styleObject = option->styleObject; + + int oldState = styleObject->property("_q_stylestate").toInt(); + oldRect = styleObject->property("_q_stylerect").toRect(); + newRect = option->rect; + styleObject->setProperty("_q_stylestate", int(option->state)); + styleObject->setProperty("_q_stylerect", option->rect); + + bool wasDefault = false; + bool isDefault = false; + if (const QStyleOptionButton *button = qstyleoption_cast<const QStyleOptionButton *>(option)) { + wasDefault = styleObject->property("_q_isdefault").toBool(); + isDefault = button->features & QStyleOptionButton::DefaultButton; + styleObject->setProperty("_q_isdefault", isDefault); + } + + bool doTransition = ((state & State_Sunken) != (oldState & State_Sunken) || + (state & State_On) != (oldState & State_On) || + (state & State_MouseOver) != (oldState & State_MouseOver)); + + if (oldRect != newRect || (wasDefault && !isDefault)) { + doTransition = false; + d->stopAnimation(styleObject); + } + + if (doTransition) { + styleObject->setProperty("_q_no_animation", true); + + QWindowsVistaTransition *t = new QWindowsVistaTransition(styleObject); + QWindowsVistaAnimation *anim = qobject_cast<QWindowsVistaAnimation *>(d->animation(styleObject)); + QStyleOption *styleOption = clonedAnimationStyleOption(option); + styleOption->state = QStyle::State(oldState); + + QImage startImage = createAnimationBuffer(option, widget); + QPainter startPainter(&startImage); + + // Use current state of existing animation if already one is running + if (!anim) { + proxy()->drawControl(element, styleOption, &startPainter, widget); + } else { + anim->paint(&startPainter, styleOption); + d->stopAnimation(styleObject); + } + + t->setStartImage(startImage); + QImage endImage = createAnimationBuffer(option, widget); + QPainter endPainter(&endImage); + styleOption->state = option->state; + proxy()->drawControl(element, styleOption, &endPainter, widget); + t->setEndImage(endImage); + + + DWORD duration = 0; + const HTHEME theme = OpenThemeData(nullptr, L"Button"); + + int fromState = buttonStateId(oldState, BP_PUSHBUTTON); + int toState = buttonStateId(option->state, BP_PUSHBUTTON); + if (GetThemeTransitionDuration(theme, BP_PUSHBUTTON, fromState, toState, TMT_TRANSITIONDURATIONS, &duration) == S_OK) + t->setDuration(int(duration)); + else + t->setDuration(0); + t->setStartTime(QTime::currentTime()); + styleObject->setProperty("_q_no_animation", false); + + deleteClonedAnimationStyleOption(styleOption); + d->startAnimation(t); + } + + QWindowsVistaAnimation *anim = qobject_cast<QWindowsVistaAnimation *>(d->animation(styleObject)); + if (anim) { + anim->paint(painter, option); + return; + } + + } + } + switch (element) { + case CE_PushButtonBevel: + if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(option)) + { + themeNumber = QWindowsXPStylePrivate::ButtonTheme; + partId = BP_PUSHBUTTON; + if (btn->features & QStyleOptionButton::CommandLinkButton) + partId = BP_COMMANDLINK; + bool justFlat = (btn->features & QStyleOptionButton::Flat) && !(flags & (State_On|State_Sunken)); + if (!(flags & State_Enabled) && !(btn->features & QStyleOptionButton::Flat)) + stateId = PBS_DISABLED; + else if (justFlat) + ; + else if (flags & (State_Sunken | State_On)) + stateId = PBS_PRESSED; + else if (flags & State_MouseOver) + stateId = PBS_HOT; + else if (btn->features & QStyleOptionButton::DefaultButton && (state & State_Active)) + stateId = PBS_DEFAULTED; + else + stateId = PBS_NORMAL; + + if (!justFlat) { + + if (d->transitionsEnabled() && (btn->features & QStyleOptionButton::DefaultButton) && + !(state & (State_Sunken | State_On)) && !(state & State_MouseOver) && + (state & State_Enabled) && (state & State_Active)) + { + QWindowsVistaAnimation *anim = qobject_cast<QWindowsVistaAnimation *>(d->animation(styleObject(option))); + + if (!anim) { + QImage startImage = createAnimationBuffer(option, widget); + QImage alternateImage = createAnimationBuffer(option, widget); + + QWindowsVistaPulse *pulse = new QWindowsVistaPulse(styleObject(option)); + + QPainter startPainter(&startImage); + stateId = PBS_DEFAULTED; + XPThemeData theme(widget, &startPainter, themeNumber, partId, stateId, rect); + d->drawBackground(theme); + + QPainter alternatePainter(&alternateImage); + theme.stateId = PBS_DEFAULTED_ANIMATING; + theme.painter = &alternatePainter; + d->drawBackground(theme); + pulse->setStartImage(startImage); + pulse->setEndImage(alternateImage); + pulse->setStartTime(QTime::currentTime()); + pulse->setDuration(2000); + d->startAnimation(pulse); + anim = pulse; + } + + if (anim) + anim->paint(painter, option); + else { + XPThemeData theme(widget, painter, themeNumber, partId, stateId, rect); + d->drawBackground(theme); + } + } + else { + XPThemeData theme(widget, painter, themeNumber, partId, stateId, rect); + d->drawBackground(theme); + } + } + + if (btn->features & QStyleOptionButton::HasMenu) { + int mbiw = 0, mbih = 0; + XPThemeData theme(widget, nullptr, QWindowsXPStylePrivate::ToolBarTheme, + TP_DROPDOWNBUTTON); + if (theme.isValid()) { + const QSizeF size = theme.size() * QStyleHelper::dpiScaled(1, option); + if (!size.isEmpty()) { + mbiw = qRound(size.width()); + mbih = qRound(size.height()); + } + } + QRect ir = subElementRect(SE_PushButtonContents, option, nullptr); + QStyleOptionButton newBtn = *btn; + newBtn.rect = QStyle::visualRect(option->direction, option->rect, + QRect(ir.right() - mbiw - 2, + option->rect.top() + (option->rect.height()/2) - (mbih/2), + mbiw + 1, mbih + 1)); + proxy()->drawPrimitive(PE_IndicatorArrowDown, &newBtn, painter, widget); + } + return; + } + break; + + case CE_ProgressBarContents: + if (const QStyleOptionProgressBar *bar + = qstyleoption_cast<const QStyleOptionProgressBar *>(option)) { + bool isIndeterminate = (bar->minimum == 0 && bar->maximum == 0); + const bool vertical = bar->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); + + int alignment = Qt::AlignCenter | Qt::TextShowMnemonic | Qt::TextDontClip | Qt::TextSingleLine; + if (!proxy()->styleHint(SH_UnderlineShortcut, mbi, widget)) + alignment |= Qt::TextHideMnemonic; + + if (widget && mbi->palette.color(QPalette::Window) != Qt::transparent) { // Not needed for QtQuick Controls + //The rect adjustment is a workaround for the menu not really filling its background. + XPThemeData theme(widget, painter, + QWindowsXPStylePrivate::MenuTheme, + MENU_BARBACKGROUND, 0, option->rect.adjusted(-1, 0, 2, 1)); + d->drawBackground(theme); + } + + int stateId = MBI_NORMAL; + if (disabled) + stateId = MBI_DISABLED; + else if (pressed) + stateId = MBI_PUSHED; + else if (selected) + stateId = MBI_HOT; + + XPThemeData theme2(widget, painter, + QWindowsXPStylePrivate::MenuTheme, + MENU_BARITEM, stateId, option->rect); + d->drawBackground(theme2); + + if (!pix.isNull()) + drawItemPixmap(painter, mbi->rect, alignment, pix); + else + drawItemText(painter, mbi->rect, alignment, mbi->palette, mbi->state & State_Enabled, mbi->text, textRole); + } + } + break; +#if QT_CONFIG(menu) + case CE_MenuItem: + if (const QStyleOptionMenuItem *menuitem = qstyleoption_cast<const QStyleOptionMenuItem *>(option)) { + // windows always has a check column, regardless whether we have an icon or not + const qreal factor = QWindowsXPStylePrivate::nativeMetricScaleFactor(widget); + int checkcol = qRound(qreal(25) * factor); + const int gutterWidth = qRound(qreal(3) * factor); + { + XPThemeData theme(widget, nullptr, QWindowsXPStylePrivate::MenuTheme, + MENU_POPUPCHECKBACKGROUND, MBI_HOT); + XPThemeData themeSize = theme; + themeSize.partId = MENU_POPUPCHECK; + themeSize.stateId = 0; + const QSizeF size = themeSize.size() * QWindowsStylePrivate::nativeMetricScaleFactor(widget); + const QMarginsF margins = themeSize.margins() * QWindowsStylePrivate::nativeMetricScaleFactor(widget); + checkcol = qMax(menuitem->maxIconWidth, qRound(gutterWidth + size.width() + margins.left() + margins.right())); + } + QRect rect = option->rect; + + //draw vertical menu line + if (option->direction == Qt::LeftToRight) + checkcol += rect.x(); + QPoint p1 = QStyle::visualPos(option->direction, menuitem->rect, QPoint(checkcol, rect.top())); + QPoint p2 = QStyle::visualPos(option->direction, menuitem->rect, QPoint(checkcol, rect.bottom())); + QRect gutterRect(p1.x(), p1.y(), gutterWidth, p2.y() - p1.y() + 1); + XPThemeData theme2(widget, painter, QWindowsXPStylePrivate::MenuTheme, + MENU_POPUPGUTTER, stateId, gutterRect); + d->drawBackground(theme2); + + int x, y, w, h; + menuitem->rect.getRect(&x, &y, &w, &h); + int tab = menuitem->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_CONFIG(menu) + case CE_HeaderSection: + if (const QStyleOptionHeader *header = qstyleoption_cast<const QStyleOptionHeader *>(option)) { + partId = HP_HEADERITEM; + if (flags & State_Sunken) + stateId = HIS_PRESSED; + else if (flags & State_MouseOver) + stateId = HIS_HOT; + else + stateId = HIS_NORMAL; + + if (header->sortIndicator != QStyleOptionHeader::None) + stateId += 3; + + XPThemeData theme(widget, painter, + QWindowsXPStylePrivate::HeaderTheme, + partId, stateId, option->rect); + d->drawBackground(theme); + } + break; + case CE_MenuBarEmptyArea: + { + stateId = MBI_NORMAL; + if (!(state & State_Enabled)) + stateId = MBI_DISABLED; + XPThemeData theme(widget, painter, + QWindowsXPStylePrivate::MenuTheme, + MENU_BARBACKGROUND, stateId, option->rect); + d->drawBackground(theme); + } + break; + case CE_ToolBar: + if (const QStyleOptionToolBar *toolbar = qstyleoption_cast<const QStyleOptionToolBar *>(option)) { + QPalette pal = option->palette; + pal.setColor(QPalette::Dark, option->palette.window().color().darker(130)); + QStyleOptionToolBar copyOpt = *toolbar; + copyOpt.palette = pal; + QWindowsStyle::drawControl(element, ©Opt, painter, widget); + } + break; +#if QT_CONFIG(dockwidget) + case CE_DockWidgetTitle: + if (const QStyleOptionDockWidget *dwOpt = qstyleoption_cast<const QStyleOptionDockWidget *>(option)) { + const QDockWidget *dockWidget = qobject_cast<const QDockWidget *>(widget); + QRect rect = option->rect; + if (dockWidget && dockWidget->isFloating()) { + QWindowsXPStyle::drawControl(element, option, painter, widget); + break; //otherwise fall through + } + + const bool verticalTitleBar = dwOpt->verticalTitleBar; + + if (verticalTitleBar) { + rect = rect.transposed(); + + painter->translate(rect.left() - 1, rect.top() + rect.width()); + painter->rotate(-90); + painter->translate(-rect.left() + 1, -rect.top()); + } + + painter->setBrush(option->palette.window().color().darker(110)); + painter->setPen(option->palette.window().color().darker(130)); + painter->drawRect(rect.adjusted(0, 1, -1, -3)); + + int buttonMargin = 4; + int mw = proxy()->pixelMetric(QStyle::PM_DockWidgetTitleMargin, dwOpt, widget); + int fw = proxy()->pixelMetric(PM_DockWidgetFrameWidth, dwOpt, widget); + const QDockWidget *dw = qobject_cast<const QDockWidget *>(widget); + bool isFloating = dw && dw->isFloating(); + + QRect r = option->rect.adjusted(0, 2, -1, -3); + QRect titleRect = r; + + if (dwOpt->closable) { + QSize sz = proxy()->standardIcon(QStyle::SP_TitleBarCloseButton, dwOpt, widget).actualSize(QSize(10, 10)); + titleRect.adjust(0, 0, -sz.width() - mw - buttonMargin, 0); + } + + if (dwOpt->floatable) { + QSize sz = proxy()->standardIcon(QStyle::SP_TitleBarMaxButton, dwOpt, widget).actualSize(QSize(10, 10)); + titleRect.adjust(0, 0, -sz.width() - mw - buttonMargin, 0); + } + + if (isFloating) { + titleRect.adjust(0, -fw, 0, 0); + if (widget && widget->windowIcon().cacheKey() != QApplication::windowIcon().cacheKey()) + titleRect.adjust(titleRect.height() + mw, 0, 0, 0); + } else { + titleRect.adjust(mw, 0, 0, 0); + if (!dwOpt->floatable && !dwOpt->closable) + titleRect.adjust(0, 0, -mw, 0); + } + if (!verticalTitleBar) + titleRect = visualRect(dwOpt->direction, r, titleRect); + + if (!dwOpt->title.isEmpty()) { + QString titleText = painter->fontMetrics().elidedText(dwOpt->title, Qt::ElideRight, + verticalTitleBar ? titleRect.height() : titleRect.width()); + const int indent = 4; + drawItemText(painter, rect.adjusted(indent + 1, 1, -indent - 1, -1), + Qt::AlignLeft | Qt::AlignVCenter | Qt::TextShowMnemonic, + dwOpt->palette, + dwOpt->state & State_Enabled, titleText, + QPalette::WindowText); + } + } + break; +#endif // QT_CONFIG(dockwidget) +#if QT_CONFIG(itemviews) + case CE_ItemViewItem: + { + const QStyleOptionViewItem *vopt; + + const QAbstractItemView *view = qobject_cast<const QAbstractItemView *>(widget); + bool newStyle = true; + + if (qobject_cast<const QTableView*>(widget)) + newStyle = false; + + if (newStyle && view && (vopt = qstyleoption_cast<const QStyleOptionViewItem *>(option))) { + /* + // We cannot currently get the correct selection color for "explorer style" views + COLORREF cref = 0; + XPThemeData theme(d->treeViewHelper(), 0, QLatin1String("LISTVIEW"), 0, 0); + unsigned int res = GetThemeColor(theme.handle(), LVP_LISTITEM, LISS_SELECTED, TMT_TEXTCOLOR, &cref); + QColor textColor(GetRValue(cref), GetGValue(cref), GetBValue(cref)); + */ + QPalette palette = vopt->palette; + palette.setColor(QPalette::All, QPalette::HighlightedText, palette.color(QPalette::Active, QPalette::Text)); + // Note that setting a saturated color here results in ugly XOR colors in the focus rect + palette.setColor(QPalette::All, QPalette::Highlight, palette.base().color().darker(108)); + QStyleOptionViewItem adjustedOption = *vopt; + adjustedOption.palette = palette; + // We hide the focusrect in singleselection as it is not required + if ((view->selectionMode() == QAbstractItemView::SingleSelection) + && !(vopt->state & State_KeyboardFocusChange)) + adjustedOption.state &= ~State_HasFocus; + QWindowsXPStyle::drawControl(element, &adjustedOption, painter, widget); + } else { + QWindowsXPStyle::drawControl(element, option, painter, widget); + } + break; + } +#endif // QT_CONFIG(itemviews) +#if QT_CONFIG(combobox) + case CE_ComboBoxLabel: + QCommonStyle::drawControl(element, option, painter, widget); + break; +#endif // QT_CONFIG(combobox) + default: + QWindowsXPStyle::drawControl(element, option, painter, widget); + break; + } +} + +/*! + \internal + + see drawPrimitive for comments on the animation support + + */ +void QWindowsVistaStyle::drawComplexControl(ComplexControl control, const QStyleOptionComplex *option, + QPainter *painter, const QWidget *widget) const +{ + QWindowsVistaStylePrivate *d = const_cast<QWindowsVistaStylePrivate*>(d_func()); + if (!QWindowsVistaStylePrivate::useVista()) { + QWindowsStyle::drawComplexControl(control, option, painter, widget); + return; + } + + State state = option->state; + SubControls sub = option->subControls; + QRect r = option->rect; + + int partId = 0; + int stateId = 0; + + State flags = option->state; + if (widget && widget->testAttribute(Qt::WA_UnderMouse) && widget->isActiveWindow()) + flags |= State_MouseOver; + + if (d->transitionsEnabled() && canAnimate(option)) + { + + if (control == CC_ScrollBar || control == CC_SpinBox || control == CC_ComboBox) { + + QObject *styleObject = option->styleObject; // Can be widget or qquickitem + + int oldState = styleObject->property("_q_stylestate").toInt(); + int oldActiveControls = styleObject->property("_q_stylecontrols").toInt(); + + QRect oldRect = styleObject->property("_q_stylerect").toRect(); + styleObject->setProperty("_q_stylestate", int(option->state)); + styleObject->setProperty("_q_stylecontrols", int(option->activeSubControls)); + styleObject->setProperty("_q_stylerect", option->rect); + + bool doTransition = ((state & State_Sunken) != (oldState & State_Sunken) || + (state & State_On) != (oldState & State_On) || + (state & State_MouseOver) != (oldState & State_MouseOver) || + oldActiveControls != int(option->activeSubControls)); + + if (qstyleoption_cast<const QStyleOptionSlider *>(option)) { + QRect oldSliderPos = styleObject->property("_q_stylesliderpos").toRect(); + QRect currentPos = proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarSlider, widget); + styleObject->setProperty("_q_stylesliderpos", currentPos); + if (oldSliderPos != currentPos) { + doTransition = false; + d->stopAnimation(styleObject); + } + } else if (control == CC_SpinBox) { + //spinboxes have a transition when focus changes + if (!doTransition) + doTransition = (state & State_HasFocus) != (oldState & State_HasFocus); + } + + if (oldRect != option->rect) { + doTransition = false; + d->stopAnimation(styleObject); + } + + if (doTransition) { + QImage startImage = createAnimationBuffer(option, widget); + QPainter startPainter(&startImage); + + QImage endImage = createAnimationBuffer(option, widget); + QPainter endPainter(&endImage); + + QWindowsVistaAnimation *anim = qobject_cast<QWindowsVistaAnimation *>(d->animation(styleObject)); + QWindowsVistaTransition *t = new QWindowsVistaTransition(styleObject); + + // Draw the image that ends the animation by using the current styleoption + QStyleOptionComplex *styleOption = qstyleoption_cast<QStyleOptionComplex*>(clonedAnimationStyleOption(option)); + + styleObject->setProperty("_q_no_animation", true); + + // Draw transition source + if (!anim) { + styleOption->state = QStyle::State(oldState); + styleOption->activeSubControls = QStyle::SubControl(oldActiveControls); + proxy()->drawComplexControl(control, styleOption, &startPainter, widget); + } else { + anim->paint(&startPainter, option); + } + t->setStartImage(startImage); + + // Draw transition target + styleOption->state = option->state; + styleOption->activeSubControls = option->activeSubControls; + proxy()->drawComplexControl(control, styleOption, &endPainter, widget); + + styleObject->setProperty("_q_no_animation", false); + + t->setEndImage(endImage); + t->setStartTime(QTime::currentTime()); + + if (option->state & State_MouseOver || option->state & State_Sunken) + t->setDuration(150); + else + t->setDuration(500); + + deleteClonedAnimationStyleOption(styleOption); + d->startAnimation(t); + } + if (QWindowsVistaAnimation *anim = qobject_cast<QWindowsVistaAnimation *>(d->animation(styleObject))) { + anim->paint(painter, option); + return; + } + } + } + + switch (control) { + case CC_ComboBox: + if (const QStyleOptionComboBox *cmb = qstyleoption_cast<const QStyleOptionComboBox *>(option)) + { + if (cmb->editable) { + if (sub & SC_ComboBoxEditField) { + partId = EP_EDITBORDER_NOSCROLL; + if (!(flags & State_Enabled)) + stateId = ETS_DISABLED; + else if (flags & State_MouseOver) + stateId = ETS_HOT; + else if (flags & State_HasFocus) + stateId = ETS_FOCUSED; + else + stateId = ETS_NORMAL; + + XPThemeData theme(widget, painter, + QWindowsXPStylePrivate::EditTheme, + partId, stateId, r); + + d->drawBackground(theme); + } + if (sub & SC_ComboBoxArrow) { + QRect subRect = proxy()->subControlRect(CC_ComboBox, option, SC_ComboBoxArrow, widget); + XPThemeData theme(widget, painter, QWindowsXPStylePrivate::ComboboxTheme); + theme.rect = subRect; + partId = option->direction == Qt::RightToLeft ? CP_DROPDOWNBUTTONLEFT : CP_DROPDOWNBUTTONRIGHT; + + if (!(cmb->state & State_Enabled)) + stateId = CBXS_DISABLED; + else if (cmb->state & State_Sunken || cmb->state & State_On) + stateId = CBXS_PRESSED; + else if (cmb->state & State_MouseOver && option->activeSubControls & SC_ComboBoxArrow) + stateId = CBXS_HOT; + else + stateId = CBXS_NORMAL; + + theme.partId = partId; + theme.stateId = stateId; + d->drawBackground(theme); + } + + } else { + if (sub & SC_ComboBoxFrame) { + XPThemeData theme(widget, painter, QWindowsXPStylePrivate::ComboboxTheme); + theme.rect = option->rect; + theme.partId = CP_READONLY; + if (!(cmb->state & State_Enabled)) + theme.stateId = CBXS_DISABLED; + else if (cmb->state & State_Sunken || cmb->state & State_On) + theme.stateId = CBXS_PRESSED; + else if (cmb->state & State_MouseOver) + theme.stateId = CBXS_HOT; + else + theme.stateId = CBXS_NORMAL; + d->drawBackground(theme); + } + if (sub & SC_ComboBoxArrow) { + XPThemeData theme(widget, painter, QWindowsXPStylePrivate::ComboboxTheme); + theme.rect = proxy()->subControlRect(CC_ComboBox, option, SC_ComboBoxArrow, widget); + theme.partId = option->direction == Qt::RightToLeft ? CP_DROPDOWNBUTTONLEFT : CP_DROPDOWNBUTTONRIGHT; + if (!(cmb->state & State_Enabled)) + theme.stateId = CBXS_DISABLED; + else + theme.stateId = CBXS_NORMAL; + d->drawBackground(theme); + } + if ((sub & SC_ComboBoxEditField) && (flags & State_HasFocus)) { + QStyleOptionFocusRect fropt; + fropt.QStyleOption::operator=(*cmb); + fropt.rect = proxy()->subControlRect(CC_ComboBox, option, SC_ComboBoxEditField, widget); + proxy()->drawPrimitive(PE_FrameFocusRect, &fropt, painter, widget); + } + } + } + break; + case CC_ScrollBar: + if (const QStyleOptionSlider *scrollbar = qstyleoption_cast<const QStyleOptionSlider *>(option)) + { + XPThemeData theme(widget, painter, QWindowsXPStylePrivate::ScrollBarTheme); + bool maxedOut = (scrollbar->maximum == scrollbar->minimum); + if (maxedOut) + flags &= ~State_Enabled; + + bool isHorz = flags & State_Horizontal; + bool isRTL = option->direction == Qt::RightToLeft; + if (sub & SC_ScrollBarAddLine) { + theme.rect = proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarAddLine, widget); + partId = SBP_ARROWBTN; + if (!(flags & State_Enabled)) + stateId = (isHorz ? (isRTL ? ABS_LEFTDISABLED : ABS_RIGHTDISABLED) : ABS_DOWNDISABLED); + else if (scrollbar->activeSubControls & SC_ScrollBarAddLine && (scrollbar->state & State_Sunken)) + stateId = (isHorz ? (isRTL ? ABS_LEFTPRESSED : ABS_RIGHTPRESSED) : ABS_DOWNPRESSED); + else if (scrollbar->activeSubControls & SC_ScrollBarAddLine && (scrollbar->state & State_MouseOver)) + stateId = (isHorz ? (isRTL ? ABS_LEFTHOT : ABS_RIGHTHOT) : ABS_DOWNHOT); + else if (scrollbar->state & State_MouseOver) + stateId = (isHorz ? (isRTL ? ABS_LEFTHOVER : ABS_RIGHTHOVER) : ABS_DOWNHOVER); + else + stateId = (isHorz ? (isRTL ? ABS_LEFTNORMAL : ABS_RIGHTNORMAL) : ABS_DOWNNORMAL); + theme.partId = partId; + theme.stateId = stateId; + d->drawBackground(theme); + } + if (sub & SC_ScrollBarSubLine) { + theme.rect = proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarSubLine, widget); + partId = SBP_ARROWBTN; + if (!(flags & State_Enabled)) + stateId = (isHorz ? (isRTL ? ABS_RIGHTDISABLED : ABS_LEFTDISABLED) : ABS_UPDISABLED); + else if (scrollbar->activeSubControls & SC_ScrollBarSubLine && (scrollbar->state & State_Sunken)) + stateId = (isHorz ? (isRTL ? ABS_RIGHTPRESSED : ABS_LEFTPRESSED) : ABS_UPPRESSED); + else if (scrollbar->activeSubControls & SC_ScrollBarSubLine && (scrollbar->state & State_MouseOver)) + stateId = (isHorz ? (isRTL ? ABS_RIGHTHOT : ABS_LEFTHOT) : ABS_UPHOT); + else if (scrollbar->state & State_MouseOver) + stateId = (isHorz ? (isRTL ? ABS_RIGHTHOVER : ABS_LEFTHOVER) : ABS_UPHOVER); + else + stateId = (isHorz ? (isRTL ? ABS_RIGHTNORMAL : ABS_LEFTNORMAL) : ABS_UPNORMAL); + theme.partId = partId; + theme.stateId = stateId; + d->drawBackground(theme); + } + if (maxedOut) { + theme.rect = proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarSlider, widget); + theme.rect = theme.rect.united(proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarSubPage, widget)); + theme.rect = theme.rect.united(proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarAddPage, widget)); + partId = flags & State_Horizontal ? SBP_LOWERTRACKHORZ : SBP_LOWERTRACKVERT; + stateId = SCRBS_DISABLED; + theme.partId = partId; + theme.stateId = stateId; + d->drawBackground(theme); + } else { + if (sub & SC_ScrollBarSubPage) { + theme.rect = proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarSubPage, widget); + partId = flags & State_Horizontal ? SBP_UPPERTRACKHORZ : SBP_UPPERTRACKVERT; + if (!(flags & State_Enabled)) + stateId = SCRBS_DISABLED; + else if (scrollbar->activeSubControls & SC_ScrollBarSubPage && (scrollbar->state & State_Sunken)) + stateId = SCRBS_PRESSED; + else if (scrollbar->activeSubControls & SC_ScrollBarSubPage && (scrollbar->state & State_MouseOver)) + stateId = SCRBS_HOT; + else + stateId = SCRBS_NORMAL; + theme.partId = partId; + theme.stateId = stateId; + d->drawBackground(theme); + } + if (sub & SC_ScrollBarAddPage) { + theme.rect = proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarAddPage, widget); + partId = flags & State_Horizontal ? SBP_LOWERTRACKHORZ : SBP_LOWERTRACKVERT; + if (!(flags & State_Enabled)) + stateId = SCRBS_DISABLED; + else if (scrollbar->activeSubControls & SC_ScrollBarAddPage && (scrollbar->state & State_Sunken)) + stateId = SCRBS_PRESSED; + else if (scrollbar->activeSubControls & SC_ScrollBarAddPage && (scrollbar->state & State_MouseOver)) + stateId = SCRBS_HOT; + else + stateId = SCRBS_NORMAL; + theme.partId = partId; + theme.stateId = stateId; + d->drawBackground(theme); + } + if (sub & SC_ScrollBarSlider) { + theme.rect = proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarSlider, widget); + if (!(flags & State_Enabled)) + stateId = SCRBS_DISABLED; + else if (scrollbar->activeSubControls & SC_ScrollBarSlider && (scrollbar->state & State_Sunken)) + stateId = SCRBS_PRESSED; + else if (scrollbar->activeSubControls & SC_ScrollBarSlider && (scrollbar->state & State_MouseOver)) + stateId = SCRBS_HOT; + else if (option->state & State_MouseOver) + stateId = SCRBS_HOVER; + else + stateId = SCRBS_NORMAL; + + // Draw handle + theme.partId = flags & State_Horizontal ? SBP_THUMBBTNHORZ : SBP_THUMBBTNVERT; + theme.stateId = stateId; + d->drawBackground(theme); + + if (QOperatingSystemVersion::current() < QOperatingSystemVersion::Windows8) { + const QRect gripperBounds = QWindowsXPStylePrivate::scrollBarGripperBounds(flags, widget, &theme); + // Draw gripper if there is enough space + if (!gripperBounds.isEmpty() && flags & State_Enabled) { + painter->save(); + XPThemeData grippBackground = theme; + grippBackground.partId = flags & State_Horizontal ? SBP_LOWERTRACKHORZ : SBP_LOWERTRACKVERT; + theme.rect = gripperBounds; + painter->setClipRegion(d->region(theme));// Only change inside the region of the gripper + d->drawBackground(grippBackground);// The gutter is the grippers background + d->drawBackground(theme); // Transparent gripper ontop of background + painter->restore(); + } + } + } + } + } + break; +#if QT_CONFIG(spinbox) + case CC_SpinBox: + if (const QStyleOptionSpinBox *sb = qstyleoption_cast<const QStyleOptionSpinBox *>(option)) + { + XPThemeData theme(widget, painter, QWindowsXPStylePrivate::SpinTheme); + if (sb->frame && (sub & SC_SpinBoxFrame)) { + partId = EP_EDITBORDER_NOSCROLL; + if (!(flags & State_Enabled)) + stateId = ETS_DISABLED; + else if (flags & State_MouseOver) + stateId = ETS_HOT; + else if (flags & State_HasFocus) + stateId = ETS_SELECTED; + else + stateId = ETS_NORMAL; + + XPThemeData ftheme(widget, painter, + QWindowsXPStylePrivate::EditTheme, + partId, stateId, r); + // The spinbox in Windows QStyle is drawn with frameless QLineEdit inside it + // That however breaks with QtQuickControls where this results in transparent + // spinbox background, so if there's no "widget" passed (QtQuickControls case), + // let ftheme.noContent be false, which fixes the spinbox rendering in QQC + ftheme.noContent = (widget != nullptr); + d->drawBackground(ftheme); + } + if (sub & SC_SpinBoxUp) { + theme.rect = proxy()->subControlRect(CC_SpinBox, option, SC_SpinBoxUp, widget).adjusted(0, 0, 0, 1); + partId = SPNP_UP; + if (!(sb->stepEnabled & QAbstractSpinBox::StepUpEnabled) || !(flags & State_Enabled)) + stateId = UPS_DISABLED; + else if (sb->activeSubControls == SC_SpinBoxUp && (sb->state & State_Sunken)) + stateId = UPS_PRESSED; + else if (sb->activeSubControls == SC_SpinBoxUp && (sb->state & State_MouseOver)) + stateId = UPS_HOT; + else + stateId = UPS_NORMAL; + theme.partId = partId; + theme.stateId = stateId; + d->drawBackground(theme); + } + if (sub & SC_SpinBoxDown) { + theme.rect = proxy()->subControlRect(CC_SpinBox, option, SC_SpinBoxDown, widget); + partId = SPNP_DOWN; + if (!(sb->stepEnabled & QAbstractSpinBox::StepDownEnabled) || !(flags & State_Enabled)) + stateId = DNS_DISABLED; + else if (sb->activeSubControls == SC_SpinBoxDown && (sb->state & State_Sunken)) + stateId = DNS_PRESSED; + else if (sb->activeSubControls == SC_SpinBoxDown && (sb->state & State_MouseOver)) + stateId = DNS_HOT; + else + stateId = DNS_NORMAL; + theme.partId = partId; + theme.stateId = stateId; + d->drawBackground(theme); + } + } + break; +#endif // QT_CONFIG(spinbox) + default: + QWindowsXPStyle::drawComplexControl(control, option, painter, widget); + break; + } +} + +/*! + \internal + */ +QSize QWindowsVistaStyle::sizeFromContents(ContentsType type, const QStyleOption *option, + const QSize &size, const QWidget *widget) const +{ + if (!QWindowsVistaStylePrivate::useVista()) + return QWindowsStyle::sizeFromContents(type, option, size, widget); + + QSize sz(size); + switch (type) { + case CT_MenuItem: + sz = QWindowsXPStyle::sizeFromContents(type, option, size, widget); + int minimumHeight; + { + XPThemeData theme(widget, nullptr, + QWindowsXPStylePrivate::MenuTheme, + MENU_POPUPCHECKBACKGROUND, MBI_HOT); + XPThemeData themeSize = theme; + themeSize.partId = MENU_POPUPCHECK; + themeSize.stateId = 0; + const QSizeF size = themeSize.size() * QWindowsStylePrivate::nativeMetricScaleFactor(widget); + const QMarginsF margins = themeSize.margins() * QWindowsStylePrivate::nativeMetricScaleFactor(widget); + minimumHeight = qMax(qRound(size.height() + margins.bottom() + margins.top()), sz.height()); + sz.rwidth() += qRound(size.width() + margins.left() + margins.right()); + } + + if (const QStyleOptionMenuItem *menuitem = qstyleoption_cast<const QStyleOptionMenuItem *>(option)) { + if (menuitem->menuItemType != QStyleOptionMenuItem::Separator) + sz.setHeight(minimumHeight); + } + return sz; +#if QT_CONFIG(menubar) + case CT_MenuBarItem: + if (!sz.isEmpty()) + sz += QSize(windowsItemHMargin * 5 + 1, 5); + return sz; +#endif + case CT_ItemViewItem: + sz = QWindowsXPStyle::sizeFromContents(type, option, size, widget); + sz.rheight() += 2; + return sz; + case CT_SpinBox: + { + //Spinbox adds frame twice + sz = QWindowsStyle::sizeFromContents(type, option, size, widget); + int border = proxy()->pixelMetric(PM_SpinBoxFrameWidth, option, widget); + sz -= QSize(2*border, 2*border); + } + return sz; + case CT_HeaderSection: + { + // When there is a sort indicator it adds to the width but it is shown + // above the text natively and not on the side + if (QStyleOptionHeader *hdr = qstyleoption_cast<QStyleOptionHeader *>(const_cast<QStyleOption *>(option))) { + QStyleOptionHeader::SortIndicator sortInd = hdr->sortIndicator; + hdr->sortIndicator = QStyleOptionHeader::None; + sz = QWindowsXPStyle::sizeFromContents(type, hdr, size, widget); + hdr->sortIndicator = sortInd; + return sz; + } + break; + } + default: + break; + } + return QWindowsXPStyle::sizeFromContents(type, option, size, widget); +} + +/*! + \internal + */ +QRect QWindowsVistaStyle::subElementRect(SubElement element, const QStyleOption *option, const QWidget *widget) const +{ + if (!QWindowsVistaStylePrivate::useVista()) + return QWindowsStyle::subElementRect(element, option, widget); + + QRect rect = QWindowsXPStyle::subElementRect(element, option, widget); + switch (element) { + + case SE_PushButtonContents: + if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(option)) { + MARGINS borderSize; + const HTHEME theme = OpenThemeData(widget ? QWindowsVistaStylePrivate::winId(widget) : nullptr, L"Button"); + if (theme) { + int stateId = PBS_NORMAL; + if (!(option->state & State_Enabled)) + stateId = PBS_DISABLED; + else if (option->state & State_Sunken) + stateId = PBS_PRESSED; + else if (option->state & State_MouseOver) + stateId = PBS_HOT; + else if (btn->features & QStyleOptionButton::DefaultButton) + stateId = PBS_DEFAULTED; + + int border = proxy()->pixelMetric(PM_DefaultFrameWidth, btn, widget); + rect = option->rect.adjusted(border, border, -border, -border); + + if (SUCCEEDED(GetThemeMargins(theme, nullptr, BP_PUSHBUTTON, stateId, TMT_CONTENTMARGINS, nullptr, &borderSize))) { + rect.adjust(borderSize.cxLeftWidth, borderSize.cyTopHeight, + -borderSize.cxRightWidth, -borderSize.cyBottomHeight); + rect = visualRect(option->direction, option->rect, rect); + } + } + } + break; + + case SE_HeaderArrow: + { + QRect r = rect; + int h = option->rect.height(); + int w = option->rect.width(); + int x = option->rect.x(); + int y = option->rect.y(); + int margin = proxy()->pixelMetric(QStyle::PM_HeaderMargin, option, widget); + + XPThemeData theme(widget, nullptr, + QWindowsXPStylePrivate::HeaderTheme, + HP_HEADERSORTARROW, HSAS_SORTEDDOWN, option->rect); + + int arrowWidth = 13; + int arrowHeight = 5; + if (theme.isValid()) { + const QSizeF size = theme.size() * QWindowsStylePrivate::nativeMetricScaleFactor(widget); + if (!size.isEmpty()) { + arrowWidth = qRound(size.width()); + arrowHeight = qRound(size.height()); + } + } + if (option->state & State_Horizontal) { + r.setRect(x + w/2 - arrowWidth/2, y , arrowWidth, arrowHeight); + } else { + int vert_size = w / 2; + r.setRect(x + 5, y + h - margin * 2 - vert_size, + w - margin * 2 - 5, vert_size); + } + rect = visualRect(option->direction, option->rect, r); + } + break; + + case SE_HeaderLabel: + { + int margin = proxy()->pixelMetric(QStyle::PM_HeaderMargin, option, widget); + QRect r = option->rect; + r.setRect(option->rect.x() + margin, option->rect.y() + margin, + option->rect.width() - margin * 2, option->rect.height() - margin * 2); + if (const QStyleOptionHeader *header = qstyleoption_cast<const QStyleOptionHeader *>(option)) { + // Subtract width needed for arrow, if there is one + if (header->sortIndicator != QStyleOptionHeader::None) { + if (!(option->state & State_Horizontal)) //horizontal arrows are positioned on top + r.setHeight(r.height() - (option->rect.width() / 2) - (margin * 2)); + } + } + rect = visualRect(option->direction, option->rect, r); + } + break; + case SE_ProgressBarContents: + rect = QCommonStyle::subElementRect(SE_ProgressBarGroove, option, widget); + break; + case SE_ItemViewItemDecoration: + if (qstyleoption_cast<const QStyleOptionViewItem *>(option)) + rect.adjust(-2, 0, 2, 0); + break; + case SE_ItemViewItemFocusRect: + if (const QStyleOptionViewItem *vopt = qstyleoption_cast<const QStyleOptionViewItem *>(option)) { + QRect textRect = subElementRect(QStyle::SE_ItemViewItemText, option, widget); + QRect displayRect = subElementRect(QStyle::SE_ItemViewItemDecoration, option, widget); + if (!vopt->icon.isNull()) + rect = textRect.united(displayRect); + else + rect = textRect; + rect = rect.adjusted(1, 0, -1, 0); + } + break; + default: + break; + } + return rect; +} + + +/* + This function is used by subControlRect to check if a button + should be drawn for the given subControl given a set of window flags. +*/ +static bool buttonVisible(const QStyle::SubControl sc, const QStyleOptionTitleBar *tb){ + + bool isMinimized = tb->titleBarState & Qt::WindowMinimized; + bool isMaximized = tb->titleBarState & Qt::WindowMaximized; + const auto flags = tb->titleBarFlags; + bool retVal = false; + switch (sc) { + case QStyle::SC_TitleBarContextHelpButton: + if (flags & Qt::WindowContextHelpButtonHint) + retVal = true; + break; + case QStyle::SC_TitleBarMinButton: + if (!isMinimized && (flags & Qt::WindowMinimizeButtonHint)) + retVal = true; + break; + case QStyle::SC_TitleBarNormalButton: + if (isMinimized && (flags & Qt::WindowMinimizeButtonHint)) + retVal = true; + else if (isMaximized && (flags & Qt::WindowMaximizeButtonHint)) + retVal = true; + break; + case QStyle::SC_TitleBarMaxButton: + if (!isMaximized && (flags & Qt::WindowMaximizeButtonHint)) + retVal = true; + break; + case QStyle::SC_TitleBarShadeButton: + if (!isMinimized && flags & Qt::WindowShadeButtonHint) + retVal = true; + break; + case QStyle::SC_TitleBarUnshadeButton: + if (isMinimized && flags & Qt::WindowShadeButtonHint) + retVal = true; + break; + case QStyle::SC_TitleBarCloseButton: + if (flags & Qt::WindowSystemMenuHint) + retVal = true; + break; + case QStyle::SC_TitleBarSysMenu: + if (flags & Qt::WindowSystemMenuHint) + retVal = true; + break; + default : + retVal = true; + } + return retVal; +} + + +/*! \internal */ +int QWindowsVistaStyle::styleHint(StyleHint hint, const QStyleOption *option, const QWidget *widget, + QStyleHintReturn *returnData) const +{ + QWindowsVistaStylePrivate *d = const_cast<QWindowsVistaStylePrivate*>(d_func()); + int ret = 0; + switch (hint) { + case SH_MessageBox_CenterButtons: + ret = false; + break; + case SH_ToolTip_Mask: + if (option) { + if (QStyleHintReturnMask *mask = qstyleoption_cast<QStyleHintReturnMask*>(returnData)) { + ret = true; + XPThemeData themeData(widget, nullptr, + QWindowsXPStylePrivate::ToolTipTheme, + TTP_STANDARD, TTSS_NORMAL, option->rect); + mask->region = d->region(themeData); + } + } + break; + case SH_Table_GridLineColor: + if (option) + ret = int(option->palette.color(QPalette::Base).darker(118).rgba()); + else + ret = -1; + break; + case SH_Header_ArrowAlignment: + ret = Qt::AlignTop | Qt::AlignHCenter; + break; + default: + ret = QWindowsXPStyle::styleHint(hint, option, widget, returnData); + break; + } + return ret; +} + + +/*! + \internal + */ +QRect QWindowsVistaStyle::subControlRect(ComplexControl control, const QStyleOptionComplex *option, + SubControl subControl, const QWidget *widget) const +{ + if (!QWindowsVistaStylePrivate::useVista()) + return QWindowsStyle::subControlRect(control, option, subControl, widget); + + QRect rect = QWindowsXPStyle::subControlRect(control, option, subControl, widget); + switch (control) { +#if QT_CONFIG(combobox) + case CC_ComboBox: + if (const QStyleOptionComboBox *cb = qstyleoption_cast<const QStyleOptionComboBox *>(option)) { + const int x = cb->rect.x(), y = cb->rect.y(), wi = cb->rect.width(), he = cb->rect.height(); + const int margin = cb->frame ? 3 : 0; + const int bmarg = cb->frame ? 2 : 0; + const int arrowWidth = qRound(QStyleHelper::dpiScaled(16, option)); + const int arrowButtonWidth = bmarg + arrowWidth; + const int xpos = x + wi - arrowButtonWidth; + + switch (subControl) { + case SC_ComboBoxFrame: + rect = cb->rect; + break; + case SC_ComboBoxArrow: + rect.setRect(xpos, y , arrowButtonWidth, he); + break; + case SC_ComboBoxEditField: + rect.setRect(x + margin, y + margin, wi - 2 * margin - arrowWidth, he - 2 * margin); + break; + case SC_ComboBoxListBoxPopup: + rect = cb->rect; + break; + default: + break; + } + rect = visualRect(cb->direction, cb->rect, rect); + return rect; + } + break; +#endif // QT_CONFIG(combobox) + case CC_TitleBar: + if (const QStyleOptionTitleBar *tb = qstyleoption_cast<const QStyleOptionTitleBar *>(option)) { + if (!buttonVisible(subControl, tb)) + return rect; + const qreal factor = QWindowsStylePrivate::nativeMetricScaleFactor(widget); + const bool isToolTitle = false; + const int height = tb->rect.height(); + const int width = tb->rect.width(); + const int buttonWidth = + qRound(qreal(GetSystemMetrics(SM_CXSIZE)) * factor - QStyleHelper::dpiScaled(4, option)); + + const int frameWidth = proxy()->pixelMetric(PM_MdiSubWindowFrameWidth, option, widget); + const bool sysmenuHint = (tb->titleBarFlags & Qt::WindowSystemMenuHint) != 0; + const bool minimizeHint = (tb->titleBarFlags & Qt::WindowMinimizeButtonHint) != 0; + const bool maximizeHint = (tb->titleBarFlags & Qt::WindowMaximizeButtonHint) != 0; + const bool contextHint = (tb->titleBarFlags & Qt::WindowContextHelpButtonHint) != 0; + const bool shadeHint = (tb->titleBarFlags & Qt::WindowShadeButtonHint) != 0; + + switch (subControl) { + case SC_TitleBarLabel: + rect = QRect(frameWidth, 0, width - (buttonWidth + frameWidth + 10), height); + if (isToolTitle) { + if (sysmenuHint) { + rect.adjust(0, 0, int(-buttonWidth - 3 * factor), 0); + } + if (minimizeHint || maximizeHint) + rect.adjust(0, 0, int(-buttonWidth - 2 * factor), 0); + } else { + if (sysmenuHint) { + const int leftOffset = int(height - 8 * factor); + rect.adjust(leftOffset, 0, 0, int(4 * factor)); + } + if (minimizeHint) + rect.adjust(0, 0, int(-buttonWidth - 2 * factor), 0); + if (maximizeHint) + rect.adjust(0, 0, int(-buttonWidth - 2 * factor), 0); + if (contextHint) + rect.adjust(0, 0, int(-buttonWidth - 2 * factor), 0); + if (shadeHint) + rect.adjust(0, 0, int(-buttonWidth - 2 * factor), 0); + } + rect.translate(0, int(2 * factor)); + rect = visualRect(option->direction, option->rect, rect); + break; + case SC_TitleBarSysMenu: + { + const int controlTop = int(6 * factor); + const int controlHeight = int(height - controlTop - 3 * factor); + int iconExtent = proxy()->pixelMetric(PM_SmallIconSize, option); + QSize iconSize = tb->icon.actualSize(QSize(iconExtent, iconExtent)); + if (tb->icon.isNull()) + iconSize = QSize(controlHeight, controlHeight); + int hPad = (controlHeight - iconSize.height())/2; + int vPad = (controlHeight - iconSize.width())/2; + rect = QRect(frameWidth + hPad, controlTop + vPad, iconSize.width(), iconSize.height()); + rect.translate(0, int(3 * factor)); + rect = visualRect(option->direction, option->rect, rect); + } + break; + default: + break; + } + } + break; + default: + break; + } + return rect; +} + +/*! + \internal + */ +QStyle::SubControl QWindowsVistaStyle::hitTestComplexControl(ComplexControl control, const QStyleOptionComplex *option, + const QPoint &pos, const QWidget *widget) const +{ + if (!QWindowsVistaStylePrivate::useVista()) { + return QWindowsStyle::hitTestComplexControl(control, option, pos, widget); + } + return QWindowsXPStyle::hitTestComplexControl(control, option, pos, widget); +} + +int QWindowsVistaStylePrivate::fixedPixelMetric(QStyle::PixelMetric pm) +{ + switch (pm) { + case QStyle::PM_DockWidgetTitleBarButtonMargin: + return 5; + case QStyle::PM_ScrollBarSliderMin: + return 18; + case QStyle::PM_MenuHMargin: + case QStyle::PM_MenuVMargin: + return 0; + case QStyle::PM_MenuPanelWidth: + return 3; + default: + break; + } + return QWindowsVistaStylePrivate::InvalidMetric; +} + +/*! + \internal + */ +int QWindowsVistaStyle::pixelMetric(PixelMetric metric, const QStyleOption *option, const QWidget *widget) const +{ + if (!QWindowsVistaStylePrivate::useVista()) + return QWindowsStyle::pixelMetric(metric, option, widget); + + int ret = QWindowsVistaStylePrivate::fixedPixelMetric(metric); + if (ret != QWindowsStylePrivate::InvalidMetric) + return int(QStyleHelper::dpiScaled(ret, option)); + + return QWindowsXPStyle::pixelMetric(metric, option, widget); +} + +/*! + \internal + */ +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); +#if QT_CONFIG(lineedit) + if (qobject_cast<QLineEdit*>(widget)) + widget->setAttribute(Qt::WA_Hover); + else +#endif // QT_CONFIG(lineedit) + if (qobject_cast<QGroupBox*>(widget)) + widget->setAttribute(Qt::WA_Hover); +#if QT_CONFIG(commandlinkbutton) + else if (qobject_cast<QCommandLinkButton*>(widget)) { + QFont buttonFont = widget->font(); + buttonFont.setFamily(QLatin1String("Segoe UI")); + widget->setFont(buttonFont); + } +#endif // QT_CONFIG(commandlinkbutton) + else if (widget->inherits("QTipLabel")){ + //note that since tooltips are not reused + //we do not have to care about unpolishing + widget->setContentsMargins(3, 0, 4, 0); + COLORREF bgRef; + HTHEME theme = OpenThemeData(widget ? QWindowsVistaStylePrivate::winId(widget) : nullptr, L"TOOLTIP"); + if (theme && SUCCEEDED(GetThemeColor(theme, TTP_STANDARD, TTSS_NORMAL, TMT_TEXTCOLOR, &bgRef))) { + QColor textColor = QColor::fromRgb(bgRef); + QPalette pal; + pal.setColor(QPalette::All, QPalette::ToolTipText, textColor); + widget->setPalette(pal); + } + } else if (qobject_cast<QMessageBox *> (widget)) { + widget->setAttribute(Qt::WA_StyledBackground); +#if QT_CONFIG(dialogbuttonbox) + QDialogButtonBox *buttonBox = widget->findChild<QDialogButtonBox *>(QLatin1String("qt_msgbox_buttonbox")); + if (buttonBox) + buttonBox->setContentsMargins(0, 9, 0, 0); +#endif + } +#if QT_CONFIG(inputdialog) + else if (qobject_cast<QInputDialog *> (widget)) { + widget->setAttribute(Qt::WA_StyledBackground); +#if QT_CONFIG(dialogbuttonbox) + QDialogButtonBox *buttonBox = widget->findChild<QDialogButtonBox *>(QLatin1String("qt_inputdlg_buttonbox")); + if (buttonBox) + buttonBox->setContentsMargins(0, 9, 0, 0); +#endif + } +#endif // QT_CONFIG(inputdialog) + else if (QTreeView *tree = qobject_cast<QTreeView *> (widget)) { + tree->viewport()->setAttribute(Qt::WA_Hover); + } + else if (QListView *list = qobject_cast<QListView *> (widget)) { + list->viewport()->setAttribute(Qt::WA_Hover); + } +} + +/*! + \internal + */ +void QWindowsVistaStyle::unpolish(QWidget *widget) +{ + QWindowsXPStyle::unpolish(widget); + + QWindowsVistaStylePrivate *d = d_func(); + + d->stopAnimation(widget); + +#if QT_CONFIG(lineedit) + if (qobject_cast<QLineEdit*>(widget)) + widget->setAttribute(Qt::WA_Hover, false); + else +#endif // QT_CONFIG(lineedit) + if (qobject_cast<QGroupBox*>(widget)) + widget->setAttribute(Qt::WA_Hover, false); + else if (qobject_cast<QMessageBox *> (widget)) { + widget->setAttribute(Qt::WA_StyledBackground, false); +#if QT_CONFIG(dialogbuttonbox) + QDialogButtonBox *buttonBox = widget->findChild<QDialogButtonBox *>(QLatin1String("qt_msgbox_buttonbox")); + if (buttonBox) + buttonBox->setContentsMargins(0, 0, 0, 0); +#endif + } +#if QT_CONFIG(inputdialog) + else if (qobject_cast<QInputDialog *> (widget)) { + widget->setAttribute(Qt::WA_StyledBackground, false); +#if QT_CONFIG(dialogbuttonbox) + QDialogButtonBox *buttonBox = widget->findChild<QDialogButtonBox *>(QLatin1String("qt_inputdlg_buttonbox")); + if (buttonBox) + buttonBox->setContentsMargins(0, 0, 0, 0); +#endif + } +#endif // QT_CONFIG(inputdialog) + else if (QTreeView *tree = qobject_cast<QTreeView *> (widget)) { + tree->viewport()->setAttribute(Qt::WA_Hover, false); + } +#if QT_CONFIG(commandlinkbutton) + else if (qobject_cast<QCommandLinkButton*>(widget)) { + QFont font = QApplication::font("QCommandLinkButton"); + QFont widgetFont = widget->font(); + widgetFont.setFamily(font.family()); //Only family set by polish + widget->setFont(widgetFont); + } +#endif // QT_CONFIG(commandlinkbutton) +} + + +/*! + \internal + */ +void QWindowsVistaStyle::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(nullptr, nullptr, + 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/imports/nativestyle/qstyle/windows/qquickwindowsvistastyle_p.h b/src/imports/nativestyle/qstyle/windows/qquickwindowsvistastyle_p.h new file mode 100644 index 00000000..07cd2b8a --- /dev/null +++ b/src/imports/nativestyle/qstyle/windows/qquickwindowsvistastyle_p.h @@ -0,0 +1,109 @@ +/**************************************************************************** +** +** 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 "qquickwindowsxpstyle_p.h" + +QT_BEGIN_NAMESPACE + +class QWindowsVistaStylePrivate; +class QWindowsVistaStyle : public QWindowsXPStyle +{ + Q_OBJECT +public: + QWindowsVistaStyle(); + ~QWindowsVistaStyle() override; + + void drawPrimitive(PrimitiveElement element, const QStyleOption *option, + QPainter *painter, + const QWidget *widget = nullptr) const override; + void drawControl(ControlElement element, const QStyleOption *option, + QPainter *painter, const QWidget *widget) const override; + void drawComplexControl(ComplexControl control, const QStyleOptionComplex *option, + QPainter *painter, const QWidget *widget) const override; + QSize sizeFromContents(ContentsType type, const QStyleOption *option, + const QSize &size, const QWidget *widget) const override; + + QRect subElementRect(SubElement element, const QStyleOption *option, + const QWidget *widget) const override; + QRect subControlRect(ComplexControl cc, const QStyleOptionComplex *opt, + SubControl sc, const QWidget *widget) const override; + + SubControl hitTestComplexControl(ComplexControl control, const QStyleOptionComplex *option, + const QPoint &pos, const QWidget *widget = nullptr) const override; + + QIcon standardIcon(StandardPixmap standardIcon, const QStyleOption *option = nullptr, + const QWidget *widget = nullptr) const override; + QPixmap standardPixmap(StandardPixmap standardPixmap, const QStyleOption *opt, + const QWidget *widget = nullptr) const override; + int pixelMetric(PixelMetric metric, const QStyleOption *option = nullptr, + const QWidget *widget = nullptr) const override; + int styleHint(StyleHint hint, const QStyleOption *opt = nullptr, + const QWidget *widget = nullptr, QStyleHintReturn *returnData = nullptr) const override; + + void polish(QWidget *widget) override; + void unpolish(QWidget *widget) override; + void polish(QPalette &pal) override; + void polish(QApplication *app) override; + void unpolish(QApplication *app) override; + QPalette standardPalette() const override; + +private: + Q_DISABLE_COPY_MOVE(QWindowsVistaStyle) + Q_DECLARE_PRIVATE(QWindowsVistaStyle) + friend class QStyleFactory; +}; + +QT_END_NAMESPACE + +#endif // QWINDOWSVISTASTYLE_P_H diff --git a/src/imports/nativestyle/qstyle/windows/qquickwindowsvistastyle_p_p.h b/src/imports/nativestyle/qstyle/windows/qquickwindowsvistastyle_p_p.h new file mode 100644 index 00000000..a5383175 --- /dev/null +++ b/src/imports/nativestyle/qstyle/windows/qquickwindowsvistastyle_p_p.h @@ -0,0 +1,204 @@ +/**************************************************************************** +** +** 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 "qquickwindowsvistastyle_p.h" +#include "qquickwindowsxpstyle_p_p.h" +#include <private/qstyleanimation_p.h> +#include <private/qpaintengine_raster_p.h> +#include <qpaintengine.h> +#include <qwidget.h> +#include <qapplication.h> +#include <qpixmapcache.h> +#include <qstyleoption.h> +#if QT_CONFIG(pushbutton) +#include <qpushbutton.h> +#endif +#include <qradiobutton.h> +#if QT_CONFIG(lineedit) +#include <qlineedit.h> +#endif +#include <qgroupbox.h> +#if QT_CONFIG(toolbutton) +#include <qtoolbutton.h> +#endif +#if QT_CONFIG(spinbox) +#include <qspinbox.h> +#endif +#if QT_CONFIG(toolbar) +#include <qtoolbar.h> +#endif +#if QT_CONFIG(combobox) +#include <qcombobox.h> +#endif +#if QT_CONFIG(scrollbar) +#include <qscrollbar.h> +#endif +#if QT_CONFIG(progressbar) +#include <qprogressbar.h> +#endif +#if QT_CONFIG(dockwidget) +#include <qdockwidget.h> +#endif +#if QT_CONFIG(listview) +#include <qlistview.h> +#endif +#if QT_CONFIG(treeview) +#include <qtreeview.h> +#endif +#include <qtextedit.h> +#include <qmessagebox.h> +#if QT_CONFIG(dialogbuttonbox) +#include <qdialogbuttonbox.h> +#endif +#include <qinputdialog.h> +#if QT_CONFIG(tableview) +#include <qtableview.h> +#endif +#include <qdatetime.h> +#if QT_CONFIG(commandlinkbutton) +#include <qcommandlinkbutton.h> +#endif + +QT_BEGIN_NAMESPACE + +#if !defined(SCHEMA_VERIFY_VSSYM32) +#define TMT_ANIMATIONDURATION 5006 +#define TMT_TRANSITIONDURATIONS 6000 +#define EP_EDITBORDER_NOSCROLL 6 +#define EP_EDITBORDER_HVSCROLL 9 +#define EP_BACKGROUND 3 +#define EBS_NORMAL 1 +#define EBS_HOT 2 +#define EBS_DISABLED 3 +#define EBS_READONLY 5 +#define PBS_DEFAULTED_ANIMATING 6 +#define MBI_NORMAL 1 +#define MBI_HOT 2 +#define MBI_PUSHED 3 +#define MBI_DISABLED 4 +#define MB_ACTIVE 1 +#define MB_INACTIVE 2 +#define PP_FILL 5 +#define PP_FILLVERT 6 +#define PP_MOVEOVERLAY 8 +#define PP_MOVEOVERLAYVERT 10 +#define MENU_BARBACKGROUND 7 +#define MENU_BARITEM 8 +#define MENU_POPUPCHECK 11 +#define MENU_POPUPCHECKBACKGROUND 12 +#define MENU_POPUPGUTTER 13 +#define MENU_POPUPITEM 14 +#define MENU_POPUPBORDERS 10 +#define MENU_POPUPSEPARATOR 15 +#define MC_CHECKMARKNORMAL 1 +#define MC_CHECKMARKDISABLED 2 +#define MC_BULLETNORMAL 3 +#define MC_BULLETDISABLED 4 +#define ABS_UPHOVER 17 +#define ABS_DOWNHOVER 18 +#define ABS_LEFTHOVER 19 +#define ABS_RIGHTHOVER 20 +#define CP_DROPDOWNBUTTONRIGHT 6 +#define CP_DROPDOWNBUTTONLEFT 7 +#define SCRBS_HOVER 5 +#define TVP_HOTGLYPH 4 +#define SPI_GETCLIENTAREAANIMATION 0x1042 +#define TDLG_PRIMARYPANEL 1 +#define TDLG_SECONDARYPANEL 8 +#endif + +class QWindowsVistaAnimation : public QBlendStyleAnimation +{ + Q_OBJECT +public: + QWindowsVistaAnimation(Type type, QObject *target) : QBlendStyleAnimation(type, target) { } + + bool isUpdateNeeded() const override; + void paint(QPainter *painter, const QStyleOption *option); +}; + + +// Handles state transition animations +class QWindowsVistaTransition : public QWindowsVistaAnimation +{ + Q_OBJECT +public: + QWindowsVistaTransition(QObject *target) : QWindowsVistaAnimation(Transition, target) {} +}; + + +// Handles pulse animations (default buttons) +class QWindowsVistaPulse: public QWindowsVistaAnimation +{ + Q_OBJECT +public: + QWindowsVistaPulse(QObject *target) : QWindowsVistaAnimation(Pulse, target) {} +}; + + +class QWindowsVistaStylePrivate : public QWindowsXPStylePrivate +{ + Q_DECLARE_PUBLIC(QWindowsVistaStyle) + +public: + 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/imports/nativestyle/qstyle/windows/qquickwindowsxpstyle.cpp b/src/imports/nativestyle/qstyle/windows/qquickwindowsxpstyle.cpp new file mode 100644 index 00000000..6c63e3d6 --- /dev/null +++ b/src/imports/nativestyle/qstyle/windows/qquickwindowsxpstyle.cpp @@ -0,0 +1,4163 @@ +/**************************************************************************** +** +** 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 "qquickstylehelper_p.h" +#include "qquickwindowsxpstyle_p.h" +#include "qquickwindowsxpstyle_p_p.h" + +#include <private/qobject_p.h> +#include <private/qpaintengine_raster_p.h> +#include <qpa/qplatformnativeinterface.h> +#include <qpainter.h> +#include <qpaintengine.h> +#include <qbackingstore.h> +#include <qpixmapcache.h> +#include <qpa/qplatformnativeinterface.h> + +#include <qvarlengtharray.h> +#include <qdebug.h> + +#include <algorithm> + +QT_BEGIN_NAMESPACE + +// General const values +static const int windowsItemFrame = 2; // menu item frame width +static const int windowsItemHMargin = 3; // menu item hor text margin +static const int windowsItemVMargin = 0; // menu item ver text margin +static const int windowsArrowHMargin = 6; // arrow horizontal margin +static const int windowsRightBorder = 12; // right border on windows + +// Theme names matching the QWindowsXPStylePrivate::Theme enumeration. +static const wchar_t *themeNames[QWindowsXPStylePrivate::NThemes] = +{ + L"BUTTON", L"COMBOBOX", L"EDIT", L"HEADER", L"LISTVIEW", + L"MENU", L"PROGRESS", L"REBAR", L"SCROLLBAR", L"SPIN", + L"TAB", L"TASKDIALOG", L"TOOLBAR", L"TOOLTIP", L"TRACKBAR", + L"TREEVIEW", L"WINDOW", L"STATUS", L"TREEVIEW" +}; + +static inline QBackingStore *backingStoreForWidget(const QWidget *widget) +{ + if (QBackingStore *backingStore = widget->backingStore()) + return backingStore; + if (const QWidget *topLevel = widget->nativeParentWidget()) + if (QBackingStore *topLevelBackingStore = topLevel->backingStore()) + return topLevelBackingStore; + return nullptr; +} + +static inline HDC hdcForWidgetBackingStore(const QWidget *widget) +{ + if (QBackingStore *backingStore = backingStoreForWidget(widget)) { + QPlatformNativeInterface *nativeInterface = QGuiApplication::platformNativeInterface(); + if (nativeInterface) + return static_cast<HDC>(nativeInterface->nativeResourceForBackingStore(QByteArrayLiteral("getDC"), backingStore)); + } + return nullptr; +} + +// Theme data helper ------------------------------------------------------------------------------ +/* \internal + Returns \c true if the themedata is valid for use. +*/ +bool XPThemeData::isValid() +{ + return QWindowsXPStylePrivate::useXP() && theme >= 0 && handle(); +} + + +/* \internal + Returns the theme engine handle to the specific class. + If the handle hasn't been opened before, it opens the data, and + adds it to a static map, for caching. +*/ +HTHEME XPThemeData::handle() +{ + if (!QWindowsXPStylePrivate::useXP()) + return nullptr; + + if (!htheme) + htheme = QWindowsXPStylePrivate::createTheme(theme, QWindowsXPStylePrivate::winId(widget)); + return htheme; +} + +/* \internal + Converts a QRect to the native RECT structure. +*/ +RECT XPThemeData::toRECT(const QRect &qr) +{ + RECT r; + r.left = qr.x(); + r.right = qr.x() + qr.width(); + r.top = qr.y(); + r.bottom = qr.y() + qr.height(); + return r; +} + +/* \internal + Returns the native region of a part, if the part is considered + transparent. The region is scaled to the parts size (rect). +*/ +HRGN XPThemeData::mask(QWidget *widget) +{ + if (!IsThemeBackgroundPartiallyTransparent(handle(), partId, stateId)) + return nullptr; + + HRGN hrgn; + HDC dc = nullptr; + if (widget) + dc = hdcForWidgetBackingStore(widget); + RECT nativeRect = toRECT(rect); + GetThemeBackgroundRegion(handle(), dc, partId, stateId, &nativeRect, &hrgn); + return hrgn; +} + +// QWindowsXPStylePrivate ------------------------------------------------------------------------- +// Static initializations +HWND QWindowsXPStylePrivate::m_vistaTreeViewHelper = nullptr; +HTHEME QWindowsXPStylePrivate::m_themes[NThemes]; +bool QWindowsXPStylePrivate::use_xp = false; +QBasicAtomicInt QWindowsXPStylePrivate::ref = Q_BASIC_ATOMIC_INITIALIZER(-1); // -1 based refcounting + +static void qt_add_rect(HRGN &winRegion, QRect r) +{ + HRGN rgn = CreateRectRgn(r.left(), r.top(), r.x() + r.width(), r.y() + r.height()); + if (rgn) { + HRGN dest = CreateRectRgn(0,0,0,0); + int result = CombineRgn(dest, winRegion, rgn, RGN_OR); + if (result) { + DeleteObject(winRegion); + winRegion = dest; + } + DeleteObject(rgn); + } +} + +static HRGN qt_hrgn_from_qregion(const QRegion ®ion) +{ + HRGN hRegion = CreateRectRgn(0,0,0,0); + if (region.rectCount() == 1) { + qt_add_rect(hRegion, region.boundingRect()); + return hRegion; + } + for (const QRect &rect : region) + qt_add_rect(hRegion, rect); + return hRegion; +} + +/* \internal + Checks if the theme engine can/should be used, or if we should + fall back to Windows style. +*/ +bool QWindowsXPStylePrivate::useXP(bool update) +{ + if (update) { + use_xp = IsThemeActive() && (IsAppThemed() || !QCoreApplication::instance()) + && !QWindowsStylePrivate::isDarkMode(); + } + return use_xp; +} + +/* \internal + Handles refcounting, and queries the theme engine for usage. +*/ +void QWindowsXPStylePrivate::init(bool force) +{ + if (ref.ref() && !force) + return; + if (!force) // -1 based atomic refcounting + ref.ref(); + + useXP(true); + std::fill(m_themes, m_themes + NThemes, nullptr); +} + +/* \internal + Cleans up all static data. +*/ +void QWindowsXPStylePrivate::cleanup(bool force) +{ + if (bufferBitmap) { + if (bufferDC && nullBitmap) + SelectObject(bufferDC, nullBitmap); + DeleteObject(bufferBitmap); + bufferBitmap = nullptr; + } + + if (bufferDC) + DeleteDC(bufferDC); + bufferDC = nullptr; + + if (ref.deref() && !force) + return; + if (!force) // -1 based atomic refcounting + ref.deref(); + + use_xp = false; + cleanupHandleMap(); +} + +/* In order to obtain the correct VistaTreeViewTheme (arrows for PE_IndicatorBranch), + * we need to set the windows "explorer" theme explicitly on a native + * window and open the "TREEVIEW" theme handle passing its window handle + * in order to get Vista-style item view themes (particulary drawBackground() + * for selected items needs this). + * We invoke a service of the native Windows interface to create + * a non-visible window handle, open the theme on it and insert it into + * the cache so that it is found by XPThemeData::handle() first. + */ + +static inline HWND createTreeViewHelperWindow() +{ + if (QPlatformNativeInterface *ni = QGuiApplication::platformNativeInterface()) { + void *hwnd = nullptr; + 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 nullptr; +} + +bool QWindowsXPStylePrivate::initVistaTreeViewTheming() +{ + if (m_vistaTreeViewHelper) + return true; + + m_vistaTreeViewHelper = createTreeViewHelperWindow(); + if (!m_vistaTreeViewHelper) { + qWarning("Unable to create the treeview helper window."); + return false; + } + if (FAILED(SetWindowTheme(m_vistaTreeViewHelper, L"explorer", nullptr))) { + qErrnoWarning("SetWindowTheme() failed."); + cleanupVistaTreeViewTheming(); + return false; + } + return true; +} + +void QWindowsXPStylePrivate::cleanupVistaTreeViewTheming() +{ + if (m_vistaTreeViewHelper) { + DestroyWindow(m_vistaTreeViewHelper); + m_vistaTreeViewHelper = nullptr; + } +} + +/* \internal + Closes all open theme data handles to ensure that we don't leak + resources, and that we don't refere to old handles when for + example the user changes the theme style. +*/ +void QWindowsXPStylePrivate::cleanupHandleMap() +{ + for (auto &theme : m_themes) { + if (theme) { + CloseThemeData(theme); + theme = nullptr; + } + } + QWindowsXPStylePrivate::cleanupVistaTreeViewTheming(); +} + +HTHEME QWindowsXPStylePrivate::createTheme(int theme, HWND hwnd) +{ + if (Q_UNLIKELY(theme < 0 || theme >= NThemes || !hwnd)) { + qWarning("Invalid parameters #%d, %p", theme, hwnd); + return nullptr; + } + if (!m_themes[theme]) { + const wchar_t *name = themeNames[theme]; + if (theme == VistaTreeViewTheme && QWindowsXPStylePrivate::initVistaTreeViewTheming()) + hwnd = QWindowsXPStylePrivate::m_vistaTreeViewHelper; + m_themes[theme] = OpenThemeData(hwnd, name); + if (Q_UNLIKELY(!m_themes[theme])) + qErrnoWarning("OpenThemeData() failed for theme %d (%s).", + theme, qPrintable(themeName(theme))); + } + return m_themes[theme]; +} + +QString QWindowsXPStylePrivate::themeName(int theme) +{ + return theme >= 0 && theme < NThemes ? + QString::fromWCharArray(themeNames[theme]) : + QString(); +} + +bool QWindowsXPStylePrivate::isItemViewDelegateLineEdit(const QWidget *widget) +{ + if (!widget) + return false; + const QWidget *parent1 = widget->parentWidget(); + // Exlude dialogs or other toplevels parented on item views. + if (!parent1 || parent1->isWindow()) + return false; + const QWidget *parent2 = parent1->parentWidget(); + return parent2 && widget->inherits("QLineEdit") + && parent2->inherits("QAbstractItemView"); +} + +// Returns whether base color is set for this widget +bool QWindowsXPStylePrivate::isLineEditBaseColorSet(const QStyleOption *option, const QWidget *widget) +{ + uint resolveMask = option->palette.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. +#if QT_CONFIG(spinbox) + if (const QAbstractSpinBox *spinbox = qobject_cast<QAbstractSpinBox*>(widget->parentWidget())) + resolveMask |= spinbox->palette().resolve(); +#endif // QT_CONFIG(spinbox) + } + return (resolveMask & (1 << QPalette::Base)) != 0; +} + +/*! \internal + This function will always return a valid window handle, and might + create a limbo widget to do so. + We often need a window handle to for example open theme data, so + this function ensures that we get one. +*/ +HWND QWindowsXPStylePrivate::winId(const QWidget *widget) +{ + if (widget) + if (const HWND hwnd = QApplicationPrivate::getHWNDForWidget(const_cast<QWidget *>(widget))) + return hwnd; + + // Find top level with native window (there might be dialogs that do not have one). + const auto allWindows = QGuiApplication::allWindows(); + for (const QWindow *window : allWindows) { + if (window->isTopLevel() && window->type() != Qt::Desktop && window->handle() != nullptr) + return reinterpret_cast<HWND>(window->winId()); + } + + return GetDesktopWindow(); +} + +/*! \internal + Returns a native buffer (DIB section) of at least the size of + ( \a x , \a y ). The buffer has a 32 bit depth, to not lose + the alpha values on proper alpha-pixmaps. +*/ +HBITMAP QWindowsXPStylePrivate::buffer(int w, int h) +{ + // If we already have a HBITMAP which is of adequate size, just return that + if (bufferBitmap) { + if (bufferW >= w && bufferH >= h) + return bufferBitmap; + // Not big enough, discard the old one + if (bufferDC && nullBitmap) + SelectObject(bufferDC, nullBitmap); + DeleteObject(bufferBitmap); + bufferBitmap = nullptr; + } + + w = qMax(bufferW, w); + h = qMax(bufferH, h); + + if (!bufferDC) { + HDC displayDC = GetDC(nullptr); + bufferDC = CreateCompatibleDC(displayDC); + ReleaseDC(nullptr, displayDC); + } + + // Define the header + BITMAPINFO bmi; + memset(&bmi, 0, sizeof(bmi)); + bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + bmi.bmiHeader.biWidth = w; + bmi.bmiHeader.biHeight = -h; + bmi.bmiHeader.biPlanes = 1; + bmi.bmiHeader.biBitCount = 32; + bmi.bmiHeader.biCompression = BI_RGB; + + // Create the pixmap + bufferPixels = nullptr; + bufferBitmap = CreateDIBSection(bufferDC, &bmi, DIB_RGB_COLORS, reinterpret_cast<void **>(&bufferPixels), nullptr, 0); + GdiFlush(); + nullBitmap = static_cast<HBITMAP>(SelectObject(bufferDC, bufferBitmap)); + + if (Q_UNLIKELY(!bufferBitmap)) { + qErrnoWarning("QWindowsXPStylePrivate::buffer(%dx%d), CreateDIBSection() failed.", w, h); + bufferW = 0; + bufferH = 0; + return nullptr; + } + if (Q_UNLIKELY(!bufferPixels)) { + qErrnoWarning("QWindowsXPStylePrivate::buffer(%dx%d), CreateDIBSection() did not allocate pixel data.", w, h); + bufferW = 0; + bufferH = 0; + return nullptr; + } + bufferW = w; + bufferH = h; +#ifdef DEBUG_XP_STYLE + qDebug("Creating new dib section (%d, %d)", w, h); +#endif + return bufferBitmap; +} + +/*! \internal + Returns \c true if the part contains any transparency at all. This does + not indicate what kind of transparency we're dealing with. It can be + - Alpha transparency + - Masked transparency +*/ +bool QWindowsXPStylePrivate::isTransparent(XPThemeData &themeData) +{ + return IsThemeBackgroundPartiallyTransparent(themeData.handle(), themeData.partId, + themeData.stateId); +} + + +/*! \internal + Returns a QRegion of the region of the part +*/ +QRegion QWindowsXPStylePrivate::region(XPThemeData &themeData) +{ + HRGN hRgn = nullptr; + const qreal factor = QWindowsStylePrivate::nativeMetricScaleFactor(themeData.widget); + RECT rect = themeData.toRECT(QRect(themeData.rect.topLeft() / factor, themeData.rect.size() / factor)); + if (!SUCCEEDED(GetThemeBackgroundRegion(themeData.handle(), bufferHDC(), themeData.partId, + themeData.stateId, &rect, &hRgn))) { + return QRegion(); + } + + HRGN dest = CreateRectRgn(0, 0, 0, 0); + const bool success = CombineRgn(dest, hRgn, nullptr, RGN_COPY) != ERROR; + + QRegion region; + + if (success) { + const auto numBytes = GetRegionData(dest, 0, nullptr); + if (numBytes == 0) + return QRegion(); + + char *buf = new (std::nothrow) char[numBytes]; + if (!buf) + return QRegion(); + + RGNDATA *rd = reinterpret_cast<RGNDATA*>(buf); + if (GetRegionData(dest, numBytes, rd) == 0) { + delete [] buf; + return QRegion(); + } + + RECT *r = reinterpret_cast<RECT*>(rd->Buffer); + for (uint i = 0; i < rd->rdh.nCount; ++i) { + QRect rect; + rect.setCoords(int(r->left * factor), int(r->top * factor), int((r->right - 1) * factor), int((r->bottom - 1) * factor)); + ++r; + region |= rect; + } + + delete [] buf; + } + + DeleteObject(hRgn); + DeleteObject(dest); + + return region; +} + +/*! \internal + Returns \c true if the native doublebuffer contains pixels with + varying alpha value. +*/ +bool QWindowsXPStylePrivate::hasAlphaChannel(const QRect &rect) +{ + const int startX = rect.left(); + const int startY = rect.top(); + const int w = rect.width(); + const int h = rect.height(); + + int firstAlpha = -1; + for (int y = startY; y < h/2; ++y) { + auto buffer = reinterpret_cast<const DWORD *>(bufferPixels) + (y * bufferW); + for (int x = startX; x < w; ++x, ++buffer) { + int alpha = (*buffer) >> 24; + if (firstAlpha == -1) + firstAlpha = alpha; + else if (alpha != firstAlpha) + return true; + } + } + return false; +} + +/*! \internal + When the theme engine paints both a true alpha pixmap and a glyph + into our buffer, the glyph might not contain a proper alpha value. + The rule of thumb for premultiplied pixmaps is that the color + values of a pixel can never be higher than the alpha values, so + we use this to our advantage here, and fix all instances where + this occures. +*/ +bool QWindowsXPStylePrivate::fixAlphaChannel(const QRect &rect) +{ + const int startX = rect.left(); + const int startY = rect.top(); + const int w = rect.width(); + const int h = rect.height(); + bool hasFixedAlphaValue = false; + + for (int y = startY; y < h; ++y) { + auto buffer = reinterpret_cast<DWORD *>(bufferPixels) + (y * bufferW); + for (int x = startX; x < w; ++x, ++buffer) { + uint pixel = *buffer; + int alpha = qAlpha(pixel); + if (qRed(pixel) > alpha || qGreen(pixel) > alpha || qBlue(pixel) > alpha) { + *buffer |= 0xff000000; + hasFixedAlphaValue = true; + } + } + } + return hasFixedAlphaValue; +} + +/*! \internal + Swaps the alpha values on certain pixels: + 0xFF?????? -> 0x00?????? + 0x00?????? -> 0xFF?????? + Used to determin the mask of a non-alpha transparent pixmap in + the native doublebuffer, and swap the alphas so we may paint + the image as a Premultiplied QImage with drawImage(), and obtain + the mask transparency. +*/ +bool QWindowsXPStylePrivate::swapAlphaChannel(const QRect &rect, bool allPixels) +{ + const int startX = rect.left(); + const int startY = rect.top(); + const int w = rect.width(); + const int h = rect.height(); + bool valueChange = false; + + // Flip the alphas, so that 255-alpha pixels are 0, and 0-alpha are 255. + for (int y = startY; y < h; ++y) { + auto buffer = reinterpret_cast<DWORD *>(bufferPixels) + (y * bufferW); + for (int x = startX; x < w; ++x, ++buffer) { + if (allPixels) { + *buffer |= 0xFF000000; + continue; + } + unsigned int alphaValue = (*buffer) & 0xFF000000; + if (alphaValue == 0xFF000000) { + *buffer = 0; + valueChange = true; + } else if (alphaValue == 0) { + *buffer |= 0xFF000000; + valueChange = true; + } + } + } + return valueChange; +} + +enum TransformType { SimpleTransform, HighDpiScalingTransform, ComplexTransform }; + +static inline TransformType transformType(const QTransform &transform, qreal devicePixelRatio) +{ + if (transform.type() <= QTransform::TxTranslate) + return SimpleTransform; + if (transform.type() > QTransform::TxScale) + return ComplexTransform; + return qFuzzyCompare(transform.m11(), devicePixelRatio) + && qFuzzyCompare(transform.m22(), devicePixelRatio) + ? HighDpiScalingTransform : ComplexTransform; +} + +// QTBUG-60571: Exclude known fully opaque theme parts which produce values +// invalid in ARGB32_Premultiplied (for example, 0x00ffffff). +static inline bool isFullyOpaque(const XPThemeData &themeData) +{ + return themeData.theme == QWindowsXPStylePrivate::TaskDialogTheme && themeData.partId == TDLG_PRIMARYPANEL; +} + +/*! \internal + Main theme drawing function. + Determines the correct lowlevel drawing method depending on several + factors. + Use drawBackgroundThruNativeBuffer() if: + - Painter does not have an HDC + - Theme part is flipped (mirrored horizontally) + else use drawBackgroundDirectly(). + \note drawBackgroundThruNativeBuffer() can return false for large + sizes due to buffer()/CreateDIBSection() failing. +*/ +bool QWindowsXPStylePrivate::drawBackground(XPThemeData &themeData, qreal correctionFactor) +{ + if (themeData.rect.isEmpty()) + return true; + + QPainter *painter = themeData.painter; + Q_ASSERT_X(painter != nullptr, "QWindowsXPStylePrivate::drawBackground()", "Trying to draw a theme part without a painter"); + if (!painter || !painter->isActive()) + return false; + + painter->save(); + + // Access paintDevice via engine since the painter may + // return the clip device which can still be a widget device in case of grabWidget(). + + bool translucentToplevel = false; + const QPaintDevice *paintDevice = painter->device(); + const qreal aditionalDevicePixelRatio = themeData.widget ? themeData.widget->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 + && !isFullyOpaque(themeData) + && tt != ComplexTransform && !themeData.mirrorVertically + && !translucentToplevel) { + // Draw on backing store DC only for real widgets or backing store images. + const QPaintDevice *enginePaintDevice = painter->paintEngine()->paintDevice(); + switch (enginePaintDevice->devType()) { + case QInternal::Widget: + canDrawDirectly = true; + break; + case QInternal::Image: + // Ensure the backing store has received as resize and is initialized. + if (QBackingStore *bs = backingStoreForWidget(themeData.widget)) + if (bs->size().isValid() && bs->paintDevice() == enginePaintDevice) + canDrawDirectly = true; + } + } + + const HDC dc = canDrawDirectly ? hdcForWidgetBackingStore(themeData.widget) : nullptr; + const bool result = dc && qFuzzyCompare(correctionFactor, qreal(1)) + ? drawBackgroundDirectly(dc, themeData, aditionalDevicePixelRatio) + : drawBackgroundThruNativeBuffer(themeData, aditionalDevicePixelRatio, correctionFactor); + painter->restore(); + return result; +} + +static inline QRectF scaleRect(const QRectF &r, qreal factor) +{ + return r.isValid() && factor > 1 + ? QRectF(r.topLeft() * factor, r.size() * factor) + : r; +} + +static QRegion scaleRegion(const QRegion ®ion, qreal factor) +{ + if (region.isEmpty() || qFuzzyCompare(factor, qreal(1))) + return region; + QRegion result; + for (const QRect &rect : region) + result += QRectF(QPointF(rect.topLeft()) * factor, QSizeF(rect.size() * factor)).toRect(); + return result; +} + +/*! \internal + This function draws the theme parts directly to the paintengines HDC. + Do not use this if you need to perform other transformations on the + resulting data. +*/ +bool QWindowsXPStylePrivate::drawBackgroundDirectly(HDC dc, XPThemeData &themeData, qreal additionalDevicePixelRatio) +{ + QPainter *painter = themeData.painter; + + const auto &deviceTransform = painter->deviceTransform(); + const QPointF redirectionDelta(deviceTransform.dx(), deviceTransform.dy()); + const QRect area = scaleRect(QRectF(themeData.rect), additionalDevicePixelRatio).translated(redirectionDelta).toRect(); + + QRegion sysRgn = painter->paintEngine()->systemClip(); + if (sysRgn.isEmpty()) + sysRgn = area; + else + sysRgn &= area; + if (painter->hasClipping()) + sysRgn &= scaleRegion(painter->clipRegion(), additionalDevicePixelRatio).translated(redirectionDelta.toPoint()); + HRGN hrgn = qt_hrgn_from_qregion(sysRgn); + SelectClipRgn(dc, hrgn); + +#ifdef DEBUG_XP_STYLE + printf("---[ DIRECT PAINTING ]------------------> Name(%-10s) Part(%d) State(%d)\n", + qPrintable(themeData.name), themeData.partId, themeData.stateId); + showProperties(themeData); +#endif + + RECT drawRECT = themeData.toRECT(area); + DTBGOPTS drawOptions; + memset(&drawOptions, 0, sizeof(drawOptions)); + drawOptions.dwSize = sizeof(drawOptions); + drawOptions.rcClip = themeData.toRECT(sysRgn.boundingRect()); + drawOptions.dwFlags = DTBG_CLIPRECT + | (themeData.noBorder ? DTBG_OMITBORDER : 0) + | (themeData.noContent ? DTBG_OMITCONTENT : 0) + | (themeData.mirrorHorizontally ? DTBG_MIRRORDC : 0); + + const HRESULT result = DrawThemeBackgroundEx(themeData.handle(), dc, themeData.partId, themeData.stateId, &(drawRECT), &drawOptions); + SelectClipRgn(dc, nullptr); + DeleteObject(hrgn); + return SUCCEEDED(result); +} + +/*! \internal + This function uses a secondary Native doublebuffer for painting parts. + It should only be used when the painteengine doesn't provide a proper + HDC for direct painting (e.g. when doing a grabWidget(), painting to + other pixmaps etc), or when special transformations are needed (e.g. + flips (horizonal mirroring only, vertical are handled by the theme + engine). + + \a correctionFactor is an additional factor used to scale up controls + that are too small on High DPI screens, as has been observed for + WP_MDICLOSEBUTTON, WP_MDIRESTOREBUTTON, WP_MDIMINBUTTON (QTBUG-75927). +*/ +bool QWindowsXPStylePrivate::drawBackgroundThruNativeBuffer(XPThemeData &themeData, + qreal additionalDevicePixelRatio, + qreal correctionFactor) +{ + QPainter *painter = themeData.painter; + QRectF rectF = scaleRect(QRectF(themeData.rect), additionalDevicePixelRatio); + + if ((themeData.rotate + 90) % 180 == 0) { // Catch 90,270,etc.. degree flips. + rectF = QRectF(0, 0, rectF.height(), rectF.width()); + } + rectF.moveTo(0, 0); + + const bool hasCorrectionFactor = !qFuzzyCompare(correctionFactor, qreal(1)); + QRect rect = rectF.toRect(); + QRect drawRect = hasCorrectionFactor + ? QRectF(rectF.topLeft() / correctionFactor, rectF.size() / correctionFactor).toRect() : rect; + int partId = themeData.partId; + int stateId = themeData.stateId; + int w = rect.width(); + int h = rect.height(); + + // Values initialized later, either from cached values, or from function calls + AlphaChannelType alphaType = UnknownAlpha; + bool stateHasData = true; // We assume so; + bool hasAlpha = false; + bool partIsTransparent; + bool potentialInvalidAlpha; + + QString pixmapCacheKey = QStringLiteral("$qt_xp_"); + pixmapCacheKey.append(themeName(themeData.theme)); + pixmapCacheKey.append(QLatin1Char('p')); + pixmapCacheKey.append(QString::number(partId)); + pixmapCacheKey.append(QLatin1Char('s')); + pixmapCacheKey.append(QString::number(stateId)); + pixmapCacheKey.append(QLatin1Char('s')); + pixmapCacheKey.append(themeData.noBorder ? QLatin1Char('0') : QLatin1Char('1')); + pixmapCacheKey.append(QLatin1Char('b')); + pixmapCacheKey.append(themeData.noContent ? QLatin1Char('0') : QLatin1Char('1')); + pixmapCacheKey.append(QString::number(w)); + pixmapCacheKey.append(QLatin1Char('w')); + pixmapCacheKey.append(QString::number(h)); + pixmapCacheKey.append(QLatin1Char('h')); + pixmapCacheKey.append(QString::number(additionalDevicePixelRatio)); + pixmapCacheKey.append(QLatin1Char('d')); + if (hasCorrectionFactor) { + pixmapCacheKey.append(QLatin1Char('c')); + pixmapCacheKey.append(QString::number(correctionFactor)); + } + + QPixmap cachedPixmap; + ThemeMapKey key(themeData); + ThemeMapData data = alphaCache.value(key); + + bool haveCachedPixmap = false; + bool isCached = data.dataValid; + if (isCached) { + partIsTransparent = data.partIsTransparent; + hasAlpha = data.hasAlphaChannel; + alphaType = data.alphaType; + potentialInvalidAlpha = data.hadInvalidAlpha; + + haveCachedPixmap = QPixmapCache::find(pixmapCacheKey, &cachedPixmap); + +#ifdef DEBUG_XP_STYLE + char buf[25]; + ::sprintf(buf, "+ Pixmap(%3d, %3d) ]", w, h); + printf("---[ CACHED %s--------> Name(%-10s) Part(%d) State(%d)\n", + haveCachedPixmap ? buf : "]-------------------", + qPrintable(themeData.name), themeData.partId, themeData.stateId); +#endif + } else { + // Not cached, so get values from Theme Engine + BOOL tmt_borderonly = false; + COLORREF tmt_transparentcolor = 0x0; + PROPERTYORIGIN proporigin = PO_NOTFOUND; + GetThemeBool(themeData.handle(), themeData.partId, themeData.stateId, TMT_BORDERONLY, &tmt_borderonly); + GetThemeColor(themeData.handle(), themeData.partId, themeData.stateId, TMT_TRANSPARENTCOLOR, &tmt_transparentcolor); + GetThemePropertyOrigin(themeData.handle(), themeData.partId, themeData.stateId, TMT_CAPTIONMARGINS, &proporigin); + + partIsTransparent = isTransparent(themeData); + + potentialInvalidAlpha = false; + GetThemePropertyOrigin(themeData.handle(), themeData.partId, themeData.stateId, TMT_GLYPHTYPE, &proporigin); + if (proporigin == PO_PART || proporigin == PO_STATE) { + int tmt_glyphtype = GT_NONE; + GetThemeEnumValue(themeData.handle(), themeData.partId, themeData.stateId, TMT_GLYPHTYPE, &tmt_glyphtype); + potentialInvalidAlpha = partIsTransparent && tmt_glyphtype == GT_IMAGEGLYPH; + } + +#ifdef DEBUG_XP_STYLE + printf("---[ NOT CACHED ]-----------------------> Name(%-10s) Part(%d) State(%d)\n", + qPrintable(themeData.name), themeData.partId, themeData.stateId); + printf("-->partIsTransparen = %d\n", partIsTransparent); + printf("-->potentialInvalidAlpha = %d\n", potentialInvalidAlpha); + showProperties(themeData); +#endif + } + bool wasAlphaSwapped = false; + bool wasAlphaFixed = false; + + // OLD PSDK Workaround ------------------------------------------------------------------------ + // See if we need extra clipping for the older PSDK, which does + // not have a DrawThemeBackgroundEx function for DTGB_OMITBORDER + // and DTGB_OMITCONTENT + bool addBorderContentClipping = false; + QRegion extraClip; + QRect area = drawRect; + if (themeData.noBorder || themeData.noContent) { + extraClip = area; + // We are running on a system where the uxtheme.dll does not have + // the DrawThemeBackgroundEx function, so we need to clip away + // borders or contents manually. + + int borderSize = 0; + PROPERTYORIGIN origin = PO_NOTFOUND; + GetThemePropertyOrigin(themeData.handle(), themeData.partId, themeData.stateId, TMT_BORDERSIZE, &origin); + GetThemeInt(themeData.handle(), themeData.partId, themeData.stateId, TMT_BORDERSIZE, &borderSize); + borderSize *= additionalDevicePixelRatio; + + // Clip away border region + if ((origin == PO_CLASS || origin == PO_PART || origin == PO_STATE) && borderSize > 0) { + if (themeData.noBorder) { + extraClip &= area; + area = area.adjusted(-borderSize, -borderSize, borderSize, borderSize); + } + + // Clip away content region + if (themeData.noContent) { + QRegion content = area.adjusted(borderSize, borderSize, -borderSize, -borderSize); + extraClip ^= content; + } + } + addBorderContentClipping = (themeData.noBorder | themeData.noContent); + } + + QImage img; + if (!haveCachedPixmap) { // If the pixmap is not cached, generate it! ------------------------- + if (!buffer(drawRect.width(), drawRect.height())) // Ensure a buffer of at least (w, h) in size + return false; + HDC dc = bufferHDC(); + + // Clear the buffer + if (alphaType != NoAlpha) { + // Consider have separate "memset" function for small chunks for more speedup + memset(bufferPixels, 0x00, bufferW * drawRect.height() * 4); + } + + // Difference between area and rect + int dx = area.x() - drawRect.x(); + int dy = area.y() - drawRect.y(); + + // Adjust so painting rect starts from Origo + rect.moveTo(0,0); + area.moveTo(dx,dy); + DTBGOPTS drawOptions; + drawOptions.dwSize = sizeof(drawOptions); + drawOptions.rcClip = themeData.toRECT(rect); + drawOptions.dwFlags = DTBG_CLIPRECT + | (themeData.noBorder ? DTBG_OMITBORDER : 0) + | (themeData.noContent ? DTBG_OMITCONTENT : 0); + + // Drawing the part into the backing store + RECT wRect(themeData.toRECT(area)); + DrawThemeBackgroundEx(themeData.handle(), dc, themeData.partId, themeData.stateId, &wRect, &drawOptions); + + // If not cached, analyze the buffer data to figure + // out alpha type, and if it contains data + if (!isCached) { + // SHORTCUT: If the part's state has no data, cache it for NOOP later + if (!stateHasData) { + memset(static_cast<void *>(&data), 0, sizeof(data)); + data.dataValid = true; + alphaCache.insert(key, data); + return true; + } + hasAlpha = hasAlphaChannel(rect); + if (!hasAlpha && partIsTransparent) + potentialInvalidAlpha = true; +#if defined(DEBUG_XP_STYLE) && 1 + dumpNativeDIB(drawRect.width(), drawRect.height()); +#endif + } + + // Fix alpha values, if needed + if (potentialInvalidAlpha) + wasAlphaFixed = fixAlphaChannel(drawRect); + + QImage::Format format; + if ((partIsTransparent && !wasAlphaSwapped) || (!partIsTransparent && hasAlpha)) { + format = QImage::Format_ARGB32_Premultiplied; + alphaType = RealAlpha; + } else if (wasAlphaSwapped) { + format = QImage::Format_ARGB32_Premultiplied; + alphaType = MaskAlpha; + } else { + format = QImage::Format_RGB32; + // The image data we got from the theme engine does not have any transparency, + // thus the alpha channel is set to 0. + // However, Format_RGB32 requires the alpha part to be set to 0xff, thus + // we must flip it from 0x00 to 0xff + swapAlphaChannel(rect, true); + alphaType = NoAlpha; + } +#if defined(DEBUG_XP_STYLE) && 1 + printf("Image format is: %s\n", alphaType == RealAlpha ? "Real Alpha" : alphaType == MaskAlpha ? "Masked Alpha" : "No Alpha"); +#endif + img = QImage(bufferPixels, bufferW, bufferH, format); + if (hasCorrectionFactor) + img = img.scaled(w, h, Qt::KeepAspectRatio, Qt::SmoothTransformation); + img.setDevicePixelRatio(additionalDevicePixelRatio); + } + + // Blitting backing store + bool useRegion = partIsTransparent && !hasAlpha && !wasAlphaSwapped; + + QRegion newRegion; + QRegion oldRegion; + if (useRegion) { + newRegion = region(themeData); + oldRegion = painter->clipRegion(); + painter->setClipRegion(newRegion); +#if defined(DEBUG_XP_STYLE) && 0 + printf("Using region:\n"); + for (const QRect &r : newRegion) + printf(" (%d, %d, %d, %d)\n", r.x(), r.y(), r.right(), r.bottom()); +#endif + } + + if (addBorderContentClipping) + painter->setClipRegion(scaleRegion(extraClip, 1.0 / additionalDevicePixelRatio), Qt::IntersectClip); + + if (!themeData.mirrorHorizontally && !themeData.mirrorVertically && !themeData.rotate) { + if (!haveCachedPixmap) + painter->drawImage(themeData.rect, img, rect); + else + painter->drawPixmap(themeData.rect, cachedPixmap); + } else { + // This is _slow_! + // Make a copy containing only the necessary data, and mirror + // on all wanted axes. Then draw the copy. + // If cached, the normal pixmap is cached, instead of caching + // all possible orientations for each part and state. + QImage imgCopy; + if (!haveCachedPixmap) + imgCopy = img.copy(rect); + else + imgCopy = cachedPixmap.toImage(); + + if (themeData.rotate) { + QTransform rotMatrix; + rotMatrix.rotate(themeData.rotate); + imgCopy = imgCopy.transformed(rotMatrix); + } + if (themeData.mirrorHorizontally || themeData.mirrorVertically) { + imgCopy = imgCopy.mirrored(themeData.mirrorHorizontally, themeData.mirrorVertically); + } + painter->drawImage(themeData.rect, + imgCopy); + } + + if (useRegion || addBorderContentClipping) { + if (oldRegion.isEmpty()) + painter->setClipping(false); + else + painter->setClipRegion(oldRegion); + } + + // Cache the pixmap to avoid expensive swapAlphaChannel() calls + if (!haveCachedPixmap && w && h) { + QPixmap pix = QPixmap::fromImage(img).copy(rect); + QPixmapCache::insert(pixmapCacheKey, pix); +#ifdef DEBUG_XP_STYLE + printf("+++Adding pixmap to cache, size(%d, %d), wasAlphaSwapped(%d), wasAlphaFixed(%d), name(%s)\n", + w, h, wasAlphaSwapped, wasAlphaFixed, qPrintable(pixmapCacheKey)); +#endif + } + + // Add to theme part cache + if (!isCached) { + memset(static_cast<void *>(&data), 0, sizeof(data)); + data.dataValid = true; + data.partIsTransparent = partIsTransparent; + data.alphaType = alphaType; + data.hasAlphaChannel = hasAlpha; + data.wasAlphaSwapped = wasAlphaSwapped; + data.hadInvalidAlpha = wasAlphaFixed; + alphaCache.insert(key, data); + } + return true; +} + + +// ------------------------------------------------------------------------------------------------ + +/*! + \class QWindowsXPStyle + \brief The QWindowsXPStyle class provides a Microsoft Windows XP-like look and feel. + + \ingroup appearance + \inmodule QtWidgets + \internal + + \warning This style is only available on the Windows XP platform + because it makes use of Windows XP's style engine. + + Most of the functions are documented in the base classes + QWindowsStyle, QCommonStyle, and QStyle, but the + QWindowsXPStyle overloads of drawComplexControl(), drawControl(), + drawControlMask(), drawPrimitive(), proxy()->subControlRect(), and + sizeFromContents(), are documented here. + + \image qwindowsxpstyle.png + \sa QMacStyle, QWindowsStyle, QFusionStyle +*/ + +/*! + Constructs a QWindowsStyle +*/ +QWindowsXPStyle::QWindowsXPStyle() + : QWindowsStyle(*new QWindowsXPStylePrivate) +{ +} + +/*! + Destroys the style. +*/ +QWindowsXPStyle::~QWindowsXPStyle() = default; + +/*! \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) +#if QT_CONFIG(combobox) + || qobject_cast<QComboBox*>(widget) +#endif // QT_CONFIG(combobox) + || qobject_cast<QScrollBar*>(widget) + || qobject_cast<QSlider*>(widget) + || qobject_cast<QHeaderView*>(widget) +#if QT_CONFIG(spinbox) + || qobject_cast<QAbstractSpinBox*>(widget) + || qobject_cast<QSpinBox*>(widget) +#endif // QT_CONFIG(spinbox) + ) { + widget->setAttribute(Qt::WA_Hover); + } + +#if QT_CONFIG(rubberband) + if (qobject_cast<QRubberBand*>(widget)) { + widget->setWindowOpacity(0.6); + } +#endif + if (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, nullptr, QWindowsXPStylePrivate::ButtonTheme, 0, 0); + GetThemeColor(theme.handle(), BP_GROUPBOX, GBS_NORMAL, TMT_TEXTCOLOR, &cref); + d->groupBoxTextColor = qRgb(GetRValue(cref), GetGValue(cref), GetBValue(cref)); + GetThemeColor(theme.handle(), BP_GROUPBOX, GBS_DISABLED, TMT_TEXTCOLOR, &cref); + d->groupBoxTextColorDisabled = qRgb(GetRValue(cref), GetGValue(cref), GetBValue(cref)); + // Where does this color come from? + //GetThemeColor(theme.handle(), TKP_TICS, TSS_NORMAL, TMT_COLOR, &cref); + d->sliderTickColor = qRgb(165, 162, 148); + d->hasInitColors = true; + } +} + +/*! \reimp */ +void QWindowsXPStyle::polish(QPalette &pal) +{ + QWindowsStyle::polish(pal); + pal.setBrush(QPalette::AlternateBase, pal.base().color().darker(110)); +} + +/*! \reimp */ +void QWindowsXPStyle::unpolish(QWidget *widget) +{ +#if QT_CONFIG(rubberband) + if (qobject_cast<QRubberBand*>(widget)) { + widget->setWindowOpacity(1.0); + } +#endif + Q_D(QWindowsXPStyle); + // Unpolish of widgets is the first thing that + // happens when a theme changes, or the theme + // engine is turned off. So we detect it here. + bool oldState = QWindowsXPStylePrivate::useXP(); + bool newState = QWindowsXPStylePrivate::useXP(true); + if ((oldState != newState) && newState) { + d->cleanup(true); + d->init(true); + } else { + // Cleanup handle map, if just changing style, + // or turning it on. In both cases the values + // already in the map might be old (other style). + d->cleanupHandleMap(); + } + if (false +#if QT_CONFIG(abstractbutton) + || qobject_cast<QAbstractButton*>(widget) +#endif + || qobject_cast<QToolButton*>(widget) + || qobject_cast<QTabBar*>(widget) +#if QT_CONFIG(combobox) + || qobject_cast<QComboBox*>(widget) +#endif // QT_CONFIG(combobox) + || qobject_cast<QScrollBar*>(widget) + || qobject_cast<QSlider*>(widget) + || qobject_cast<QHeaderView*>(widget) +#if QT_CONFIG(spinbox) + || qobject_cast<QAbstractSpinBox*>(widget) + || qobject_cast<QSpinBox*>(widget) +#endif // QT_CONFIG(spinbox) + ) { + widget->setAttribute(Qt::WA_Hover, false); + } + QWindowsStyle::unpolish(widget); +} + +/*! \reimp */ +QRect QWindowsXPStyle::subElementRect(SubElement sr, const QStyleOption *option, const QWidget *widget) const +{ + if (!QWindowsXPStylePrivate::useXP()) { + return QWindowsStyle::subElementRect(sr, option, widget); + } + + QRect rect(option->rect); + switch (sr) { + case SE_DockWidgetCloseButton: + case SE_DockWidgetFloatButton: + rect = QWindowsStyle::subElementRect(sr, option, widget); + return rect.translated(0, 1); + break; + case SE_TabWidgetTabContents: + if (qstyleoption_cast<const QStyleOptionTabWidgetFrame *>(option)) + { + rect = QWindowsStyle::subElementRect(sr, option, widget); + if (sr == SE_TabWidgetTabContents) { + if (const QTabWidget *tabWidget = qobject_cast<const QTabWidget *>(widget)) { + if (tabWidget->documentMode()) + break; + } + + rect.adjust(0, 0, -2, -2); + } + } + break; + case SE_TabWidgetTabBar: { + rect = QWindowsStyle::subElementRect(sr, option, widget); + const QStyleOptionTabWidgetFrame *twfOption = + qstyleoption_cast<const QStyleOptionTabWidgetFrame *>(option); + if (twfOption && twfOption->direction == Qt::RightToLeft + && (twfOption->shape == QTabBar::RoundedNorth + || twfOption->shape == QTabBar::RoundedSouth)) + { + QStyleOptionTab otherOption; + otherOption.shape = (twfOption->shape == QTabBar::RoundedNorth + ? QTabBar::RoundedEast : QTabBar::RoundedSouth); + int overlap = proxy()->pixelMetric(PM_TabBarBaseOverlap, &otherOption, widget); + int borderThickness = proxy()->pixelMetric(PM_DefaultFrameWidth, option, widget); + rect.adjust(-overlap + borderThickness, 0, -overlap + borderThickness, 0); + } + break;} + + case SE_PushButtonContents: + if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(option)) { + MARGINS borderSize; + if (widget) { + XPThemeData buttontheme(widget, nullptr, QWindowsXPStylePrivate::ButtonTheme); + HTHEME theme = buttontheme.handle(); + if (theme) { + int stateId; + if (!(option->state & State_Enabled)) + stateId = PBS_DISABLED; + else if (option->state & State_Sunken) + stateId = PBS_PRESSED; + else if (option->state & State_MouseOver) + stateId = PBS_HOT; + else if (btn->features & QStyleOptionButton::DefaultButton) + stateId = PBS_DEFAULTED; + else + stateId = PBS_NORMAL; + + int border = proxy()->pixelMetric(PM_DefaultFrameWidth, btn, widget); + rect = option->rect.adjusted(border, border, -border, -border); + + if (SUCCEEDED(GetThemeMargins(theme, nullptr, BP_PUSHBUTTON, stateId, TMT_CONTENTMARGINS, nullptr, &borderSize))) { + rect.adjust(borderSize.cxLeftWidth, borderSize.cyTopHeight, + -borderSize.cxRightWidth, -borderSize.cyBottomHeight); + rect = visualRect(option->direction, option->rect, rect); + } + } + } + } + break; + case SE_ProgressBarContents: + rect = QCommonStyle::subElementRect(SE_ProgressBarGroove, option, widget); + if (option->state & QStyle::State_Horizontal) + rect.adjust(4, 3, -4, -3); + else + rect.adjust(3, 2, -3, -2); + break; + default: + rect = QWindowsStyle::subElementRect(sr, option, widget); + } + return rect; +} + +/*! + \reimp +*/ +void QWindowsXPStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *option, QPainter *p, + const QWidget *widget) const +{ + QWindowsXPStylePrivate *d = const_cast<QWindowsXPStylePrivate*>(d_func()); + + if (!QWindowsXPStylePrivate::useXP()) { + QWindowsStyle::drawPrimitive(pe, option, p, widget); + return; + } + + int themeNumber = -1; + int partId = 0; + int stateId = 0; + QRect rect = option->rect; + State flags = option->state; + bool hMirrored = false; + bool vMirrored = false; + bool noBorder = false; + bool noContent = false; + int rotate = 0; + + switch (pe) { + case PE_FrameTabBarBase: + if (const QStyleOptionTabBarBase *tbb + = qstyleoption_cast<const QStyleOptionTabBarBase *>(option)) { + p->save(); + switch (tbb->shape) { + case QTabBar::RoundedNorth: + p->setPen(QPen(tbb->palette.dark(), 0)); + p->drawLine(tbb->rect.topLeft(), tbb->rect.topRight()); + break; + case QTabBar::RoundedWest: + p->setPen(QPen(tbb->palette.dark(), 0)); + p->drawLine(tbb->rect.left(), tbb->rect.top(), tbb->rect.left(), tbb->rect.bottom()); + break; + case QTabBar::RoundedSouth: + p->setPen(QPen(tbb->palette.dark(), 0)); + p->drawLine(tbb->rect.left(), tbb->rect.top(), + tbb->rect.right(), tbb->rect.top()); + break; + case QTabBar::RoundedEast: + p->setPen(QPen(tbb->palette.dark(), 0)); + p->drawLine(tbb->rect.topLeft(), tbb->rect.bottomLeft()); + break; + case QTabBar::TriangularNorth: + case QTabBar::TriangularEast: + case QTabBar::TriangularWest: + case QTabBar::TriangularSouth: + p->restore(); + QWindowsStyle::drawPrimitive(pe, option, p, widget); + return; + } + p->restore(); + } + return; + case PE_PanelButtonBevel: + themeNumber = QWindowsXPStylePrivate::ButtonTheme; + partId = BP_PUSHBUTTON; + if (!(flags & State_Enabled)) + stateId = PBS_DISABLED; + else if ((flags & State_Sunken) || (flags & State_On)) + stateId = PBS_PRESSED; + else if (flags & State_MouseOver) + stateId = PBS_HOT; + //else if (flags & State_ButtonDefault) + // stateId = PBS_DEFAULTED; + else + stateId = PBS_NORMAL; + break; + + case PE_PanelButtonTool: + if (widget && widget->inherits("QDockWidgetTitleButton")) { + if (const QWidget *dw = widget->parentWidget()) + if (dw->isWindow()) + return; + } + themeNumber = QWindowsXPStylePrivate::ToolBarTheme; + partId = TP_BUTTON; + if (!(flags & State_Enabled)) + stateId = TS_DISABLED; + else if (flags & State_Sunken) + stateId = TS_PRESSED; + else if (flags & State_MouseOver) + stateId = flags & State_On ? TS_HOTCHECKED : TS_HOT; + else if (flags & State_On) + stateId = TS_CHECKED; + else if (!(flags & State_AutoRaise)) + stateId = TS_HOT; + else + stateId = TS_NORMAL; + break; + + case PE_IndicatorButtonDropDown: + themeNumber = QWindowsXPStylePrivate::ToolBarTheme; + partId = TP_SPLITBUTTONDROPDOWN; + if (!(flags & State_Enabled)) + stateId = TS_DISABLED; + else if (flags & State_Sunken) + stateId = TS_PRESSED; + else if (flags & State_MouseOver) + stateId = flags & State_On ? TS_HOTCHECKED : TS_HOT; + else if (flags & State_On) + stateId = TS_CHECKED; + else if (!(flags & State_AutoRaise)) + stateId = TS_HOT; + else + stateId = TS_NORMAL; + if (option->direction == Qt::RightToLeft) + hMirrored = true; + break; + + case PE_IndicatorCheckBox: + themeNumber = QWindowsXPStylePrivate::ButtonTheme; + partId = BP_CHECKBOX; + if (!(flags & State_Enabled)) + stateId = CBS_UNCHECKEDDISABLED; + else if (flags & State_Sunken) + stateId = CBS_UNCHECKEDPRESSED; + else if (flags & State_MouseOver) + stateId = CBS_UNCHECKEDHOT; + else + stateId = CBS_UNCHECKEDNORMAL; + + if (flags & State_On) + stateId += CBS_CHECKEDNORMAL-1; + else if (flags & State_NoChange) + stateId += CBS_MIXEDNORMAL-1; + + break; + + case PE_IndicatorRadioButton: + themeNumber = QWindowsXPStylePrivate::ButtonTheme; + partId = BP_RADIOBUTTON; + if (!(flags & State_Enabled)) + stateId = RBS_UNCHECKEDDISABLED; + else if (flags & State_Sunken) + stateId = RBS_UNCHECKEDPRESSED; + else if (flags & State_MouseOver) + stateId = RBS_UNCHECKEDHOT; + else + stateId = RBS_UNCHECKEDNORMAL; + + if (flags & State_On) + stateId += RBS_CHECKEDNORMAL-1; + break; + + case PE_IndicatorDockWidgetResizeHandle: + return; + +case PE_Frame: + { + if (flags & State_Raised) + return; + themeNumber = QWindowsXPStylePrivate::ListViewTheme; + partId = LVP_LISTGROUP; + XPThemeData theme(widget, nullptr, themeNumber, partId); + + if (!(flags & State_Enabled)) + stateId = ETS_DISABLED; + else + stateId = ETS_NORMAL; + int fillType; + if (GetThemeEnumValue(theme.handle(), partId, stateId, TMT_BGTYPE, &fillType) == S_OK) { + if (fillType == BT_BORDERFILL) { + COLORREF bcRef; + GetThemeColor(theme.handle(), partId, stateId, TMT_BORDERCOLOR, &bcRef); + QColor bordercolor(qRgb(GetRValue(bcRef), GetGValue(bcRef), GetBValue(bcRef))); + QPen oldPen = p->pen(); + // int borderSize = 1; + // GetThemeInt(theme.handle(), partId, stateId, TMT_BORDERCOLOR, &borderSize); + + // Inner white border + p->setPen(QPen(option->palette.base().color(), 0)); + const qreal dpi = QStyleHelper::dpi(option); + const auto topLevelAdjustment = QStyleHelper::dpiScaled(0.5, dpi); + const auto bottomRightAdjustment = QStyleHelper::dpiScaled(-1, dpi); + p->drawRect(QRectF(option->rect).adjusted(topLevelAdjustment, topLevelAdjustment, + bottomRightAdjustment, bottomRightAdjustment)); + // Outer dark border + p->setPen(QPen(bordercolor, 0)); + p->drawRect(QRectF(option->rect).adjusted(0, 0, -topLevelAdjustment, -topLevelAdjustment)); + p->setPen(oldPen); + return; + } + if (fillType == BT_NONE) + return; + } + break; + } + case PE_FrameLineEdit: { + // we try to check if this lineedit is a delegate on a QAbstractItemView-derived class. + if (QWindowsXPStylePrivate::isItemViewDelegateLineEdit(widget)) { + QPen oldPen = p->pen(); + // Inner white border + p->setPen(QPen(option->palette.base().color(), 1)); + p->drawRect(option->rect.adjusted(1, 1, -2, -2)); + // Outer dark border + p->setPen(QPen(option->palette.shadow().color(), 1)); + p->drawRect(option->rect.adjusted(0, 0, -1, -1)); + p->setPen(oldPen); + return; + } + if (qstyleoption_cast<const QStyleOptionFrame *>(option)) { + themeNumber = QWindowsXPStylePrivate::EditTheme; + partId = EP_EDITTEXT; + noContent = true; + if (!(flags & State_Enabled)) + stateId = ETS_DISABLED; + else + stateId = ETS_NORMAL; + } + break; + } + + case PE_PanelLineEdit: + if (const QStyleOptionFrame *panel = qstyleoption_cast<const QStyleOptionFrame *>(option)) { + themeNumber = QWindowsXPStylePrivate::EditTheme; + partId = EP_EDITTEXT; + noBorder = true; + bool isEnabled = flags & State_Enabled; + + stateId = isEnabled ? ETS_NORMAL : ETS_DISABLED; + + if (QWindowsXPStylePrivate::isLineEditBaseColorSet(option, widget)) { + p->fillRect(panel->rect, panel->palette.brush(QPalette::Base)); + } else { + XPThemeData theme(nullptr, p, themeNumber, partId, stateId, rect); + if (!theme.isValid()) { + QWindowsStyle::drawPrimitive(pe, option, p, widget); + return; + } + int bgType; + GetThemeEnumValue(theme.handle(), partId, stateId, TMT_BGTYPE, &bgType); + if ( bgType == BT_IMAGEFILE ) { + theme.mirrorHorizontally = hMirrored; + theme.mirrorVertically = vMirrored; + theme.noBorder = noBorder; + theme.noContent = noContent; + theme.rotate = rotate; + d->drawBackground(theme); + } else { + QBrush fillColor = option->palette.brush(QPalette::Base); + + if (!isEnabled) { + PROPERTYORIGIN origin = PO_NOTFOUND; + GetThemePropertyOrigin(theme.handle(), theme.partId, theme.stateId, TMT_FILLCOLOR, &origin); + // Use only if the fill property comes from our part + if ((origin == PO_PART || origin == PO_STATE)) { + COLORREF bgRef; + GetThemeColor(theme.handle(), partId, stateId, TMT_FILLCOLOR, &bgRef); + fillColor = QBrush(qRgb(GetRValue(bgRef), GetGValue(bgRef), GetBValue(bgRef))); + } + } + p->fillRect(option->rect, fillColor); + } + } + + if (panel->lineWidth > 0) + proxy()->drawPrimitive(PE_FrameLineEdit, panel, p, widget); + return; + } + break; + + case PE_FrameTabWidget: + if (const QStyleOptionTabWidgetFrame *tab = qstyleoption_cast<const QStyleOptionTabWidgetFrame *>(option)) + { + themeNumber = QWindowsXPStylePrivate::TabTheme; + partId = TABP_PANE; + + if (widget) { + bool useGradient = true; + const int maxlength = 256; + wchar_t themeFileName[maxlength]; + wchar_t themeColor[maxlength]; + // Due to a a scaling issue with the XP Silver theme, tab gradients are not used with it + if (GetCurrentThemeName(themeFileName, maxlength, themeColor, maxlength, nullptr, 0) == S_OK) { + wchar_t *offset = nullptr; + if ((offset = wcsrchr(themeFileName, QChar(QLatin1Char('\\')).unicode())) != nullptr) { + offset++; + if (!lstrcmp(offset, L"Luna.msstyles") && !lstrcmp(offset, L"Metallic")) { + useGradient = false; + } + } + } + // This should work, but currently there's an error in the ::drawBackgroundDirectly() + // code, when using the HDC directly.. + if (useGradient) { + QStyleOptionTabWidgetFrame frameOpt = *tab; + frameOpt.rect = widget->rect(); + QRect contentsRect = subElementRect(SE_TabWidgetTabContents, &frameOpt, widget); + QRegion reg = option->rect; + reg -= contentsRect; + p->setClipRegion(reg); + XPThemeData theme(widget, p, themeNumber, partId, stateId, rect); + theme.mirrorHorizontally = hMirrored; + theme.mirrorVertically = vMirrored; + d->drawBackground(theme); + p->setClipRect(contentsRect); + partId = TABP_BODY; + } + } + switch (tab->shape) { + case QTabBar::RoundedNorth: + case QTabBar::TriangularNorth: + break; + case QTabBar::RoundedSouth: + case QTabBar::TriangularSouth: + vMirrored = true; + break; + case QTabBar::RoundedEast: + case QTabBar::TriangularEast: + rotate = 90; + break; + case QTabBar::RoundedWest: + case QTabBar::TriangularWest: + rotate = 90; + hMirrored = true; + break; + default: + break; + } + } + break; + + case PE_FrameMenu: + p->save(); + p->setPen(option->palette.dark().color()); + p->drawRect(rect.adjusted(0, 0, -1, -1)); + p->restore(); + return; + + case PE_PanelMenuBar: + break; + + case PE_FrameDockWidget: + if (const QStyleOptionFrame *frm = qstyleoption_cast<const QStyleOptionFrame *>(option)) + { + themeNumber = QWindowsXPStylePrivate::WindowTheme; + if (flags & State_Active) + stateId = FS_ACTIVE; + else + stateId = FS_INACTIVE; + + int fwidth = proxy()->pixelMetric(PM_DockWidgetFrameWidth, frm, widget); + + XPThemeData theme(widget, p, themeNumber, 0, stateId); + if (!theme.isValid()) + break; + theme.rect = QRect(frm->rect.x(), frm->rect.y(), frm->rect.x()+fwidth, frm->rect.height()-fwidth); theme.partId = WP_SMALLFRAMELEFT; + d->drawBackground(theme); + theme.rect = QRect(frm->rect.width()-fwidth, frm->rect.y(), fwidth, frm->rect.height()-fwidth); + theme.partId = WP_SMALLFRAMERIGHT; + d->drawBackground(theme); + theme.rect = QRect(frm->rect.x(), frm->rect.bottom()-fwidth+1, frm->rect.width(), fwidth); + theme.partId = WP_SMALLFRAMEBOTTOM; + d->drawBackground(theme); + return; + } + break; + + case PE_IndicatorHeaderArrow: + { +#if 0 // XP theme engine doesn't know about this :( + name = QWindowsXPStylePrivate::HeaderTheme; + partId = HP_HEADERSORTARROW; + if (flags & State_Down) + stateId = HSAS_SORTEDDOWN; + else + stateId = HSAS_SORTEDUP; +#else + if (const QStyleOptionHeader *header = qstyleoption_cast<const QStyleOptionHeader *>(option)) { + p->save(); + p->setPen(option->palette.dark().color()); + p->translate(0, option->rect.height()/2 - 4); + if (header->sortIndicator & QStyleOptionHeader::SortUp) { // invert logic to follow Windows style guide + p->drawLine(option->rect.x(), option->rect.y(), option->rect.x()+8, option->rect.y()); + p->drawLine(option->rect.x()+1, option->rect.y()+1, option->rect.x()+7, option->rect.y()+1); + p->drawLine(option->rect.x()+2, option->rect.y()+2, option->rect.x()+6, option->rect.y()+2); + p->drawLine(option->rect.x()+3, option->rect.y()+3, option->rect.x()+5, option->rect.y()+3); + p->drawPoint(option->rect.x()+4, option->rect.y()+4); + } else if (header->sortIndicator & QStyleOptionHeader::SortDown) { + p->drawLine(option->rect.x(), option->rect.y()+4, option->rect.x()+8, option->rect.y()+4); + p->drawLine(option->rect.x()+1, option->rect.y()+3, option->rect.x()+7, option->rect.y()+3); + p->drawLine(option->rect.x()+2, option->rect.y()+2, option->rect.x()+6, option->rect.y()+2); + p->drawLine(option->rect.x()+3, option->rect.y()+1, option->rect.x()+5, option->rect.y()+1); + p->drawPoint(option->rect.x()+4, option->rect.y()); + } + p->restore(); + return; + } +#endif + } + break; + + case PE_FrameStatusBarItem: + themeNumber = QWindowsXPStylePrivate::StatusTheme; + partId = SP_PANE; + break; + + case PE_FrameGroupBox: + themeNumber = QWindowsXPStylePrivate::ButtonTheme; + partId = BP_GROUPBOX; + if (!(flags & State_Enabled)) + stateId = GBS_DISABLED; + else + stateId = GBS_NORMAL; + if (const QStyleOptionFrame *frame = qstyleoption_cast<const QStyleOptionFrame *>(option)) { + if (frame->features & QStyleOptionFrame::Flat) { + // Windows XP does not have a theme part for a flat GroupBox, paint it with the windows style + QRect fr = frame->rect; + QPoint p1(fr.x(), fr.y() + 1); + QPoint p2(fr.x() + fr.width(), p1.y() + 1); + rect = QRect(p1, p2); + themeNumber = -1; + } + } + break; + + case PE_IndicatorProgressChunk: + { + Qt::Orientation orient = Qt::Horizontal; + bool inverted = false; + if (const QStyleOptionProgressBar *pb = qstyleoption_cast<const QStyleOptionProgressBar *>(option)) { + orient = pb->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 = int((frm->lineWidth + frm->midLineWidth) / QWindowsStylePrivate::nativeMetricScaleFactor(widget)); + + XPThemeData theme(widget, p, themeNumber, 0, stateId); + if (!theme.isValid()) + break; + + // May fail due to too-large buffers for large widgets, fall back to Windows style. + theme.rect = QRect(option->rect.x(), option->rect.y()+fwidth, option->rect.x()+fwidth, option->rect.height()-fwidth); + theme.partId = WP_FRAMELEFT; + if (!d->drawBackground(theme)) { + QWindowsStyle::drawPrimitive(pe, option, p, widget); + return; + } + theme.rect = QRect(option->rect.width()-fwidth, option->rect.y()+fwidth, fwidth, option->rect.height()-fwidth); + theme.partId = WP_FRAMERIGHT; + if (!d->drawBackground(theme)) { + QWindowsStyle::drawPrimitive(pe, option, p, widget); + return; + } + theme.rect = QRect(option->rect.x(), option->rect.height()-fwidth, option->rect.width(), fwidth); + theme.partId = WP_FRAMEBOTTOM; + if (!d->drawBackground(theme)) { + QWindowsStyle::drawPrimitive(pe, option, p, widget); + return; + } + theme.rect = QRect(option->rect.x(), option->rect.y(), option->rect.width(), option->rect.y()+fwidth); + theme.partId = WP_CAPTION; + if (!d->drawBackground(theme)) + QWindowsStyle::drawPrimitive(pe, option, p, widget); + return; + } + break; + + case PE_IndicatorBranch: + { + static const int decoration_size = 9; + int mid_h = option->rect.x() + option->rect.width() / 2; + int mid_v = option->rect.y() + option->rect.height() / 2; + int bef_h = mid_h; + int bef_v = mid_v; + int aft_h = mid_h; + int aft_v = mid_v; + QBrush brush(option->palette.dark().color(), Qt::Dense4Pattern); + if (option->state & State_Item) { + if (option->direction == Qt::RightToLeft) + p->fillRect(option->rect.left(), mid_v, bef_h - option->rect.left(), 1, brush); + else + p->fillRect(aft_h, mid_v, option->rect.right() - aft_h + 1, 1, brush); + } + if (option->state & State_Sibling) + p->fillRect(mid_h, aft_v, 1, option->rect.bottom() - aft_v + 1, brush); + if (option->state & (State_Open | State_Children | State_Item | State_Sibling)) + p->fillRect(mid_h, option->rect.y(), 1, bef_v - option->rect.y(), brush); + if (option->state & State_Children) { + int delta = decoration_size / 2; + bef_h -= delta; + bef_v -= delta; + aft_h += delta; + aft_v += delta; + XPThemeData theme(nullptr, p, QWindowsXPStylePrivate::XpTreeViewTheme); + theme.rect = QRect(bef_h, bef_v, decoration_size, decoration_size); + theme.partId = TVP_GLYPH; + theme.stateId = flags & QStyle::State_Open ? GLPS_OPENED : GLPS_CLOSED; + d->drawBackground(theme); + } + } + return; + + case PE_IndicatorToolBarSeparator: + if (option->rect.height() < 3) { + // XP style requires a few pixels for the separator + // to be visible. + QWindowsStyle::drawPrimitive(pe, option, p, widget); + return; + } + themeNumber = QWindowsXPStylePrivate::ToolBarTheme; + partId = TP_SEPARATOR; + + if (option->state & State_Horizontal) + partId = TP_SEPARATOR; + else + partId = TP_SEPARATORVERT; + + break; + + case PE_IndicatorToolBarHandle: + + themeNumber = QWindowsXPStylePrivate::RebarTheme; + partId = RP_GRIPPER; + if (option->state & State_Horizontal) { + partId = RP_GRIPPER; + rect.adjust(0, 0, -2, 0); + } + else { + partId = RP_GRIPPERVERT; + rect.adjust(0, 0, 0, -2); + } + break; + + case PE_IndicatorItemViewItemCheck: { + QStyleOptionButton button; + button.QStyleOption::operator=(*option); + button.state &= ~State_MouseOver; + proxy()->drawPrimitive(PE_IndicatorCheckBox, &button, p, widget); + return; + } + + default: + break; + } + + XPThemeData theme(widget, p, themeNumber, partId, stateId, rect); + if (!theme.isValid()) { + QWindowsStyle::drawPrimitive(pe, option, p, widget); + return; + } + theme.mirrorHorizontally = hMirrored; + theme.mirrorVertically = vMirrored; + theme.noBorder = noBorder; + theme.noContent = noContent; + theme.rotate = rotate; + d->drawBackground(theme); +} + +/*! + \reimp +*/ +void QWindowsXPStyle::drawControl(ControlElement element, const QStyleOption *option, QPainter *p, + const QWidget *widget) const +{ + QWindowsXPStylePrivate *d = const_cast<QWindowsXPStylePrivate*>(d_func()); + if (!QWindowsXPStylePrivate::useXP()) { + QWindowsStyle::drawControl(element, option, p, widget); + return; + } + + QRect rect(option->rect); + State flags = option->state; + + int rotate = 0; + bool hMirrored = false; + bool vMirrored = false; + + int themeNumber = -1; + int partId = 0; + int stateId = 0; + switch (element) { + case CE_SizeGrip: + { + themeNumber = QWindowsXPStylePrivate::StatusTheme; + partId = SP_GRIPPER; + XPThemeData theme(nullptr, p, themeNumber, partId); + QSize size = (theme.size() * QWindowsStylePrivate::nativeMetricScaleFactor(widget)).toSize(); + size.rheight()--; + if (const QStyleOptionSizeGrip *sg = qstyleoption_cast<const QStyleOptionSizeGrip *>(option)) { + switch (sg->corner) { + case Qt::BottomRightCorner: + rect = QRect(QPoint(rect.right() - size.width(), rect.bottom() - size.height()), size); + break; + case Qt::BottomLeftCorner: + rect = QRect(QPoint(rect.left() + 1, rect.bottom() - size.height()), size); + hMirrored = true; + break; + case Qt::TopRightCorner: + rect = QRect(QPoint(rect.right() - size.width(), rect.top() + 1), size); + vMirrored = true; + break; + case Qt::TopLeftCorner: + rect = QRect(rect.topLeft() + QPoint(1, 1), size); + hMirrored = vMirrored = true; + } + } + } + break; + + case CE_HeaderSection: + themeNumber = QWindowsXPStylePrivate::HeaderTheme; + partId = HP_HEADERITEM; + if (flags & State_Sunken) + stateId = HIS_PRESSED; + else if (flags & State_MouseOver) + stateId = HIS_HOT; + else + stateId = HIS_NORMAL; + break; + + case CE_Splitter: + p->eraseRect(option->rect); + return; + + case CE_PushButtonBevel: + if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(option)) + { + themeNumber = QWindowsXPStylePrivate::ButtonTheme; + partId = BP_PUSHBUTTON; + bool justFlat = ((btn->features & QStyleOptionButton::Flat) && !(flags & (State_On|State_Sunken))) + || ((btn->features & QStyleOptionButton::CommandLinkButton) + && !(flags & State_MouseOver) + && !(btn->features & QStyleOptionButton::DefaultButton)); + if (!(flags & State_Enabled) && !(btn->features & QStyleOptionButton::Flat)) + stateId = PBS_DISABLED; + else if (justFlat) + ; + else if (flags & (State_Sunken | State_On)) + stateId = PBS_PRESSED; + else if (flags & State_MouseOver) + stateId = PBS_HOT; + else if (btn->features & QStyleOptionButton::DefaultButton) + stateId = PBS_DEFAULTED; + else + stateId = PBS_NORMAL; + + if (!justFlat) { + XPThemeData theme(widget, p, themeNumber, partId, stateId, rect); + d->drawBackground(theme); + } + + if (btn->features & QStyleOptionButton::HasMenu) { + int mbiw = 0, mbih = 0; + XPThemeData theme(widget, nullptr, + QWindowsXPStylePrivate::ToolBarTheme, + TP_SPLITBUTTONDROPDOWN); + if (theme.isValid()) { + const QSize size = (theme.size() * QWindowsStylePrivate::nativeMetricScaleFactor(widget)).toSize(); + mbiw = size.width(); + mbih = size.height(); + } + + QRect ir = btn->rect; + QStyleOptionButton newBtn = *btn; + newBtn.rect = QRect(ir.right() - mbiw - 1, 1 + (ir.height()/2) - (mbih/2), mbiw, mbih); + proxy()->drawPrimitive(PE_IndicatorArrowDown, &newBtn, p, widget); + } + return; + } + break; + case CE_TabBarTab: + if (const QStyleOptionTab *tab = qstyleoption_cast<const QStyleOptionTab *>(option)) + { + stateId = tab->state & State_Enabled ? TIS_NORMAL : TIS_DISABLED; + } + break; + + case CE_TabBarTabShape: + if (const QStyleOptionTab *tab = qstyleoption_cast<const QStyleOptionTab *>(option)) + { + themeNumber = QWindowsXPStylePrivate::TabTheme; + bool isDisabled = !(tab->state & State_Enabled); + bool hasFocus = tab->state & State_HasFocus; + bool isHot = tab->state & State_MouseOver; + bool selected = tab->state & State_Selected; + bool lastTab = tab->position == QStyleOptionTab::End; + bool firstTab = tab->position == QStyleOptionTab::Beginning; + bool onlyOne = tab->position == QStyleOptionTab::OnlyOneTab; + bool leftAligned = proxy()->styleHint(SH_TabBar_Alignment, tab, widget) == Qt::AlignLeft; + bool centerAligned = proxy()->styleHint(SH_TabBar_Alignment, tab, widget) == Qt::AlignCenter; + int borderThickness = proxy()->pixelMetric(PM_DefaultFrameWidth, option, widget); + int tabOverlap = proxy()->pixelMetric(PM_TabBarTabOverlap, option, widget); + + if (isDisabled) + stateId = TIS_DISABLED; + else if (selected) + stateId = TIS_SELECTED; + else if (hasFocus) + stateId = TIS_FOCUSED; + else if (isHot) + stateId = TIS_HOT; + else + stateId = TIS_NORMAL; + + // Selecting proper part depending on position + if (firstTab || onlyOne) { + if (leftAligned) { + partId = TABP_TABITEMLEFTEDGE; + } else if (centerAligned) { + partId = TABP_TABITEM; + } else { // rightAligned + partId = TABP_TABITEMRIGHTEDGE; + } + } else { + partId = TABP_TABITEM; + } + + if (tab->direction == Qt::RightToLeft + && (tab->shape == QTabBar::RoundedNorth + || tab->shape == QTabBar::RoundedSouth)) { + bool temp = firstTab; + firstTab = lastTab; + lastTab = temp; + } + bool begin = firstTab || onlyOne; + bool end = lastTab || onlyOne; + switch (tab->shape) { + case QTabBar::RoundedNorth: + if (selected) + rect.adjust(begin ? 0 : -tabOverlap, 0, end ? 0 : tabOverlap, borderThickness); + else + rect.adjust(begin? tabOverlap : 0, tabOverlap, end ? -tabOverlap : 0, 0); + break; + case QTabBar::RoundedSouth: + //vMirrored = true; + rotate = 180; // Not 100% correct, but works + if (selected) + rect.adjust(begin ? 0 : -tabOverlap , -borderThickness, end ? 0 : tabOverlap, 0); + else + rect.adjust(begin ? tabOverlap : 0, 0, end ? -tabOverlap : 0 , -tabOverlap); + break; + case QTabBar::RoundedEast: + rotate = 90; + if (selected) { + rect.adjust(-borderThickness, begin ? 0 : -tabOverlap, 0, end ? 0 : tabOverlap); + }else{ + rect.adjust(0, begin ? tabOverlap : 0, -tabOverlap, end ? -tabOverlap : 0); + } + break; + case QTabBar::RoundedWest: + hMirrored = true; + rotate = 90; + if (selected) { + rect.adjust(0, begin ? 0 : -tabOverlap, borderThickness, end ? 0 : tabOverlap); + }else{ + rect.adjust(tabOverlap, begin ? tabOverlap : 0, 0, end ? -tabOverlap : 0); + } + break; + default: + themeNumber = -1; // Do our own painting for triangular + break; + } + + if (!selected) { + switch (tab->shape) { + case QTabBar::RoundedNorth: + rect.adjust(0,0, 0,-1); + break; + case QTabBar::RoundedSouth: + rect.adjust(0,1, 0,0); + break; + case QTabBar::RoundedEast: + rect.adjust( 1,0, 0,0); + break; + case QTabBar::RoundedWest: + rect.adjust(0,0, -1,0); + break; + default: + break; + } + } + } + break; + + case CE_ProgressBarGroove: + { + Qt::Orientation orient = 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; +#if QT_CONFIG(dockwidget) + case CE_DockWidgetTitle: + if (const QStyleOptionDockWidget *dwOpt = qstyleoption_cast<const QStyleOptionDockWidget *>(option)) + { + int buttonMargin = 4; + int mw = proxy()->pixelMetric(QStyle::PM_DockWidgetTitleMargin, dwOpt, widget); + int fw = proxy()->pixelMetric(PM_DockWidgetFrameWidth, dwOpt, widget); + bool isFloating = widget && widget->isWindow(); + bool isActive = dwOpt->state & State_Active; + + const bool verticalTitleBar = dwOpt->verticalTitleBar; + + if (verticalTitleBar) { + rect = rect.transposed(); + + p->translate(rect.left() - 1, rect.top() + rect.width()); + p->rotate(-90); + p->translate(-rect.left() + 1, -rect.top()); + } + QRect r = rect.adjusted(0, 2, -1, -3); + QRect titleRect = r; + + if (dwOpt->closable) { + QSize sz = proxy()->standardIcon(QStyle::SP_TitleBarCloseButton, dwOpt, widget).actualSize(QSize(10, 10)); + titleRect.adjust(0, 0, -sz.width() - mw - buttonMargin, 0); + } + + if (dwOpt->floatable) { + QSize sz = proxy()->standardIcon(QStyle::SP_TitleBarMaxButton, dwOpt, widget).actualSize(QSize(10, 10)); + titleRect.adjust(0, 0, -sz.width() - mw - buttonMargin, 0); + } + + if (isFloating) { + titleRect.adjust(0, -fw, 0, 0); + if (widget != nullptr && widget->windowIcon().cacheKey() != QApplication::windowIcon().cacheKey()) + titleRect.adjust(titleRect.height() + mw, 0, 0, 0); + } else { + titleRect.adjust(mw, 0, 0, 0); + if (!dwOpt->floatable && !dwOpt->closable) + titleRect.adjust(0, 0, -mw, 0); + } + + if (!verticalTitleBar) + titleRect = visualRect(dwOpt->direction, r, titleRect); + + if (!isFloating) { + QPen oldPen = p->pen(); + QString titleText = p->fontMetrics().elidedText(dwOpt->title, Qt::ElideRight, titleRect.width()); + p->setPen(dwOpt->palette.color(QPalette::Dark)); + p->drawRect(r); + + if (!titleText.isEmpty()) { + drawItemText(p, titleRect, + Qt::AlignLeft | Qt::AlignVCenter | Qt::TextShowMnemonic, dwOpt->palette, + dwOpt->state & State_Enabled, titleText, + QPalette::WindowText); + } + + p->setPen(oldPen); + } else { + themeNumber = QWindowsXPStylePrivate::WindowTheme; + if (isActive) + stateId = CS_ACTIVE; + else + stateId = CS_INACTIVE; + + int titleHeight = rect.height() - 2; + rect = rect.adjusted(-fw, -fw, fw, 0); + + XPThemeData theme(widget, p, themeNumber, 0, stateId); + if (!theme.isValid()) + break; + + // Draw small type title bar + theme.rect = rect; + theme.partId = WP_SMALLCAPTION; + d->drawBackground(theme); + + // Figure out maximal button space on title bar + + QIcon ico = widget->windowIcon(); + bool hasIcon = (ico.cacheKey() != QApplication::windowIcon().cacheKey()); + if (hasIcon) { + QPixmap pxIco = ico.pixmap(titleHeight); + if (!verticalTitleBar && dwOpt->direction == Qt::RightToLeft) + p->drawPixmap(rect.width() - titleHeight - pxIco.width(), rect.bottom() - titleHeight - 2, pxIco); + else + p->drawPixmap(fw, rect.bottom() - titleHeight - 2, pxIco); + } + if (!dwOpt->title.isEmpty()) { + QPen oldPen = p->pen(); + QFont oldFont = p->font(); + QFont titleFont = oldFont; + titleFont.setBold(true); + p->setFont(titleFont); + QString titleText + = p->fontMetrics().elidedText(dwOpt->title, Qt::ElideRight, titleRect.width()); + + int result = TST_NONE; + GetThemeEnumValue(theme.handle(), WP_SMALLCAPTION, isActive ? CS_ACTIVE : CS_INACTIVE, TMT_TEXTSHADOWTYPE, &result); + if (result != TST_NONE) { + COLORREF textShadowRef; + GetThemeColor(theme.handle(), WP_SMALLCAPTION, isActive ? CS_ACTIVE : CS_INACTIVE, TMT_TEXTSHADOWCOLOR, &textShadowRef); + QColor textShadow = qRgb(GetRValue(textShadowRef), GetGValue(textShadowRef), GetBValue(textShadowRef)); + p->setPen(textShadow); + drawItemText(p, titleRect.adjusted(1, 1, 1, 1), + Qt::AlignLeft | Qt::AlignBottom, dwOpt->palette, + dwOpt->state & State_Enabled, titleText); + } + + COLORREF captionText = GetSysColor(isActive ? COLOR_CAPTIONTEXT : COLOR_INACTIVECAPTIONTEXT); + QColor textColor = qRgb(GetRValue(captionText), GetGValue(captionText), GetBValue(captionText)); + p->setPen(textColor); + drawItemText(p, titleRect, + Qt::AlignLeft | Qt::AlignBottom, dwOpt->palette, + dwOpt->state & State_Enabled, titleText); + p->setFont(oldFont); + p->setPen(oldPen); + } + + } + + return; + } + break; +#endif // QT_CONFIG(dockwidget) +#if QT_CONFIG(rubberband) + case CE_RubberBand: + if (qstyleoption_cast<const QStyleOptionRubberBand *>(option)) { + QColor highlight = option->palette.color(QPalette::Active, QPalette::Highlight); + p->save(); + p->setPen(highlight.darker(120)); + QColor dimHighlight(qMin(highlight.red()/2 + 110, 255), + qMin(highlight.green()/2 + 110, 255), + qMin(highlight.blue()/2 + 110, 255), + (widget && widget->isTopLevel())? 255 : 127); + p->setBrush(dimHighlight); + p->drawRect(option->rect.adjusted(0, 0, -1, -1)); + p->restore(); + return; + } + break; +#endif // QT_CONFIG(rubberband) + case CE_HeaderEmptyArea: + if (option->state & State_Horizontal) + { + themeNumber = QWindowsXPStylePrivate::HeaderTheme; + stateId = HIS_NORMAL; + } + else { + QWindowsStyle::drawControl(CE_HeaderEmptyArea, option, p, widget); + return; + } + break; + default: + break; + } + + XPThemeData theme(widget, p, themeNumber, partId, stateId, rect); + if (!theme.isValid()) { + QWindowsStyle::drawControl(element, option, p, widget); + return; + } + + theme.rotate = rotate; + theme.mirrorHorizontally = hMirrored; + theme.mirrorVertically = vMirrored; + d->drawBackground(theme); +} + +QRect QWindowsXPStylePrivate::scrollBarGripperBounds(QStyle::State flags, const QWidget *widget, XPThemeData *theme) +{ + const bool horizontal = flags & QStyle::State_Horizontal; + const qreal factor = QWindowsStylePrivate::nativeMetricScaleFactor(widget); + const QMargins contentsMargin = + (theme->margins(theme->rect, TMT_SIZINGMARGINS) * factor).toMargins(); + theme->partId = horizontal ? SBP_GRIPPERHORZ : SBP_GRIPPERVERT; + const QSize size = (theme->size() * factor).toSize(); + + const int hSpace = theme->rect.width() - size.width(); + const int vSpace = theme->rect.height() - size.height(); + const bool sufficientSpace = (horizontal && hSpace > (contentsMargin.left() + contentsMargin.right())) + || vSpace > contentsMargin.top() + contentsMargin.bottom(); + return sufficientSpace ? QRect(theme->rect.topLeft() + QPoint(hSpace, vSpace) / 2, size) : QRect(); +} + +#if QT_CONFIG(mdiarea) +// Helper for drawing MDI buttons into the corner widget of QMenuBar in case a +// QMdiSubWindow is maximized. +static void populateMdiButtonTheme(const QStyle *proxy, const QWidget *widget, + const QStyleOptionComplex *option, + QStyle::SubControl subControl, int part, + XPThemeData *theme) +{ + theme->partId = part; + theme->rect = proxy->subControlRect(QStyle::CC_MdiControls, option, subControl, widget); + if (!option->state.testFlag(QStyle::State_Enabled)) + theme->stateId = CBS_INACTIVE; + else if (option->state.testFlag(QStyle::State_Sunken) && option->activeSubControls.testFlag(subControl)) + theme->stateId = CBS_PUSHED; + else if (option->state.testFlag(QStyle::State_MouseOver) && option->activeSubControls.testFlag(subControl)) + theme->stateId = CBS_HOT; + else + theme->stateId = CBS_NORMAL; +} + +// Calculate an small (max 2), empirical correction factor for scaling up +// WP_MDICLOSEBUTTON, WP_MDIRESTOREBUTTON, WP_MDIMINBUTTON, which are too +// small on High DPI screens (QTBUG-75927). +qreal mdiButtonCorrectionFactor(XPThemeData &theme, const QPaintDevice *pd = nullptr) +{ + const auto dpr = pd ? pd->devicePixelRatioF() : qApp->devicePixelRatio(); + const QSizeF nativeSize = QSizeF(theme.size()) / dpr; + const QSizeF requestedSize(theme.rect.size()); + const auto rawFactor = qMin(requestedSize.width() / nativeSize.width(), + requestedSize.height() / nativeSize.height()); + const auto factor = rawFactor >= qreal(2) ? qreal(2) : qreal(1); + return factor; +} +#endif // QT_CONFIG(mdiarea) + +static void populateTitleBarButtonTheme(const QStyle *proxy, const QWidget *widget, + const QStyleOptionComplex *option, + QStyle::SubControl subControl, + bool isTitleBarActive, int part, + XPThemeData *theme) +{ + theme->rect = proxy->subControlRect(QStyle::CC_TitleBar, option, subControl, widget); + theme->partId = part; + if (widget && !widget->isEnabled()) + theme->stateId = RBS_DISABLED; + else if (option->activeSubControls == subControl && option->state.testFlag(QStyle::State_Sunken)) + theme->stateId = RBS_PUSHED; + else if (option->activeSubControls == subControl && option->state.testFlag(QStyle::State_MouseOver)) + theme->stateId = RBS_HOT; + else if (!isTitleBarActive) + theme->stateId = RBS_INACTIVE; + else + theme->stateId = RBS_NORMAL; +} + +/*! + \reimp +*/ +void QWindowsXPStyle::drawComplexControl(ComplexControl cc, const QStyleOptionComplex *option, + QPainter *p, const QWidget *widget) const +{ + QWindowsXPStylePrivate *d = const_cast<QWindowsXPStylePrivate*>(d_func()); + + if (!QWindowsXPStylePrivate::useXP()) { + QWindowsStyle::drawComplexControl(cc, option, p, widget); + return; + } + + State flags = option->state; + SubControls sub = option->subControls; + QRect r = option->rect; + + int partId = 0; + int stateId = 0; + if (widget && widget->testAttribute(Qt::WA_UnderMouse) && widget->isActiveWindow()) + flags |= State_MouseOver; + + switch (cc) { +#if QT_CONFIG(spinbox) + case CC_SpinBox: + if (const QStyleOptionSpinBox *sb = qstyleoption_cast<const QStyleOptionSpinBox *>(option)) + { + XPThemeData theme(widget, p, QWindowsXPStylePrivate::SpinTheme); + + if (sb->frame && (sub & SC_SpinBoxFrame)) { + partId = EP_EDITTEXT; + if (!(flags & State_Enabled)) + stateId = ETS_DISABLED; + else if (flags & State_HasFocus) + stateId = ETS_FOCUSED; + else + stateId = ETS_NORMAL; + + XPThemeData ftheme(widget, p, QWindowsXPStylePrivate::EditTheme, + partId, stateId, r); + ftheme.noContent = true; + d->drawBackground(ftheme); + } + if (sub & SC_SpinBoxUp) { + theme.rect = proxy()->subControlRect(CC_SpinBox, option, SC_SpinBoxUp, widget); + partId = SPNP_UP; + if (!(sb->stepEnabled & QAbstractSpinBox::StepUpEnabled) || !(flags & State_Enabled)) + stateId = UPS_DISABLED; + else if (sb->activeSubControls == SC_SpinBoxUp && (sb->state & State_Sunken)) + stateId = UPS_PRESSED; + else if (sb->activeSubControls == SC_SpinBoxUp && (sb->state & State_MouseOver)) + stateId = UPS_HOT; + else + stateId = UPS_NORMAL; + theme.partId = partId; + theme.stateId = stateId; + d->drawBackground(theme); + } + if (sub & SC_SpinBoxDown) { + theme.rect = proxy()->subControlRect(CC_SpinBox, option, SC_SpinBoxDown, widget); + partId = SPNP_DOWN; + if (!(sb->stepEnabled & QAbstractSpinBox::StepDownEnabled) || !(flags & State_Enabled)) + stateId = DNS_DISABLED; + else if (sb->activeSubControls == SC_SpinBoxDown && (sb->state & State_Sunken)) + stateId = DNS_PRESSED; + else if (sb->activeSubControls == SC_SpinBoxDown && (sb->state & State_MouseOver)) + stateId = DNS_HOT; + else + stateId = DNS_NORMAL; + theme.partId = partId; + theme.stateId = stateId; + d->drawBackground(theme); + } + } + break; +#endif // QT_CONFIG(spinbox) +#if QT_CONFIG(combobox) + case CC_ComboBox: + if (const QStyleOptionComboBox *cmb = qstyleoption_cast<const QStyleOptionComboBox *>(option)) + { + if (sub & SC_ComboBoxEditField) { + if (cmb->frame) { + partId = EP_EDITTEXT; + if (!(flags & State_Enabled)) + stateId = ETS_DISABLED; + else if (flags & State_HasFocus) + stateId = ETS_FOCUSED; + else + stateId = ETS_NORMAL; + XPThemeData theme(widget, p, QWindowsXPStylePrivate::EditTheme, partId, stateId, r); + d->drawBackground(theme); + } else { + QBrush editBrush = cmb->palette.brush(QPalette::Base); + p->fillRect(option->rect, editBrush); + } + if (!cmb->editable) { + QRect re = proxy()->subControlRect(CC_ComboBox, option, SC_ComboBoxEditField, widget); + if (option->state & State_HasFocus) { + p->fillRect(re, option->palette.highlight()); + p->setPen(option->palette.highlightedText().color()); + p->setBackground(option->palette.highlight()); + } else { + p->fillRect(re, option->palette.base()); + p->setPen(option->palette.text().color()); + p->setBackground(option->palette.base()); + } + } + } + + if (sub & SC_ComboBoxArrow) { + XPThemeData theme(widget, p, QWindowsXPStylePrivate::ComboboxTheme); + theme.rect = proxy()->subControlRect(CC_ComboBox, option, SC_ComboBoxArrow, widget); + partId = CP_DROPDOWNBUTTON; + if (!(flags & State_Enabled)) + stateId = CBXS_DISABLED; + else if (cmb->activeSubControls == SC_ComboBoxArrow && (cmb->state & State_Sunken)) + stateId = CBXS_PRESSED; + else if (cmb->activeSubControls == SC_ComboBoxArrow && (cmb->state & State_MouseOver)) + stateId = CBXS_HOT; + else + stateId = CBXS_NORMAL; + theme.partId = partId; + theme.stateId = stateId; + d->drawBackground(theme); + } + } + break; +#endif // QT_CONFIG(combobox) + case CC_ScrollBar: + if (const QStyleOptionSlider *scrollbar = qstyleoption_cast<const QStyleOptionSlider *>(option)) + { + XPThemeData theme(widget, p, QWindowsXPStylePrivate::ScrollBarTheme); + bool maxedOut = (scrollbar->maximum == scrollbar->minimum); + if (maxedOut) + flags &= ~State_Enabled; + + bool isHorz = flags & State_Horizontal; + bool isRTL = option->direction == Qt::RightToLeft; + if (sub & SC_ScrollBarAddLine) { + theme.rect = proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarAddLine, widget); + partId = SBP_ARROWBTN; + if (!(flags & State_Enabled)) + stateId = (isHorz ? (isRTL ? ABS_LEFTDISABLED : ABS_RIGHTDISABLED) : ABS_DOWNDISABLED); + else if (scrollbar->activeSubControls & SC_ScrollBarAddLine && (scrollbar->state & State_Sunken)) + stateId = (isHorz ? (isRTL ? ABS_LEFTPRESSED : ABS_RIGHTPRESSED) : ABS_DOWNPRESSED); + else if (scrollbar->activeSubControls & SC_ScrollBarAddLine && (scrollbar->state & State_MouseOver)) + stateId = (isHorz ? (isRTL ? ABS_LEFTHOT : ABS_RIGHTHOT) : ABS_DOWNHOT); + else + stateId = (isHorz ? (isRTL ? ABS_LEFTNORMAL : ABS_RIGHTNORMAL) : ABS_DOWNNORMAL); + theme.partId = partId; + theme.stateId = stateId; + d->drawBackground(theme); + } + if (sub & SC_ScrollBarSubLine) { + theme.rect = proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarSubLine, widget); + partId = SBP_ARROWBTN; + if (!(flags & State_Enabled)) + stateId = (isHorz ? (isRTL ? ABS_RIGHTDISABLED : ABS_LEFTDISABLED) : ABS_UPDISABLED); + else if (scrollbar->activeSubControls & SC_ScrollBarSubLine && (scrollbar->state & State_Sunken)) + stateId = (isHorz ? (isRTL ? ABS_RIGHTPRESSED : ABS_LEFTPRESSED) : ABS_UPPRESSED); + else if (scrollbar->activeSubControls & SC_ScrollBarSubLine && (scrollbar->state & State_MouseOver)) + stateId = (isHorz ? (isRTL ? ABS_RIGHTHOT : ABS_LEFTHOT) : ABS_UPHOT); + else + stateId = (isHorz ? (isRTL ? ABS_RIGHTNORMAL : ABS_LEFTNORMAL) : ABS_UPNORMAL); + theme.partId = partId; + theme.stateId = stateId; + d->drawBackground(theme); + } + if (maxedOut) { + theme.rect = proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarSlider, widget); + theme.rect = theme.rect.united(proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarSubPage, widget)); + theme.rect = theme.rect.united(proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarAddPage, widget)); + partId = scrollbar->orientation == Qt::Horizontal ? SBP_LOWERTRACKHORZ : SBP_LOWERTRACKVERT; + stateId = SCRBS_DISABLED; + theme.partId = partId; + theme.stateId = stateId; + d->drawBackground(theme); + } else { + if (sub & SC_ScrollBarSubPage) { + theme.rect = proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarSubPage, widget); + partId = flags & State_Horizontal ? SBP_UPPERTRACKHORZ : SBP_UPPERTRACKVERT; + if (!(flags & State_Enabled)) + stateId = SCRBS_DISABLED; + else if (scrollbar->activeSubControls & SC_ScrollBarSubPage && (scrollbar->state & State_Sunken)) + stateId = SCRBS_PRESSED; + else if (scrollbar->activeSubControls & SC_ScrollBarSubPage && (scrollbar->state & State_MouseOver)) + stateId = SCRBS_HOT; + else + stateId = SCRBS_NORMAL; + theme.partId = partId; + theme.stateId = stateId; + d->drawBackground(theme); + } + if (sub & SC_ScrollBarAddPage) { + theme.rect = proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarAddPage, widget); + partId = flags & State_Horizontal ? SBP_LOWERTRACKHORZ : SBP_LOWERTRACKVERT; + if (!(flags & State_Enabled)) + stateId = SCRBS_DISABLED; + else if (scrollbar->activeSubControls & SC_ScrollBarAddPage && (scrollbar->state & State_Sunken)) + stateId = SCRBS_PRESSED; + else if (scrollbar->activeSubControls & SC_ScrollBarAddPage && (scrollbar->state & State_MouseOver)) + stateId = SCRBS_HOT; + else + stateId = SCRBS_NORMAL; + theme.partId = partId; + theme.stateId = stateId; + d->drawBackground(theme); + } + if (sub & SC_ScrollBarSlider) { + theme.rect = proxy()->subControlRect(CC_ScrollBar, option, SC_ScrollBarSlider, widget); + if (!(flags & State_Enabled)) + stateId = SCRBS_DISABLED; + else if (scrollbar->activeSubControls & SC_ScrollBarSlider && (scrollbar->state & State_Sunken)) + stateId = SCRBS_PRESSED; + else if (scrollbar->activeSubControls & SC_ScrollBarSlider && (scrollbar->state & State_MouseOver)) + stateId = SCRBS_HOT; + else + stateId = SCRBS_NORMAL; + + // Draw handle + theme.partId = flags & State_Horizontal ? SBP_THUMBBTNHORZ : SBP_THUMBBTNVERT; + theme.stateId = stateId; + d->drawBackground(theme); + + const QRect gripperBounds = QWindowsXPStylePrivate::scrollBarGripperBounds(flags, widget, &theme); + // Draw gripper if there is enough space + if (!gripperBounds.isEmpty()) { + p->save(); + theme.rect = gripperBounds; + p->setClipRegion(d->region(theme));// Only change inside the region of the gripper + d->drawBackground(theme); // Transparent gripper ontop of background + p->restore(); + } + } + } + } + break; + +#if QT_CONFIG(slider) + case CC_Slider: + if (const QStyleOptionSlider *slider = qstyleoption_cast<const QStyleOptionSlider *>(option)) + { + XPThemeData theme(widget, p, QWindowsXPStylePrivate::TrackBarTheme); + QRect slrect = slider->rect; + QRegion tickreg = slrect; + if (sub & SC_SliderGroove) { + theme.rect = proxy()->subControlRect(CC_Slider, option, SC_SliderGroove, widget); + if (slider->orientation == Qt::Horizontal) { + partId = TKP_TRACK; + stateId = TRS_NORMAL; + theme.rect = QRect(slrect.left(), theme.rect.center().y() - 2, slrect.width(), 4); + } else { + partId = TKP_TRACKVERT; + stateId = TRVS_NORMAL; + theme.rect = QRect(theme.rect.center().x() - 2, slrect.top(), 4, slrect.height()); + } + theme.partId = partId; + theme.stateId = stateId; + d->drawBackground(theme); + tickreg -= theme.rect; + } + if (sub & SC_SliderTickmarks) { + int tickOffset = proxy()->pixelMetric(PM_SliderTickmarkOffset, slider, widget); + int ticks = slider->tickPosition; + int thickness = proxy()->pixelMetric(PM_SliderControlThickness, slider, widget); + int len = proxy()->pixelMetric(PM_SliderLength, slider, widget); + int available = proxy()->pixelMetric(PM_SliderSpaceAvailable, slider, widget); + int interval = slider->tickInterval; + if (interval <= 0) { + interval = slider->singleStep; + if (QStyle::sliderPositionFromValue(slider->minimum, slider->maximum, interval, + available) + - QStyle::sliderPositionFromValue(slider->minimum, slider->maximum, + 0, available) < 3) + interval = slider->pageStep; + } + if (!interval) + interval = 1; + int fudge = len / 2; + int pos; + int bothOffset = (ticks & QSlider::TicksAbove && ticks & QSlider::TicksBelow) ? 1 : 0; + p->setPen(d->sliderTickColor); + QVarLengthArray<QLine, 32> lines; + int v = slider->minimum; + while (v <= slider->maximum + 1) { + if (v == slider->maximum + 1 && interval == 1) + break; + const int v_ = qMin(v, slider->maximum); + int tickLength = (v_ == slider->minimum || v_ >= slider->maximum) ? 4 : 3; + pos = QStyle::sliderPositionFromValue(slider->minimum, slider->maximum, + v_, available) + fudge; + if (slider->orientation == Qt::Horizontal) { + if (ticks & QSlider::TicksAbove) + lines.append(QLine(pos, tickOffset - 1 - bothOffset, + pos, tickOffset - 1 - bothOffset - tickLength)); + + if (ticks & QSlider::TicksBelow) + lines.append(QLine(pos, tickOffset + thickness + bothOffset, + pos, tickOffset + thickness + bothOffset + tickLength)); + } else { + if (ticks & QSlider::TicksAbove) + lines.append(QLine(tickOffset - 1 - bothOffset, pos, + tickOffset - 1 - bothOffset - tickLength, pos)); + + if (ticks & QSlider::TicksBelow) + lines.append(QLine(tickOffset + thickness + bothOffset, pos, + tickOffset + thickness + bothOffset + tickLength, pos)); + } + // in the case where maximum is max int + int nextInterval = v + interval; + if (nextInterval < v) + break; + v = nextInterval; + } + if (!lines.isEmpty()) { + p->save(); + p->translate(slrect.topLeft()); + p->drawLines(lines.constData(), lines.size()); + p->restore(); + } + } + if (sub & SC_SliderHandle) { + theme.rect = proxy()->subControlRect(CC_Slider, option, SC_SliderHandle, widget); + if (slider->orientation == Qt::Horizontal) { + if (slider->tickPosition == QSlider::TicksAbove) + partId = TKP_THUMBTOP; + else if (slider->tickPosition == QSlider::TicksBelow) + partId = TKP_THUMBBOTTOM; + else + partId = TKP_THUMB; + + if (!(slider->state & State_Enabled)) + stateId = TUS_DISABLED; + else if (slider->activeSubControls & SC_SliderHandle && (slider->state & State_Sunken)) + stateId = TUS_PRESSED; + else if (slider->activeSubControls & SC_SliderHandle && (slider->state & State_MouseOver)) + stateId = TUS_HOT; + else if (flags & State_HasFocus) + stateId = TUS_FOCUSED; + else + stateId = TUS_NORMAL; + } else { + if (slider->tickPosition == QSlider::TicksLeft) + partId = TKP_THUMBLEFT; + else if (slider->tickPosition == QSlider::TicksRight) + partId = TKP_THUMBRIGHT; + else + partId = TKP_THUMBVERT; + + if (!(slider->state & State_Enabled)) + stateId = TUVS_DISABLED; + else if (slider->activeSubControls & SC_SliderHandle && (slider->state & State_Sunken)) + stateId = TUVS_PRESSED; + else if (slider->activeSubControls & SC_SliderHandle && (slider->state & State_MouseOver)) + stateId = TUVS_HOT; + else if (flags & State_HasFocus) + stateId = TUVS_FOCUSED; + else + stateId = TUVS_NORMAL; + } + theme.partId = partId; + theme.stateId = stateId; + d->drawBackground(theme); + } + if (slider->state & State_HasFocus) { + QStyleOptionFocusRect fropt; + fropt.QStyleOption::operator=(*slider); + fropt.rect = subElementRect(SE_SliderFocusRect, slider, widget); + proxy()->drawPrimitive(PE_FrameFocusRect, &fropt, p, widget); + } + } + break; +#endif +#if QT_CONFIG(toolbutton) + case CC_ToolButton: + if (const QStyleOptionToolButton *toolbutton + = qstyleoption_cast<const QStyleOptionToolButton *>(option)) { + QRect button, menuarea; + button = proxy()->subControlRect(cc, toolbutton, SC_ToolButton, widget); + menuarea = proxy()->subControlRect(cc, toolbutton, SC_ToolButtonMenu, widget); + + State bflags = toolbutton->state & ~State_Sunken; + State mflags = bflags; + bool autoRaise = flags & State_AutoRaise; + if (autoRaise) { + if (!(bflags & State_MouseOver) || !(bflags & State_Enabled)) { + bflags &= ~State_Raised; + } + } + + if (toolbutton->state & State_Sunken) { + if (toolbutton->activeSubControls & SC_ToolButton) { + bflags |= State_Sunken; + mflags |= State_MouseOver | State_Sunken; + } else if (toolbutton->activeSubControls & SC_ToolButtonMenu) { + mflags |= State_Sunken; + bflags |= State_MouseOver; + } + } + + QStyleOption tool = *toolbutton; + if (toolbutton->subControls & SC_ToolButton) { + if (flags & (State_Sunken | State_On | State_Raised) || !autoRaise) { + if (toolbutton->features & QStyleOptionToolButton::MenuButtonPopup && autoRaise) { + XPThemeData theme(widget, p, QWindowsXPStylePrivate::ToolBarTheme); + theme.partId = TP_SPLITBUTTON; + theme.rect = button; + if (!(bflags & State_Enabled)) + stateId = TS_DISABLED; + else if (bflags & State_Sunken) + stateId = TS_PRESSED; + else if (bflags & State_MouseOver || !(flags & State_AutoRaise)) + stateId = flags & State_On ? TS_HOTCHECKED : TS_HOT; + else if (bflags & State_On) + stateId = TS_CHECKED; + else + stateId = TS_NORMAL; + if (option->direction == Qt::RightToLeft) + theme.mirrorHorizontally = true; + theme.stateId = stateId; + d->drawBackground(theme); + } else { + tool.rect = option->rect; + tool.state = bflags; + if (autoRaise) // for tool bars + proxy()->drawPrimitive(PE_PanelButtonTool, &tool, p, widget); + else + proxy()->drawPrimitive(PE_PanelButtonBevel, &tool, p, widget); + } + } + } + + if (toolbutton->state & State_HasFocus) { + QStyleOptionFocusRect fr; + fr.QStyleOption::operator=(*toolbutton); + fr.rect.adjust(3, 3, -3, -3); + if (toolbutton->features & QStyleOptionToolButton::MenuButtonPopup) + fr.rect.adjust(0, 0, -proxy()->pixelMetric(QStyle::PM_MenuButtonIndicator, + toolbutton, widget), 0); + proxy()->drawPrimitive(PE_FrameFocusRect, &fr, p, widget); + } + QStyleOptionToolButton label = *toolbutton; + label.state = bflags; + int fw = 2; + if (!autoRaise) + label.state &= ~State_Sunken; + label.rect = button.adjusted(fw, fw, -fw, -fw); + proxy()->drawControl(CE_ToolButtonLabel, &label, p, widget); + + if (toolbutton->subControls & SC_ToolButtonMenu) { + tool.rect = menuarea; + tool.state = mflags; + if (autoRaise) { + proxy()->drawPrimitive(PE_IndicatorButtonDropDown, &tool, p, widget); + } else { + tool.state = mflags; + menuarea.adjust(-2, 0, 0, 0); + // Draw menu button + if ((bflags & State_Sunken) != (mflags & State_Sunken)){ + p->save(); + p->setClipRect(menuarea); + tool.rect = option->rect; + proxy()->drawPrimitive(PE_PanelButtonBevel, &tool, p, nullptr); + p->restore(); + } + // Draw arrow + p->save(); + p->setPen(option->palette.dark().color()); + p->drawLine(menuarea.left(), menuarea.top() + 3, + menuarea.left(), menuarea.bottom() - 3); + p->setPen(option->palette.light().color()); + p->drawLine(menuarea.left() - 1, menuarea.top() + 3, + menuarea.left() - 1, menuarea.bottom() - 3); + + tool.rect = menuarea.adjusted(2, 3, -2, -1); + proxy()->drawPrimitive(PE_IndicatorArrowDown, &tool, p, widget); + p->restore(); + } + } else if (toolbutton->features & QStyleOptionToolButton::HasMenu) { + int mbi = proxy()->pixelMetric(PM_MenuButtonIndicator, toolbutton, widget); + QRect ir = toolbutton->rect; + QStyleOptionToolButton newBtn = *toolbutton; + newBtn.rect = QRect(ir.right() + 4 - mbi, ir.height() - mbi + 4, mbi - 5, mbi - 5); + proxy()->drawPrimitive(PE_IndicatorArrowDown, &newBtn, p, widget); + } + } + break; +#endif // QT_CONFIG(toolbutton) + + case CC_TitleBar: + { + if (const QStyleOptionTitleBar *tb = qstyleoption_cast<const QStyleOptionTitleBar *>(option)) + { + const qreal factor = QWindowsStylePrivate::nativeMetricScaleFactor(widget); + bool isActive = tb->titleBarState & QStyle::State_Active; + XPThemeData theme(widget, p, QWindowsXPStylePrivate::WindowTheme); + if (sub & SC_TitleBarLabel) { + + partId = (tb->titleBarState & Qt::WindowMinimized) ? WP_MINCAPTION : WP_CAPTION; + theme.rect = option->rect; + if (widget && !widget->isEnabled()) + stateId = CS_DISABLED; + else if (isActive) + stateId = CS_ACTIVE; + else + stateId = CS_INACTIVE; + + theme.partId = partId; + theme.stateId = stateId; + d->drawBackground(theme); + + QRect ir = proxy()->subControlRect(CC_TitleBar, tb, SC_TitleBarLabel, widget); + + int result = TST_NONE; + GetThemeEnumValue(theme.handle(), WP_CAPTION, isActive ? CS_ACTIVE : CS_INACTIVE, TMT_TEXTSHADOWTYPE, &result); + if (result != TST_NONE) { + COLORREF textShadowRef; + GetThemeColor(theme.handle(), WP_CAPTION, isActive ? CS_ACTIVE : CS_INACTIVE, TMT_TEXTSHADOWCOLOR, &textShadowRef); + QColor textShadow = qRgb(GetRValue(textShadowRef), GetGValue(textShadowRef), GetBValue(textShadowRef)); + p->setPen(textShadow); + p->drawText(int(ir.x() + 3 * factor), int(ir.y() + 2 * factor), + int(ir.width() - 1 * factor), ir.height(), + Qt::AlignLeft | Qt::AlignVCenter | Qt::TextSingleLine, tb->text); + } + COLORREF captionText = GetSysColor(isActive ? COLOR_CAPTIONTEXT : COLOR_INACTIVECAPTIONTEXT); + QColor textColor = qRgb(GetRValue(captionText), GetGValue(captionText), GetBValue(captionText)); + p->setPen(textColor); + p->drawText(int(ir.x() + 2 * factor), int(ir.y() + 1 * factor), + int(ir.width() - 2 * factor), ir.height(), + Qt::AlignLeft | Qt::AlignVCenter | Qt::TextSingleLine, tb->text); + } + if (sub & SC_TitleBarSysMenu && tb->titleBarFlags & Qt::WindowSystemMenuHint) { + theme.rect = proxy()->subControlRect(CC_TitleBar, option, SC_TitleBarSysMenu, widget); + partId = WP_SYSBUTTON; + if ((widget && !widget->isEnabled()) || !isActive) + stateId = SBS_DISABLED; + else if (option->activeSubControls == SC_TitleBarSysMenu && (option->state & State_Sunken)) + stateId = SBS_PUSHED; + else if (option->activeSubControls == SC_TitleBarSysMenu && (option->state & State_MouseOver)) + stateId = SBS_HOT; + else + stateId = SBS_NORMAL; + if (!tb->icon.isNull()) { + tb->icon.paint(p, theme.rect); + } else { + theme.partId = partId; + theme.stateId = stateId; + if (theme.size().isEmpty()) { + int iconSize = proxy()->pixelMetric(PM_SmallIconSize, tb, widget); + QPixmap pm = proxy()->standardIcon(SP_TitleBarMenuButton, tb, widget).pixmap(iconSize, iconSize); + p->save(); + drawItemPixmap(p, theme.rect, Qt::AlignCenter, pm); + p->restore(); + } else { + d->drawBackground(theme); + } + } + } + + if (sub & SC_TitleBarMinButton && tb->titleBarFlags & Qt::WindowMinimizeButtonHint + && !(tb->titleBarState & Qt::WindowMinimized)) { + populateTitleBarButtonTheme(proxy(), widget, option, SC_TitleBarMinButton, isActive, WP_MINBUTTON, &theme); + d->drawBackground(theme); + } + if (sub & SC_TitleBarMaxButton && tb->titleBarFlags & Qt::WindowMaximizeButtonHint + && !(tb->titleBarState & Qt::WindowMaximized)) { + populateTitleBarButtonTheme(proxy(), widget, option, SC_TitleBarMaxButton, isActive, WP_MAXBUTTON, &theme); + d->drawBackground(theme); + } + if (sub & SC_TitleBarContextHelpButton + && tb->titleBarFlags & Qt::WindowContextHelpButtonHint) { + populateTitleBarButtonTheme(proxy(), widget, option, SC_TitleBarContextHelpButton, isActive, WP_HELPBUTTON, &theme); + d->drawBackground(theme); + } + bool drawNormalButton = (sub & SC_TitleBarNormalButton) + && (((tb->titleBarFlags & Qt::WindowMinimizeButtonHint) + && (tb->titleBarState & Qt::WindowMinimized)) + || ((tb->titleBarFlags & Qt::WindowMaximizeButtonHint) + && (tb->titleBarState & Qt::WindowMaximized))); + if (drawNormalButton) { + populateTitleBarButtonTheme(proxy(), widget, option, SC_TitleBarNormalButton, isActive, WP_RESTOREBUTTON, &theme); + d->drawBackground(theme); + } + if (sub & SC_TitleBarShadeButton && tb->titleBarFlags & Qt::WindowShadeButtonHint + && !(tb->titleBarState & Qt::WindowMinimized)) { + populateTitleBarButtonTheme(proxy(), widget, option, SC_TitleBarShadeButton, isActive, WP_MINBUTTON, &theme); + d->drawBackground(theme); + } + if (sub & SC_TitleBarUnshadeButton && tb->titleBarFlags & Qt::WindowShadeButtonHint + && tb->titleBarState & Qt::WindowMinimized) { + populateTitleBarButtonTheme(proxy(), widget, option, SC_TitleBarUnshadeButton, isActive, WP_RESTOREBUTTON, &theme); + d->drawBackground(theme); + } + if (sub & SC_TitleBarCloseButton && tb->titleBarFlags & Qt::WindowSystemMenuHint) { + populateTitleBarButtonTheme(proxy(), widget, option, SC_TitleBarCloseButton, isActive, WP_CLOSEBUTTON, &theme); + d->drawBackground(theme); + } + } + } + break; + +#if QT_CONFIG(mdiarea) + case CC_MdiControls: + { + XPThemeData theme(widget, p, QWindowsXPStylePrivate::WindowTheme, WP_MDICLOSEBUTTON, CBS_NORMAL); + if (Q_UNLIKELY(!theme.isValid())) + return; + + if (option->subControls.testFlag(SC_MdiCloseButton)) { + populateMdiButtonTheme(proxy(), widget, option, SC_MdiCloseButton, WP_MDICLOSEBUTTON, &theme); + d->drawBackground(theme, mdiButtonCorrectionFactor(theme, widget)); + } + if (option->subControls.testFlag(SC_MdiNormalButton)) { + populateMdiButtonTheme(proxy(), widget, option, SC_MdiNormalButton, WP_MDIRESTOREBUTTON, &theme); + d->drawBackground(theme, mdiButtonCorrectionFactor(theme, widget)); + } + if (option->subControls.testFlag(QStyle::SC_MdiMinButton)) { + populateMdiButtonTheme(proxy(), widget, option, SC_MdiMinButton, WP_MDIMINBUTTON, &theme); + d->drawBackground(theme, mdiButtonCorrectionFactor(theme, widget)); + } + } + break; +#endif // QT_CONFIG(mdiarea) +#if QT_CONFIG(dial) + case CC_Dial: + if (const QStyleOptionSlider *dial = qstyleoption_cast<const QStyleOptionSlider *>(option)) + QStyleHelper::drawDial(dial, p); + break; +#endif // QT_CONFIG(dial) + default: + QWindowsStyle::drawComplexControl(cc, option, p, widget); + break; + } +} + +static inline Qt::Orientation progressBarOrientation(const QStyleOption *option = nullptr) +{ + 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, nullptr, QWindowsXPStylePrivate::ButtonTheme, BP_CHECKBOX, CBS_UNCHECKEDNORMAL).width(); + case QStyle::PM_IndicatorHeight: + return XPThemeData::themeSize(widget, nullptr, QWindowsXPStylePrivate::ButtonTheme, BP_CHECKBOX, CBS_UNCHECKEDNORMAL).height(); + case QStyle::PM_ExclusiveIndicatorWidth: + return XPThemeData::themeSize(widget, nullptr, QWindowsXPStylePrivate::ButtonTheme, BP_RADIOBUTTON, RBS_UNCHECKEDNORMAL).width(); + case QStyle::PM_ExclusiveIndicatorHeight: + return XPThemeData::themeSize(widget, nullptr, QWindowsXPStylePrivate::ButtonTheme, BP_RADIOBUTTON, RBS_UNCHECKEDNORMAL).height(); + case QStyle::PM_ProgressBarChunkWidth: + return progressBarOrientation(option) == Qt::Horizontal + ? XPThemeData::themeSize(widget, nullptr, QWindowsXPStylePrivate::ProgressTheme, PP_CHUNK).width() + : XPThemeData::themeSize(widget, nullptr, QWindowsXPStylePrivate::ProgressTheme, PP_CHUNKVERT).height(); + case QStyle::PM_SliderThickness: + return XPThemeData::themeSize(widget, nullptr, QWindowsXPStylePrivate::TrackBarTheme, TKP_THUMB).height(); + case QStyle::PM_TitleBarHeight: + return widget && (widget->windowType() == Qt::Tool) + ? GetSystemMetrics(SM_CYSMCAPTION) + GetSystemMetrics(SM_CXSIZEFRAME) + : GetSystemMetrics(SM_CYCAPTION) + GetSystemMetrics(SM_CXSIZEFRAME); + case QStyle::PM_MdiSubWindowFrameWidth: + return XPThemeData::themeSize(widget, nullptr, QWindowsXPStylePrivate::WindowTheme, WP_FRAMELEFT, FS_ACTIVE).width(); + case QStyle::PM_DockWidgetFrameWidth: + return XPThemeData::themeSize(widget, nullptr, QWindowsXPStylePrivate::WindowTheme, WP_SMALLFRAMERIGHT, FS_ACTIVE).width(); + default: + break; + } + return QWindowsXPStylePrivate::InvalidMetric; +} + +/*! \reimp */ +int QWindowsXPStyle::pixelMetric(PixelMetric pm, const QStyleOption *option, const QWidget *widget) const +{ + if (!QWindowsXPStylePrivate::useXP()) + return QWindowsStyle::pixelMetric(pm, option, widget); + + int res = QWindowsXPStylePrivate::pixelMetricFromSystemDp(pm, option, widget); + if (res != QWindowsStylePrivate::InvalidMetric) + return qRound(qreal(res) * QWindowsStylePrivate::nativeMetricScaleFactor(widget)); + + res = 0; + switch (pm) { + case PM_MenuBarPanelWidth: + case PM_ButtonDefaultIndicator: + res = 0; + break; + + case PM_DefaultFrameWidth: + res = qobject_cast<const QListView*>(widget) ? 2 : 1; + break; + case PM_MenuPanelWidth: + case PM_SpinBoxFrameWidth: + res = 1; + break; + + case PM_TabBarTabOverlap: + case PM_MenuHMargin: + case PM_MenuVMargin: + res = 2; + break; + + case PM_TabBarBaseOverlap: + if (const QStyleOptionTab *tab = qstyleoption_cast<const QStyleOptionTab *>(option)) { + switch (tab->shape) { + case QTabBar::RoundedNorth: + case QTabBar::TriangularNorth: + case QTabBar::RoundedWest: + case QTabBar::TriangularWest: + res = 1; + break; + case QTabBar::RoundedSouth: + case QTabBar::TriangularSouth: + res = 2; + break; + case QTabBar::RoundedEast: + case QTabBar::TriangularEast: + res = 3; + break; + } + } + break; + + case PM_SplitterWidth: + res = QStyleHelper::dpiScaled(5., option); + break; + + case PM_MdiSubWindowMinimizedWidth: + res = 160; + break; + +#if QT_CONFIG(toolbar) + case PM_ToolBarHandleExtent: + res = int(QStyleHelper::dpiScaled(8., option)); + break; + +#endif // QT_CONFIG(toolbar) + case PM_DockWidgetSeparatorExtent: + case PM_DockWidgetTitleMargin: + res = int(QStyleHelper::dpiScaled(4., option)); + break; + + case PM_ButtonShiftHorizontal: + case PM_ButtonShiftVertical: + res = qstyleoption_cast<const QStyleOptionToolButton *>(option) ? 1 : 0; + break; + + default: + res = QWindowsStyle::pixelMetric(pm, option, widget); + } + + return res; +} + +/* + This function is used by subControlRect to check if a button + should be drawn for the given subControl given a set of window flags. +*/ +static bool buttonVisible(const QStyle::SubControl sc, const QStyleOptionTitleBar *tb){ + + bool isMinimized = tb->titleBarState & Qt::WindowMinimized; + bool isMaximized = tb->titleBarState & Qt::WindowMaximized; + const uint flags = tb->titleBarFlags; + bool retVal = false; + switch (sc) { + case QStyle::SC_TitleBarContextHelpButton: + if (flags & Qt::WindowContextHelpButtonHint) + retVal = true; + break; + case QStyle::SC_TitleBarMinButton: + if (!isMinimized && (flags & Qt::WindowMinimizeButtonHint)) + retVal = true; + break; + case QStyle::SC_TitleBarNormalButton: + if (isMinimized && (flags & Qt::WindowMinimizeButtonHint)) + retVal = true; + else if (isMaximized && (flags & Qt::WindowMaximizeButtonHint)) + retVal = true; + break; + case QStyle::SC_TitleBarMaxButton: + if (!isMaximized && (flags & Qt::WindowMaximizeButtonHint)) + retVal = true; + break; + case QStyle::SC_TitleBarShadeButton: + if (!isMinimized && flags & Qt::WindowShadeButtonHint) + retVal = true; + break; + case QStyle::SC_TitleBarUnshadeButton: + if (isMinimized && flags & Qt::WindowShadeButtonHint) + retVal = true; + break; + case QStyle::SC_TitleBarCloseButton: + if (flags & Qt::WindowSystemMenuHint) + retVal = true; + break; + case QStyle::SC_TitleBarSysMenu: + if (flags & Qt::WindowSystemMenuHint) + retVal = true; + break; + default : + retVal = true; + } + return retVal; +} + +/*! + \reimp +*/ +QRect QWindowsXPStyle::subControlRect(ComplexControl cc, const QStyleOptionComplex *option, + SubControl subControl, const QWidget *widget) const +{ + if (!QWindowsXPStylePrivate::useXP()) + return QWindowsStyle::subControlRect(cc, option, subControl, widget); + + QRect rect; + + switch (cc) { + case CC_TitleBar: + if (const QStyleOptionTitleBar *tb = qstyleoption_cast<const QStyleOptionTitleBar *>(option)) { + if (!buttonVisible(subControl, tb)) + return rect; + const bool isToolTitle = false; + const int height = tb->rect.height(); + const int width = tb->rect.width(); + const int buttonMargin = int(QStyleHelper::dpiScaled(4, option)); + const qreal factor = QWindowsStylePrivate::nativeMetricScaleFactor(widget); + int buttonHeight = qRound(qreal(GetSystemMetrics(SM_CYSIZE)) * factor) + - buttonMargin; + int buttonWidth = qRound(qreal(GetSystemMetrics(SM_CXSIZE)) * factor) + - buttonMargin; + const int delta = buttonWidth + 2; + int controlTop = option->rect.bottom() - buttonHeight - 2; + const int frameWidth = proxy()->pixelMetric(PM_MdiSubWindowFrameWidth, option, widget); + const bool sysmenuHint = (tb->titleBarFlags & Qt::WindowSystemMenuHint) != 0; + const bool minimizeHint = (tb->titleBarFlags & Qt::WindowMinimizeButtonHint) != 0; + const bool maximizeHint = (tb->titleBarFlags & Qt::WindowMaximizeButtonHint) != 0; + const bool contextHint = (tb->titleBarFlags & Qt::WindowContextHelpButtonHint) != 0; + const bool shadeHint = (tb->titleBarFlags & Qt::WindowShadeButtonHint) != 0; + bool isMinimized = tb->titleBarState & Qt::WindowMinimized; + bool isMaximized = tb->titleBarState & Qt::WindowMaximized; + int offset = 0; + + switch (subControl) { + case SC_TitleBarLabel: + rect = QRect(frameWidth, 0, width - (buttonWidth + frameWidth + 10), height); + if (isToolTitle) { + if (sysmenuHint) { + rect.adjust(0, 0, -buttonWidth - 3, 0); + } + if (minimizeHint || maximizeHint) + rect.adjust(0, 0, -buttonWidth - 2, 0); + } else { + if (sysmenuHint) { + const int leftOffset = height - 8; + rect.adjust(leftOffset, 0, 0, 0); + } + if (minimizeHint) + rect.adjust(0, 0, -buttonWidth - 2, 0); + if (maximizeHint) + rect.adjust(0, 0, -buttonWidth - 2, 0); + if (contextHint) + rect.adjust(0, 0, -buttonWidth - 2, 0); + if (shadeHint) + rect.adjust(0, 0, -buttonWidth - 2, 0); + } + break; + + case SC_TitleBarContextHelpButton: + if (tb->titleBarFlags & Qt::WindowContextHelpButtonHint) + offset += delta; + Q_FALLTHROUGH(); + case SC_TitleBarMinButton: + if (!isMinimized && (tb->titleBarFlags & Qt::WindowMinimizeButtonHint)) + offset += delta; + else if (subControl == SC_TitleBarMinButton) + break; + Q_FALLTHROUGH(); + case SC_TitleBarNormalButton: + if (isMinimized && (tb->titleBarFlags & Qt::WindowMinimizeButtonHint)) + offset += delta; + else if (isMaximized && (tb->titleBarFlags & Qt::WindowMaximizeButtonHint)) + offset += delta; + else if (subControl == SC_TitleBarNormalButton) + break; + Q_FALLTHROUGH(); + case SC_TitleBarMaxButton: + if (!isMaximized && (tb->titleBarFlags & Qt::WindowMaximizeButtonHint)) + offset += delta; + else if (subControl == SC_TitleBarMaxButton) + break; + Q_FALLTHROUGH(); + case SC_TitleBarShadeButton: + if (!isMinimized && (tb->titleBarFlags & Qt::WindowShadeButtonHint)) + offset += delta; + else if (subControl == SC_TitleBarShadeButton) + break; + Q_FALLTHROUGH(); + case SC_TitleBarUnshadeButton: + if (isMinimized && (tb->titleBarFlags & Qt::WindowShadeButtonHint)) + offset += delta; + else if (subControl == SC_TitleBarUnshadeButton) + break; + Q_FALLTHROUGH(); + case SC_TitleBarCloseButton: + if (tb->titleBarFlags & Qt::WindowSystemMenuHint) + offset += delta; + else if (subControl == SC_TitleBarCloseButton) + break; + + rect.setRect(width - offset - controlTop + 1, controlTop, + buttonWidth, buttonHeight); + break; + + case SC_TitleBarSysMenu: + { + const int controlTop = 6; + const int controlHeight = height - controlTop - 3; + const int iconExtent = proxy()->pixelMetric(PM_SmallIconSize, option); + QSize iconSize = tb->icon.actualSize(QSize(iconExtent, iconExtent)); + if (tb->icon.isNull()) + iconSize = QSize(controlHeight, controlHeight); + int hPad = (controlHeight - iconSize.height())/2; + int vPad = (controlHeight - iconSize.width())/2; + rect = QRect(frameWidth + hPad, controlTop + vPad, iconSize.width(), iconSize.height()); + } + break; + default: + break; + } + } + break; + + case CC_ComboBox: + if (const QStyleOptionComboBox *cmb = qstyleoption_cast<const QStyleOptionComboBox *>(option)) { + const int x = cmb->rect.x(), y = cmb->rect.y(), wi = cmb->rect.width(), he = cmb->rect.height(); + const int xpos = x + wi - qRound(QStyleHelper::dpiScaled(1 + 16, option)); + + switch (subControl) { + case SC_ComboBoxFrame: + rect = cmb->rect; + break; + + case SC_ComboBoxArrow: { + const qreal dpi = QStyleHelper::dpi(option); + rect = QRect(xpos, y + qRound(QStyleHelper::dpiScaled(1, dpi)), + qRound(QStyleHelper::dpiScaled(16, dpi)), + he - qRound(QStyleHelper::dpiScaled(2, dpi))); + } + break; + + case SC_ComboBoxEditField: { + const qreal dpi = QStyleHelper::dpi(option); + const int frame = qRound(QStyleHelper::dpiScaled(2, dpi)); + rect = QRect(x + frame, y + frame, + wi - qRound(QStyleHelper::dpiScaled(3 + 16, dpi)), + he - qRound(QStyleHelper::dpiScaled(4, dpi))); + } + break; + + case SC_ComboBoxListBoxPopup: + rect = cmb->rect; + break; + + default: + break; + } + } + break; +#if QT_CONFIG(mdiarea) + case CC_MdiControls: + { + int numSubControls = 0; + if (option->subControls & SC_MdiCloseButton) + ++numSubControls; + if (option->subControls & SC_MdiMinButton) + ++numSubControls; + if (option->subControls & SC_MdiNormalButton) + ++numSubControls; + if (numSubControls == 0) + break; + + int buttonWidth = option->rect.width() / numSubControls; + int offset = 0; + switch (subControl) { + case SC_MdiCloseButton: + // Only one sub control, no offset needed. + if (numSubControls == 1) + break; + offset += buttonWidth; + Q_FALLTHROUGH(); + case SC_MdiNormalButton: + // No offset needed if + // 1) There's only one sub control + // 2) We have a close button and a normal button (offset already added in SC_MdiClose) + if (numSubControls == 1 || (numSubControls == 2 && !(option->subControls & SC_MdiMinButton))) + break; + if (option->subControls & SC_MdiNormalButton) + offset += buttonWidth; + break; + default: + break; + } + rect = QRect(offset, 0, buttonWidth, option->rect.height()); + break; + } +#endif // QT_CONFIG(mdiarea) + + default: + rect = visualRect(option->direction, option->rect, + QWindowsStyle::subControlRect(cc, option, subControl, widget)); + break; + } + return visualRect(option->direction, option->rect, rect); +} + +/*! + \reimp +*/ +QSize QWindowsXPStyle::sizeFromContents(ContentsType ct, const QStyleOption *option, + const QSize &contentsSize, const QWidget *widget) const +{ + if (!QWindowsXPStylePrivate::useXP()) + return QWindowsStyle::sizeFromContents(ct, option, contentsSize, widget); + + QSize sz(contentsSize); + switch (ct) { + case CT_LineEdit: + case CT_ComboBox: + { + XPThemeData buttontheme(widget, nullptr, QWindowsXPStylePrivate::ButtonTheme, BP_PUSHBUTTON, PBS_NORMAL); + if (buttontheme.isValid()) { + const qreal factor = QWindowsXPStylePrivate::nativeMetricScaleFactor(widget); + const QMarginsF borderSize = buttontheme.margins() * factor; + if (!borderSize.isNull()) { + const qreal margin = qreal(2) * factor; + sz.rwidth() += qRound(borderSize.left() + borderSize.right() - margin); + sz.rheight() += int(borderSize.bottom() + borderSize.top() - margin + + qreal(1) / factor - 1); + } + const int textMargins = 2*(proxy()->pixelMetric(PM_FocusFrameHMargin, option) + 1); + sz += QSize(qMax(pixelMetric(QStyle::PM_ScrollBarExtent, option, widget) + + textMargins, 23), 0); //arrow button + } + } + break; + case CT_SpinBox: + { + //Spinbox adds frame twice + sz = QWindowsStyle::sizeFromContents(ct, option, contentsSize, widget); + int border = proxy()->pixelMetric(PM_SpinBoxFrameWidth, option, widget); + sz -= QSize(2*border, 2*border); + } + break; + case CT_TabWidget: + sz += QSize(6, 6); + break; + case CT_Menu: + sz += QSize(1, 0); + break; +#if QT_CONFIG(menubar) + case CT_MenuBarItem: + if (!sz.isEmpty()) + sz += QSize(windowsItemHMargin * 5 + 1, 6); + break; +#endif + case CT_MenuItem: + if (const QStyleOptionMenuItem *menuitem = qstyleoption_cast<const QStyleOptionMenuItem *>(option)) + { + if (menuitem->menuItemType != QStyleOptionMenuItem::Separator) { + sz = QWindowsStyle::sizeFromContents(ct, option, sz, widget); + sz.setHeight(sz.height() - 2); + return sz; + } + } + sz = QWindowsStyle::sizeFromContents(ct, option, sz, widget); + break; + + case CT_MdiControls: { + sz.setHeight(int(QStyleHelper::dpiScaled(19, option))); + int width = 54; + if (const QStyleOptionComplex *styleOpt = qstyleoption_cast<const QStyleOptionComplex *>(option)) { + width = 0; + if (styleOpt->subControls & SC_MdiMinButton) + width += 17 + 1; + if (styleOpt->subControls & SC_MdiNormalButton) + width += 17 + 1; + if (styleOpt->subControls & SC_MdiCloseButton) + width += 17 + 1; + } + sz.setWidth(int(QStyleHelper::dpiScaled(width, option))); + } + break; + + default: + sz = QWindowsStyle::sizeFromContents(ct, option, sz, widget); + break; + } + + return sz; +} + + +/*! \reimp */ +int QWindowsXPStyle::styleHint(StyleHint hint, const QStyleOption *option, const QWidget *widget, + QStyleHintReturn *returnData) const +{ + QWindowsXPStylePrivate *d = const_cast<QWindowsXPStylePrivate*>(d_func()); + if (!QWindowsXPStylePrivate::useXP()) + return QWindowsStyle::styleHint(hint, option, widget, returnData); + + int res = 0; + switch (hint) { + + case SH_EtchDisabledText: + res = (qobject_cast<const QLabel*>(widget) != 0); + break; + + case SH_SpinControls_DisableOnBounds: + res = 0; + break; + + case SH_TitleBar_AutoRaise: + case SH_TitleBar_NoBorder: + res = 1; + break; + + case SH_GroupBox_TextLabelColor: + if (!widget || (widget && widget->isEnabled())) + res = d->groupBoxTextColor; + else + res = d->groupBoxTextColorDisabled; + break; + + case SH_Table_GridLineColor: + res = 0xC0C0C0; + break; + + case SH_WindowFrame_Mask: + { + res = 1; + QStyleHintReturnMask *mask = qstyleoption_cast<QStyleHintReturnMask *>(returnData); + const QStyleOptionTitleBar *titlebar = qstyleoption_cast<const QStyleOptionTitleBar *>(option); + if (mask && titlebar) { + // Note certain themes will not return the whole window frame but only the titlebar part when + // queried This function needs to return the entire window mask, hence we will only fetch the mask for the + // titlebar itself and add the remaining part of the window rect at the bottom. + int tbHeight = proxy()->pixelMetric(PM_TitleBarHeight, option, widget); + QRect titleBarRect = option->rect; + titleBarRect.setHeight(tbHeight); + XPThemeData themeData; + if (titlebar->titleBarState & Qt::WindowMinimized) { + themeData = XPThemeData(widget, nullptr, + QWindowsXPStylePrivate::WindowTheme, + WP_MINCAPTION, CS_ACTIVE, titleBarRect); + } else + themeData = XPThemeData(widget, nullptr, + QWindowsXPStylePrivate::WindowTheme, + WP_CAPTION, CS_ACTIVE, titleBarRect); + mask->region = d->region(themeData) + + QRect(0, tbHeight, option->rect.width(), option->rect.height() - tbHeight); + } + } + break; +#if QT_CONFIG(rubberband) + case SH_RubberBand_Mask: + if (qstyleoption_cast<const QStyleOptionRubberBand *>(option)) + res = 0; + break; +#endif // QT_CONFIG(rubberband) + + case SH_ItemView_DrawDelegateFrame: + res = 1; + break; + + default: + res =QWindowsStyle::styleHint(hint, option, widget, returnData); + } + + return res; +} + +/*! \reimp */ +QPalette QWindowsXPStyle::standardPalette() const +{ + return QWindowsXPStylePrivate::useXP() ? QPalette() : QWindowsStyle::standardPalette(); +} + +/*! + \reimp +*/ +QPixmap QWindowsXPStyle::standardPixmap(StandardPixmap standardPixmap, const QStyleOption *option, + const QWidget *widget) const +{ + if (!QWindowsXPStylePrivate::useXP()) + return QWindowsStyle::standardPixmap(standardPixmap, option, widget); + + switch (standardPixmap) { + case SP_TitleBarMaxButton: + case SP_TitleBarCloseButton: + if (qstyleoption_cast<const QStyleOptionDockWidget *>(option)) + { + if (widget && widget->isWindow()) { + XPThemeData theme(widget, nullptr, QWindowsXPStylePrivate::WindowTheme, WP_SMALLCLOSEBUTTON, CBS_NORMAL); + if (theme.isValid()) { + const QSize size = (theme.size() * QWindowsStylePrivate::nativeMetricScaleFactor(widget)).toSize(); + return QIcon(QWindowsStyle::standardPixmap(standardPixmap, option, widget)).pixmap(size); + } + } + } + break; + default: + break; + } + return QWindowsStyle::standardPixmap(standardPixmap, option, widget); +} + +/*! + \reimp +*/ +QIcon QWindowsXPStyle::standardIcon(StandardPixmap standardIcon, + const QStyleOption *option, + const QWidget *widget) const +{ + if (!QWindowsXPStylePrivate::useXP()) { + return QWindowsStyle::standardIcon(standardIcon, option, widget); + } + + QWindowsXPStylePrivate *d = const_cast<QWindowsXPStylePrivate*>(d_func()); + switch (standardIcon) { + case SP_TitleBarMaxButton: + if (qstyleoption_cast<const QStyleOptionDockWidget *>(option)) + { + if (d->dockFloat.isNull()) { + XPThemeData themeSize(nullptr, nullptr, QWindowsXPStylePrivate::WindowTheme, + WP_SMALLCLOSEBUTTON, CBS_NORMAL); + XPThemeData theme(nullptr, nullptr, QWindowsXPStylePrivate::WindowTheme, + WP_MAXBUTTON, MAXBS_NORMAL); + if (theme.isValid()) { + const QSize size = (themeSize.size() * QWindowsStylePrivate::nativeMetricScaleFactor(widget)).toSize(); + QPixmap pm(size); + pm.fill(Qt::transparent); + QPainter p(&pm); + theme.painter = &p; + theme.rect = QRect(QPoint(0, 0), size); + d->drawBackground(theme); + d->dockFloat.addPixmap(pm, QIcon::Normal, QIcon::Off); // Normal + pm.fill(Qt::transparent); + theme.stateId = MAXBS_PUSHED; + d->drawBackground(theme); + d->dockFloat.addPixmap(pm, QIcon::Normal, QIcon::On); // Pressed + pm.fill(Qt::transparent); + theme.stateId = MAXBS_HOT; + d->drawBackground(theme); + d->dockFloat.addPixmap(pm, QIcon::Active, QIcon::Off); // Hover + pm.fill(Qt::transparent); + theme.stateId = MAXBS_INACTIVE; + d->drawBackground(theme); + d->dockFloat.addPixmap(pm, QIcon::Disabled, QIcon::Off); // Disabled + } + } + if (widget && widget->isWindow()) + return d->dockFloat; + + } + break; + case SP_TitleBarCloseButton: + if (qstyleoption_cast<const QStyleOptionDockWidget *>(option)) + { + if (d->dockClose.isNull()) { + XPThemeData theme(nullptr, nullptr, QWindowsXPStylePrivate::WindowTheme, + WP_SMALLCLOSEBUTTON, CBS_NORMAL); + if (theme.isValid()) { + const QSize size = (theme.size() * QWindowsStylePrivate::nativeMetricScaleFactor(widget)).toSize(); + QPixmap pm(size); + pm.fill(Qt::transparent); + QPainter p(&pm); + theme.painter = &p; + theme.partId = WP_CLOSEBUTTON; // #### + theme.rect = QRect(QPoint(0, 0), size); + d->drawBackground(theme); + d->dockClose.addPixmap(pm, QIcon::Normal, QIcon::Off); // Normal + pm.fill(Qt::transparent); + theme.stateId = CBS_PUSHED; + d->drawBackground(theme); + d->dockClose.addPixmap(pm, QIcon::Normal, QIcon::On); // Pressed + pm.fill(Qt::transparent); + theme.stateId = CBS_HOT; + d->drawBackground(theme); + d->dockClose.addPixmap(pm, QIcon::Active, QIcon::Off); // Hover + pm.fill(Qt::transparent); + theme.stateId = CBS_INACTIVE; + d->drawBackground(theme); + d->dockClose.addPixmap(pm, QIcon::Disabled, QIcon::Off); // Disabled + } + } + if (widget && widget->isWindow()) + return d->dockClose; + } + break; + case SP_TitleBarNormalButton: + if (qstyleoption_cast<const QStyleOptionDockWidget *>(option)) + { + if (d->dockFloat.isNull()) { + XPThemeData themeSize(nullptr, nullptr, QWindowsXPStylePrivate::WindowTheme, + WP_SMALLCLOSEBUTTON, CBS_NORMAL); + XPThemeData theme(nullptr, nullptr, QWindowsXPStylePrivate::WindowTheme, + WP_RESTOREBUTTON, RBS_NORMAL); + if (theme.isValid()) { + const QSize size = (themeSize.size() * QWindowsStylePrivate::nativeMetricScaleFactor(widget)).toSize(); + QPixmap pm(size); + pm.fill(Qt::transparent); + QPainter p(&pm); + theme.painter = &p; + theme.rect = QRect(QPoint(0, 0), size); + d->drawBackground(theme); + d->dockFloat.addPixmap(pm, QIcon::Normal, QIcon::Off); // Normal + pm.fill(Qt::transparent); + theme.stateId = RBS_PUSHED; + d->drawBackground(theme); + d->dockFloat.addPixmap(pm, QIcon::Normal, QIcon::On); // Pressed + pm.fill(Qt::transparent); + theme.stateId = RBS_HOT; + d->drawBackground(theme); + d->dockFloat.addPixmap(pm, QIcon::Active, QIcon::Off); // Hover + pm.fill(Qt::transparent); + theme.stateId = RBS_INACTIVE; + d->drawBackground(theme); + d->dockFloat.addPixmap(pm, QIcon::Disabled, QIcon::Off); // Disabled + } + } + if (widget && widget->isWindow()) + return d->dockFloat; + + } + break; + default: + break; + } + + return QWindowsStyle::standardIcon(standardIcon, option, widget); +} + +/*! + \internal + + Constructs a QWindowsXPStyle object. +*/ +QWindowsXPStyle::QWindowsXPStyle(QWindowsXPStylePrivate &dd) : QWindowsStyle(dd) +{ +} + +#ifndef QT_NO_DEBUG_STREAM +QDebug operator<<(QDebug d, const XPThemeData &t) +{ + QDebugStateSaver saver(d); + d.nospace(); + d << "XPThemeData(" << t.widget << ", theme=#" << t.theme << ", " << t.htheme + << ", partId=" << t.partId << ", stateId=" << t.stateId << ", rect=" << t.rect + << ", mirrorHorizontally=" << t.mirrorHorizontally << ", mirrorVertically=" + << t.mirrorVertically << ", noBorder=" << t.noBorder << ", noContent=" << t.noContent + << ", rotate=" << t.rotate << ')'; + return d; +} + +QDebug operator<<(QDebug d, const ThemeMapKey &k) +{ + QDebugStateSaver saver(d); + d.nospace(); + d << "ThemeMapKey(theme=#" << k.theme + << ", partId=" << k.partId << ", stateId=" << k.stateId + << ", noBorder=" << k.noBorder << ", noContent=" << k.noContent << ')'; + return d; +} + +QDebug operator<<(QDebug d, const ThemeMapData &td) +{ + QDebugStateSaver saver(d); + d.nospace(); + d << "ThemeMapData(alphaType=" << td.alphaType + << ", dataValid=" << td.dataValid << ", partIsTransparent=" << td.partIsTransparent + << ", hasAlphaChannel=" << td.hasAlphaChannel << ", wasAlphaSwapped=" << td.wasAlphaSwapped + << ", hadInvalidAlpha=" << td.hadInvalidAlpha << ')'; + return d; +} +#endif // QT_NO_DEBUG_STREAM + +// Debugging code ---------------------------------------------------------------------[ START ]--- +// The code for this point on is not compiled by default, but only used as assisting +// debugging code when you uncomment the DEBUG_XP_STYLE define at the top of the file. + +#ifdef DEBUG_XP_STYLE +// The schema file expects these to be defined by the user. +#define TMT_ENUMDEF 8 +#define TMT_ENUMVAL TEXT('A') +#define TMT_ENUM TEXT('B') +#define SCHEMA_STRINGS // For 2nd pass on schema file +QT_BEGIN_INCLUDE_NAMESPACE +#include <tmschema.h> +QT_END_INCLUDE_NAMESPACE + +// A property's value, type and name combo +struct PropPair { + int propValue; + int propType; + LPCWSTR propName; +}; + +// Operator for sorting of PropPairs +bool operator<(PropPair a, PropPair b) { + return wcscmp(a.propName, b.propName) < 0; +} + +// Our list of all possible properties +static QList<PropPair> all_props; + + +/*! \internal + Dumps a portion of the full native DIB section double buffer. + The DIB section double buffer is only used when doing special + transformations to the theme part, or when the real double + buffer in the paintengine does not have an HDC we may use + directly. + Since we cannot rely on the pixel data we get from Microsoft + when drawing into the DIB section, we use this function to + see the actual data we got, and can determin the appropriate + action. +*/ +void QWindowsXPStylePrivate::dumpNativeDIB(int w, int h) +{ + if (w && h) { + static int pCount = 0; + DWORD *bufPix = (DWORD*)bufferPixels; + + char *bufferDump = new char[bufferH * bufferW * 16]; + char *bufferPos = bufferDump; + + memset(bufferDump, 0, sizeof(bufferDump)); + bufferPos += sprintf(bufferPos, "const int pixelBufferW%d = %d;\n", pCount, w); + bufferPos += sprintf(bufferPos, "const int pixelBufferH%d = %d;\n", pCount, h); + bufferPos += sprintf(bufferPos, "const unsigned DWORD pixelBuffer%d[] = {", pCount); + for (int iy = 0; iy < h; ++iy) { + bufferPos += sprintf(bufferPos, "\n "); + bufPix = (DWORD*)(bufferPixels + (iy * bufferW * 4)); + for (int ix = 0; ix < w; ++ix) { + bufferPos += sprintf(bufferPos, "0x%08x, ", *bufPix); + ++bufPix; + } + } + bufferPos += sprintf(bufferPos, "\n};\n\n"); + printf(bufferDump); + + delete[] bufferDump; + ++pCount; + } +} + +/*! \internal + Shows the value of a given property for a part. +*/ +static void showProperty(XPThemeData &themeData, const PropPair &prop) +{ + PROPERTYORIGIN origin = PO_NOTFOUND; + GetThemePropertyOrigin(themeData.handle(), themeData.partId, themeData.stateId, prop.propValue, &origin); + const char *originStr; + switch (origin) { + case PO_STATE: + originStr = "State "; + break; + case PO_PART: + originStr = "Part "; + break; + case PO_CLASS: + originStr = "Class "; + break; + case PO_GLOBAL: + originStr = "Globl "; + break; + case PO_NOTFOUND: + default: + originStr = "Unkwn "; + break; + } + + switch (prop.propType) { + case TMT_STRING: + { + wchar_t buffer[512]; + GetThemeString(themeData.handle(), themeData.partId, themeData.stateId, prop.propValue, buffer, 512); + printf(" (%sString) %-20S: %S\n", originStr, prop.propName, buffer); + } + break; + case TMT_ENUM: + { + int result = -1; + GetThemeEnumValue(themeData.handle(), themeData.partId, themeData.stateId, prop.propValue, &result); + printf(" (%sEnum) %-20S: %d\n", originStr, prop.propName, result); + } + break; + case TMT_INT: + { + int result = -1; + GetThemeInt(themeData.handle(), themeData.partId, themeData.stateId, prop.propValue, &result); + printf(" (%sint) %-20S: %d\n", originStr, prop.propName, result); + } + break; + case TMT_BOOL: + { + BOOL result = false; + GetThemeBool(themeData.handle(), themeData.partId, themeData.stateId, prop.propValue, &result); + printf(" (%sbool) %-20S: %d\n", originStr, prop.propName, result); + } + break; + case TMT_COLOR: + { + COLORREF result = 0; + GetThemeColor(themeData.handle(), themeData.partId, themeData.stateId, prop.propValue, &result); + printf(" (%scolor) %-20S: 0x%08X\n", originStr, prop.propName, result); + } + break; + case TMT_MARGINS: + { + MARGINS result; + memset(&result, 0, sizeof(result)); + GetThemeMargins(themeData.handle(), 0, themeData.partId, themeData.stateId, prop.propValue, 0, &result); + printf(" (%smargins) %-20S: (%d, %d, %d, %d)\n", originStr, + prop.propName, result.cxLeftWidth, result.cyTopHeight, result.cxRightWidth, result.cyBottomHeight); + } + break; + case TMT_FILENAME: + { + wchar_t buffer[512]; + GetThemeFilename(themeData.handle(), themeData.partId, themeData.stateId, prop.propValue, buffer, 512); + printf(" (%sfilename)%-20S: %S\n", originStr, prop.propName, buffer); + } + break; + case TMT_SIZE: + { + SIZE result1; + SIZE result2; + SIZE result3; + memset(&result1, 0, sizeof(result1)); + memset(&result2, 0, sizeof(result2)); + memset(&result3, 0, sizeof(result3)); + GetThemePartSize(themeData.handle(), 0, themeData.partId, themeData.stateId, 0, TS_MIN, &result1); + GetThemePartSize(themeData.handle(), 0, themeData.partId, themeData.stateId, 0, TS_TRUE, &result2); + GetThemePartSize(themeData.handle(), 0, themeData.partId, themeData.stateId, 0, TS_DRAW, &result3); + printf(" (%ssize) %-20S: Min (%d, %d), True(%d, %d), Draw(%d, %d)\n", originStr, prop.propName, + result1.cx, result1.cy, result2.cx, result2.cy, result3.cx, result3.cy); + } + break; + case TMT_POSITION: + { + POINT result; + memset(&result, 0, sizeof(result)); + GetThemePosition(themeData.handle(), themeData.partId, themeData.stateId, prop.propValue, &result); + printf(" (%sPosition)%-20S: (%d, %d)\n", originStr, prop.propName, result.x, result.y); + } + break; + case TMT_RECT: + { + RECT result; + memset(&result, 0, sizeof(result)); + GetThemeRect(themeData.handle(), themeData.partId, themeData.stateId, prop.propValue, &result); + printf(" (%sRect) %-20S: (%d, %d, %d, %d)\n", originStr, prop.propName, result.left, result.top, result.right, result.bottom); + } + break; + case TMT_FONT: + { + LOGFONT result; + memset(&result, 0, sizeof(result)); + GetThemeFont(themeData.handle(), 0, themeData.partId, themeData.stateId, prop.propValue, &result); + printf(" (%sFont) %-20S: %S height(%d) width(%d) weight(%d)\n", originStr, prop.propName, + result.lfFaceName, result.lfHeight, result.lfWidth, result.lfWeight); + } + break; + case TMT_INTLIST: + { + INTLIST result; + memset(&result, 0, sizeof(result)); + GetThemeIntList(themeData.handle(), themeData.partId, themeData.stateId, prop.propValue, &result); + printf(" (%sInt list)%-20S: { ", originStr, prop.propName); + for (int i = 0; i < result.iValueCount; ++i) + printf("%d ", result.iValues[i]); + printf("}\n"); + } + break; + default: + printf(" %s%S : Unknown property type (%d)!\n", originStr, prop.propName, prop.propType); + } +} + +/*! \internal + Dump all valid properties for a part. + If it's the first time this function is called, then the name, + enum value and documentation of all properties are shown, as + well as all global properties. +*/ +void QWindowsXPStylePrivate::showProperties(XPThemeData &themeData) +{ + if (!all_props.count()) { + const TMSCHEMAINFO *infoTable = GetSchemaInfo(); + for (int i = 0; i < infoTable->iPropCount; ++i) { + int propType = infoTable->pPropTable[i].bPrimVal; + int propValue = infoTable->pPropTable[i].sEnumVal; + LPCWSTR propName = infoTable->pPropTable[i].pszName; + + switch (propType) { + case TMT_ENUMDEF: + case TMT_ENUMVAL: + continue; + default: + if (propType != propValue) { + PropPair prop; + prop.propValue = propValue; + prop.propName = propName; + prop.propType = propType; + all_props.append(prop); + } + } + } + std::sort(all_props.begin(), all_props.end()); + + {// List all properties + printf("part properties count = %d:\n", all_props.count()); + printf(" Enum Property Name Description\n"); + printf("-----------------------------------------------------------\n"); + wchar_t themeName[256]; + pGetCurrentThemeName(themeName, 256, 0, 0, 0, 0); + for (int j = 0; j < all_props.count(); ++j) { + PropPair prop = all_props.at(j); + wchar_t buf[500]; + GetThemeDocumentationProperty(themeName, prop.propName, buf, 500); + printf("%3d: (%4d) %-20S %S\n", j, prop.propValue, prop.propName, buf); + } + } + + {// Show Global values + printf("Global Properties:\n"); + for (int j = 0; j < all_props.count(); ++j) { + PropPair prop = all_props.at(j); + PROPERTYORIGIN origin = PO_NOTFOUND; + GetThemePropertyOrigin(themeData.handle(), themeData.partId, themeData.stateId, prop.propValue, &origin); + if (origin == PO_GLOBAL) { + showProperty(themeData, prop); + } + } + } + } + + for (int j = 0; j < all_props.count(); ++j) { + PropPair prop = all_props.at(j); + PROPERTYORIGIN origin = PO_NOTFOUND; + GetThemePropertyOrigin(themeData.handle(), themeData.partId, themeData.stateId, prop.propValue, &origin); + if (origin != PO_NOTFOUND) + { + showProperty(themeData, prop); + } + } +} +#endif +// Debugging code -----------------------------------------------------------------------[ END ]--- + + +QT_END_NAMESPACE diff --git a/src/imports/nativestyle/qstyle/windows/qquickwindowsxpstyle_p.h b/src/imports/nativestyle/qstyle/windows/qquickwindowsxpstyle_p.h new file mode 100644 index 00000000..af08e3e0 --- /dev/null +++ b/src/imports/nativestyle/qstyle/windows/qquickwindowsxpstyle_p.h @@ -0,0 +1,108 @@ +/**************************************************************************** +** +** 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 + +namespace QQC2 { + +class QWindowsXPStylePrivate; +class QWindowsXPStyle : public QWindowsStyle +{ + Q_OBJECT +public: + QWindowsXPStyle(); + QWindowsXPStyle(QWindowsXPStylePrivate &dd); + ~QWindowsXPStyle() override; + + void unpolish(QApplication*) override; + void polish(QApplication*) override; + void polish(QWidget*) override; + void polish(QPalette&) override; + void unpolish(QWidget*) override; + + void drawPrimitive(PrimitiveElement pe, const QStyleOption *option, QPainter *p, + const QWidget *widget = nullptr) const override; + void drawControl(ControlElement element, const QStyleOption *option, QPainter *p, + const QWidget *wwidget = nullptr) const override; + QRect subElementRect(SubElement r, const QStyleOption *option, + const QWidget *widget = nullptr) const override; + QRect subControlRect(ComplexControl cc, const QStyleOptionComplex *option, SubControl sc, + const QWidget *widget = nullptr) const override; + void drawComplexControl(ComplexControl cc, const QStyleOptionComplex *option, QPainter *p, + const QWidget *widget = nullptr) const override; + QSize sizeFromContents(ContentsType ct, const QStyleOption *option, const QSize &contentsSize, + const QWidget *widget = nullptr) const override; + int pixelMetric(PixelMetric pm, const QStyleOption *option = nullptr, + const QWidget *widget = nullptr) const override; + int styleHint(StyleHint hint, const QStyleOption *option = nullptr, + const QWidget *widget = nullptr, + QStyleHintReturn *returnData = nullptr) const override; + + QPalette standardPalette() const override; + QPixmap standardPixmap(StandardPixmap standardIcon, const QStyleOption *option, + const QWidget *widget = nullptr) const override; + QIcon standardIcon(StandardPixmap standardIcon, const QStyleOption *option = nullptr, + const QWidget *widget = nullptr) const override; + +private: + Q_DISABLE_COPY_MOVE(QWindowsXPStyle) + Q_DECLARE_PRIVATE(QWindowsXPStyle) + friend class QStyleFactory; +}; + +QT_END_NAMESPACE + +#endif // QWINDOWSXPSTYLE_P_H diff --git a/src/imports/nativestyle/qstyle/windows/qquickwindowsxpstyle_p_p.h b/src/imports/nativestyle/qstyle/windows/qquickwindowsxpstyle_p_p.h new file mode 100644 index 00000000..827e7bc2 --- /dev/null +++ b/src/imports/nativestyle/qstyle/windows/qquickwindowsxpstyle_p_p.h @@ -0,0 +1,345 @@ +/**************************************************************************** +** +** 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 "qquickwindowsxpstyle_p.h" +#include <QtWidgets/private/qwindowsstyle_p_p.h> +#include <qmap.h> +#include <qt_windows.h> + +#include <uxtheme.h> +#include <vssym32.h> + +#include <limits.h> + +QT_BEGIN_NAMESPACE + +class QDebug; + +// TMT_TEXTSHADOWCOLOR is wrongly defined in mingw +#if TMT_TEXTSHADOWCOLOR != 3818 +#undef TMT_TEXTSHADOWCOLOR +#define TMT_TEXTSHADOWCOLOR 3818 +#endif +#ifndef TST_NONE +# define TST_NONE 0 +#endif + +// These defines are missing from the tmschema, but still exist as +// states for their parts +#ifndef MINBS_INACTIVE +#define MINBS_INACTIVE 5 +#endif +#ifndef MAXBS_INACTIVE +#define MAXBS_INACTIVE 5 +#endif +#ifndef RBS_INACTIVE +#define RBS_INACTIVE 5 +#endif +#ifndef HBS_INACTIVE +#define HBS_INACTIVE 5 +#endif +#ifndef CBS_INACTIVE +#define CBS_INACTIVE 5 +#endif + +// Uncomment define below to build debug assisting code, and output +// #define DEBUG_XP_STYLE + +// Declarations ----------------------------------------------------------------------------------- +class XPThemeData +{ +public: + explicit XPThemeData(const QWidget *w = nullptr, QPainter *p = nullptr, int themeIn = -1, + int part = 0, int state = 0, const QRect &r = QRect()) + : widget(w), painter(p), theme(themeIn), partId(part), stateId(state), + mirrorHorizontally(false), mirrorVertically(false), noBorder(false), + noContent(false), rect(r) + {} + + HRGN mask(QWidget *widget); + HTHEME handle(); + + static RECT toRECT(const QRect &qr); + bool isValid(); + + QSizeF size(); + QMarginsF margins(const QRect &rect, int propId = TMT_CONTENTMARGINS); + QMarginsF margins(int propId = TMT_CONTENTMARGINS); + + static QSizeF themeSize(const QWidget *w = nullptr, QPainter *p = nullptr, int themeIn = -1, int part = 0, int state = 0); + static QMarginsF themeMargins(const QRect &rect, const QWidget *w = nullptr, QPainter *p = nullptr, int themeIn = -1, + int part = 0, int state = 0, int propId = TMT_CONTENTMARGINS); + static QMarginsF themeMargins(const QWidget *w = nullptr, QPainter *p = nullptr, int themeIn = -1, + int part = 0, int state = 0, int propId = TMT_CONTENTMARGINS); + + const QWidget *widget; + QPainter *painter; + + int theme; + HTHEME htheme = nullptr; + int partId; + int stateId; + + uint mirrorHorizontally : 1; + uint mirrorVertically : 1; + uint noBorder : 1; + uint noContent : 1; + uint rotate = 0; + QRect rect; +}; + +struct ThemeMapKey { + int theme = 0; + int partId = -1; + int stateId = -1; + bool noBorder = false; + bool noContent = false; + + ThemeMapKey() = default; + ThemeMapKey(const XPThemeData &data) + : theme(data.theme), partId(data.partId), stateId(data.stateId), + noBorder(data.noBorder), noContent(data.noContent) {} + +}; + +inline size_t qHash(const ThemeMapKey &key) +{ return key.theme ^ key.partId ^ key.stateId; } + +inline bool operator==(const ThemeMapKey &k1, const ThemeMapKey &k2) +{ + return k1.theme == k2.theme + && k1.partId == k2.partId + && k1.stateId == k2.stateId; +} + +enum AlphaChannelType { + UnknownAlpha = -1, // Alpha of part & state not yet known + NoAlpha, // Totally opaque, no need to touch alpha (RGB) + MaskAlpha, // Alpha channel must be fixed (ARGB) + RealAlpha // Proper alpha values from Windows (ARGB_Premultiplied) +}; + +struct ThemeMapData { + AlphaChannelType alphaType = UnknownAlpha; // Which type of alpha on part & state + + bool dataValid : 1; // Only used to detect if hash value is ok + bool partIsTransparent : 1; + bool hasAlphaChannel : 1; // True = part & state has real Alpha + bool wasAlphaSwapped : 1; // True = alpha channel needs to be swapped + bool hadInvalidAlpha : 1; // True = alpha channel contained invalid alpha values + + ThemeMapData() : dataValid(false), partIsTransparent(false), + hasAlphaChannel(false), wasAlphaSwapped(false), hadInvalidAlpha(false) {} +}; + +#ifndef QT_NO_DEBUG_STREAM +QDebug operator<<(QDebug d, const XPThemeData &t); +QDebug operator<<(QDebug d, const ThemeMapKey &k); +QDebug operator<<(QDebug d, const ThemeMapData &td); +#endif + +class QWindowsXPStylePrivate : public QWindowsStylePrivate +{ + Q_DECLARE_PUBLIC(QWindowsXPStyle) +public: + enum Theme { + ButtonTheme, + ComboboxTheme, + EditTheme, + HeaderTheme, + ListViewTheme, + MenuTheme, + ProgressTheme, + RebarTheme, + ScrollBarTheme, + SpinTheme, + TabTheme, + TaskDialogTheme, + ToolBarTheme, + ToolTipTheme, + TrackBarTheme, + XpTreeViewTheme, // '+'/'-' shape treeview indicators (XP) + WindowTheme, + StatusTheme, + VistaTreeViewTheme, // arrow shape treeview indicators (Vista) obtained from "explorer" theme. + NThemes + }; + + QWindowsXPStylePrivate() + { init(); } + + ~QWindowsXPStylePrivate() + { cleanup(); } + + static int pixelMetricFromSystemDp(QStyle::PixelMetric pm, const QStyleOption *option = nullptr, const QWidget *widget = nullptr); + static int fixedPixelMetric(QStyle::PixelMetric pm, const QStyleOption *option = nullptr, const QWidget *widget = nullptr); + + static HWND winId(const QWidget *widget); + + void init(bool force = false); + void cleanup(bool force = false); + void cleanupHandleMap(); + + HBITMAP buffer(int w = 0, int h = 0); + HDC bufferHDC() + { return bufferDC;} + + static bool useXP(bool update = false); + static QRect scrollBarGripperBounds(QStyle::State flags, const QWidget *widget, XPThemeData *theme); + + bool isTransparent(XPThemeData &themeData); + QRegion region(XPThemeData &themeData); + + bool drawBackground(XPThemeData &themeData, qreal correctionFactor = 1); + bool drawBackgroundThruNativeBuffer(XPThemeData &themeData, qreal aditionalDevicePixelRatio, qreal correctionFactor); + bool drawBackgroundDirectly(HDC dc, XPThemeData &themeData, qreal aditionalDevicePixelRatio); + + bool hasAlphaChannel(const QRect &rect); + bool fixAlphaChannel(const QRect &rect); + bool swapAlphaChannel(const QRect &rect, bool allPixels = false); + + QRgb groupBoxTextColor = 0; + QRgb groupBoxTextColorDisabled = 0; + QRgb sliderTickColor = 0; + bool hasInitColors = false; + + static HTHEME createTheme(int theme, HWND hwnd); + static QString themeName(int theme); + static inline bool hasTheme(int theme) { return theme >= 0 && theme < NThemes && m_themes[theme]; } + static bool isItemViewDelegateLineEdit(const QWidget *widget); + static bool isLineEditBaseColorSet(const QStyleOption *option, const QWidget *widget); + + QIcon dockFloat, dockClose; + +private: +#ifdef DEBUG_XP_STYLE + void dumpNativeDIB(int w, int h); + void showProperties(XPThemeData &themeData); +#endif + + static bool initVistaTreeViewTheming(); + static void cleanupVistaTreeViewTheming(); + + static QBasicAtomicInt ref; + static bool use_xp; + + QHash<ThemeMapKey, ThemeMapData> alphaCache; + HDC bufferDC = nullptr; + HBITMAP bufferBitmap = nullptr; + HBITMAP nullBitmap = nullptr; + uchar *bufferPixels = nullptr; + int bufferW = 0; + int bufferH = 0; + + static HWND m_vistaTreeViewHelper; + static HTHEME m_themes[NThemes]; +}; + +inline QSizeF XPThemeData::size() +{ + QSizeF result(0, 0); + if (isValid()) { + SIZE size; + if (SUCCEEDED(GetThemePartSize(handle(), nullptr, partId, stateId, nullptr, TS_TRUE, &size))) + result = QSize(size.cx, size.cy); + } + return result; +} + +inline QMarginsF XPThemeData::margins(const QRect &qRect, int propId) +{ + QMarginsF result(0, 0, 0 ,0); + if (isValid()) { + MARGINS margins; + RECT rect = XPThemeData::toRECT(qRect); + if (SUCCEEDED(GetThemeMargins(handle(), nullptr, partId, stateId, propId, &rect, &margins))) + result = QMargins(margins.cxLeftWidth, margins.cyTopHeight, margins.cxRightWidth, margins.cyBottomHeight); + } + return result; +} + +inline QMarginsF XPThemeData::margins(int propId) +{ + QMarginsF result(0, 0, 0 ,0); + if (isValid()) { + MARGINS margins; + if (SUCCEEDED(GetThemeMargins(handle(), nullptr, partId, stateId, propId, nullptr, &margins))) + result = QMargins(margins.cxLeftWidth, margins.cyTopHeight, margins.cxRightWidth, margins.cyBottomHeight); + } + return result; +} + +inline QSizeF XPThemeData::themeSize(const QWidget *w, QPainter *p, int themeIn, int part, int state) +{ + XPThemeData theme(w, p, themeIn, part, state); + return theme.size(); +} + +inline QMarginsF XPThemeData::themeMargins(const QRect &rect, const QWidget *w, QPainter *p, int themeIn, + int part, int state, int propId) +{ + XPThemeData theme(w, p, themeIn, part, state); + return theme.margins(rect, propId); +} + +inline QMarginsF XPThemeData::themeMargins(const QWidget *w, QPainter *p, int themeIn, + int part, int state, int propId) +{ + XPThemeData theme(w, p, themeIn, part, state); + return theme.margins(propId); +} + +QT_END_NAMESPACE + +#endif //QWINDOWSXPSTYLE_P_P_H diff --git a/src/imports/nativestyle/qstyle/windows/windows.pri b/src/imports/nativestyle/qstyle/windows/windows.pri new file mode 100644 index 00000000..2042c620 --- /dev/null +++ b/src/imports/nativestyle/qstyle/windows/windows.pri @@ -0,0 +1,18 @@ + +INCLUDEPATH += $$PWD + +HEADERS += \ + $$PWD/qquickwindowsstyle_p.h \ + $$PWD/qquickwindowsstyle_p_p.h \ +# $$PWD/qquickwindowsvistastyle_p.h \ +# $$PWD/qquickwindowsvistastyle_p_p.h \ +# $$PWD/qquickwindowsxpstyle_p.h \ +# $$PWD/qquickwindowsxpstyle_p_p.h + +SOURCES += \ + $$PWD/qquickwindowsstyle.cpp \ +# $$PWD/qquickwindowsvistastyle.cpp \ +# $$PWD/qquickwindowsxpstyle.cpp + +QMAKE_USE_PRIVATE += user32 gdi32 +LIBS_PRIVATE *= -luxtheme diff --git a/src/imports/nativestyle/qtquickcontrols2nativestyleplugin.cpp b/src/imports/nativestyle/qtquickcontrols2nativestyleplugin.cpp new file mode 100644 index 00000000..8aedcdb8 --- /dev/null +++ b/src/imports/nativestyle/qtquickcontrols2nativestyleplugin.cpp @@ -0,0 +1,119 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtQml/qqml.h> +#include <QtQuickControls2/private/qquickstyleplugin_p.h> + +#include "qquicknativestyle.h" +#include "qquickcommonstyle.h" + +#if defined(Q_OS_MACOS) +#include "qquickmacstyle_mac_p.h" +#elif defined(Q_OS_WINDOWS) +# include "qquickwindowsstyle_p.h" +#endif + +QT_BEGIN_NAMESPACE + +using namespace QQC2; + +class QtQuickControls2NativeStylePlugin : public QQuickStylePlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID QQmlExtensionInterface_iid) + +public: + ~QtQuickControls2NativeStylePlugin() override; + + void initializeEngine(QQmlEngine *engine, const char *uri) override; + void registerTypes(const char *uri) override; + QString name() const override; +}; + +QtQuickControls2NativeStylePlugin::~QtQuickControls2NativeStylePlugin() +{ + QQuickNativeStyle::setStyle(nullptr); +} + +QString QtQuickControls2NativeStylePlugin::name() const +{ + return QStringLiteral("NativeStyle"); +} + +void QtQuickControls2NativeStylePlugin::initializeEngine(QQmlEngine *engine, const char *uri) +{ + Q_UNUSED(engine); + Q_UNUSED(uri); + // Enable commonstyle as a reference style while + // the native styles are under development. + QStyle *style = nullptr; + if (qEnvironmentVariable("QQC2_COMMONSTYLE") == QStringLiteral("true")) { + style = new QCommonStyle; + } else { + const QString envStyle = qEnvironmentVariable("QQC2_STYLE"); + if (!envStyle.isNull()) { + if (envStyle.compare(QLatin1String("common"))) + style = new QCommonStyle; +#if defined(Q_OS_MACOS) + else if (envStyle.compare(QLatin1String("mac"))) + style = new QMacStyle; +#endif +#if defined(Q_OS_WINDOWS) + else if (envStyle.compare(QLatin1String("windows"))) + style = new QWindowsStyle; +#endif + } + if (!style) { +#if defined(Q_OS_MACOS) + style = new QMacStyle; +#elif defined(Q_OS_WINDOWS) + style = new QWindowsStyle; +#else + style = new QCommonStyle; +#endif + } + } + QQuickNativeStyle::setStyle(style); +} + +void QtQuickControls2NativeStylePlugin::registerTypes(const char *uri) +{ + Q_UNUSED(uri); +} + +QT_END_NAMESPACE + +#include "qtquickcontrols2nativestyleplugin.moc" diff --git a/src/quickcontrols2/configure.cmake b/src/quickcontrols2/configure.cmake index 456b3e37..c17574eb 100644 --- a/src/quickcontrols2/configure.cmake +++ b/src/quickcontrols2/configure.cmake @@ -41,10 +41,22 @@ qt_feature("quickcontrols2-universal" PRIVATE PURPOSE "Provides a style based on the Universal Design guidelines." CONDITION QT_FEATURE_quickcontrols2_default ) +qt_feature("quickcontrols2-macos" PRIVATE + SECTION "Quick Controls 2" + LABEL "macOS" + PURPOSE "Provides a native macOS desktop style." + CONDITION QT_FEATURE_quickcontrols2_default +) +qt_feature("quickcontrols2-windows" PRIVATE + SECTION "Quick Controls 2" + LABEL "Windows" + PURPOSE "Provides a native Windows desktop style." + CONDITION QT_FEATURE_quickcontrols2_default +) qt_configure_add_summary_section(NAME "Qt Quick Controls 2") qt_configure_add_summary_entry( TYPE "featureList" - ARGS "quickcontrols2-default quickcontrols2-fusion quickcontrols2-imagine quickcontrols2-material quickcontrols2-universal" + ARGS "quickcontrols2-default quickcontrols2-fusion quickcontrols2-imagine quickcontrols2-material quickcontrols2-universal quickcontrols2-macos quickcontrols2-windows" MESSAGE "Styles" ) qt_configure_end_summary_section() # end of "Qt Quick Controls 2" section diff --git a/src/quickcontrols2/configure.json b/src/quickcontrols2/configure.json index 22a602d3..490b9cdf 100644 --- a/src/quickcontrols2/configure.json +++ b/src/quickcontrols2/configure.json @@ -6,7 +6,9 @@ "style-fusion": { "type": "boolean", "name": "quickcontrols2-fusion" }, "style-imagine": { "type": "boolean", "name": "quickcontrols2-imagine" }, "style-material": { "type": "boolean", "name": "quickcontrols2-material" }, - "style-universal": { "type": "boolean", "name": "quickcontrols2-universal" } + "style-universal": { "type": "boolean", "name": "quickcontrols2-universal" }, + "style-macos": { "type": "boolean", "name": "quickcontrols2-macos" }, + "style-windows": { "type": "boolean", "name": "quickcontrols2-windows" } } }, @@ -42,6 +44,20 @@ "section": "Quick Controls 2", "condition": "features.quickcontrols2-default", "output": [ "privateFeature" ] + }, + "quickcontrols2-macos": { + "label": "macOS", + "purpose": "Provides a native macOS desktop style.", + "section": "Quick Controls 2", + "condition": "features.quickcontrols2-default", + "output": [ "privateFeature" ] + }, + "quickcontrols2-windows": { + "label": "Windows", + "purpose": "Provides a native Windows desktop style.", + "section": "Quick Controls 2", + "condition": "features.quickcontrols2-default", + "output": [ "privateFeature" ] } }, @@ -52,7 +68,7 @@ { "message": "Styles", "type": "featureList", - "args": "quickcontrols2-default quickcontrols2-fusion quickcontrols2-imagine quickcontrols2-material quickcontrols2-universal" + "args": "quickcontrols2-default quickcontrols2-fusion quickcontrols2-imagine quickcontrols2-material quickcontrols2-universal quickcontrols2-macos quickcontrols2-windows" } ] } diff --git a/src/quicktemplates2/qquickabstractbutton.cpp b/src/quicktemplates2/qquickabstractbutton.cpp index 4e7c03c9..0c481d01 100644 --- a/src/quicktemplates2/qquickabstractbutton.cpp +++ b/src/quicktemplates2/qquickabstractbutton.cpp @@ -436,7 +436,11 @@ QQuickAbstractButton::QQuickAbstractButton(QQuickItem *parent) : QQuickControl(*(new QQuickAbstractButtonPrivate), parent) { setActiveFocusOnTab(true); +#ifdef Q_OS_MACOS + setFocusPolicy(Qt::TabFocus); +#else setFocusPolicy(Qt::StrongFocus); +#endif setAcceptedMouseButtons(Qt::LeftButton); #if QT_CONFIG(cursor) setCursor(Qt::ArrowCursor); @@ -447,7 +451,11 @@ QQuickAbstractButton::QQuickAbstractButton(QQuickAbstractButtonPrivate &dd, QQui : QQuickControl(dd, parent) { setActiveFocusOnTab(true); +#ifdef Q_OS_MACOS + setFocusPolicy(Qt::TabFocus); +#else setFocusPolicy(Qt::StrongFocus); +#endif setAcceptedMouseButtons(Qt::LeftButton); #if QT_CONFIG(cursor) setCursor(Qt::ArrowCursor); diff --git a/tests/auto/controls/data/tst_switch.qml b/tests/auto/controls/data/tst_switch.qml index b3fab41c..8ec8ffb4 100644 --- a/tests/auto/controls/data/tst_switch.qml +++ b/tests/auto/controls/data/tst_switch.qml @@ -606,6 +606,7 @@ TestCase { verify(!control.activeFocus) mouseClick(control.indicator) - verify(control.activeFocus) + // should not get activeFocus on mouseClick on macOS + compare(control.activeFocus, Qt.platform.os !== "osx" && Qt.platform.os !== "macos") } } |