diff options
81 files changed, 3046 insertions, 644 deletions
diff --git a/.qmake.conf b/.qmake.conf index c66a9cce..8114dfd7 100644 --- a/.qmake.conf +++ b/.qmake.conf @@ -1,5 +1,6 @@ load(qt_build_config) CONFIG += warning_clean +DEFINES += QT_NO_FOREACH QQC2_SOURCE_TREE = $$PWD diff --git a/examples/quickcontrols2/texteditor/qml/+touch/texteditor.qml b/examples/quickcontrols2/texteditor/qml/+touch/texteditor.qml index a7f176a9..11d153f5 100644 --- a/examples/quickcontrols2/texteditor/qml/+touch/texteditor.qml +++ b/examples/quickcontrols2/texteditor/qml/+touch/texteditor.qml @@ -38,7 +38,6 @@ ** ****************************************************************************/ -import QtGraphicalEffects 1.0 import QtQuick 2.8 import QtQuick.Controls 2.1 import QtQuick.Controls.Material 2.1 @@ -226,7 +225,7 @@ ApplicationWindow { } } - Button { + RoundButton { id: editButton font.family: "fontello" text: "\uE809" // icon-pencil @@ -245,20 +244,6 @@ ApplicationWindow { // Force focus on the text area so the cursor and footer show up. textArea.forceActiveFocus() } - - background: Rectangle { - implicitWidth: parent.width - implicitHeight: parent.height - radius: width / 2 - color: parent.down ? Qt.darker(Material.accent) : Material.accent - - layer.enabled: editButton.enabled - layer.effect: DropShadow { - color: "#44000000" - verticalOffset: 2 - samples: 16 - } - } } Dialog { diff --git a/examples/quickcontrols2/texteditor/texteditor.cpp b/examples/quickcontrols2/texteditor/texteditor.cpp index 7fda4fa9..10ba675c 100644 --- a/examples/quickcontrols2/texteditor/texteditor.cpp +++ b/examples/quickcontrols2/texteditor/texteditor.cpp @@ -70,10 +70,17 @@ int main(int argc, char *argv[]) qmlRegisterType<DocumentHandler>("io.qt.examples.texteditor", 1, 0, "DocumentHandler"); - QQmlApplicationEngine engine; + QStringList selectors; #ifdef QT_EXTRA_FILE_SELECTOR - QQmlFileSelector::get(&engine)->setExtraSelectors(QStringList() << QT_EXTRA_FILE_SELECTOR); + selectors += QT_EXTRA_FILE_SELECTOR; +#else + if (app.arguments().contains("-touch")) + selectors += "touch"; #endif + + QQmlApplicationEngine engine; + QQmlFileSelector::get(&engine)->setExtraSelectors(selectors); + engine.load(QUrl("qrc:/qml/texteditor.qml")); if (engine.rootObjects().isEmpty()) return -1; diff --git a/src/imports/controls/Container.qml b/src/imports/controls/Container.qml new file mode 100644 index 00000000..9e9215f8 --- /dev/null +++ b/src/imports/controls/Container.qml @@ -0,0 +1,47 @@ +/**************************************************************************** +** +** Copyright (C) 2016 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.6 +import QtQuick.Templates 2.0 as T + +T.Container { + id: control + + implicitWidth: Math.max(background ? background.implicitWidth : 0, + (contentItem ? contentItem.implicitWidth : 0) + leftPadding + rightPadding) + implicitHeight: Math.max(background ? background.implicitHeight : 0, + (contentItem ? contentItem.implicitHeight : 0) + topPadding + bottomPadding) +} diff --git a/src/imports/controls/Control.qml b/src/imports/controls/Control.qml new file mode 100644 index 00000000..a4bb95fe --- /dev/null +++ b/src/imports/controls/Control.qml @@ -0,0 +1,47 @@ +/**************************************************************************** +** +** Copyright (C) 2016 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.6 +import QtQuick.Templates 2.0 as T + +T.Control { + id: control + + implicitWidth: Math.max(background ? background.implicitWidth : 0, + (contentItem ? contentItem.implicitWidth : 0) + leftPadding + rightPadding) + implicitHeight: Math.max(background ? background.implicitHeight : 0, + (contentItem ? contentItem.implicitHeight : 0) + topPadding + bottomPadding) +} diff --git a/src/imports/controls/RoundButton.qml b/src/imports/controls/RoundButton.qml new file mode 100644 index 00000000..102774e0 --- /dev/null +++ b/src/imports/controls/RoundButton.qml @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2016 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.6 +import QtQuick.Templates 2.1 as T + +T.RoundButton { + id: control + + implicitWidth: Math.max(background ? background.implicitWidth : 0, + contentItem.implicitWidth + leftPadding + rightPadding) + implicitHeight: Math.max(background ? background.implicitHeight : 0, + contentItem.implicitHeight + topPadding + bottomPadding) + baselineOffset: contentItem.y + contentItem.baselineOffset + + padding: 6 + + //! [contentItem] + contentItem: Text { + text: control.text + font: control.font + opacity: enabled || control.highlighted || control.checked ? 1 : 0.3 + color: control.checked || control.highlighted ? "#ffffff" : (control.visualFocus ? "#0066ff" : (control.down ? "#26282a" : "#353637")) + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + elide: Text.ElideRight + } + //! [contentItem] + + //! [background] + background: Rectangle { + implicitWidth: 40 + implicitHeight: 40 + radius: control.radius + opacity: enabled ? 1 : 0.3 + visible: !control.flat || control.down || control.checked || control.highlighted + color: control.checked || control.highlighted ? + (control.visualFocus ? (control.down ? "#599bff" : "#0066ff") : (control.down ? "#585a5c" : "#353637")) : + (control.visualFocus ? (control.down ? "#cce0ff" : "#f0f6ff") : (control.down ? "#d0d0d0" : "#e0e0e0")) + border.color: "#0066ff" + border.width: control.visualFocus ? 2 : 0 + } + //! [background] +} diff --git a/src/imports/controls/TextField.qml b/src/imports/controls/TextField.qml index 5fdb6fc9..540a8da4 100644 --- a/src/imports/controls/TextField.qml +++ b/src/imports/controls/TextField.qml @@ -41,8 +41,10 @@ T.TextField { id: control implicitWidth: Math.max(background ? background.implicitWidth : 0, - placeholder.implicitWidth + leftPadding + rightPadding) - implicitHeight: Math.max(background ? background.implicitHeight : 0, + placeholderText ? placeholder.implicitWidth + leftPadding + rightPadding : 0) + || contentWidth + leftPadding + rightPadding + implicitHeight: Math.max(contentHeight + topPadding + bottomPadding, + background ? background.implicitHeight : 0, placeholder.implicitHeight + topPadding + bottomPadding) padding: 6 diff --git a/src/imports/controls/controls.pri b/src/imports/controls/controls.pri index 9eeb2c1f..f84c59bc 100644 --- a/src/imports/controls/controls.pri +++ b/src/imports/controls/controls.pri @@ -16,6 +16,8 @@ QML_CONTROLS = \ CheckDelegate.qml \ CheckIndicator.qml \ ComboBox.qml \ + Container.qml \ + Control.qml \ Dial.qml \ Dialog.qml \ DialogButtonBox.qml \ @@ -36,6 +38,7 @@ QML_CONTROLS = \ RadioDelegate.qml \ RadioIndicator.qml \ RangeSlider.qml \ + RoundButton.qml \ ScrollBar.qml \ ScrollIndicator.qml \ Slider.qml \ diff --git a/src/imports/controls/doc/images/qtquickcontrols2-roundbutton.png b/src/imports/controls/doc/images/qtquickcontrols2-roundbutton.png Binary files differnew file mode 100644 index 00000000..9f1d44fc --- /dev/null +++ b/src/imports/controls/doc/images/qtquickcontrols2-roundbutton.png diff --git a/src/imports/controls/doc/snippets/qtquickcontrols2-roundbutton.qml b/src/imports/controls/doc/snippets/qtquickcontrols2-roundbutton.qml new file mode 100644 index 00000000..fa926302 --- /dev/null +++ b/src/imports/controls/doc/snippets/qtquickcontrols2-roundbutton.qml @@ -0,0 +1,36 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: http://www.gnu.org/copyleft/fdl.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 +import QtQuick.Controls 2.1 + +//! [1] +RoundButton { + text: "\u2713" // Unicode Character 'CHECK MARK' + onClicked: textArea.readOnly = true +} +//! [1] diff --git a/src/imports/controls/doc/src/qtquickcontrols2-customize.qdoc b/src/imports/controls/doc/src/qtquickcontrols2-customize.qdoc index d98b70d1..3e804bd4 100644 --- a/src/imports/controls/doc/src/qtquickcontrols2-customize.qdoc +++ b/src/imports/controls/doc/src/qtquickcontrols2-customize.qdoc @@ -548,6 +548,12 @@ \snippet qtquickcontrols2-rangeslider-custom.qml file + \section2 Customizing RoundButton + + RoundButton can be customized in the same manner as + \l {Customizing Button}{Button}. + + \section2 Customizing ScrollBar ScrollBar consists of two visual items: \l {Control::background}{background} diff --git a/src/imports/controls/doc/src/qtquickcontrols2-material.qdoc b/src/imports/controls/doc/src/qtquickcontrols2-material.qdoc index 74a7ece1..5685a852 100644 --- a/src/imports/controls/doc/src/qtquickcontrols2-material.qdoc +++ b/src/imports/controls/doc/src/qtquickcontrols2-material.qdoc @@ -39,6 +39,7 @@ \list \li \l {material-accent-attached-prop}{\b accent} : color \li \l {material-background-attached-prop}{\b background} : color + \li \l {material-elevation-attached-prop}{\b elevation} : int \li \l {material-foreground-attached-prop}{\b foreground} : color \li \l {material-primary-attached-prop}{\b primary} : color \li \l {material-theme-attached-prop}{\b theme} : enumeration @@ -280,6 +281,16 @@ \endstyleproperty + \styleproperty {Material.elevation} {int} {material-elevation-attached-prop} + \target material-elevation-attached-prop + This attached property holds the elevation of the control. The higher the + elevation, the deeper the shadow. The property can be attached to any control, + but not all controls visualize elevation. + + The default value is control-specific. + + \endstyleproperty + \styleproperty {Material.foreground} {color} {material-foreground-attached-prop} \target material-foreground-attached-prop This attached property holds the foreground color of the theme. The property diff --git a/src/imports/controls/material/RoundButton.qml b/src/imports/controls/material/RoundButton.qml new file mode 100644 index 00000000..57f30e30 --- /dev/null +++ b/src/imports/controls/material/RoundButton.qml @@ -0,0 +1,115 @@ +/**************************************************************************** +** +** Copyright (C) 2016 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.6 +import QtQuick.Templates 2.1 as T +import QtQuick.Controls.Material 2.1 +import QtQuick.Controls.Material.impl 2.1 + +T.RoundButton { + id: control + + implicitWidth: Math.max(background ? background.implicitWidth : 0, + contentItem.implicitWidth + leftPadding + rightPadding) + implicitHeight: Math.max(background ? background.implicitHeight : 0, + contentItem.implicitHeight + topPadding + bottomPadding) + baselineOffset: contentItem.y + contentItem.baselineOffset + + // external vertical padding is 6 (to increase touch area) + padding: 12 + + hoverEnabled: Qt.styleHints.useHoverEffects + + Material.elevation: flat ? control.down || control.hovered ? 2 : 0 + : control.down ? 8 : 2 + Material.background: flat ? "transparent" : undefined + + contentItem: Text { + text: control.text + font: control.font + color: !control.enabled ? control.Material.hintTextColor : + control.flat && control.highlighted ? control.Material.accentColor : + control.highlighted ? control.Material.primaryHighlightedTextColor : control.Material.primaryTextColor + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + elide: Text.ElideRight + } + + // TODO: Add a proper ripple/ink effect for mouse/touch input and focus state + background: Rectangle { + implicitWidth: 48 + implicitHeight: 48 + + // external vertical padding is 6 (to increase touch area) + x: 6 + y: 6 + width: parent.width - 12 + height: parent.height - 12 + radius: control.radius + color: !control.enabled ? control.Material.buttonDisabledColor + : control.checked || control.highlighted ? control.Material.highlightedButtonColor : control.Material.buttonColor + + Rectangle { + width: parent.width + height: parent.height + radius: control.radius + visible: control.hovered || control.visualFocus + color: control.Material.rippleColor + } + + Rectangle { + width: parent.width + height: parent.height + radius: control.radius + visible: control.down + color: control.Material.rippleColor + } + + Behavior on color { + ColorAnimation { + duration: 400 + } + } + + // The layer is disabled when the button color is transparent so that you can do + // Material.background: "transparent" and get a proper flat button without needing + // to set Material.elevation as well + layer.enabled: control.enabled && control.Material.buttonColor.a > 0 + layer.effect: ElevationEffect { + elevation: control.Material.elevation + } + } +} diff --git a/src/imports/controls/material/TextField.qml b/src/imports/controls/material/TextField.qml index a6406afa..25811322 100644 --- a/src/imports/controls/material/TextField.qml +++ b/src/imports/controls/material/TextField.qml @@ -42,9 +42,11 @@ T.TextField { id: control implicitWidth: Math.max(background ? background.implicitWidth : 0, - placeholder.implicitWidth + leftPadding + rightPadding) - implicitHeight: Math.max(background ? background.implicitHeight : 0, - placeholder.implicitHeight + 1 + topPadding + bottomPadding) + placeholderText ? placeholder.implicitWidth + leftPadding + rightPadding : 0) + || contentWidth + leftPadding + rightPadding + implicitHeight: Math.max(contentHeight + topPadding + bottomPadding, + background ? background.implicitHeight : 0, + placeholder.implicitHeight + topPadding + bottomPadding) topPadding: 8 bottomPadding: 16 diff --git a/src/imports/controls/material/ToolBar.qml b/src/imports/controls/material/ToolBar.qml index ce4f64ec..0da814f1 100644 --- a/src/imports/controls/material/ToolBar.qml +++ b/src/imports/controls/material/ToolBar.qml @@ -56,7 +56,7 @@ T.ToolBar { background: Rectangle { implicitHeight: 48 - color: control.Material.primaryColor + color: control.Material.toolBarColor layer.enabled: control.Material.elevation > 0 layer.effect: ElevationEffect { diff --git a/src/imports/controls/material/material.pri b/src/imports/controls/material/material.pri index 437edeee..3170a695 100644 --- a/src/imports/controls/material/material.pri +++ b/src/imports/controls/material/material.pri @@ -42,6 +42,7 @@ QML_FILES += \ $$PWD/RadioDelegate.qml \ $$PWD/RadioIndicator.qml \ $$PWD/RangeSlider.qml \ + $$PWD/RoundButton.qml \ $$PWD/RectangularGlow.qml \ $$PWD/ScrollBar.qml \ $$PWD/ScrollIndicator.qml \ diff --git a/src/imports/controls/material/qquickmaterialstyle.cpp b/src/imports/controls/material/qquickmaterialstyle.cpp index 76332c51..0e9f31df 100644 --- a/src/imports/controls/material/qquickmaterialstyle.cpp +++ b/src/imports/controls/material/qquickmaterialstyle.cpp @@ -706,6 +706,7 @@ void QQuickMaterialStyle::setBackground(const QVariant &var) m_background = background; propagateBackground(); emit backgroundChanged(); + emit paletteChanged(); } void QQuickMaterialStyle::inheritBackground(uint background, bool custom, bool has) @@ -854,7 +855,7 @@ QColor QQuickMaterialStyle::buttonColor(bool highlighted) const QColor color = Qt::transparent; - if (m_hasBackground) { + if (m_explicitBackground) { color = backgroundColor(shade); } else if (highlighted) { color = accentColor(shade); @@ -950,16 +951,11 @@ QColor QQuickMaterialStyle::scrollBarPressedColor() const return QColor::fromRgba(m_theme == Light ? 0x80000000 : 0x80FFFFFF); } -QColor QQuickMaterialStyle::drawerBackgroundColor() const -{ - return QColor::fromRgba(dividerColorLight); -} - QColor QQuickMaterialStyle::dialogColor() const { - if (!m_hasBackground) - return QColor::fromRgba(m_theme == Light ? dialogColorLight : dialogColorDark); - return backgroundColor(); + if (m_hasBackground) + return backgroundColor(); + return QColor::fromRgba(m_theme == Light ? dialogColorLight : dialogColorDark); } QColor QQuickMaterialStyle::backgroundDimColor() const @@ -974,9 +970,18 @@ QColor QQuickMaterialStyle::listHighlightColor() const QColor QQuickMaterialStyle::tooltipColor() const { + if (m_explicitBackground) + return backgroundColor(); return color(Grey, Shade700); } +QColor QQuickMaterialStyle::toolBarColor() const +{ + if (m_explicitBackground) + return backgroundColor(); + return primaryColor(); +} + QColor QQuickMaterialStyle::toolTextColor() const { if (m_hasForeground || m_customPrimary) diff --git a/src/imports/controls/material/qquickmaterialstyle_p.h b/src/imports/controls/material/qquickmaterialstyle_p.h index 78ba88da..e6e4fe05 100644 --- a/src/imports/controls/material/qquickmaterialstyle_p.h +++ b/src/imports/controls/material/qquickmaterialstyle_p.h @@ -91,11 +91,11 @@ class QQuickMaterialStyle : public QQuickStyleAttached Q_PROPERTY(QColor scrollBarColor READ scrollBarColor NOTIFY paletteChanged FINAL) Q_PROPERTY(QColor scrollBarHoveredColor READ scrollBarHoveredColor NOTIFY paletteChanged FINAL) Q_PROPERTY(QColor scrollBarPressedColor READ scrollBarPressedColor NOTIFY paletteChanged FINAL) - Q_PROPERTY(QColor drawerBackgroundColor READ drawerBackgroundColor NOTIFY paletteChanged FINAL) Q_PROPERTY(QColor dialogColor READ dialogColor NOTIFY paletteChanged FINAL) Q_PROPERTY(QColor backgroundDimColor READ backgroundDimColor NOTIFY paletteChanged FINAL) Q_PROPERTY(QColor listHighlightColor READ listHighlightColor NOTIFY paletteChanged FINAL) Q_PROPERTY(QColor tooltipColor READ tooltipColor NOTIFY paletteChanged FINAL) + Q_PROPERTY(QColor toolBarColor READ toolBarColor NOTIFY paletteChanged FINAL) Q_PROPERTY(QColor toolTextColor READ toolTextColor NOTIFY paletteChanged FINAL) Q_PROPERTY(QColor spinBoxDisabledIconColor READ spinBoxDisabledIconColor NOTIFY paletteChanged FINAL) @@ -213,11 +213,11 @@ public: QColor scrollBarColor() const; QColor scrollBarHoveredColor() const; QColor scrollBarPressedColor() const; - QColor drawerBackgroundColor() const; QColor dialogColor() const; QColor backgroundDimColor() const; QColor listHighlightColor() const; QColor tooltipColor() const; + QColor toolBarColor() const; QColor toolTextColor() const; QColor spinBoxDisabledIconColor() const; diff --git a/src/imports/controls/plugins.qmltypes b/src/imports/controls/plugins.qmltypes index 0700e7bf..a1ca509b 100644 --- a/src/imports/controls/plugins.qmltypes +++ b/src/imports/controls/plugins.qmltypes @@ -16,14 +16,14 @@ Module { name: "QQuickBusyIndicatorRing" defaultProperty: "data" prototype: "QQuickItem" - exports: ["QtQuick.Controls.impl/BusyRing 2.1"] + exports: ["QtQuick.Controls.impl/BusyRing 2.0"] exportMetaObjectRevisions: [0] } Component { name: "QQuickDialRing" defaultProperty: "data" prototype: "QQuickPaintedItem" - exports: ["QtQuick.Controls.impl/DialRing 2.1"] + exports: ["QtQuick.Controls.impl/DialRing 2.0"] exportMetaObjectRevisions: [0] Property { name: "progress"; type: "double" } Property { name: "color"; type: "QColor" } @@ -32,7 +32,7 @@ Module { name: "QQuickProgressStrip" defaultProperty: "data" prototype: "QQuickItem" - exports: ["QtQuick.Controls.impl/ProgressStrip 2.1"] + exports: ["QtQuick.Controls.impl/ProgressStrip 2.0"] exportMetaObjectRevisions: [0] Property { name: "indeterminate"; type: "bool" } Property { name: "progress"; type: "double" } @@ -89,9 +89,9 @@ Module { } Component { prototype: "QQuickRectangle" - name: "QtQuick.Controls.impl/CheckIndicator 2.1" - exports: ["QtQuick.Controls.impl/CheckIndicator 2.1"] - exportMetaObjectRevisions: [1] + name: "QtQuick.Controls.impl/CheckIndicator 2.0" + exports: ["QtQuick.Controls.impl/CheckIndicator 2.0"] + exportMetaObjectRevisions: [0] isComposite: true defaultProperty: "data" Property { name: "control"; type: "QQuickItem"; isPointer: true } @@ -105,6 +105,22 @@ Module { defaultProperty: "data" } Component { + prototype: "QQuickContainer" + name: "QtQuick.Controls/Container 2.0" + exports: ["QtQuick.Controls/Container 2.0"] + exportMetaObjectRevisions: [0] + isComposite: true + defaultProperty: "contentData" + } + Component { + prototype: "QQuickControl" + name: "QtQuick.Controls/Control 2.0" + exports: ["QtQuick.Controls/Control 2.0"] + exportMetaObjectRevisions: [0] + isComposite: true + defaultProperty: "data" + } + Component { prototype: "QQuickDial" name: "QtQuick.Controls/Dial 2.0" exports: ["QtQuick.Controls/Dial 2.0"] @@ -185,6 +201,14 @@ Module { defaultProperty: "data" } Component { + prototype: "QQuickMenuSeparator" + name: "QtQuick.Controls/MenuSeparator 2.1" + exports: ["QtQuick.Controls/MenuSeparator 2.1"] + exportMetaObjectRevisions: [1] + isComposite: true + defaultProperty: "data" + } + Component { prototype: "QQuickPage" name: "QtQuick.Controls/Page 2.0" exports: ["QtQuick.Controls/Page 2.0"] @@ -242,9 +266,9 @@ Module { } Component { prototype: "QQuickRectangle" - name: "QtQuick.Controls.impl/RadioIndicator 2.1" - exports: ["QtQuick.Controls.impl/RadioIndicator 2.1"] - exportMetaObjectRevisions: [1] + name: "QtQuick.Controls.impl/RadioIndicator 2.0" + exports: ["QtQuick.Controls.impl/RadioIndicator 2.0"] + exportMetaObjectRevisions: [0] isComposite: true defaultProperty: "data" Property { name: "control"; type: "QQuickItem"; isPointer: true } @@ -258,6 +282,14 @@ Module { defaultProperty: "data" } Component { + prototype: "QQuickRoundButton" + name: "QtQuick.Controls/RoundButton 2.1" + exports: ["QtQuick.Controls/RoundButton 2.1"] + exportMetaObjectRevisions: [1] + isComposite: true + defaultProperty: "data" + } + Component { prototype: "QQuickScrollBar" name: "QtQuick.Controls/ScrollBar 2.0" exports: ["QtQuick.Controls/ScrollBar 2.0"] @@ -331,9 +363,9 @@ Module { } Component { prototype: "QQuickItem" - name: "QtQuick.Controls.impl/SwitchIndicator 2.1" - exports: ["QtQuick.Controls.impl/SwitchIndicator 2.1"] - exportMetaObjectRevisions: [1] + name: "QtQuick.Controls.impl/SwitchIndicator 2.0" + exports: ["QtQuick.Controls.impl/SwitchIndicator 2.0"] + exportMetaObjectRevisions: [0] isComposite: true defaultProperty: "data" Property { name: "control"; type: "QQuickItem"; isPointer: true } @@ -529,11 +561,15 @@ Module { name: "QQuickComboBox" defaultProperty: "data" prototype: "QQuickControl" - exports: ["QtQuick.Templates/ComboBox 2.0"] - exportMetaObjectRevisions: [0] + exports: [ + "QtQuick.Templates/ComboBox 2.0", + "QtQuick.Templates/ComboBox 2.1" + ] + exportMetaObjectRevisions: [0, 1] Property { name: "count"; type: "int"; isReadonly: true } Property { name: "model"; type: "QVariant" } Property { name: "delegateModel"; type: "QQmlInstanceModel"; isReadonly: true; isPointer: true } + Property { name: "flat"; revision: 1; type: "bool" } Property { name: "pressed"; type: "bool" } Property { name: "highlightedIndex"; type: "int"; isReadonly: true } Property { name: "currentIndex"; type: "int" } @@ -543,6 +579,7 @@ Module { Property { name: "delegate"; type: "QQmlComponent"; isPointer: true } Property { name: "indicator"; type: "QQuickItem"; isPointer: true } Property { name: "popup"; type: "QQuickPopup"; isPointer: true } + Signal { name: "flatChanged"; revision: 1 } Signal { name: "activated" Parameter { name: "index"; type: "int" } @@ -982,6 +1019,13 @@ Module { Signal { name: "triggered" } } Component { + name: "QQuickMenuSeparator" + defaultProperty: "data" + prototype: "QQuickControl" + exports: ["QtQuick.Templates/MenuSeparator 2.1"] + exportMetaObjectRevisions: [0] + } + Component { name: "QQuickOverlay" defaultProperty: "data" prototype: "QQuickItem" @@ -1202,6 +1246,14 @@ Module { } } Component { + name: "QQuickRoundButton" + defaultProperty: "data" + prototype: "QQuickButton" + exports: ["QtQuick.Templates/RoundButton 2.1"] + exportMetaObjectRevisions: [1] + Property { name: "radius"; type: "double" } + } + Component { name: "QQuickScrollBar" defaultProperty: "data" prototype: "QQuickControl" @@ -1333,6 +1385,7 @@ Module { Signal { name: "activating" } Signal { name: "deactivated" } Signal { name: "deactivating" } + Signal { name: "removed" } } Component { name: "QQuickStackView" diff --git a/src/imports/controls/qtquickcontrols2plugin.cpp b/src/imports/controls/qtquickcontrols2plugin.cpp index 3cd3b8b2..f7ee00a6 100644 --- a/src/imports/controls/qtquickcontrols2plugin.cpp +++ b/src/imports/controls/qtquickcontrols2plugin.cpp @@ -87,8 +87,6 @@ void QtQuickControls2Plugin::registerTypes(const char *uri) qmlRegisterType<QQuickAbstractButton>(uri, 2, 0, "AbstractButton"); qmlRegisterType<QQuickButtonGroup>(uri, 2, 0, "ButtonGroup"); qmlRegisterType<QQuickButtonGroupAttached>(); - qmlRegisterType<QQuickContainer>(uri, 2, 0, "Container"); - qmlRegisterType<QQuickControl>(uri, 2, 0, "Control"); QQuickStylePrivate::init(typeUrl()); const QString style = QQuickStyle::name(); @@ -104,6 +102,8 @@ void QtQuickControls2Plugin::registerTypes(const char *uri) qmlRegisterType(selector.select(QStringLiteral("CheckBox.qml")), uri, 2, 0, "CheckBox"); qmlRegisterType(selector.select(QStringLiteral("CheckDelegate.qml")), uri, 2, 0, "CheckDelegate"); qmlRegisterType(selector.select(QStringLiteral("ComboBox.qml")), uri, 2, 0, "ComboBox"); + qmlRegisterType(selector.select(QStringLiteral("Container.qml")), uri, 2, 0, "Container"); + qmlRegisterType(selector.select(QStringLiteral("Control.qml")), uri, 2, 0, "Control"); qmlRegisterType(selector.select(QStringLiteral("Dial.qml")), uri, 2, 0, "Dial"); qmlRegisterType(selector.select(QStringLiteral("Drawer.qml")), uri, 2, 0, "Drawer"); qmlRegisterType(selector.select(QStringLiteral("Frame.qml")), uri, 2, 0, "Frame"); @@ -144,6 +144,7 @@ void QtQuickControls2Plugin::registerTypes(const char *uri) qmlRegisterType(selector.select(QStringLiteral("Dialog.qml")), uri, 2, 1, "Dialog"); qmlRegisterType(selector.select(QStringLiteral("DialogButtonBox.qml")), uri, 2, 1, "DialogButtonBox"); qmlRegisterType(selector.select(QStringLiteral("MenuSeparator.qml")), uri, 2, 1, "MenuSeparator"); + qmlRegisterType(selector.select(QStringLiteral("RoundButton.qml")), uri, 2, 1, "RoundButton"); qmlRegisterType(selector.select(QStringLiteral("ToolSeparator.qml")), uri, 2, 1, "ToolSeparator"); } diff --git a/src/imports/controls/universal/RoundButton.qml b/src/imports/controls/universal/RoundButton.qml new file mode 100644 index 00000000..61701099 --- /dev/null +++ b/src/imports/controls/universal/RoundButton.qml @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2016 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.6 +import QtQuick.Templates 2.1 as T +import QtQuick.Controls.Universal 2.1 + +T.RoundButton { + id: control + + implicitWidth: Math.max(background ? background.implicitWidth : 0, + contentItem.implicitWidth + leftPadding + rightPadding) + implicitHeight: Math.max(background ? background.implicitHeight : 0, + contentItem.implicitHeight + topPadding + bottomPadding) + baselineOffset: contentItem.y + contentItem.baselineOffset + + hoverEnabled: Qt.styleHints.useHoverEffects + + padding: 8 + + property bool useSystemFocusVisuals: true + + contentItem: Text { + text: control.text + font: control.font + elide: Text.ElideRight + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + + opacity: enabled ? 1.0 : 0.2 + color: control.Universal.foreground + } + + background: Rectangle { + implicitWidth: 32 + implicitHeight: 32 + + radius: control.radius + visible: !control.flat || control.down || control.checked || control.highlighted + color: control.down ? control.Universal.baseMediumLowColor : + control.enabled && (control.highlighted || control.checked) ? control.Universal.accent : + control.Universal.baseLowColor + + Rectangle { + width: parent.width + height: parent.height + radius: control.radius + color: "transparent" + visible: control.hovered + border.width: 2 // ButtonBorderThemeThickness + border.color: control.Universal.baseMediumLowColor + } + } +} diff --git a/src/imports/controls/universal/TextField.qml b/src/imports/controls/universal/TextField.qml index 88bc4fc9..afa09946 100644 --- a/src/imports/controls/universal/TextField.qml +++ b/src/imports/controls/universal/TextField.qml @@ -41,9 +41,9 @@ import QtQuick.Controls.Universal 2.1 T.TextField { id: control - implicitWidth: Math.max(contentWidth + leftPadding + rightPadding, - background ? background.implicitWidth : 0, - placeholder.implicitWidth + leftPadding + rightPadding) + implicitWidth: Math.max(background ? background.implicitWidth : 0, + placeholderText ? placeholder.implicitWidth + leftPadding + rightPadding : 0) + || contentWidth + leftPadding + rightPadding implicitHeight: Math.max(contentHeight + topPadding + bottomPadding, background ? background.implicitHeight : 0, placeholder.implicitHeight + topPadding + bottomPadding) diff --git a/src/imports/controls/universal/universal.pri b/src/imports/controls/universal/universal.pri index 72edbc32..3697a2a6 100644 --- a/src/imports/controls/universal/universal.pri +++ b/src/imports/controls/universal/universal.pri @@ -26,6 +26,7 @@ QML_FILES += \ $$PWD/RadioDelegate.qml \ $$PWD/RadioIndicator.qml \ $$PWD/RangeSlider.qml \ + $$PWD/RoundButton.qml \ $$PWD/ScrollBar.qml \ $$PWD/ScrollIndicator.qml \ $$PWD/Slider.qml \ diff --git a/src/imports/templates/plugins.qmltypes b/src/imports/templates/plugins.qmltypes index cbcc1fc7..dd991cfe 100644 --- a/src/imports/templates/plugins.qmltypes +++ b/src/imports/templates/plugins.qmltypes @@ -144,6 +144,7 @@ Module { Property { name: "delegate"; type: "QQmlComponent"; isPointer: true } Property { name: "indicator"; type: "QQuickItem"; isPointer: true } Property { name: "popup"; type: "QQuickPopup"; isPointer: true } + Signal { name: "flatChanged"; revision: 1 } Signal { name: "activated" Parameter { name: "index"; type: "int" } @@ -583,6 +584,13 @@ Module { Signal { name: "triggered" } } Component { + name: "QQuickMenuSeparator" + defaultProperty: "data" + prototype: "QQuickControl" + exports: ["QtQuick.Templates/MenuSeparator 2.1"] + exportMetaObjectRevisions: [0] + } + Component { name: "QQuickOverlay" defaultProperty: "data" prototype: "QQuickItem" @@ -803,6 +811,14 @@ Module { } } Component { + name: "QQuickRoundButton" + defaultProperty: "data" + prototype: "QQuickButton" + exports: ["QtQuick.Templates/RoundButton 2.1"] + exportMetaObjectRevisions: [1] + Property { name: "radius"; type: "double" } + } + Component { name: "QQuickScrollBar" defaultProperty: "data" prototype: "QQuickControl" @@ -934,6 +950,7 @@ Module { Signal { name: "activating" } Signal { name: "deactivated" } Signal { name: "deactivating" } + Signal { name: "removed" } } Component { name: "QQuickStackView" diff --git a/src/imports/templates/qtquicktemplates2plugin.cpp b/src/imports/templates/qtquicktemplates2plugin.cpp index 67a52d14..6624b019 100644 --- a/src/imports/templates/qtquicktemplates2plugin.cpp +++ b/src/imports/templates/qtquicktemplates2plugin.cpp @@ -66,6 +66,7 @@ #include <QtQuickTemplates2/private/qquickradiobutton_p.h> #include <QtQuickTemplates2/private/qquickradiodelegate_p.h> #include <QtQuickTemplates2/private/qquickrangeslider_p.h> +#include <QtQuickTemplates2/private/qquickroundbutton_p.h> #include <QtQuickTemplates2/private/qquickscrollbar_p.h> #include <QtQuickTemplates2/private/qquickscrollindicator_p.h> #include <QtQuickTemplates2/private/qquickslider_p.h> @@ -186,6 +187,7 @@ void QtQuickTemplates2Plugin::registerTypes(const char *uri) qmlRegisterType<QQuickMenuSeparator>(uri, 2, 1, "MenuSeparator"); qmlRegisterType<QQuickPopup, 1>(uri, 2, 1, "Popup"); qmlRegisterType<QQuickRangeSlider, 1>(uri, 2, 1, "RangeSlider"); + qmlRegisterType<QQuickRoundButton, 1>(uri, 2, 1, "RoundButton"); qmlRegisterType<QQuickSlider, 1>(uri, 2, 1, "Slider"); qmlRegisterType<QQuickSpinBox, 1>(uri, 2, 1, "SpinBox"); qmlRegisterType<QQuickStackView, 1>(uri, 2, 1, "StackView"); diff --git a/src/quickcontrols2/qquickstyle.cpp b/src/quickcontrols2/qquickstyle.cpp index bee837e9..ca5beafe 100644 --- a/src/quickcontrols2/qquickstyle.cpp +++ b/src/quickcontrols2/qquickstyle.cpp @@ -109,6 +109,24 @@ struct QQuickStyleSpec resolve(); } + static QString findStyle(const QString &path, const QString &name) + { + QDir dir(path); + if (!dir.exists()) + return QString(); + + if (name.isEmpty()) + return dir.absolutePath() + QLatin1Char('/'); + + const QStringList entries = dir.entryList(QStringList(), QDir::Dirs | QDir::NoDotAndDotDot); + for (const QString &entry : entries) { + if (entry.compare(name, Qt::CaseInsensitive) == 0) + return dir.absoluteFilePath(entry); + } + + return QString(); + } + void resolve(const QUrl &baseUrl = QUrl()) { if (style.isEmpty()) @@ -122,10 +140,12 @@ struct QQuickStyleSpec } if (baseUrl.isValid()) { - if (style.isEmpty()) - style = baseUrl.toString(QUrl::StripTrailingSlash) + QLatin1Char('/'); - else if (!style.contains(QLatin1Char('/'))) - style = baseUrl.toString(QUrl::StripTrailingSlash) + QLatin1Char('/') + style; + QString path = QQmlFile::urlToLocalFileOrQrc(baseUrl); + QString stylePath = findStyle(path, style); + if (!stylePath.isEmpty()) { + style = stylePath; + resolved = true; + } } if (QGuiApplication::instance()) { @@ -134,24 +154,12 @@ struct QQuickStyleSpec const QStringList importPaths = QQmlEngine().importPathList(); for (const QString &importPath : importPaths) { - QDir importDir(importPath); - if (importDir.cd(targetPath)) { - if (style.isEmpty()) { - style = importDir.absolutePath() + QLatin1Char('/'); - resolved = true; - break; - } - const QStringList entries = importDir.entryList(QStringList(), QDir::Dirs | QDir::NoDotAndDotDot); - for (const QString &entry : entries) { - if (entry.compare(style, Qt::CaseInsensitive) == 0) { - style = importDir.absoluteFilePath(entry); - resolved = true; - break; - } - } - } - if (resolved) + QString stylePath = findStyle(importPath + QLatin1Char('/') + targetPath, style); + if (!stylePath.isEmpty()) { + style = stylePath; + resolved = true; break; + } } } resolved = true; diff --git a/src/quicktemplates2/qquickapplicationwindow.cpp b/src/quicktemplates2/qquickapplicationwindow.cpp index af14ba6f..7f466fd7 100644 --- a/src/quicktemplates2/qquickapplicationwindow.cpp +++ b/src/quicktemplates2/qquickapplicationwindow.cpp @@ -158,12 +158,6 @@ void QQuickApplicationWindowPrivate::relayout() content->setWidth(q->width()); content->setHeight(q->height() - hh - fh); - if (overlay) { - overlay->setWidth(q->width()); - overlay->setHeight(q->height()); - overlay->stackAfter(content); - } - if (header) { header->setY(-hh); QQuickItemPrivate *p = QQuickItemPrivate::get(header); @@ -489,9 +483,12 @@ QQuickItem *QQuickApplicationWindow::activeFocusControl() const QQuickOverlay *QQuickApplicationWindow::overlay() const { QQuickApplicationWindowPrivate *d = const_cast<QQuickApplicationWindowPrivate *>(d_func()); + if (!d) // being deleted + return nullptr; + if (!d->overlay) { d->overlay = new QQuickOverlay(QQuickWindow::contentItem()); - d->relayout(); + d->overlay->stackAfter(QQuickApplicationWindow::contentItem()); } return d->overlay; } diff --git a/src/quicktemplates2/qquickbutton.cpp b/src/quicktemplates2/qquickbutton.cpp index a51e2210..19310713 100644 --- a/src/quicktemplates2/qquickbutton.cpp +++ b/src/quicktemplates2/qquickbutton.cpp @@ -35,7 +35,7 @@ ****************************************************************************/ #include "qquickbutton_p.h" -#include "qquickabstractbutton_p_p.h" +#include "qquickbutton_p_p.h" #include <QtGui/qpa/qplatformtheme.h> @@ -91,17 +91,6 @@ QT_BEGIN_NAMESPACE \sa {Customizing Button}, {Button Controls} */ -class QQuickButtonPrivate : public QQuickAbstractButtonPrivate -{ - Q_DECLARE_PUBLIC(QQuickButton) - -public: - QQuickButtonPrivate(); - - bool flat; - bool highlighted; -}; - QQuickButtonPrivate::QQuickButtonPrivate() : flat(false), highlighted(false) { @@ -112,6 +101,11 @@ QQuickButton::QQuickButton(QQuickItem *parent) : { } +QQuickButton::QQuickButton(QQuickButtonPrivate &dd, QQuickItem *parent) : + QQuickAbstractButton(dd, parent) +{ +} + /*! \qmlproperty bool QtQuick.Controls::Button::checkable diff --git a/src/quicktemplates2/qquickbutton_p.h b/src/quicktemplates2/qquickbutton_p.h index fafce150..95f94f10 100644 --- a/src/quicktemplates2/qquickbutton_p.h +++ b/src/quicktemplates2/qquickbutton_p.h @@ -78,6 +78,8 @@ Q_SIGNALS: void flatChanged(); protected: + QQuickButton(QQuickButtonPrivate &dd, QQuickItem *parent); + void checkableChange() override; void autoRepeatChange() override; diff --git a/src/quicktemplates2/qquickbutton_p_p.h b/src/quicktemplates2/qquickbutton_p_p.h new file mode 100644 index 00000000..86663e9e --- /dev/null +++ b/src/quicktemplates2/qquickbutton_p_p.h @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 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 QQUICKBUTTON_P_P_H +#define QQUICKBUTTON_P_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 <QtQuickTemplates2/private/qquickabstractbutton_p_p.h> + +QT_BEGIN_NAMESPACE + +class QQuickButtonPrivate : public QQuickAbstractButtonPrivate +{ + Q_DECLARE_PUBLIC(QQuickButton) + +public: + QQuickButtonPrivate(); + + bool flat; + bool highlighted; +}; + +QT_END_NAMESPACE + +#endif // QQUICKBUTTON_P_P_H diff --git a/src/quicktemplates2/qquickdrawer.cpp b/src/quicktemplates2/qquickdrawer.cpp index e2e6c84e..a64b5f4e 100644 --- a/src/quicktemplates2/qquickdrawer.cpp +++ b/src/quicktemplates2/qquickdrawer.cpp @@ -35,8 +35,7 @@ ****************************************************************************/ #include "qquickdrawer_p.h" -#include "qquickpopup_p_p.h" -#include "qquickvelocitycalculator_p_p.h" +#include "qquickdrawer_p_p.h" #include <QtGui/qstylehints.h> #include <QtGui/private/qguiapplication_p.h> @@ -114,33 +113,11 @@ QT_BEGIN_NAMESPACE \sa SwipeView, {Customizing Drawer}, {Navigation Controls}, {Popup Controls} */ -class QQuickDrawerPrivate : public QQuickPopupPrivate +QQuickDrawerPrivate::QQuickDrawerPrivate() + : edge(Qt::LeftEdge), offset(0), position(0), + dragMargin(QGuiApplication::styleHints()->startDragDistance()) { - Q_DECLARE_PUBLIC(QQuickDrawer) - -public: - QQuickDrawerPrivate() : edge(Qt::LeftEdge), offset(0), position(0), - dragMargin(QGuiApplication::styleHints()->startDragDistance()) { } - - qreal positionAt(const QPointF &point) const; - void reposition() override; - - bool handleMousePressEvent(QQuickItem *item, QMouseEvent *event); - bool handleMouseMoveEvent(QQuickItem *item, QMouseEvent *event); - bool handleMouseReleaseEvent(QQuickItem *item, QMouseEvent *event); - - void prepareEnterTransition(bool notify = true) override; - void prepareExitTransition() override; - void finalizeEnterTransition() override; - void finalizeExitTransition(bool hide = true) override; - - Qt::Edge edge; - qreal offset; - qreal position; - qreal dragMargin; - QPointF pressPoint; - QQuickVelocityCalculator velocityCalculator; -}; +} qreal QQuickDrawerPrivate::positionAt(const QPointF &point) const { @@ -191,100 +168,94 @@ static bool dragOverThreshold(qreal d, Qt::Axis axis, QMouseEvent *event, int th return QQuickWindowPrivate::dragOverThreshold(d, axis, event, threshold); } -bool QQuickDrawerPrivate::handleMousePressEvent(QQuickItem *item, QMouseEvent *event) +bool QQuickDrawerPrivate::startDrag(QQuickWindow *window, QMouseEvent *event) { - pressPoint = event->windowPos(); - offset = 0; - - QQuickWindow *window = item->window(); - if (!window) + if (!window || dragMargin < 0.0 || qFuzzyIsNull(dragMargin)) return false; - if (qFuzzyIsNull(position)) { - // only accept pressing at drag margins when fully closed - switch (edge) { - case Qt::LeftEdge: - event->setAccepted(dragMargin > 0 && !dragOverThreshold(event->windowPos().x(), Qt::XAxis, event, dragMargin)); - break; - case Qt::RightEdge: - event->setAccepted(dragMargin > 0 && !dragOverThreshold(window->width() - event->windowPos().x(), Qt::XAxis, event, dragMargin)); - break; - case Qt::TopEdge: - event->setAccepted(dragMargin > 0 && !dragOverThreshold(event->windowPos().y(), Qt::YAxis, event, dragMargin)); - break; - case Qt::BottomEdge: - event->setAccepted(dragMargin > 0 && !dragOverThreshold(window->height() - event->windowPos().y(), Qt::YAxis, event, dragMargin)); - break; - } - } else { - if (modal) - event->setAccepted(item->isAncestorOf(popupItem)); - else - event->setAccepted(false); + bool drag = false; + switch (edge) { + case Qt::LeftEdge: + drag = !dragOverThreshold(event->windowPos().x(), Qt::XAxis, event, dragMargin); + break; + case Qt::RightEdge: + drag = !dragOverThreshold(window->width() - event->windowPos().x(), Qt::XAxis, event, dragMargin); + break; + case Qt::TopEdge: + drag = !dragOverThreshold(event->windowPos().y(), Qt::YAxis, event, dragMargin); + break; + case Qt::BottomEdge: + drag = !dragOverThreshold(window->height() - event->windowPos().y(), Qt::YAxis, event, dragMargin); + break; + default: + break; } - velocityCalculator.startMeasuring(pressPoint, event->timestamp()); + if (drag) { + prepareEnterTransition(); + reposition(); + handleMousePressEvent(window->contentItem(), event); + } - return event->isAccepted(); + return drag; } -bool QQuickDrawerPrivate::handleMouseMoveEvent(QQuickItem *item, QMouseEvent *event) +bool QQuickDrawerPrivate::grabMouse(QMouseEvent *event) { Q_Q(QQuickDrawer); - QQuickWindow *window = item->window(); - if (!window) + if (!window || popupItem->keepMouseGrab()) return false; - QPointF movePoint = event->windowPos(); - - if (!popupItem->keepMouseGrab()) { - // Flickable uses a hard-coded threshold of 15 for flicking, and - // QStyleHints::startDragDistance for dragging. Drawer uses a bit - // larger threshold to avoid being too eager to steal touch (QTBUG-50045) - int threshold = qMax(20, QGuiApplication::styleHints()->startDragDistance() + 5); - bool overThreshold = false; - if (position > 0 || dragMargin > 0) { - if (edge == Qt::LeftEdge || edge == Qt::RightEdge) - overThreshold = dragOverThreshold(movePoint.x() - pressPoint.x(), Qt::XAxis, event, threshold); - else - overThreshold = dragOverThreshold(movePoint.y() - pressPoint.y(), Qt::YAxis, event, threshold); - } - - // Don't be too eager to steal presses outside the drawer (QTBUG-53929) - if (overThreshold && qFuzzyCompare(position, qreal(1.0)) && !popupItem->contains(popupItem->mapFromScene(movePoint))) { - if (edge == Qt::LeftEdge || edge == Qt::RightEdge) - overThreshold = qAbs(movePoint.x() - q->width()) < dragMargin; - else - overThreshold = qAbs(movePoint.y() - q->height()) < dragMargin; - } + const QPointF movePoint = event->windowPos(); - if (overThreshold) { - QQuickItem *grabber = window->mouseGrabberItem(); - if (!grabber || !grabber->keepMouseGrab()) { - popupItem->grabMouse(); - popupItem->setKeepMouseGrab(overThreshold); - offset = qMin<qreal>(0.0, positionAt(movePoint) - position); - } - } + // Flickable uses a hard-coded threshold of 15 for flicking, and + // QStyleHints::startDragDistance for dragging. Drawer uses a bit + // larger threshold to avoid being too eager to steal touch (QTBUG-50045) + const int threshold = qMax(20, QGuiApplication::styleHints()->startDragDistance() + 5); + bool overThreshold = false; + if (position > 0 || dragMargin > 0) { + if (edge == Qt::LeftEdge || edge == Qt::RightEdge) + overThreshold = dragOverThreshold(movePoint.x() - pressPoint.x(), Qt::XAxis, event, threshold); + else + overThreshold = dragOverThreshold(movePoint.y() - pressPoint.y(), Qt::YAxis, event, threshold); } - if (popupItem->keepMouseGrab()) - q->setPosition(positionAt(movePoint) - offset); - event->accept(); + // Don't be too eager to steal presses outside the drawer (QTBUG-53929) + if (overThreshold && qFuzzyCompare(position, qreal(1.0)) && !popupItem->contains(popupItem->mapFromScene(movePoint))) { + if (edge == Qt::LeftEdge || edge == Qt::RightEdge) + overThreshold = qAbs(movePoint.x() - q->width()) < dragMargin; + else + overThreshold = qAbs(movePoint.y() - q->height()) < dragMargin; + } - return popupItem->keepMouseGrab(); + return overThreshold; } static const qreal openCloseVelocityThreshold = 300; -bool QQuickDrawerPrivate::handleMouseReleaseEvent(QQuickItem *item, QMouseEvent *event) +bool QQuickDrawerPrivate::ungrabMouse(QMouseEvent *event) { - Q_UNUSED(item); - bool wasGrabbed = popupItem->keepMouseGrab(); if (wasGrabbed) { - velocityCalculator.stopMeasuring(event->pos(), event->timestamp()); - const qreal velocity = velocityCalculator.velocity().x(); + const QPointF releasePoint = event->windowPos(); + velocityCalculator.stopMeasuring(releasePoint, event->timestamp()); + + qreal velocity = 0; + if (edge == Qt::LeftEdge || edge == Qt::RightEdge) + velocity = velocityCalculator.velocity().x(); + else + velocity = velocityCalculator.velocity().y(); + + // the velocity is calculated so that swipes from left to right + // and top to bottom have positive velocity, and swipes from right + // to left and bottom to top have negative velocity. + // + // - top/left edge: positive velocity opens, negative velocity closes + // - bottom/right edge: negative velocity opens, positive velocity closes + // + // => invert the velocity for bottom and right edges, for the threshold comparison below + if (edge == Qt::RightEdge || edge == Qt::BottomEdge) + velocity = -velocity; if (position > 0.7 || velocity > openCloseVelocityThreshold) { transitionManager.transitionEnter(); @@ -293,35 +264,79 @@ bool QQuickDrawerPrivate::handleMouseReleaseEvent(QQuickItem *item, QMouseEvent } else { switch (edge) { case Qt::LeftEdge: - if (event->x() - pressPoint.x() > 0) + if (releasePoint.x() - pressPoint.x() > 0) transitionManager.transitionEnter(); else transitionManager.transitionExit(); break; case Qt::RightEdge: - if (event->x() - pressPoint.x() < 0) + if (releasePoint.x() - pressPoint.x() < 0) transitionManager.transitionEnter(); else transitionManager.transitionExit(); break; case Qt::TopEdge: - if (event->y() - pressPoint.y() > 0) + if (releasePoint.y() - pressPoint.y() > 0) transitionManager.transitionEnter(); else transitionManager.transitionExit(); break; case Qt::BottomEdge: - if (event->y() - pressPoint.y() < 0) + if (releasePoint.y() - pressPoint.y() < 0) transitionManager.transitionEnter(); else transitionManager.transitionExit(); break; } } - popupItem->setKeepMouseGrab(false); } + return wasGrabbed; +} + +bool QQuickDrawerPrivate::handleMousePressEvent(QQuickItem *item, QMouseEvent *event) +{ + offset = 0; + pressPoint = event->windowPos(); + velocityCalculator.startMeasuring(pressPoint, event->timestamp()); + + // don't block press events a) outside a non-modal drawer, or b) to drawer children + event->setAccepted(modal && !popupItem->isAncestorOf(item)); + return event->isAccepted(); +} + +bool QQuickDrawerPrivate::handleMouseMoveEvent(QQuickItem *item, QMouseEvent *event) +{ + Q_Q(QQuickDrawer); + Q_UNUSED(item); + + const QPointF movePoint = event->windowPos(); + + if (grabMouse(event)) { + QQuickItem *grabber = window->mouseGrabberItem(); + if (!grabber || !grabber->keepMouseGrab()) { + popupItem->grabMouse(); + popupItem->setKeepMouseGrab(true); + offset = qMin<qreal>(0.0, positionAt(movePoint) - position); + } + } + + if (popupItem->keepMouseGrab()) + q->setPosition(positionAt(movePoint) - offset); + event->accept(); + + return popupItem->keepMouseGrab(); +} + +bool QQuickDrawerPrivate::handleMouseReleaseEvent(QQuickItem *item, QMouseEvent *event) +{ + Q_UNUSED(item); + + const bool wasGrabbed = ungrabMouse(event); + + popupItem->setKeepMouseGrab(false); pressPoint = QPoint(); event->accept(); + return wasGrabbed; } @@ -345,28 +360,18 @@ static QList<QQuickStateAction> prepareTransition(QQuickDrawer *drawer, QQuickTr return actions; } -void QQuickDrawerPrivate::prepareEnterTransition(bool notify) +bool QQuickDrawerPrivate::prepareEnterTransition() { Q_Q(QQuickDrawer); enterActions = prepareTransition(q, enter, 1.0); - QQuickPopupPrivate::prepareEnterTransition(notify); + return QQuickPopupPrivate::prepareEnterTransition(); } -void QQuickDrawerPrivate::prepareExitTransition() +bool QQuickDrawerPrivate::prepareExitTransition() { Q_Q(QQuickDrawer); exitActions = prepareTransition(q, exit, 0.0); - QQuickPopupPrivate::prepareExitTransition(); -} - -void QQuickDrawerPrivate::finalizeEnterTransition() -{ - QQuickPopupPrivate::finalizeEnterTransition(); -} - -void QQuickDrawerPrivate::finalizeExitTransition(bool hide) -{ - QQuickPopupPrivate::finalizeExitTransition(hide = false); + return QQuickPopupPrivate::prepareExitTransition(); } QQuickDrawer::QQuickDrawer(QObject *parent) : @@ -430,12 +435,8 @@ void QQuickDrawer::setPosition(qreal position) d->position = position; if (isComponentComplete()) d->reposition(); - if (d->dimmer) { + if (d->dimmer) d->dimmer->setOpacity(position); - // TODO: check QStyleHints::useHoverEffects in Qt 5.8 - d->dimmer->setAcceptHoverEvents(d->modal && position > 0.0); - // d->dimmer->setAcceptHoverEvents(d->modal && position > 0.0 && QGuiApplication::styleHints()->useHoverEffects()); - } emit positionChanged(); } @@ -530,15 +531,4 @@ bool QQuickDrawer::overlayEvent(QQuickItem *item, QEvent *event) } } -void QQuickDrawer::componentComplete() -{ - Q_D(QQuickDrawer); - QQuickPopup::componentComplete(); - if (d->window) { - bool notify = false; - d->prepareEnterTransition(notify); - d->reposition(); - } -} - QT_END_NAMESPACE diff --git a/src/quicktemplates2/qquickdrawer_p.h b/src/quicktemplates2/qquickdrawer_p.h index 23c5c2ab..e694e27b 100644 --- a/src/quicktemplates2/qquickdrawer_p.h +++ b/src/quicktemplates2/qquickdrawer_p.h @@ -87,8 +87,6 @@ protected: void mouseUngrabEvent() override; bool overlayEvent(QQuickItem *item, QEvent *event) override; - void componentComplete() override; - private: Q_DISABLE_COPY(QQuickDrawer) Q_DECLARE_PRIVATE(QQuickDrawer) diff --git a/src/quicktemplates2/qquickdrawer_p_p.h b/src/quicktemplates2/qquickdrawer_p_p.h new file mode 100644 index 00000000..f14c36dd --- /dev/null +++ b/src/quicktemplates2/qquickdrawer_p_p.h @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 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 QQUICKDRAWER_P_P_H +#define QQUICKDRAWER_P_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 "qquickdrawer_p.h" +#include "qquickpopup_p_p.h" +#include "qquickvelocitycalculator_p_p.h" + +QT_BEGIN_NAMESPACE + +class QQuickDrawerPrivate : public QQuickPopupPrivate +{ + Q_DECLARE_PUBLIC(QQuickDrawer) + +public: + QQuickDrawerPrivate(); + + static QQuickDrawerPrivate *get(QQuickDrawer *drawer) + { + return drawer->d_func(); + } + + qreal positionAt(const QPointF &point) const; + void reposition() override; + + bool startDrag(QQuickWindow *window, QMouseEvent *event); + bool grabMouse(QMouseEvent *event); + bool ungrabMouse(QMouseEvent *event); + + bool handleMousePressEvent(QQuickItem *item, QMouseEvent *event); + bool handleMouseMoveEvent(QQuickItem *item, QMouseEvent *event); + bool handleMouseReleaseEvent(QQuickItem *item, QMouseEvent *event); + + bool prepareEnterTransition() override; + bool prepareExitTransition() override; + + Qt::Edge edge; + qreal offset; + qreal position; + qreal dragMargin; + QPointF pressPoint; + QQuickVelocityCalculator velocityCalculator; +}; + +QT_END_NAMESPACE + +#endif // QQUICKDRAWER_P_P_H diff --git a/src/quicktemplates2/qquickoverlay.cpp b/src/quicktemplates2/qquickoverlay.cpp index 850de875..172307e1 100644 --- a/src/quicktemplates2/qquickoverlay.cpp +++ b/src/quicktemplates2/qquickoverlay.cpp @@ -35,39 +35,17 @@ ****************************************************************************/ #include "qquickoverlay_p.h" +#include "qquickoverlay_p_p.h" #include "qquickpopup_p_p.h" -#include "qquickdrawer_p.h" +#include "qquickdrawer_p_p.h" +#include "qquickapplicationwindow_p.h" #include <QtQml/qqmlinfo.h> #include <QtQml/qqmlproperty.h> #include <QtQml/qqmlcomponent.h> -#include <QtQuick/private/qquickitem_p.h> +#include <algorithm> QT_BEGIN_NAMESPACE -class QQuickOverlayPrivate : public QQuickItemPrivate -{ - Q_DECLARE_PUBLIC(QQuickOverlay) - -public: - QQuickOverlayPrivate(); - - void popupAboutToShow(); - void popupAboutToHide(); - - void createOverlay(QQuickPopup *popup); - void destroyOverlay(QQuickPopup *popup); - void resizeOverlay(QQuickPopup *popup); - - QVector<QQuickPopup *> stackingOrderPopups() const; - - QQmlComponent *modal; - QQmlComponent *modeless; - QVector<QQuickDrawer *> drawers; - QVector<QQuickPopup *> popups; - QPointer<QQuickPopup> mouseGrabberPopup; - int modalPopups; -}; - void QQuickOverlayPrivate::popupAboutToShow() { Q_Q(QQuickOverlay); @@ -96,27 +74,35 @@ void QQuickOverlayPrivate::popupAboutToHide() static QQuickItem *createDimmer(QQmlComponent *component, QQuickPopup *popup, QQuickItem *parent) { - if (!component) - return nullptr; + QQuickItem *item = nullptr; + if (component) { + QQmlContext *creationContext = component->creationContext(); + if (!creationContext) + creationContext = qmlContext(parent); + QQmlContext *context = new QQmlContext(creationContext); + context->setContextObject(popup); + item = qobject_cast<QQuickItem*>(component->beginCreate(context)); + } + + // when there is no overlay component available (with plain QQuickWindow), + // use a plain QQuickItem as a fallback to block hover events + if (!item && popup->isModal()) + item = new QQuickItem; - QQmlContext *creationContext = component->creationContext(); - if (!creationContext) - creationContext = qmlContext(parent); - QQmlContext *context = new QQmlContext(creationContext); - context->setContextObject(popup); - QQuickItem *item = qobject_cast<QQuickItem*>(component->beginCreate(context)); if (item) { - item->setOpacity(0.0); + item->setOpacity(popup->isVisible() ? 1.0 : 0.0); item->setParentItem(parent); item->stackBefore(popup->popupItem()); item->setZ(popup->z()); - if (popup->isModal() && !qobject_cast<QQuickDrawer *>(popup)) { + if (popup->isModal()) { + item->setAcceptedMouseButtons(Qt::AllButtons); // TODO: switch to QStyleHints::useHoverEffects in Qt 5.8 item->setAcceptHoverEvents(true); // item->setAcceptHoverEvents(QGuiApplication::styleHints()->useHoverEffects()); // connect(QGuiApplication::styleHints(), &QStyleHints::useHoverEffectsChanged, item, &QQuickItem::setAcceptHoverEvents); } - component->completeCreate(); + if (component) + component->completeCreate(); } return item; } @@ -150,6 +136,18 @@ void QQuickOverlayPrivate::resizeOverlay(QQuickPopup *popup) } } +void QQuickOverlayPrivate::toggleOverlay() +{ + Q_Q(QQuickOverlay); + QQuickPopup *popup = qobject_cast<QQuickPopup *>(q->sender()); + if (!popup) + return; + + destroyOverlay(popup); + if (popup->dim()) + createOverlay(popup); +} + QVector<QQuickPopup *> QQuickOverlayPrivate::stackingOrderPopups() const { const QList<QQuickItem *> children = paintOrderChildItems(); @@ -166,20 +164,65 @@ QVector<QQuickPopup *> QQuickOverlayPrivate::stackingOrderPopups() const return popups; } +QVector<QQuickDrawer *> QQuickOverlayPrivate::stackingOrderDrawers() const +{ + QVector<QQuickDrawer *> sorted(allDrawers); + std::sort(sorted.begin(), sorted.end(), [](const QQuickDrawer *one, const QQuickDrawer *another) { + return one->z() > another->z(); + }); + return sorted; +} + +void QQuickOverlayPrivate::itemGeometryChanged(QQuickItem *item, QQuickGeometryChange, const QRectF &) +{ + Q_Q(QQuickOverlay); + q->setSize(QSizeF(item->width(), item->height())); +} + QQuickOverlayPrivate::QQuickOverlayPrivate() : modal(nullptr), - modeless(nullptr), - modalPopups(0) + modeless(nullptr) { } +void QQuickOverlayPrivate::addPopup(QQuickPopup *popup) +{ + Q_Q(QQuickOverlay); + allPopups += popup; + if (QQuickDrawer *drawer = qobject_cast<QQuickDrawer *>(popup)) { + allDrawers += drawer; + q->setVisible(!allDrawers.isEmpty() || !q->childItems().isEmpty()); + } +} + +void QQuickOverlayPrivate::removePopup(QQuickPopup *popup) +{ + Q_Q(QQuickOverlay); + allPopups.removeOne(popup); + if (allDrawers.removeOne(static_cast<QQuickDrawer *>(popup))) + q->setVisible(!allDrawers.isEmpty() || !q->childItems().isEmpty()); +} + QQuickOverlay::QQuickOverlay(QQuickItem *parent) : QQuickItem(*(new QQuickOverlayPrivate), parent) { + Q_D(QQuickOverlay); setZ(1000001); // DefaultWindowDecoration+1 setAcceptedMouseButtons(Qt::AllButtons); setFiltersChildMouseEvents(true); setVisible(false); + + if (parent) { + setSize(QSizeF(parent->width(), parent->height())); + QQuickItemPrivate::get(parent)->addItemChangeListener(d, QQuickItemPrivate::Geometry); + } +} + +QQuickOverlay::~QQuickOverlay() +{ + Q_D(QQuickOverlay); + if (QQuickItem *parent = parentItem()) + QQuickItemPrivate::get(parent)->removeItemChangeListener(d, QQuickItemPrivate::Geometry); } QQmlComponent *QQuickOverlay::modal() const @@ -216,6 +259,29 @@ void QQuickOverlay::setModeless(QQmlComponent *modeless) emit modelessChanged(); } +QQuickOverlay *QQuickOverlay::overlay(QQuickWindow *window) +{ + if (!window) + return nullptr; + + QQuickApplicationWindow *applicationWindow = qobject_cast<QQuickApplicationWindow *>(window); + if (applicationWindow) + return applicationWindow->overlay(); + + const char *name = "_q_QQuickOverlay"; + QQuickOverlay *overlay = window->property(name).value<QQuickOverlay *>(); + if (!overlay) { + QQuickItem *content = window->contentItem(); + // Do not re-create the overlay if the window is being destroyed + // and thus, its content item no longer has a window associated. + if (content->window()) { + overlay = new QQuickOverlay(window->contentItem()); + window->setProperty(name, QVariant::fromValue(overlay)); + } + } + return overlay; +} + void QQuickOverlay::itemChange(ItemChange change, const ItemChangeData &data) { Q_D(QQuickOverlay); @@ -224,37 +290,25 @@ void QQuickOverlay::itemChange(ItemChange change, const ItemChangeData &data) QQuickPopup *popup = nullptr; if (change == ItemChildAddedChange || change == ItemChildRemovedChange) { popup = qobject_cast<QQuickPopup *>(data.item->parent()); - setVisible(!childItems().isEmpty()); + setVisible(!d->allDrawers.isEmpty() || !childItems().isEmpty()); } if (!popup) return; if (change == ItemChildAddedChange) { - d->popups.append(popup); if (popup->dim()) d->createOverlay(popup); - - QQuickDrawer *drawer = qobject_cast<QQuickDrawer *>(popup); - if (drawer) { - d->drawers.append(drawer); - } else { - if (popup->isModal()) - ++d->modalPopups; - + QObjectPrivate::connect(popup, &QQuickPopup::dimChanged, d, &QQuickOverlayPrivate::toggleOverlay); + QObjectPrivate::connect(popup, &QQuickPopup::modalChanged, d, &QQuickOverlayPrivate::toggleOverlay); + if (!qobject_cast<QQuickDrawer *>(popup)) { QObjectPrivate::connect(popup, &QQuickPopup::aboutToShow, d, &QQuickOverlayPrivate::popupAboutToShow); QObjectPrivate::connect(popup, &QQuickPopup::aboutToHide, d, &QQuickOverlayPrivate::popupAboutToHide); } } else if (change == ItemChildRemovedChange) { - d->popups.removeOne(popup); d->destroyOverlay(popup); - - QQuickDrawer *drawer = qobject_cast<QQuickDrawer *>(popup); - if (drawer) { - d->drawers.removeOne(drawer); - } else { - if (popup->isModal()) - --d->modalPopups; - + QObjectPrivate::disconnect(popup, &QQuickPopup::dimChanged, d, &QQuickOverlayPrivate::toggleOverlay); + QObjectPrivate::disconnect(popup, &QQuickPopup::modalChanged, d, &QQuickOverlayPrivate::toggleOverlay); + if (!qobject_cast<QQuickDrawer *>(popup)) { QObjectPrivate::disconnect(popup, &QQuickPopup::aboutToShow, d, &QQuickOverlayPrivate::popupAboutToShow); QObjectPrivate::disconnect(popup, &QQuickPopup::aboutToHide, d, &QQuickOverlayPrivate::popupAboutToHide); } @@ -265,79 +319,86 @@ void QQuickOverlay::geometryChanged(const QRectF &newGeometry, const QRectF &old { Q_D(QQuickOverlay); QQuickItem::geometryChanged(newGeometry, oldGeometry); - for (QQuickPopup *popup : d->popups) + for (QQuickPopup *popup : qAsConst(d->allPopups)) d->resizeOverlay(popup); } -bool QQuickOverlay::event(QEvent *event) +void QQuickOverlay::mousePressEvent(QMouseEvent *event) { Q_D(QQuickOverlay); - switch (event->type()) { - case QEvent::MouseButtonPress: { - emit pressed(); + emit pressed(); + + if (!d->allDrawers.isEmpty()) { + // the overlay background was pressed, so there are no modal popups open. + // test if the press point lands on any drawer's drag margin + + const QVector<QQuickDrawer *> drawers = d->stackingOrderDrawers(); + for (QQuickDrawer *drawer : drawers) { + QQuickDrawerPrivate *p = QQuickDrawerPrivate::get(drawer); + if (p->startDrag(window(), event)) { + d->mouseGrabberPopup = drawer; + return; + } + } + } + + if (!d->mouseGrabberPopup) { const auto popups = d->stackingOrderPopups(); for (QQuickPopup *popup : popups) { if (popup->overlayEvent(this, event)) { d->mouseGrabberPopup = popup; - return true; + return; } } - break; } - case QEvent::MouseMove: - if (d->mouseGrabberPopup) { - if (d->mouseGrabberPopup->overlayEvent(this, event)) - return true; - } else { - const auto popups = d->stackingOrderPopups(); - for (QQuickPopup *popup : popups) { - if (popup->overlayEvent(this, event)) - return true; - } - } - break; - case QEvent::MouseButtonRelease: - emit released(); - if (d->mouseGrabberPopup) { - QQuickPopup *grabber = d->mouseGrabberPopup; - d->mouseGrabberPopup = nullptr; - if (grabber->overlayEvent(this, event)) - return true; - } else { - const auto popups = d->stackingOrderPopups(); - for (QQuickPopup *popup : popups) { - if (popup->overlayEvent(this, event)) - return true; - } + + event->ignore(); +} + +void QQuickOverlay::mouseMoveEvent(QMouseEvent *event) +{ + Q_D(QQuickOverlay); + if (d->mouseGrabberPopup) + d->mouseGrabberPopup->overlayEvent(this, event); +} + +void QQuickOverlay::mouseReleaseEvent(QMouseEvent *event) +{ + Q_D(QQuickOverlay); + emit released(); + + if (d->mouseGrabberPopup) { + d->mouseGrabberPopup->overlayEvent(this, event); + d->mouseGrabberPopup = nullptr; + } else { + const auto popups = d->stackingOrderPopups(); + for (QQuickPopup *popup : popups) { + if (popup->overlayEvent(this, event)) + break; } - break; - default: - break; } - - return QQuickItem::event(event); } bool QQuickOverlay::childMouseEventFilter(QQuickItem *item, QEvent *event) { Q_D(QQuickOverlay); - if (d->modalPopups == 0) - return false; - // TODO Filter touch events - if (event->type() != QEvent::MouseButtonPress) - return false; - while (item->parentItem() != this) - item = item->parentItem(); - - const auto popups = d->stackingOrderPopups(); - for (QQuickPopup *popup : popups) { - if (popup->popupItem() == item) - break; - - if (popup->overlayEvent(item, event)) - return true; + for (QQuickPopup *popup : qAsConst(d->allPopups)) { + QQuickItem *dimmer = QQuickPopupPrivate::get(popup)->dimmer; + if (item == dimmer) { + switch (event->type()) { + case QEvent::MouseButtonPress: + emit pressed(); + return popup->overlayEvent(item, event); + case QEvent::MouseMove: + return popup->overlayEvent(item, event); + case QEvent::MouseButtonRelease: + emit released(); + return popup->overlayEvent(item, event); + default: + break; + } + } } - return false; } diff --git a/src/quicktemplates2/qquickoverlay_p.h b/src/quicktemplates2/qquickoverlay_p.h index 99d7ba75..07f7daec 100644 --- a/src/quicktemplates2/qquickoverlay_p.h +++ b/src/quicktemplates2/qquickoverlay_p.h @@ -64,6 +64,7 @@ class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickOverlay : public QQuickItem public: explicit QQuickOverlay(QQuickItem *parent = nullptr); + ~QQuickOverlay(); QQmlComponent *modal() const; void setModal(QQmlComponent *modal); @@ -71,6 +72,8 @@ public: QQmlComponent *modeless() const; void setModeless(QQmlComponent *modeless); + static QQuickOverlay *overlay(QQuickWindow *window); + Q_SIGNALS: void modalChanged(); void modelessChanged(); @@ -81,7 +84,9 @@ protected: void itemChange(ItemChange change, const ItemChangeData &data) override; void geometryChanged(const QRectF &oldGeometry, const QRectF &newGeometry) override; - bool event(QEvent *event) override; + void mousePressEvent(QMouseEvent *event) override; + void mouseMoveEvent(QMouseEvent *event) override; + void mouseReleaseEvent(QMouseEvent *event) override; bool childMouseEventFilter(QQuickItem *item, QEvent *event) override; private: diff --git a/src/quicktemplates2/qquickoverlay_p_p.h b/src/quicktemplates2/qquickoverlay_p_p.h new file mode 100644 index 00000000..db005555 --- /dev/null +++ b/src/quicktemplates2/qquickoverlay_p_p.h @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 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 QQUICKOVERLAY_P_P_H +#define QQUICKOVERLAY_P_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 "qquickoverlay_p.h" + +#include <QtQuick/private/qquickitem_p.h> +#include <QtQuick/private/qquickitemchangelistener_p.h> + +QT_BEGIN_NAMESPACE + +class QQuickPopup; +class QQuickDrawer; + +class QQuickOverlayPrivate : public QQuickItemPrivate, public QQuickItemChangeListener +{ + Q_DECLARE_PUBLIC(QQuickOverlay) + +public: + QQuickOverlayPrivate(); + + static QQuickOverlayPrivate *get(QQuickOverlay *overlay) + { + return overlay->d_func(); + } + + void addPopup(QQuickPopup *popup); + void removePopup(QQuickPopup *popup); + + void popupAboutToShow(); + void popupAboutToHide(); + + void createOverlay(QQuickPopup *popup); + void destroyOverlay(QQuickPopup *popup); + void resizeOverlay(QQuickPopup *popup); + void toggleOverlay(); + + QVector<QQuickPopup *> stackingOrderPopups() const; + QVector<QQuickDrawer *> stackingOrderDrawers() const; + + void itemGeometryChanged(QQuickItem *item, QQuickGeometryChange change, const QRectF &diff) override; + + QQmlComponent *modal; + QQmlComponent *modeless; + QVector<QQuickPopup *> allPopups; + QVector<QQuickDrawer *> allDrawers; + QPointer<QQuickPopup> mouseGrabberPopup; +}; + +QT_END_NAMESPACE + +#endif // QQUICKOVERLAY_P_P_H diff --git a/src/quicktemplates2/qquickpopup.cpp b/src/quicktemplates2/qquickpopup.cpp index 414ba782..4143ee3f 100644 --- a/src/quicktemplates2/qquickpopup.cpp +++ b/src/quicktemplates2/qquickpopup.cpp @@ -37,7 +37,7 @@ #include "qquickpopup_p.h" #include "qquickpopup_p_p.h" #include "qquickapplicationwindow_p.h" -#include "qquickoverlay_p.h" +#include "qquickoverlay_p_p.h" #include "qquickcontrol_p_p.h" #include <QtQml/qqmlinfo.h> @@ -143,6 +143,7 @@ QQuickPopupPrivate::QQuickPopupPrivate() , bottomMargin(0) , contentWidth(0) , contentHeight(0) + , transitionState(QQuickPopupPrivate::NoTransition) , closePolicy(QQuickPopup::CloseOnEscape | QQuickPopup::CloseOnPressOutside) , parentItem(nullptr) , dimmer(nullptr) @@ -180,43 +181,46 @@ bool QQuickPopupPrivate::tryClose(QQuickItem *item, QMouseEvent *event) return false; } -void QQuickPopupPrivate::prepareEnterTransition(bool notify) +bool QQuickPopupPrivate::prepareEnterTransition() { Q_Q(QQuickPopup); if (!window) { qmlInfo(q) << "cannot find any window to open popup in."; - return; + return false; } - QQuickApplicationWindow *applicationWindow = qobject_cast<QQuickApplicationWindow*>(window); - if (!applicationWindow) { - window->installEventFilter(q); - popupItem->setZ(1000001); // DefaultWindowDecoration+1 - popupItem->setParentItem(window->contentItem()); - } else { - popupItem->setParentItem(applicationWindow->overlay()); - } + if (transitionState == EnterTransition && transitionManager.isRunning()) + return false; - if (notify) + if (transitionState != EnterTransition) { + popupItem->setParentItem(QQuickOverlay::overlay(window)); emit q->aboutToShow(); - visible = notify; - popupItem->setVisible(true); - positioner.setParentItem(parentItem); - emit q->visibleChanged(); + visible = true; + transitionState = EnterTransition; + popupItem->setVisible(true); + positioner.setParentItem(parentItem); + emit q->visibleChanged(); + } + return true; } -void QQuickPopupPrivate::prepareExitTransition() +bool QQuickPopupPrivate::prepareExitTransition() { Q_Q(QQuickPopup); - if (window && !qobject_cast<QQuickApplicationWindow *>(window)) - window->removeEventFilter(q); - if (focus) { - // The setFocus(false) call below removes any active focus before we're - // able to check it in finalizeExitTransition. - hadActiveFocusBeforeExitTransition = popupItem->hasActiveFocus(); - popupItem->setFocus(false); + if (transitionState == ExitTransition && transitionManager.isRunning()) + return false; + + if (transitionState != ExitTransition) { + if (focus) { + // The setFocus(false) call below removes any active focus before we're + // able to check it in finalizeExitTransition. + hadActiveFocusBeforeExitTransition = popupItem->hasActiveFocus(); + popupItem->setFocus(false); + } + transitionState = ExitTransition; + emit q->aboutToHide(); } - emit q->aboutToHide(); + return true; } void QQuickPopupPrivate::finalizeEnterTransition() @@ -224,17 +228,16 @@ void QQuickPopupPrivate::finalizeEnterTransition() Q_Q(QQuickPopup); if (focus) popupItem->setFocus(true); + transitionState = NoTransition; emit q->opened(); } -void QQuickPopupPrivate::finalizeExitTransition(bool hide) +void QQuickPopupPrivate::finalizeExitTransition() { Q_Q(QQuickPopup); - if (hide) { - positioner.setParentItem(nullptr); - popupItem->setParentItem(nullptr); - popupItem->setVisible(false); - } + positioner.setParentItem(nullptr); + popupItem->setParentItem(nullptr); + popupItem->setVisible(false); if (hadActiveFocusBeforeExitTransition) { QQuickApplicationWindow *applicationWindow = qobject_cast<QQuickApplicationWindow*>(window); @@ -245,6 +248,7 @@ void QQuickPopupPrivate::finalizeExitTransition(bool hide) } visible = false; + transitionState = NoTransition; hadActiveFocusBeforeExitTransition = false; emit q->visibleChanged(); emit q->closed(); @@ -314,6 +318,18 @@ void QQuickPopupPrivate::setWindow(QQuickWindow *newWindow) if (window == newWindow) return; + if (window) { + QQuickOverlay *overlay = QQuickOverlay::overlay(window); + if (overlay) + QQuickOverlayPrivate::get(overlay)->removePopup(q); + } + + if (newWindow) { + QQuickOverlay *overlay = QQuickOverlay::overlay(newWindow); + if (overlay) + QQuickOverlayPrivate::get(overlay)->addPopup(q); + } + window = newWindow; emit q->windowChanged(newWindow); } @@ -711,19 +727,15 @@ bool QQuickPopupPositioner::isAncestor(QQuickItem *item) const } QQuickPopupTransitionManager::QQuickPopupTransitionManager(QQuickPopupPrivate *popup) - : QQuickTransitionManager() - , state(Off) - , popup(popup) + : QQuickTransitionManager(), popup(popup) { } void QQuickPopupTransitionManager::transitionEnter() { - if (state == Enter && isRunning()) + if (!popup->prepareEnterTransition()) return; - state = Enter; - popup->prepareEnterTransition(); if (popup->window) transition(popup->enterActions, popup->enter, popup->q_func()); else @@ -732,11 +744,9 @@ void QQuickPopupTransitionManager::transitionEnter() void QQuickPopupTransitionManager::transitionExit() { - if (state == Exit && isRunning()) + if (!popup->prepareExitTransition()) return; - state = Exit; - popup->prepareExitTransition(); if (popup->window) transition(popup->exitActions, popup->exit, popup->q_func()); else @@ -745,12 +755,10 @@ void QQuickPopupTransitionManager::transitionExit() void QQuickPopupTransitionManager::finished() { - if (state == Enter) + if (popup->transitionState == QQuickPopupPrivate::EnterTransition) popup->finalizeEnterTransition(); - else if (state == Exit) + else if (popup->transitionState == QQuickPopupPrivate::ExitTransition) popup->finalizeExitTransition(); - - state = Off; } QQuickPopup::QQuickPopup(QObject *parent) @@ -1895,13 +1903,6 @@ bool QQuickPopup::isComponentComplete() const return d->complete; } -bool QQuickPopup::eventFilter(QObject *object, QEvent *event) -{ - if (QQuickWindow *window = qobject_cast<QQuickWindow *>(object)) - return overlayEvent(window->contentItem(), event); - return false; -} - bool QQuickPopup::childMouseEventFilter(QQuickItem *child, QEvent *event) { Q_UNUSED(child); @@ -1961,6 +1962,12 @@ void QQuickPopup::mouseDoubleClickEvent(QMouseEvent *event) void QQuickPopup::mouseUngrabEvent() { + QQuickOverlay *overlay = QQuickOverlay::overlay(window()); + if (overlay) { + QQuickOverlayPrivate *p = QQuickOverlayPrivate::get(overlay); + if (p->mouseGrabberPopup == this) + p->mouseGrabberPopup = nullptr; + } } bool QQuickPopup::overlayEvent(QQuickItem *item, QEvent *event) diff --git a/src/quicktemplates2/qquickpopup_p.h b/src/quicktemplates2/qquickpopup_p.h index 1f2cabcf..5f5fd6bd 100644 --- a/src/quicktemplates2/qquickpopup_p.h +++ b/src/quicktemplates2/qquickpopup_p.h @@ -344,7 +344,6 @@ protected: void componentComplete() override; bool isComponentComplete() const; - bool eventFilter(QObject *object, QEvent *event) override; virtual bool childMouseEventFilter(QQuickItem *child, QEvent *event); virtual void focusInEvent(QFocusEvent *event); virtual void focusOutEvent(QFocusEvent *event); diff --git a/src/quicktemplates2/qquickpopup_p_p.h b/src/quicktemplates2/qquickpopup_p_p.h index 4478c555..93039287 100644 --- a/src/quicktemplates2/qquickpopup_p_p.h +++ b/src/quicktemplates2/qquickpopup_p_p.h @@ -76,11 +76,6 @@ protected: void finished() override; private: - enum TransitionState { - Off, Enter, Exit - }; - - TransitionState state; QQuickPopupPrivate *popup; }; @@ -163,10 +158,10 @@ public: bool tryClose(QQuickItem *item, QMouseEvent *event); virtual void reposition(); - virtual void prepareEnterTransition(bool notify = true); - virtual void prepareExitTransition(); + virtual bool prepareEnterTransition(); + virtual bool prepareExitTransition(); virtual void finalizeEnterTransition(); - virtual void finalizeExitTransition(bool hide = true); + virtual void finalizeExitTransition(); QMarginsF getMargins() const; @@ -178,6 +173,10 @@ public: void setWindow(QQuickWindow *window); void itemDestroyed(QQuickItem *item) override; + enum TransitionState { + NoTransition, EnterTransition, ExitTransition + }; + bool focus; bool modal; bool dim; @@ -202,6 +201,7 @@ public: qreal bottomMargin; qreal contentWidth; qreal contentHeight; + TransitionState transitionState; QQuickPopup::ClosePolicy closePolicy; QQuickItem *parentItem; QQuickItem *dimmer; diff --git a/src/quicktemplates2/qquickroundbutton.cpp b/src/quicktemplates2/qquickroundbutton.cpp new file mode 100644 index 00000000..4169b269 --- /dev/null +++ b/src/quicktemplates2/qquickroundbutton.cpp @@ -0,0 +1,143 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 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 "qquickroundbutton_p.h" + +#include <QtQuickTemplates2/private/qquickbutton_p_p.h> + +QT_BEGIN_NAMESPACE + +/*! + \qmltype RoundButton + \inherits Button + \instantiates QQuickRoundButton + \inqmlmodule QtQuick.Controls + \since 5.8 + \ingroup qtquickcontrols2-buttons + \brief A push-button control with rounded corners that can be clicked by the user. + + \image qtquickcontrols2-roundbutton.png + + RoundButton is identical to \l Button, except that it has a \l radius property + which allows the corners to be rounded without having to customize the + \l background. + + \snippet qtquickcontrols2-roundbutton.qml 1 + + \sa {Customizing RoundButton}, {Button Controls} +*/ + +class QQuickRoundButtonPrivate : public QQuickButtonPrivate +{ + Q_DECLARE_PUBLIC(QQuickRoundButton) + +public: + QQuickRoundButtonPrivate(); + + qreal radius; + bool explicitRadius; + + void setRadius(qreal newRadius = -1.0); +}; + +QQuickRoundButtonPrivate::QQuickRoundButtonPrivate() : + radius(0), + explicitRadius(false) +{ +} + +void QQuickRoundButtonPrivate::setRadius(qreal newRadius) +{ + Q_Q(QQuickRoundButton); + const qreal oldRadius = radius; + if (newRadius < 0) + radius = qMax<qreal>(0, qMin(width, height) / 2); + else + radius = newRadius; + + if (!qFuzzyCompare(radius, oldRadius)) + emit q->radiusChanged(); +} + +QQuickRoundButton::QQuickRoundButton(QQuickItem *parent) : + QQuickButton(*(new QQuickRoundButtonPrivate), parent) +{ +} + +/*! + \qmlproperty real QtQuick.Controls::RoundButton::radius + + This property holds the radius of the button. + + To create a relatively square button that has slightly rounded corners, + use a small value, such as \c 3. + + To create a completely circular button (the default), use a value that is + equal to half of the width or height of the button, and make the button's + width and height identical. + + To reset this property back to the default value, set its value to + \c undefined. +*/ +qreal QQuickRoundButton::radius() const +{ + Q_D(const QQuickRoundButton); + return d->radius; +} + +void QQuickRoundButton::setRadius(qreal radius) +{ + Q_D(QQuickRoundButton); + d->explicitRadius = true; + d->setRadius(radius); +} + +void QQuickRoundButton::resetRadius() +{ + Q_D(QQuickRoundButton); + d->explicitRadius = false; + d->setRadius(); +} + +void QQuickRoundButton::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) +{ + Q_D(QQuickRoundButton); + QQuickControl::geometryChanged(newGeometry, oldGeometry); + if (!d->explicitRadius) + d->setRadius(); +} + +QT_END_NAMESPACE diff --git a/src/quicktemplates2/qquickroundbutton_p.h b/src/quicktemplates2/qquickroundbutton_p.h new file mode 100644 index 00000000..f308ddd3 --- /dev/null +++ b/src/quicktemplates2/qquickroundbutton_p.h @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 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 QQUICKROUNDBUTTON_P_H +#define QQUICKROUNDBUTTON_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 <QtQuickTemplates2/private/qquickbutton_p.h> + +QT_BEGIN_NAMESPACE + +class QQuickRoundButtonPrivate; + +class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickRoundButton : public QQuickButton +{ + Q_OBJECT + Q_PROPERTY(qreal radius READ radius WRITE setRadius RESET resetRadius NOTIFY radiusChanged FINAL) + +public: + explicit QQuickRoundButton(QQuickItem *parent = nullptr); + + qreal radius() const; + void setRadius(qreal radius); + void resetRadius(); + +Q_SIGNALS: + void radiusChanged(); + +protected: + void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) override; + +private: + Q_DISABLE_COPY(QQuickRoundButton) + Q_DECLARE_PRIVATE(QQuickRoundButton) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickRoundButton) + +#endif // QQUICKROUNDBUTTON_P_H diff --git a/src/quicktemplates2/qquickscrollindicator.cpp b/src/quicktemplates2/qquickscrollindicator.cpp index acb8dd46..351f3113 100644 --- a/src/quicktemplates2/qquickscrollindicator.cpp +++ b/src/quicktemplates2/qquickscrollindicator.cpp @@ -109,12 +109,17 @@ void QQuickScrollIndicatorPrivate::resizeContent() if (!contentItem) return; + // - negative overshoot (pos < 0): clamp the pos to 0, and deduct the overshoot from the size + // - positive overshoot (pos + size > 1): clamp the size to 1-pos + const qreal clampedSize = qBound<qreal>(0, size + qMin<qreal>(0, position), 1.0 - position); + const qreal clampedPos = qBound<qreal>(0, position, 1.0 - clampedSize); + if (orientation == Qt::Horizontal) { - contentItem->setPosition(QPointF(q->leftPadding() + position * q->availableWidth(), q->topPadding())); - contentItem->setSize(QSizeF(q->availableWidth() * size, q->availableHeight())); + contentItem->setPosition(QPointF(q->leftPadding() + clampedPos * q->availableWidth(), q->topPadding())); + contentItem->setSize(QSizeF(q->availableWidth() * clampedSize, q->availableHeight())); } else { - contentItem->setPosition(QPointF(q->leftPadding(), q->topPadding() + position * q->availableHeight())); - contentItem->setSize(QSizeF(q->availableWidth(), q->availableHeight() * size)); + contentItem->setPosition(QPointF(q->leftPadding(), q->topPadding() + clampedPos * q->availableHeight())); + contentItem->setSize(QSizeF(q->availableWidth(), q->availableHeight() * clampedSize)); } } diff --git a/src/quicktemplates2/qquickstackview.cpp b/src/quicktemplates2/qquickstackview.cpp index df163089..040eff1b 100644 --- a/src/quicktemplates2/qquickstackview.cpp +++ b/src/quicktemplates2/qquickstackview.cpp @@ -1092,4 +1092,21 @@ QQuickStackView::Status QQuickStackAttached::status() const \sa status */ +/*! + \qmlattachedsignal QtQuick.Controls::StackView::removed() + \since QtQuick.Controls 2.1 + + This attached signal is emitted when the item it's attached to has been + removed from the stack. It can be used to safely destroy an Item that was + pushed onto the stack, for example: + + \code + Item { + StackView.onRemoved: destroy() // Will be destroyed sometime after this call. + } + \endcode + + \sa status +*/ + QT_END_NAMESPACE diff --git a/src/quicktemplates2/qquickstackview_p.cpp b/src/quicktemplates2/qquickstackview_p.cpp index 0f5674c2..438b4269 100644 --- a/src/quicktemplates2/qquickstackview_p.cpp +++ b/src/quicktemplates2/qquickstackview_p.cpp @@ -85,6 +85,7 @@ QQuickStackElement::~QQuickStackElement() if (ownComponent) delete component; + QQuickStackAttached *attached = attachedStackObject(this); if (item) { if (ownItem) { item->setParentItem(nullptr); @@ -99,13 +100,15 @@ QQuickStackElement::~QQuickStackElement() if (item->parentItem() != originalParent) { item->setParentItem(originalParent); } else { - QQuickStackAttached *attached = attachedStackObject(this); if (attached) QQuickStackAttachedPrivate::get(attached)->itemParentChanged(item, nullptr); } } } + if (attached) + emit attached->removed(); + delete context; } diff --git a/src/quicktemplates2/qquickstackview_p.h b/src/quicktemplates2/qquickstackview_p.h index d8b6ce7c..9bf60183 100644 --- a/src/quicktemplates2/qquickstackview_p.h +++ b/src/quicktemplates2/qquickstackview_p.h @@ -182,6 +182,7 @@ Q_SIGNALS: /*Q_REVISION(1)*/ void activating(); /*Q_REVISION(1)*/ void deactivated(); /*Q_REVISION(1)*/ void deactivating(); + /*Q_REVISION(1)*/ void removed(); private: Q_DISABLE_COPY(QQuickStackAttached) diff --git a/src/quicktemplates2/qquickswitch.cpp b/src/quicktemplates2/qquickswitch.cpp index 439950d8..37ae53f0 100644 --- a/src/quicktemplates2/qquickswitch.cpp +++ b/src/quicktemplates2/qquickswitch.cpp @@ -37,6 +37,8 @@ #include "qquickswitch_p.h" #include "qquickabstractbutton_p_p.h" +#include <QtGui/qstylehints.h> +#include <QtGui/qguiapplication.h> #include <QtQuick/private/qquickwindow_p.h> #include <QtQuick/private/qquickevents_p_p.h> @@ -119,8 +121,12 @@ bool QQuickSwitchPrivate::handleMousePressEvent(QQuickItem *child, QMouseEvent * { Q_Q(QQuickSwitch); Q_UNUSED(child); + if ((focusPolicy & Qt::ClickFocus) == Qt::ClickFocus && !QGuiApplication::styleHints()->setFocusOnTouchRelease()) + q->forceActiveFocus(Qt::MouseFocusReason); + pressPoint = event->pos(); q->setPressed(true); + emit q->pressed(); event->accept(); return true; } @@ -140,6 +146,9 @@ bool QQuickSwitchPrivate::handleMouseMoveEvent(QQuickItem *child, QMouseEvent *e bool QQuickSwitchPrivate::handleMouseReleaseEvent(QQuickItem *child, QMouseEvent *event) { Q_Q(QQuickSwitch); + if ((focusPolicy & Qt::ClickFocus) == Qt::ClickFocus && QGuiApplication::styleHints()->setFocusOnTouchRelease()) + q->forceActiveFocus(Qt::MouseFocusReason); + pressPoint = QPoint(); q->setPressed(false); if (child->keepMouseGrab()) { @@ -147,11 +156,14 @@ bool QQuickSwitchPrivate::handleMouseReleaseEvent(QQuickItem *child, QMouseEvent q->setChecked(position > 0.5); q->setPosition(checked ? 1.0 : 0.0); child->setKeepMouseGrab(false); - if (wasChecked != checked) + if (wasChecked != checked) { + emit q->released(); emit q->clicked(); + } event->accept(); } else { q->toggle(); + emit q->released(); emit q->clicked(); event->accept(); } diff --git a/src/quicktemplates2/quicktemplates2.pri b/src/quicktemplates2/quicktemplates2.pri index f5e95aea..0d6a0e5a 100644 --- a/src/quicktemplates2/quicktemplates2.pri +++ b/src/quicktemplates2/quicktemplates2.pri @@ -6,6 +6,7 @@ HEADERS += \ $$PWD/qquickapplicationwindow_p.h \ $$PWD/qquickbusyindicator_p.h \ $$PWD/qquickbutton_p.h \ + $$PWD/qquickbutton_p_p.h \ $$PWD/qquickbuttongroup_p.h \ $$PWD/qquickcheckbox_p.h \ $$PWD/qquickcheckdelegate_p.h \ @@ -20,6 +21,7 @@ HEADERS += \ $$PWD/qquickdialogbuttonbox_p.h \ $$PWD/qquickdialogbuttonbox_p_p.h \ $$PWD/qquickdrawer_p.h \ + $$PWD/qquickdrawer_p_p.h \ $$PWD/qquickframe_p.h \ $$PWD/qquickframe_p_p.h \ $$PWD/qquickgroupbox_p.h \ @@ -32,6 +34,7 @@ HEADERS += \ $$PWD/qquickmenuitem_p.h \ $$PWD/qquickmenuseparator_p.h \ $$PWD/qquickoverlay_p.h \ + $$PWD/qquickoverlay_p_p.h \ $$PWD/qquickpage_p.h \ $$PWD/qquickpageindicator_p.h \ $$PWD/qquickpagelayout_p_p.h \ @@ -44,6 +47,7 @@ HEADERS += \ $$PWD/qquickradiobutton_p.h \ $$PWD/qquickradiodelegate_p.h \ $$PWD/qquickrangeslider_p.h \ + $$PWD/qquickroundbutton_p.h \ $$PWD/qquickscrollbar_p.h \ $$PWD/qquickscrollindicator_p.h \ $$PWD/qquickslider_p.h \ @@ -101,6 +105,7 @@ SOURCES += \ $$PWD/qquickradiobutton.cpp \ $$PWD/qquickradiodelegate.cpp \ $$PWD/qquickrangeslider.cpp \ + $$PWD/qquickroundbutton.cpp \ $$PWD/qquickscrollbar.cpp \ $$PWD/qquickscrollindicator.cpp \ $$PWD/qquickslider.cpp \ diff --git a/tests/auto/controls/data/tst_container.qml b/tests/auto/controls/data/tst_container.qml new file mode 100644 index 00000000..d1ff54e0 --- /dev/null +++ b/tests/auto/controls/data/tst_container.qml @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.2 +import QtTest 1.0 +import QtQuick.Controls 2.0 +import QtQuick.Templates 2.0 as T + +TestCase { + id: testCase + width: 400 + height: 400 + visible: true + when: windowShown + name: "Container" + + Component { + id: container + Container { } + } + + Component { + id: rectangle + Rectangle { } + } + + function test_implicitSize() { + var control = container.createObject(testCase) + verify(control) + + compare(control.implicitWidth, 0) + compare(control.implicitHeight, 0) + + control.contentItem = rectangle.createObject(control, {implicitWidth: 10, implicitHeight: 20}) + compare(control.implicitWidth, 10) + compare(control.implicitHeight, 20) + + control.background = rectangle.createObject(control, {implicitWidth: 20, implicitHeight: 30}) + compare(control.implicitWidth, 20) + compare(control.implicitHeight, 30) + + control.padding = 100 + compare(control.implicitWidth, 210) + compare(control.implicitHeight, 220) + + control.destroy() + } +} diff --git a/tests/auto/controls/data/tst_control.qml b/tests/auto/controls/data/tst_control.qml index fc839493..0061440b 100644 --- a/tests/auto/controls/data/tst_control.qml +++ b/tests/auto/controls/data/tst_control.qml @@ -53,7 +53,12 @@ TestCase { Component { id: component - T.Control { } + Control { } + } + + Component { + id: rectangle + Rectangle { } } Component { @@ -877,4 +882,26 @@ TestCase { control.destroy() } + + function test_implicitSize() { + var control = component.createObject(testCase) + verify(control) + + compare(control.implicitWidth, 0) + compare(control.implicitHeight, 0) + + control.contentItem = rectangle.createObject(control, {implicitWidth: 10, implicitHeight: 20}) + compare(control.implicitWidth, 10) + compare(control.implicitHeight, 20) + + control.background = rectangle.createObject(control, {implicitWidth: 20, implicitHeight: 30}) + compare(control.implicitWidth, 20) + compare(control.implicitHeight, 30) + + control.padding = 100 + compare(control.implicitWidth, 210) + compare(control.implicitHeight, 220) + + control.destroy() + } } diff --git a/tests/auto/controls/data/tst_popup.qml b/tests/auto/controls/data/tst_popup.qml index 759d6988..a86e800e 100644 --- a/tests/auto/controls/data/tst_popup.qml +++ b/tests/auto/controls/data/tst_popup.qml @@ -1006,18 +1006,13 @@ TestCase { ApplicationWindow { property alias firstDrawer: firstDrawer property alias secondDrawer: secondDrawer - property alias upperDrawer: upperDrawer property alias modalPopup: modalPopup property alias modelessPopup: modelessPopup property alias plainPopup: plainPopup property alias modalPopupWithoutDim: modalPopupWithoutDim visible: true Drawer { - z: 5 - id: upperDrawer - } - Drawer { - z: 1 + z: 0 id: firstDrawer } Drawer { @@ -1073,52 +1068,41 @@ TestCase { window.requestActivate() tryCompare(window, "active", true) - var countBefore = window.overlay.children.length - compare(countBefore, 6) // 3 drawers + 3 overlays + compare(window.overlay.children.length, 0) var firstOverlay = findOverlay(window, window.firstDrawer) + verify(!firstOverlay) + window.firstDrawer.open() + compare(window.overlay.children.length, 2) // 1 drawer + 1 overlay + firstOverlay = findOverlay(window, window.firstDrawer) verify(firstOverlay) - compare(firstOverlay.opacity, 0.0) compare(firstOverlay.z, window.firstDrawer.z) compare(indexOf(window.overlay.children, firstOverlay), indexOf(window.overlay.children, window.firstDrawer.contentItem.parent) - 1) + tryCompare(firstOverlay, "opacity", 1.0) var secondOverlay = findOverlay(window, window.secondDrawer) + verify(!secondOverlay) + window.secondDrawer.open() + compare(window.overlay.children.length, 4) // 2 drawers + 2 overlays + secondOverlay = findOverlay(window, window.secondDrawer) verify(secondOverlay) - compare(secondOverlay.opacity, 0.0) compare(secondOverlay.z, window.secondDrawer.z) compare(indexOf(window.overlay.children, secondOverlay), indexOf(window.overlay.children, window.secondDrawer.contentItem.parent) - 1) + tryCompare(secondOverlay, "opacity", 1.0) - var upperOverlay = findOverlay(window, window.upperDrawer) - verify(upperOverlay) - compare(upperOverlay.opacity, 0.0) - compare(upperOverlay.z, window.upperDrawer.z) - compare(indexOf(window.overlay.children, upperOverlay), - indexOf(window.overlay.children, window.upperDrawer.contentItem.parent) - 1) - - window.firstDrawer.open() - compare(firstOverlay.z, 1.0) - tryCompare(firstOverlay, "opacity", 1.0) window.firstDrawer.close() - tryCompare(firstOverlay, "opacity", 0.0) tryCompare(window.firstDrawer, "visible", false) + firstOverlay = findOverlay(window, window.firstDrawer) + verify(!firstOverlay) + compare(window.overlay.children.length, 2) // 1 drawer + 1 overlay - window.secondDrawer.open() - compare(secondOverlay.z, 1.0) - tryCompare(secondOverlay, "opacity", 1.0) window.secondDrawer.close() - tryCompare(secondOverlay, "opacity", 0.0) tryCompare(window.secondDrawer, "visible", false) - - window.firstDrawer.open() - window.secondDrawer.open() - tryCompare(firstOverlay, "opacity", 1.0) - tryCompare(secondOverlay, "opacity", 1.0) - window.firstDrawer.close() - window.secondDrawer.close() - tryCompare(firstOverlay, "opacity", 0.0) - tryCompare(secondOverlay, "opacity", 0.0) + secondOverlay = findOverlay(window, window.secondDrawer) + verify(!secondOverlay) + compare(window.overlay.children.length, 0) var modalOverlay = findOverlay(window, window.modalPopup) verify(!modalOverlay) @@ -1128,7 +1112,7 @@ TestCase { compare(modalOverlay.z, window.modalPopup.z) compare(window.modalPopup.visible, true) tryCompare(modalOverlay, "opacity", 1.0) - compare(window.overlay.children.length, countBefore + 2) // 1 popup + 1 overlay + compare(window.overlay.children.length, 2) // 1 popup + 1 overlay var modelessOverlay = findOverlay(window, window.modelessPopup) verify(!modelessOverlay) @@ -1138,13 +1122,13 @@ TestCase { compare(modelessOverlay.z, window.modelessPopup.z) compare(window.modelessPopup.visible, true) tryCompare(modelessOverlay, "opacity", 1.0) - compare(window.overlay.children.length, countBefore + 4) // 2 popups + 2 overlays + compare(window.overlay.children.length, 4) // 2 popups + 2 overlays window.modelessPopup.close() tryCompare(window.modelessPopup, "visible", false) modelessOverlay = findOverlay(window, window.modelessPopup) verify(!modelessOverlay) - compare(window.overlay.children.length, countBefore + 2) // 1 popup + 1 overlay + compare(window.overlay.children.length, 2) // 1 popup + 1 overlay compare(window.modalPopup.visible, true) compare(modalOverlay.opacity, 1.0) @@ -1153,23 +1137,29 @@ TestCase { tryCompare(window.modalPopup, "visible", false) modalOverlay = findOverlay(window, window.modalPopup) verify(!modalOverlay) - compare(window.overlay.children.length, countBefore) + compare(window.overlay.children.length, 0) window.plainPopup.open() tryCompare(window.plainPopup, "visible", true) - compare(window.overlay.children.length, countBefore + 1) // only popup added, no overlays involved + compare(window.overlay.children.length, 1) // only popup added, no overlays involved + + window.plainPopup.modal = true + compare(window.overlay.children.length, 2) // overlay added window.plainPopup.close() tryCompare(window.plainPopup, "visible", false) - compare(window.overlay.children.length, countBefore) // only popup removed, no overlays involved + compare(window.overlay.children.length, 0) // popup + overlay removed window.modalPopupWithoutDim.open() tryCompare(window.modalPopupWithoutDim, "visible", true) - compare(window.overlay.children.length, countBefore + 1) // only popup added, no overlays involved + compare(window.overlay.children.length, 1) // only popup added, no overlays involved + + window.modalPopupWithoutDim.dim = true + compare(window.overlay.children.length, 2) // overlay added window.modalPopupWithoutDim.close() tryCompare(window.modalPopupWithoutDim, "visible", false) - compare(window.overlay.children.length, countBefore) // only popup added, no overlays involved + compare(window.overlay.children.length, 0) // popup + overlay removed window.destroy() } diff --git a/tests/auto/controls/data/tst_roundbutton.qml b/tests/auto/controls/data/tst_roundbutton.qml new file mode 100644 index 00000000..aa956776 --- /dev/null +++ b/tests/auto/controls/data/tst_roundbutton.qml @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.2 +import QtTest 1.0 +import QtQuick.Controls 2.1 + +TestCase { + id: testCase + width: 200 + height: 200 + visible: true + when: windowShown + name: "RoundButton" + + Component { + id: roundButton + RoundButton { } + } + + function test_radius() { + var control = roundButton.createObject(testCase); + verify(control); + + var implicitRadius = control.radius; + compare(implicitRadius, Math.min(control.width, control.height) / 2); + + control.radius = 10; + compare(control.radius, 10); + + control.radius = undefined; + compare(control.radius, implicitRadius); + + control.width = -1; + compare(control.radius, 0); + + control.width = 10; + compare(control.radius, 5); + + control.destroy(); + } +} diff --git a/tests/auto/controls/data/tst_scrollindicator.qml b/tests/auto/controls/data/tst_scrollindicator.qml index 00730bf4..551247f6 100644 --- a/tests/auto/controls/data/tst_scrollindicator.qml +++ b/tests/auto/controls/data/tst_scrollindicator.qml @@ -157,4 +157,38 @@ TestCase { ignoreWarning(Qt.resolvedUrl("tst_scrollindicator.qml") + ":45:1: QML TestCase: ScrollIndicator must be attached to a Flickable") testCase.ScrollIndicator.vertical = null } + + function test_overshoot() { + var container = flickable.createObject(testCase) + verify(container) + waitForRendering(container) + + var vertical = scrollIndicator.createObject(container, {size: 0.5}) + container.ScrollIndicator.vertical = vertical + + var horizontal = scrollIndicator.createObject(container, {size: 0.5}) + container.ScrollIndicator.horizontal = horizontal + + // negative vertical overshoot (pos < 0) + vertical.position = -0.1 + compare(vertical.contentItem.y, vertical.topPadding) + compare(vertical.contentItem.height, 0.4 * vertical.availableHeight) + + // positive vertical overshoot (pos + size > 1) + vertical.position = 0.8 + compare(vertical.contentItem.y, vertical.topPadding + 0.8 * vertical.availableHeight) + compare(vertical.contentItem.height, 0.2 * vertical.availableHeight) + + // negative horizontal overshoot (pos < 0) + horizontal.position = -0.1 + compare(horizontal.contentItem.x, horizontal.leftPadding) + compare(horizontal.contentItem.width, 0.4 * horizontal.availableWidth) + + // positive horizontal overshoot (pos + size > 1) + horizontal.position = 0.8 + compare(horizontal.contentItem.x, horizontal.leftPadding + 0.8 * horizontal.availableWidth) + compare(horizontal.contentItem.width, 0.2 * horizontal.availableWidth) + + container.destroy() + } } diff --git a/tests/auto/controls/data/tst_stackview.qml b/tests/auto/controls/data/tst_stackview.qml index 2de0fb98..4de8bed0 100644 --- a/tests/auto/controls/data/tst_stackview.qml +++ b/tests/auto/controls/data/tst_stackview.qml @@ -680,6 +680,60 @@ TestCase { } Component { + id: removeComponent + + Item { + objectName: "removeItem" + StackView.onRemoved: destroy() + } + } + + function test_destroyOnRemoved() { + var control = stackView.createObject(testCase, { initialItem: component }) + verify(control) + + var item = removeComponent.createObject(control) + verify(item) + + var removedSpy = signalSpy.createObject(control, { target: item.StackView, signalName: "removed" }) + verify(removedSpy) + verify(removedSpy.valid) + + var destructionSpy = signalSpy.createObject(control, { target: item.Component, signalName: "destruction" }) + verify(destructionSpy) + verify(destructionSpy.valid) + + // push-pop + control.push(item, StackView.Immediate) + compare(control.currentItem, item) + control.pop(StackView.Transition) + item = null + tryCompare(removedSpy, "count", 1) + tryCompare(destructionSpy, "count", 1) + compare(control.busy, false) + + item = removeComponent.createObject(control) + verify(item) + + removedSpy.target = item.StackView + verify(removedSpy.valid) + + destructionSpy.target = item.Component + verify(destructionSpy.valid) + + // push-replace + control.push(item, StackView.Immediate) + compare(control.currentItem, item) + control.replace(component, StackView.Transition) + item = null + tryCompare(removedSpy, "count", 2) + tryCompare(destructionSpy, "count", 2) + compare(control.busy, false) + + control.destroy() + } + + Component { id: attachedItem Item { property int index: StackView.index diff --git a/tests/auto/controls/data/tst_switch.qml b/tests/auto/controls/data/tst_switch.qml index b75e2713..6c145fc1 100644 --- a/tests/auto/controls/data/tst_switch.qml +++ b/tests/auto/controls/data/tst_switch.qml @@ -50,42 +50,16 @@ TestCase { when: windowShown name: "Switch" - SignalSpy { - id: checkedSpy - signalName: "checkedChanged" - } - - SignalSpy { - id: pressedSpy - signalName: "pressedChanged" - } - - SignalSpy { - id: clickedSpy - signalName: "clicked" - } - Component { id: swtch Switch { } } - function init() { - verify(!checkedSpy.target) - verify(!pressedSpy.target) - verify(!clickedSpy.target) - compare(checkedSpy.count, 0) - compare(pressedSpy.count, 0) - compare(clickedSpy.count, 0) - } - - function cleanup() { - checkedSpy.target = null - pressedSpy.target = null - clickedSpy.target = null - checkedSpy.clear() - pressedSpy.clear() - clickedSpy.clear() + Component { + id: signalSequenceSpy + SignalSequenceSpy { + signals: ["pressed", "released", "canceled", "clicked", "pressedChanged", "checkedChanged"] + } } function test_text() { @@ -105,19 +79,18 @@ TestCase { var control = swtch.createObject(testCase) verify(control) - checkedSpy.target = control - verify(checkedSpy.valid) - compare(control.checked, false) - compare(checkedSpy.count, 0) + var spy = signalSequenceSpy.createObject(control, {target: control}) + spy.expectedSequence = [["checkedChanged", { "checked": true }]] control.checked = true compare(control.checked, true) - compare(checkedSpy.count, 1) + verify(spy.success) + spy.expectedSequence = [["checkedChanged", { "checked": false }]] control.checked = false compare(control.checked, false) - compare(checkedSpy.count, 2) + verify(spy.success) control.destroy() } @@ -126,71 +99,80 @@ TestCase { var control = swtch.createObject(testCase) verify(control) - checkedSpy.target = control - pressedSpy.target = control - clickedSpy.target = control - verify(checkedSpy.valid) - verify(pressedSpy.valid) - verify(clickedSpy.valid) - // check + var spy = signalSequenceSpy.createObject(control, {target: control}) + spy.expectedSequence = [["pressedChanged", { "pressed": true, "checked": false }], + "pressed"] mousePress(control, control.width / 2, control.height / 2, Qt.LeftButton) - compare(pressedSpy.count, 1) compare(control.pressed, true) + verify(spy.success) + spy.expectedSequence = [["pressedChanged", { "pressed": false, "checked": false }], + ["checkedChanged", { "pressed": false, "checked": true }], + "released", + "clicked"] mouseRelease(control, control.width / 2, control.height / 2, Qt.LeftButton) - compare(clickedSpy.count, 1) - compare(checkedSpy.count, 1) - compare(pressedSpy.count, 2) compare(control.checked, true) compare(control.pressed, false) + verify(spy.success) // uncheck + spy.expectedSequence = [["pressedChanged", { "pressed": true, "checked": true }], + "pressed"] mousePress(control, control.width / 2, control.height / 2, Qt.LeftButton) - compare(pressedSpy.count, 3) compare(control.pressed, true) + verify(spy.success) + spy.expectedSequence = [["pressedChanged", { "pressed": false, "checked": true }], + ["checkedChanged", { "pressed": false, "checked": false }], + "released", + "clicked"] mouseRelease(control, control.width / 2, control.height / 2, Qt.LeftButton) - compare(clickedSpy.count, 2) - compare(checkedSpy.count, 2) - compare(pressedSpy.count, 4) compare(control.checked, false) compare(control.pressed, false) + verify(spy.success) // release on the right + spy.expectedSequence = [["pressedChanged", { "pressed": true, "checked": false }], + "pressed"] mousePress(control, control.width / 2, control.height / 2, Qt.LeftButton) - compare(pressedSpy.count, 5) compare(control.pressed, true) + verify(spy.success) mouseMove(control, control.width * 2, control.height / 2, 0, Qt.LeftButton) compare(control.pressed, true) + spy.expectedSequence = [["pressedChanged", { "pressed": false, "checked": false }], + ["checkedChanged", { "pressed": false, "checked": true }], + "released", + "clicked"] mouseRelease(control, control.width * 2, control.height / 2, Qt.LeftButton) - compare(clickedSpy.count, 3) - compare(checkedSpy.count, 3) - compare(pressedSpy.count, 6) compare(control.checked, true) compare(control.pressed, false) + verify(spy.success) // release on the left + spy.expectedSequence = [["pressedChanged", { "pressed": true, "checked": true }], + "pressed"] mousePress(control, control.width / 2, control.height / 2, Qt.LeftButton) - compare(pressedSpy.count, 7) compare(control.pressed, true) + verify(spy.success) mouseMove(control, -control.width, control.height / 2, 0, Qt.LeftButton) compare(control.pressed, true) + spy.expectedSequence = [["pressedChanged", { "pressed": false, "checked": true }], + ["checkedChanged", { "pressed": false, "checked": false }], + "released", + "clicked"] mouseRelease(control, -control.width, control.height / 2, Qt.LeftButton) - compare(clickedSpy.count, 4) - compare(checkedSpy.count, 4) - compare(pressedSpy.count, 8) compare(control.checked, false) compare(control.pressed, false) + verify(spy.success) // right button + spy.expectedSequence = [] mousePress(control, control.width / 2, control.height / 2, Qt.RightButton) - compare(pressedSpy.count, 8) compare(control.pressed, false) + verify(spy.success) mouseRelease(control, control.width / 2, control.height / 2, Qt.RightButton) - compare(clickedSpy.count, 4) - compare(checkedSpy.count, 4) - compare(pressedSpy.count, 8) compare(control.checked, false) compare(control.pressed, false) + verify(spy.success) control.destroy() } @@ -199,33 +181,39 @@ TestCase { var control = swtch.createObject(testCase) verify(control) - checkedSpy.target = control - clickedSpy.target = control - verify(checkedSpy.valid) - verify(clickedSpy.valid) - control.forceActiveFocus() verify(control.activeFocus) // check + var spy = signalSequenceSpy.createObject(control, {target: control}) + spy.expectedSequence = [["pressedChanged", { "pressed": true, "checked": false }], + "pressed", + ["pressedChanged", { "pressed": false, "checked": false }], + ["checkedChanged", { "pressed": false, "checked": true }], + "released", + "clicked"] keyClick(Qt.Key_Space) - compare(clickedSpy.count, 1) - compare(checkedSpy.count, 1) compare(control.checked, true) + verify(spy.success) // uncheck + spy.expectedSequence = [["pressedChanged", { "pressed": true, "checked": true }], + "pressed", + ["pressedChanged", { "pressed": false, "checked": true }], + ["checkedChanged", { "pressed": false, "checked": false }], + "released", + "clicked"] keyClick(Qt.Key_Space) - compare(clickedSpy.count, 2) - compare(checkedSpy.count, 2) compare(control.checked, false) + verify(spy.success) // no change + spy.expectedSequence = [] var keys = [Qt.Key_Enter, Qt.Key_Return, Qt.Key_Escape, Qt.Key_Tab] for (var i = 0; i < keys.length; ++i) { keyClick(keys[i]) - compare(clickedSpy.count, 2) - compare(checkedSpy.count, 2) compare(control.checked, false) + verify(spy.success) } control.destroy() @@ -263,4 +251,15 @@ TestCase { compare(control.baselineOffset, control.contentItem.y + control.contentItem.baselineOffset) control.destroy() } + + function test_focus() { + var control = swtch.createObject(testCase) + verify(control) + + verify(!control.activeFocus) + mouseClick(control.indicator) + verify(control.activeFocus) + + control.destroy() + } } diff --git a/tests/auto/controls/data/tst_textfield.qml b/tests/auto/controls/data/tst_textfield.qml index 1f1de748..5dc93287 100644 --- a/tests/auto/controls/data/tst_textfield.qml +++ b/tests/auto/controls/data/tst_textfield.qml @@ -72,6 +72,7 @@ TestCase { var implicitWidthSpy = signalSpy.createObject(control, { target: control, signalName: "implicitWidthChanged"} ) var implicitHeightSpy = signalSpy.createObject(control, { target: control, signalName: "implicitHeightChanged"} ) + control.background.implicitWidth = 400 control.background.implicitHeight = 200 compare(control.implicitWidth, 400) @@ -79,6 +80,24 @@ TestCase { compare(implicitWidthSpy.count, 1) compare(implicitHeightSpy.count, 1) + control.background = null + compare(control.implicitWidth, control.leftPadding + control.rightPadding) + compare(control.implicitHeight, control.contentHeight + control.topPadding + control.bottomPadding) + compare(implicitWidthSpy.count, 2) + compare(implicitHeightSpy.count, 2) + + control.text = "TextField" + compare(control.implicitWidth, control.contentWidth + control.leftPadding + control.rightPadding) + compare(control.implicitHeight, control.contentHeight + control.topPadding + control.bottomPadding) + compare(implicitWidthSpy.count, 3) + compare(implicitHeightSpy.count, 2) + + control.placeholderText = "..." + verify(control.implicitWidth < control.contentWidth + control.leftPadding + control.rightPadding) + compare(control.implicitHeight, control.contentHeight + control.topPadding + control.bottomPadding) + compare(implicitWidthSpy.count, 4) + compare(implicitHeightSpy.count, 2) + control.destroy() } diff --git a/tests/auto/drawer/data/hover.qml b/tests/auto/drawer/data/applicationwindow-hover.qml index 5ac41457..5ac41457 100644 --- a/tests/auto/drawer/data/hover.qml +++ b/tests/auto/drawer/data/applicationwindow-hover.qml diff --git a/tests/auto/drawer/data/applicationwindow.qml b/tests/auto/drawer/data/applicationwindow.qml index a826efab..0aa4b34e 100644 --- a/tests/auto/drawer/data/applicationwindow.qml +++ b/tests/auto/drawer/data/applicationwindow.qml @@ -51,12 +51,5 @@ ApplicationWindow { id: drawer width: 200 height: 200 - - MouseArea { - // QTBUG-54629 - anchors.fill: parent - anchors.margins: -Qt.styleHints.startDragDistance - Rectangle { color: "red"; opacity: 0.25; anchors.fill: parent } - } } } diff --git a/tests/auto/drawer/data/multiple.qml b/tests/auto/drawer/data/multiple.qml new file mode 100644 index 00000000..b2ef9781 --- /dev/null +++ b/tests/auto/drawer/data/multiple.qml @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.6 +import QtQuick.Controls 2.0 + +ApplicationWindow { + width: 400 + height: 400 + + property alias leftDrawer: leftDrawer + property alias leftButton: leftButton + + property alias rightDrawer: rightDrawer + property alias rightButton: rightButton + + property alias contentButton: contentButton + + Drawer { + id: leftDrawer + width: 300 + height: 400 + z: 1 + + contentItem: Button { + id: leftButton + text: "Left" + } + } + + Button { + id: contentButton + text: "Content" + anchors.fill: parent + } + + Drawer { + id: rightDrawer + width: 300 + height: 400 + edge: Qt.RightEdge + + contentItem: Button { + id: rightButton + text: "Right" + } + } +} diff --git a/tests/auto/drawer/data/window-hover.qml b/tests/auto/drawer/data/window-hover.qml new file mode 100644 index 00000000..872b934e --- /dev/null +++ b/tests/auto/drawer/data/window-hover.qml @@ -0,0 +1,73 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.6 +import QtQuick.Window 2.2 +import QtQuick.Controls 2.0 + +Window { + width: 400 + height: 400 + + property alias drawer: drawer + property alias backgroundButton: backgroundButton + property alias drawerButton: drawerButton + + Button { + id: backgroundButton + text: "Background" + anchors.fill: parent + } + + Drawer { + id: drawer + width: 100 + height: 400 + topPadding: 2 + leftPadding: 2 + rightPadding: 2 + bottomPadding: 2 + + contentItem: Button { + id: drawerButton + text: "Drawer" + } + } +} diff --git a/tests/auto/drawer/data/window.qml b/tests/auto/drawer/data/window.qml new file mode 100644 index 00000000..47ef5cb0 --- /dev/null +++ b/tests/auto/drawer/data/window.qml @@ -0,0 +1,56 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.6 +import QtQuick.Window 2.2 +import QtQuick.Controls 2.0 + +Window { + width: 400 + height: 400 + + property alias drawer: drawer + + Drawer { + id: drawer + width: 200 + height: 200 + } +} diff --git a/tests/auto/drawer/tst_drawer.cpp b/tests/auto/drawer/tst_drawer.cpp index 0507d01e..8f023c29 100644 --- a/tests/auto/drawer/tst_drawer.cpp +++ b/tests/auto/drawer/tst_drawer.cpp @@ -42,6 +42,7 @@ #include <QtGui/qstylehints.h> #include <QtGui/qguiapplication.h> #include <QtQuickTemplates2/private/qquickapplicationwindow_p.h> +#include <QtQuickTemplates2/private/qquickoverlay_p.h> #include <QtQuickTemplates2/private/qquickdrawer_p.h> #include <QtQuickTemplates2/private/qquickbutton_p.h> @@ -52,6 +53,11 @@ class tst_Drawer : public QQmlDataTest Q_OBJECT private slots: + void visible_data(); + void visible(); + + void state(); + void position_data(); void position(); @@ -62,8 +68,158 @@ private slots: void hover_data(); void hover(); + + void multiple(); }; +void tst_Drawer::visible_data() +{ + QTest::addColumn<QString>("source"); + QTest::newRow("Window") << "window.qml"; + QTest::newRow("ApplicationWindow") << "applicationwindow.qml"; +} + +void tst_Drawer::visible() +{ + QFETCH(QString, source); + QQuickApplicationHelper helper(this, source); + + QQuickWindow *window = helper.window; + window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window)); + + QQuickDrawer *drawer = window->property("drawer").value<QQuickDrawer*>(); + QVERIFY(drawer); + QQuickItem *popupItem = drawer->popupItem(); + + QCOMPARE(drawer->isVisible(), false); + QCOMPARE(drawer->position(), qreal(0.0)); + + QQuickOverlay *overlay = QQuickOverlay::overlay(window); + QVERIFY(overlay); + QVERIFY(!overlay->childItems().contains(popupItem)); + + drawer->open(); + QVERIFY(drawer->isVisible()); + QVERIFY(overlay->childItems().contains(popupItem)); + QTRY_COMPARE(drawer->position(), qreal(1.0)); + + drawer->close(); + QTRY_VERIFY(!drawer->isVisible()); + QTRY_COMPARE(drawer->position(), qreal(0.0)); + QVERIFY(!overlay->childItems().contains(popupItem)); + + drawer->setVisible(true); + QVERIFY(drawer->isVisible()); + QVERIFY(overlay->childItems().contains(popupItem)); + QTRY_COMPARE(drawer->position(), qreal(1.0)); + + drawer->setVisible(false); + QTRY_VERIFY(!drawer->isVisible()); + QTRY_COMPARE(drawer->position(), qreal(0.0)); + QTRY_VERIFY(!overlay->childItems().contains(popupItem)); +} + +void tst_Drawer::state() +{ + QQuickApplicationHelper helper(this, "applicationwindow.qml"); + + QQuickWindow *window = helper.window; + window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window)); + + QQuickDrawer *drawer = window->property("drawer").value<QQuickDrawer*>(); + QVERIFY(drawer); + + QCOMPARE(drawer->isVisible(), false); + + QSignalSpy visibleChangedSpy(drawer, SIGNAL(visibleChanged())); + QSignalSpy aboutToShowSpy(drawer, SIGNAL(aboutToShow())); + QSignalSpy aboutToHideSpy(drawer, SIGNAL(aboutToHide())); + QSignalSpy openedSpy(drawer, SIGNAL(opened())); + QSignalSpy closedSpy(drawer, SIGNAL(closed())); + + QVERIFY(visibleChangedSpy.isValid()); + QVERIFY(aboutToShowSpy.isValid()); + QVERIFY(aboutToHideSpy.isValid()); + QVERIFY(openedSpy.isValid()); + QVERIFY(closedSpy.isValid()); + + int visibleChangedCount = 0; + int aboutToShowCount = 0; + int aboutToHideCount = 0; + int openedCount = 0; + int closedCount = 0; + + // open programmatically... + drawer->open(); + QCOMPARE(visibleChangedSpy.count(), ++visibleChangedCount); + QCOMPARE(aboutToShowSpy.count(), ++aboutToShowCount); + QCOMPARE(aboutToHideSpy.count(), aboutToHideCount); + QCOMPARE(openedSpy.count(), openedCount); + QCOMPARE(closedSpy.count(), closedCount); + + // ...and wait until fully open + QVERIFY(openedSpy.wait()); + QCOMPARE(visibleChangedSpy.count(), visibleChangedCount); + QCOMPARE(aboutToShowSpy.count(), aboutToShowCount); + QCOMPARE(aboutToHideSpy.count(), aboutToHideCount); + QCOMPARE(openedSpy.count(), ++openedCount); + QCOMPARE(closedSpy.count(), closedCount); + + // close programmatically... + drawer->close(); + QCOMPARE(visibleChangedSpy.count(), visibleChangedCount); + QCOMPARE(aboutToShowSpy.count(), aboutToShowCount); + QCOMPARE(aboutToHideSpy.count(), ++aboutToHideCount); + QCOMPARE(openedSpy.count(), openedCount); + QCOMPARE(closedSpy.count(), closedCount); + + // ...and wait until fully closed + QVERIFY(closedSpy.wait()); + QCOMPARE(visibleChangedSpy.count(), ++visibleChangedCount); + QCOMPARE(aboutToShowSpy.count(), aboutToShowCount); + QCOMPARE(aboutToHideSpy.count(), aboutToHideCount); + QCOMPARE(openedSpy.count(), openedCount); + QCOMPARE(closedSpy.count(), ++closedCount); + + // open interactively... + QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, QPoint(0, drawer->height() / 2)); + QTest::mouseMove(window, QPoint(drawer->width() * 0.2, drawer->height() / 2), 16); + QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, QPoint(drawer->width() * 0.8, drawer->height() / 2), 16); + QCOMPARE(visibleChangedSpy.count(), ++visibleChangedCount); + QCOMPARE(aboutToShowSpy.count(), ++aboutToShowCount); + QCOMPARE(aboutToHideSpy.count(), aboutToHideCount); + QCOMPARE(openedSpy.count(), openedCount); + QCOMPARE(closedSpy.count(), closedCount); + + // ...and wait until fully open + QVERIFY(openedSpy.wait()); + QCOMPARE(visibleChangedSpy.count(), visibleChangedCount); + QCOMPARE(aboutToShowSpy.count(), aboutToShowCount); + QCOMPARE(aboutToHideSpy.count(), aboutToHideCount); + QCOMPARE(openedSpy.count(), ++openedCount); + QCOMPARE(closedSpy.count(), closedCount); + + // close interactively... + QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, QPoint(drawer->width(), drawer->height() / 2)); + QTest::mouseMove(window, QPoint(drawer->width() * 0.8, drawer->height() / 2), 16); + QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, QPoint(drawer->width() * 0.2, drawer->height() / 2), 16); + QCOMPARE(visibleChangedSpy.count(), visibleChangedCount); + QCOMPARE(aboutToShowSpy.count(), aboutToShowCount); + QCOMPARE(aboutToHideSpy.count(), ++aboutToHideCount); + QCOMPARE(openedSpy.count(), openedCount); + QCOMPARE(closedSpy.count(), closedCount); + + // ...and wait until fully closed + QVERIFY(closedSpy.wait()); + QCOMPARE(visibleChangedSpy.count(), ++visibleChangedCount); + QCOMPARE(aboutToShowSpy.count(), aboutToShowCount); + QCOMPARE(aboutToHideSpy.count(), aboutToHideCount); + QCOMPARE(openedSpy.count(), openedCount); + QCOMPARE(closedSpy.count(), ++closedCount); +} + void tst_Drawer::position_data() { QTest::addColumn<Qt::Edge>("edge"); @@ -86,19 +242,22 @@ void tst_Drawer::position() QQuickApplicationHelper helper(this, QStringLiteral("applicationwindow.qml")); - QQuickApplicationWindow *window = helper.window; + QQuickApplicationWindow *window = helper.appWindow; window->show(); window->requestActivate(); QVERIFY(QTest::qWaitForWindowActive(window)); - QQuickDrawer *drawer = helper.window->property("drawer").value<QQuickDrawer*>(); + QQuickDrawer *drawer = helper.appWindow->property("drawer").value<QQuickDrawer*>(); QVERIFY(drawer); drawer->setEdge(edge); QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, from); QTest::mouseMove(window, to); QCOMPARE(drawer->position(), position); + + // moved half-way open at almost infinite speed => snap to open QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, to); + QTRY_COMPARE(drawer->position(), 1.0); } void tst_Drawer::dragMargin_data() @@ -110,8 +269,8 @@ void tst_Drawer::dragMargin_data() QTest::newRow("left:0") << Qt::LeftEdge << qreal(0) << qreal(0) << qreal(0); QTest::newRow("left:-1") << Qt::LeftEdge << qreal(-1) << qreal(0) << qreal(0); - QTest::newRow("left:startDragDistance") << Qt::LeftEdge << qreal(QGuiApplication::styleHints()->startDragDistance()) << qreal(0.25) << qreal(0); - QTest::newRow("left:startDragDistance*2") << Qt::LeftEdge << qreal(QGuiApplication::styleHints()->startDragDistance() * 2) << qreal(0.25) << qreal(0); + QTest::newRow("left:startDragDistance") << Qt::LeftEdge << qreal(QGuiApplication::styleHints()->startDragDistance()) << qreal(0.45) << qreal(0); + QTest::newRow("left:startDragDistance*2") << Qt::LeftEdge << qreal(QGuiApplication::styleHints()->startDragDistance() * 2) << qreal(0.45) << qreal(0); QTest::newRow("right:0") << Qt::RightEdge << qreal(0) << qreal(0) << qreal(0); QTest::newRow("right:-1") << Qt::RightEdge << qreal(-1) << qreal(0) << qreal(0); @@ -128,84 +287,92 @@ void tst_Drawer::dragMargin() QQuickApplicationHelper helper(this, QStringLiteral("applicationwindow.qml")); - QQuickApplicationWindow *window = helper.window; + QQuickApplicationWindow *window = helper.appWindow; window->show(); window->requestActivate(); QVERIFY(QTest::qWaitForWindowActive(window)); - QQuickDrawer *drawer = helper.window->property("drawer").value<QQuickDrawer*>(); + QQuickDrawer *drawer = helper.appWindow->property("drawer").value<QQuickDrawer*>(); QVERIFY(drawer); drawer->setEdge(edge); drawer->setDragMargin(dragMargin); // drag from the left int leftX = qMax<int>(0, dragMargin); + int leftDistance = drawer->width() * 0.45; + QVERIFY(leftDistance > QGuiApplication::styleHints()->startDragDistance()); QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, QPoint(leftX, drawer->height() / 2)); - QTest::mouseMove(window, QPoint(drawer->width() * 0.25, drawer->height() / 2)); + QTest::mouseMove(window, QPoint(leftDistance, drawer->height() / 2)); QCOMPARE(drawer->position(), dragFromLeft); - QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, QPoint(drawer->width() * 0.25, drawer->height() / 2)); + QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, QPoint(leftDistance, drawer->height() / 2)); drawer->close(); QTRY_COMPARE(drawer->position(), qreal(0.0)); // drag from the right int rightX = qMin<int>(window->width() - 1, window->width() - dragMargin); + int rightDistance = drawer->width() * 0.75; + QVERIFY(rightDistance > QGuiApplication::styleHints()->startDragDistance()); QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, QPoint(rightX, drawer->height() / 2)); - QTest::mouseMove(window, QPoint(window->width() - drawer->width() * 0.75, drawer->height() / 2)); + QTest::mouseMove(window, QPoint(window->width() - rightDistance, drawer->height() / 2)); QCOMPARE(drawer->position(), dragFromRight); - QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, QPoint(window->width() - drawer->width() * 0.75, drawer->height() / 2)); + QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, QPoint(window->width() - rightDistance, drawer->height() / 2)); } void tst_Drawer::reposition() { QQuickApplicationHelper helper(this, QStringLiteral("applicationwindow.qml")); - QQuickApplicationWindow *window = helper.window; + QQuickApplicationWindow *window = helper.appWindow; window->show(); window->requestActivate(); QVERIFY(QTest::qWaitForWindowActive(window)); - QQuickDrawer *drawer = helper.window->property("drawer").value<QQuickDrawer*>(); + QQuickDrawer *drawer = helper.appWindow->property("drawer").value<QQuickDrawer*>(); QVERIFY(drawer); drawer->setEdge(Qt::RightEdge); drawer->open(); QTRY_COMPARE(drawer->popupItem()->x(), window->width() - drawer->width()); - drawer->close(); - QTRY_COMPARE(drawer->popupItem()->x(), static_cast<qreal>(window->width())); - window->setWidth(window->width() + 100); + QTRY_COMPARE(drawer->popupItem()->x(), window->width() - drawer->width()); + + drawer->close(); QTRY_COMPARE(drawer->popupItem()->x(), static_cast<qreal>(window->width())); } void tst_Drawer::hover_data() { + QTest::addColumn<QString>("source"); QTest::addColumn<bool>("modal"); - QTest::newRow("modal") << true; - QTest::newRow("modeless") << false; + QTest::newRow("Window:modal") << "window-hover.qml" << true; + QTest::newRow("Window:modeless") << "window-hover.qml" << false; + QTest::newRow("ApplicationWindow:modal") << "applicationwindow-hover.qml" << true; + QTest::newRow("ApplicationWindow:modeless") << "applicationwindow-hover.qml" << false; } void tst_Drawer::hover() { + QFETCH(QString, source); QFETCH(bool, modal); - QQuickApplicationHelper helper(this, QStringLiteral("hover.qml")); - QQuickApplicationWindow *window = helper.window; + QQuickApplicationHelper helper(this, source); + QQuickWindow *window = helper.window; window->show(); window->requestActivate(); QVERIFY(QTest::qWaitForWindowActive(window)); - QQuickDrawer *drawer = helper.window->property("drawer").value<QQuickDrawer*>(); + QQuickDrawer *drawer = window->property("drawer").value<QQuickDrawer*>(); QVERIFY(drawer); drawer->setModal(modal); - QQuickButton *backgroundButton = helper.window->property("backgroundButton").value<QQuickButton*>(); + QQuickButton *backgroundButton = window->property("backgroundButton").value<QQuickButton*>(); QVERIFY(backgroundButton); backgroundButton->setHoverEnabled(true); - QQuickButton *drawerButton = helper.window->property("drawerButton").value<QQuickButton*>(); + QQuickButton *drawerButton = window->property("drawerButton").value<QQuickButton*>(); QVERIFY(drawerButton); drawerButton->setHoverEnabled(true); @@ -239,6 +406,127 @@ void tst_Drawer::hover() QVERIFY(backgroundButton->isHovered()); } +void tst_Drawer::multiple() +{ + QQuickApplicationHelper helper(this, QStringLiteral("multiple.qml")); + QQuickWindow *window = helper.window; + window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window)); + + QQuickDrawer *leftDrawer = window->property("leftDrawer").value<QQuickDrawer*>(); + QVERIFY(leftDrawer); + QQuickButton *leftButton = window->property("leftButton").value<QQuickButton*>(); + QVERIFY(leftButton); + QSignalSpy leftClickSpy(leftButton, SIGNAL(clicked())); + QVERIFY(leftClickSpy.isValid()); + + QQuickDrawer *rightDrawer = window->property("rightDrawer").value<QQuickDrawer*>(); + QVERIFY(rightDrawer); + QQuickButton *rightButton = window->property("rightButton").value<QQuickButton*>(); + QVERIFY(rightButton); + QSignalSpy rightClickSpy(rightButton, SIGNAL(clicked())); + QVERIFY(rightClickSpy.isValid()); + + QQuickButton *contentButton = window->property("contentButton").value<QQuickButton*>(); + QVERIFY(contentButton); + QSignalSpy contentClickSpy(contentButton, SIGNAL(clicked())); + QVERIFY(contentClickSpy.isValid()); + + // no drawers open, click the content + QTest::mouseClick(window, Qt::LeftButton); + QCOMPARE(contentClickSpy.count(), 1); + QCOMPARE(leftClickSpy.count(), 0); + QCOMPARE(rightClickSpy.count(), 0); + + // drag the left drawer open + QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, QPoint(0, window->height() / 2)); + QTest::mouseMove(window, QPoint(leftDrawer->width() / 2, window->height() / 2)); + QCOMPARE(leftDrawer->position(), 0.5); + QCOMPARE(rightDrawer->position(), 0.0); + QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, QPoint(leftDrawer->width() / 2, window->height() / 2)); + QTRY_COMPARE(leftDrawer->position(), 1.0); + QCOMPARE(rightDrawer->position(), 0.0); + + // cannot drag the right drawer while the left drawer is open + QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, QPoint(window->width() - 1, window->height() / 2)); + QTest::mouseMove(window, QPoint(window->width() - leftDrawer->width() / 2, window->height() / 2)); + QCOMPARE(leftDrawer->position(), 1.0); + QCOMPARE(rightDrawer->position(), 0.0); + QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, QPoint(window->width() - leftDrawer->width() / 2, window->height() / 2)); + QCOMPARE(rightDrawer->position(), 0.0); + QCOMPARE(leftDrawer->position(), 1.0); + + // open the right drawer below the left drawer + rightDrawer->open(); + QTRY_COMPARE(rightDrawer->position(), 1.0); + + // click the left drawer's button + QTest::mouseClick(window, Qt::LeftButton); + QCOMPARE(contentClickSpy.count(), 1); + QCOMPARE(leftClickSpy.count(), 1); + QCOMPARE(rightClickSpy.count(), 0); + + // click the left drawer's background (button disabled, don't leak through to the right drawer below) + leftButton->setEnabled(false); + QTest::mouseClick(window, Qt::LeftButton); + QCOMPARE(contentClickSpy.count(), 1); + QCOMPARE(leftClickSpy.count(), 1); + QCOMPARE(rightClickSpy.count(), 0); + leftButton->setEnabled(true); + + // click the overlay of the left drawer (don't leak through to right drawer below) + QTest::mouseClick(window, Qt::LeftButton, Qt::NoModifier, QPoint(window->width() - (window->width() - leftDrawer->width()) / 2, window->height() / 2)); + QCOMPARE(contentClickSpy.count(), 1); + QCOMPARE(leftClickSpy.count(), 1); + QCOMPARE(rightClickSpy.count(), 0); + QTRY_VERIFY(!leftDrawer->isVisible()); + + // click the right drawer's button + QTest::mouseClick(window, Qt::LeftButton); + QCOMPARE(contentClickSpy.count(), 1); + QCOMPARE(leftClickSpy.count(), 1); + QCOMPARE(rightClickSpy.count(), 1); + + // cannot drag the left drawer while the right drawer is open + QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, QPoint(0, window->height() / 2)); + QTest::mouseMove(window, QPoint(leftDrawer->width() / 2, window->height() / 2)); + QCOMPARE(leftDrawer->position(), 0.0); + QCOMPARE(rightDrawer->position(), 1.0); + QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, QPoint(leftDrawer->width() / 2, window->height() / 2)); + QCOMPARE(leftDrawer->position(), 0.0); + QCOMPARE(rightDrawer->position(), 1.0); + + // click the right drawer's background (button disabled, don't leak through to the content below) + rightButton->setEnabled(false); + QTest::mouseClick(window, Qt::LeftButton); + QCOMPARE(contentClickSpy.count(), 1); + QCOMPARE(leftClickSpy.count(), 1); + QCOMPARE(rightClickSpy.count(), 1); + rightButton->setEnabled(true); + + // click the overlay of the right drawer (don't leak through to the content below) + QTest::mouseClick(window, Qt::LeftButton, Qt::NoModifier, QPoint((window->width() - rightDrawer->width()) / 2, window->height() / 2)); + QCOMPARE(contentClickSpy.count(), 1); + QCOMPARE(leftClickSpy.count(), 1); + QCOMPARE(rightClickSpy.count(), 1); + QTRY_VERIFY(!rightDrawer->isVisible()); + + // no drawers open, click the content + QTest::mouseClick(window, Qt::LeftButton); + QCOMPARE(contentClickSpy.count(), 2); + QCOMPARE(leftClickSpy.count(), 1); + QCOMPARE(rightClickSpy.count(), 1); + + // drag the right drawer open + QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, QPoint(window->width() - 1, window->height() / 2)); + QTest::mouseMove(window, QPoint(window->width() - rightDrawer->width() / 2, window->height() / 2)); + QCOMPARE(rightDrawer->position(), 0.5); + QCOMPARE(leftDrawer->position(), 0.0); + QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, QPoint(window->width() - rightDrawer->width() / 2, window->height() / 2)); + QTRY_COMPARE(rightDrawer->position(), 1.0); + QCOMPARE(leftDrawer->position(), 0.0); +} + QTEST_MAIN(tst_Drawer) #include "tst_drawer.moc" diff --git a/tests/auto/focus/tst_focus.cpp b/tests/auto/focus/tst_focus.cpp index cab2625a..dec4fecd 100644 --- a/tests/auto/focus/tst_focus.cpp +++ b/tests/auto/focus/tst_focus.cpp @@ -107,7 +107,7 @@ void tst_focus::navigation() QVERIFY(QTest::qWaitForWindowActive(&view)); QVERIFY(QGuiApplication::focusWindow() == &view); - foreach (const QString &name, order) { + for (const QString &name : qAsConst(order)) { QKeyEvent event(QEvent::KeyPress, key, Qt::NoModifier); QGuiApplication::sendEvent(&view, &event); QVERIFY(event.isAccepted()); diff --git a/tests/auto/menu/tst_menu.cpp b/tests/auto/menu/tst_menu.cpp index 28882f04..0635ad81 100644 --- a/tests/auto/menu/tst_menu.cpp +++ b/tests/auto/menu/tst_menu.cpp @@ -73,7 +73,7 @@ void tst_menu::defaults() { QQuickApplicationHelper helper(this, QLatin1String("applicationwindow.qml")); - QQuickMenu *emptyMenu = helper.window->property("emptyMenu").value<QQuickMenu*>(); + QQuickMenu *emptyMenu = helper.appWindow->property("emptyMenu").value<QQuickMenu*>(); QCOMPARE(emptyMenu->isVisible(), false); QCOMPARE(emptyMenu->contentItem()->property("currentIndex"), QVariant(-1)); } @@ -82,7 +82,7 @@ void tst_menu::mouse() { QQuickApplicationHelper helper(this, QLatin1String("applicationwindow.qml")); - QQuickApplicationWindow *window = helper.window; + QQuickApplicationWindow *window = helper.appWindow; window->show(); QVERIFY(QTest::qWaitForWindowActive(window)); @@ -154,7 +154,7 @@ void tst_menu::contextMenuKeyboard() QQuickApplicationHelper helper(this, QLatin1String("applicationwindow.qml")); - QQuickApplicationWindow *window = helper.window; + QQuickApplicationWindow *window = helper.appWindow; window->show(); window->requestActivate(); QVERIFY(QTest::qWaitForWindowActive(window)); @@ -232,7 +232,7 @@ void tst_menu::menuButton() QQuickApplicationHelper helper(this, QLatin1String("applicationwindow.qml")); - QQuickApplicationWindow *window = helper.window; + QQuickApplicationWindow *window = helper.appWindow; window->show(); window->requestActivate(); QVERIFY(QTest::qWaitForWindowActive(window)); @@ -256,7 +256,7 @@ void tst_menu::menuButton() void tst_menu::addItem() { QQuickApplicationHelper helper(this, QLatin1String("addItem.qml")); - QQuickApplicationWindow *window = helper.window; + QQuickApplicationWindow *window = helper.appWindow; window->show(); QVERIFY(QTest::qWaitForWindowActive(window)); @@ -277,7 +277,7 @@ void tst_menu::addItem() void tst_menu::menuSeparator() { QQuickApplicationHelper helper(this, QLatin1String("menuSeparator.qml")); - QQuickApplicationWindow *window = helper.window; + QQuickWindow *window = helper.window; window->show(); QVERIFY(QTest::qWaitForWindowActive(window)); diff --git a/tests/auto/popup/data/hover.qml b/tests/auto/popup/data/applicationwindow-hover.qml index 044d983c..044d983c 100644 --- a/tests/auto/popup/data/hover.qml +++ b/tests/auto/popup/data/applicationwindow-hover.qml diff --git a/tests/auto/popup/data/window-hover.qml b/tests/auto/popup/data/window-hover.qml new file mode 100644 index 00000000..78d650fa --- /dev/null +++ b/tests/auto/popup/data/window-hover.qml @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.6 +import QtQuick.Window 2.2 +import QtQuick.Controls 2.0 + +Window { + width: 400 + height: 400 + + property alias popup: popup + property alias parentButton: parentButton + property alias childButton: childButton + + Button { + id: parentButton + text: "Parent" + anchors.fill: parent + + Popup { + id: popup + x: 1 + y: 1 + padding: 1 + + Button { + id: childButton + text: "Child" + } + } + } +} diff --git a/tests/auto/popup/data/window.qml b/tests/auto/popup/data/window.qml new file mode 100644 index 00000000..92bfbd70 --- /dev/null +++ b/tests/auto/popup/data/window.qml @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.6 +import QtQuick.Window 2.2 +import QtQuick.Controls 2.0 + +Window { + width: 400 + height: 400 + + property alias popup: popup + property alias popup2: popup2 + property alias button: button + + Button { + id: button + text: "Open" + anchors.centerIn: parent + anchors.verticalCenterOffset: -height + + Popup { + id: popup + y: parent.height + + Text { + color: "white" + text: "Hello, world!" + + MouseArea { + anchors.fill: parent + onClicked: popup.close() + } + } + } + } + + Popup { + id: popup2 + y: popup.y + z: 1 + contentItem: Text { + text: "Popup2" + font.pixelSize: 36 + } + } +} diff --git a/tests/auto/popup/tst_popup.cpp b/tests/auto/popup/tst_popup.cpp index 1166b517..a6848fc8 100644 --- a/tests/auto/popup/tst_popup.cpp +++ b/tests/auto/popup/tst_popup.cpp @@ -51,8 +51,12 @@ class tst_popup : public QQmlDataTest Q_OBJECT private slots: + void visible_data(); void visible(); + void state(); + void overlay_data(); void overlay(); + void zOrder_data(); void zOrder(); void windowChange(); void closePolicy_data(); @@ -64,46 +68,107 @@ private slots: void parentDestroyed(); }; +void tst_popup::visible_data() +{ + QTest::addColumn<QString>("source"); + QTest::newRow("Window") << "window.qml"; + QTest::newRow("ApplicationWindow") << "applicationwindow.qml"; +} + void tst_popup::visible() { - QQuickApplicationHelper helper(this, QStringLiteral("applicationwindow.qml")); + QFETCH(QString, source); + QQuickApplicationHelper helper(this, source); - QQuickApplicationWindow *window = helper.window; + QQuickWindow *window = helper.window; window->show(); window->requestActivate(); QVERIFY(QTest::qWaitForWindowActive(window)); - QQuickPopup *popup = helper.window->property("popup").value<QQuickPopup*>(); + QQuickPopup *popup = window->property("popup").value<QQuickPopup*>(); QVERIFY(popup); QQuickItem *popupItem = popup->popupItem(); popup->open(); QVERIFY(popup->isVisible()); - QVERIFY(window->overlay()->childItems().contains(popupItem)); + + QQuickOverlay *overlay = QQuickOverlay::overlay(window); + QVERIFY(overlay); + QVERIFY(overlay->childItems().contains(popupItem)); popup->close(); QVERIFY(!popup->isVisible()); - QVERIFY(!window->overlay()->childItems().contains(popupItem)); + QVERIFY(!overlay->childItems().contains(popupItem)); popup->setVisible(true); QVERIFY(popup->isVisible()); - QVERIFY(window->overlay()->childItems().contains(popupItem)); + QVERIFY(overlay->childItems().contains(popupItem)); popup->setVisible(false); QVERIFY(!popup->isVisible()); - QVERIFY(!window->overlay()->childItems().contains(popupItem)); + QVERIFY(!overlay->childItems().contains(popupItem)); +} + +void tst_popup::state() +{ + QQuickApplicationHelper helper(this, "applicationwindow.qml"); + + QQuickWindow *window = helper.window; + window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window)); + + QQuickPopup *popup = window->property("popup").value<QQuickPopup*>(); + QVERIFY(popup); + + QCOMPARE(popup->isVisible(), false); + + QSignalSpy visibleChangedSpy(popup, SIGNAL(visibleChanged())); + QSignalSpy aboutToShowSpy(popup, SIGNAL(aboutToShow())); + QSignalSpy aboutToHideSpy(popup, SIGNAL(aboutToHide())); + QSignalSpy openedSpy(popup, SIGNAL(opened())); + QSignalSpy closedSpy(popup, SIGNAL(closed())); + + QVERIFY(visibleChangedSpy.isValid()); + QVERIFY(aboutToShowSpy.isValid()); + QVERIFY(aboutToHideSpy.isValid()); + QVERIFY(openedSpy.isValid()); + QVERIFY(closedSpy.isValid()); + + popup->open(); + QCOMPARE(visibleChangedSpy.count(), 1); + QCOMPARE(aboutToShowSpy.count(), 1); + QCOMPARE(aboutToHideSpy.count(), 0); + QTRY_COMPARE(openedSpy.count(), 1); + QCOMPARE(closedSpy.count(), 0); + + popup->close(); + QCOMPARE(visibleChangedSpy.count(), 2); + QCOMPARE(aboutToShowSpy.count(), 1); + QCOMPARE(aboutToHideSpy.count(), 1); + QCOMPARE(openedSpy.count(), 1); + QTRY_COMPARE(closedSpy.count(), 1); +} + +void tst_popup::overlay_data() +{ + QTest::addColumn<QString>("source"); + QTest::newRow("Window") << "window.qml"; + QTest::newRow("ApplicationWindow") << "applicationwindow.qml"; } void tst_popup::overlay() { - QQuickApplicationHelper helper(this, QStringLiteral("applicationwindow.qml")); + QFETCH(QString, source); + QQuickApplicationHelper helper(this, source); - QQuickApplicationWindow *window = helper.window; + QQuickWindow *window = helper.window; window->show(); window->requestActivate(); QVERIFY(QTest::qWaitForWindowActive(window)); - QQuickItem *overlay = window->overlay(); + QQuickOverlay *overlay = QQuickOverlay::overlay(window); + QVERIFY(overlay); + QSignalSpy overlayPressedSignal(overlay, SIGNAL(pressed())); QSignalSpy overlayReleasedSignal(overlay, SIGNAL(released())); QVERIFY(overlayPressedSignal.isValid()); @@ -115,10 +180,10 @@ void tst_popup::overlay() QCOMPARE(overlayPressedSignal.count(), 0); QCOMPARE(overlayReleasedSignal.count(), 0); - QQuickPopup *popup = helper.window->property("popup").value<QQuickPopup*>(); + QQuickPopup *popup = window->property("popup").value<QQuickPopup*>(); QVERIFY(popup); - QQuickButton *button = helper.window->property("button").value<QQuickButton*>(); + QQuickButton *button = window->property("button").value<QQuickButton*>(); QVERIFY(button); popup->open(); @@ -156,20 +221,28 @@ void tst_popup::overlay() QVERIFY(!overlay->isVisible()); } +void tst_popup::zOrder_data() +{ + QTest::addColumn<QString>("source"); + QTest::newRow("Window") << "window.qml"; + QTest::newRow("ApplicationWindow") << "applicationwindow.qml"; +} + void tst_popup::zOrder() { - QQuickApplicationHelper helper(this, QStringLiteral("applicationwindow.qml")); + QFETCH(QString, source); + QQuickApplicationHelper helper(this, source); - QQuickApplicationWindow *window = helper.window; + QQuickWindow *window = helper.window; window->show(); window->requestActivate(); QVERIFY(QTest::qWaitForWindowActive(window)); - QQuickPopup *popup = helper.window->property("popup").value<QQuickPopup*>(); + QQuickPopup *popup = window->property("popup").value<QQuickPopup*>(); QVERIFY(popup); popup->setModal(true); - QQuickPopup *popup2 = helper.window->property("popup2").value<QQuickPopup*>(); + QQuickPopup *popup2 = window->property("popup2").value<QQuickPopup*>(); QVERIFY(popup2); popup2->setModal(true); @@ -220,32 +293,42 @@ void tst_popup::closePolicy_data() { qRegisterMetaType<QQuickPopup::ClosePolicy>(); + QTest::addColumn<QString>("source"); QTest::addColumn<QQuickPopup::ClosePolicy>("closePolicy"); - QTest::newRow("NoAutoClose") << static_cast<QQuickPopup::ClosePolicy>(QQuickPopup::NoAutoClose); - QTest::newRow("CloseOnPressOutside") << static_cast<QQuickPopup::ClosePolicy>(QQuickPopup::CloseOnPressOutside); - QTest::newRow("CloseOnPressOutsideParent") << static_cast<QQuickPopup::ClosePolicy>(QQuickPopup::CloseOnPressOutsideParent); - QTest::newRow("CloseOnPressOutside|Parent") << static_cast<QQuickPopup::ClosePolicy>(QQuickPopup::CloseOnPressOutside | QQuickPopup::CloseOnPressOutsideParent); - QTest::newRow("CloseOnReleaseOutside") << static_cast<QQuickPopup::ClosePolicy>(QQuickPopup::CloseOnReleaseOutside); - QTest::newRow("CloseOnReleaseOutside|Parent") << static_cast<QQuickPopup::ClosePolicy>(QQuickPopup::CloseOnReleaseOutside | QQuickPopup::CloseOnReleaseOutsideParent); - QTest::newRow("CloseOnEscape") << static_cast<QQuickPopup::ClosePolicy>(QQuickPopup::CloseOnEscape); + QTest::newRow("Window:NoAutoClose") << "window.qml"<< static_cast<QQuickPopup::ClosePolicy>(QQuickPopup::NoAutoClose); + QTest::newRow("Window:CloseOnPressOutside") << "window.qml"<< static_cast<QQuickPopup::ClosePolicy>(QQuickPopup::CloseOnPressOutside); + QTest::newRow("Window:CloseOnPressOutsideParent") << "window.qml"<< static_cast<QQuickPopup::ClosePolicy>(QQuickPopup::CloseOnPressOutsideParent); + QTest::newRow("Window:CloseOnPressOutside|Parent") << "window.qml"<< static_cast<QQuickPopup::ClosePolicy>(QQuickPopup::CloseOnPressOutside | QQuickPopup::CloseOnPressOutsideParent); + QTest::newRow("Window:CloseOnReleaseOutside") << "window.qml"<< static_cast<QQuickPopup::ClosePolicy>(QQuickPopup::CloseOnReleaseOutside); + QTest::newRow("Window:CloseOnReleaseOutside|Parent") << "window.qml"<< static_cast<QQuickPopup::ClosePolicy>(QQuickPopup::CloseOnReleaseOutside | QQuickPopup::CloseOnReleaseOutsideParent); + QTest::newRow("Window:CloseOnEscape") << "window.qml"<< static_cast<QQuickPopup::ClosePolicy>(QQuickPopup::CloseOnEscape); + + QTest::newRow("ApplicationWindow:NoAutoClose") << "applicationwindow.qml"<< static_cast<QQuickPopup::ClosePolicy>(QQuickPopup::NoAutoClose); + QTest::newRow("ApplicationWindow:CloseOnPressOutside") << "applicationwindow.qml"<< static_cast<QQuickPopup::ClosePolicy>(QQuickPopup::CloseOnPressOutside); + QTest::newRow("ApplicationWindow:CloseOnPressOutsideParent") << "applicationwindow.qml"<< static_cast<QQuickPopup::ClosePolicy>(QQuickPopup::CloseOnPressOutsideParent); + QTest::newRow("ApplicationWindow:CloseOnPressOutside|Parent") << "applicationwindow.qml"<< static_cast<QQuickPopup::ClosePolicy>(QQuickPopup::CloseOnPressOutside | QQuickPopup::CloseOnPressOutsideParent); + QTest::newRow("ApplicationWindow:CloseOnReleaseOutside") << "applicationwindow.qml"<< static_cast<QQuickPopup::ClosePolicy>(QQuickPopup::CloseOnReleaseOutside); + QTest::newRow("ApplicationWindow:CloseOnReleaseOutside|Parent") << "applicationwindow.qml"<< static_cast<QQuickPopup::ClosePolicy>(QQuickPopup::CloseOnReleaseOutside | QQuickPopup::CloseOnReleaseOutsideParent); + QTest::newRow("ApplicationWindow:CloseOnEscape") << "applicationwindow.qml"<< static_cast<QQuickPopup::ClosePolicy>(QQuickPopup::CloseOnEscape); } void tst_popup::closePolicy() { + QFETCH(QString, source); QFETCH(QQuickPopup::ClosePolicy, closePolicy); - QQuickApplicationHelper helper(this, QStringLiteral("applicationwindow.qml")); + QQuickApplicationHelper helper(this, source); - QQuickApplicationWindow *window = helper.window; + QQuickWindow *window = helper.window; window->show(); window->requestActivate(); QVERIFY(QTest::qWaitForWindowActive(window)); - QQuickPopup *popup = helper.window->property("popup").value<QQuickPopup*>(); + QQuickPopup *popup = window->property("popup").value<QQuickPopup*>(); QVERIFY(popup); - QQuickButton *button = helper.window->property("button").value<QQuickButton*>(); + QQuickButton *button = window->property("button").value<QQuickButton*>(); QVERIFY(button); popup->setModal(true); @@ -308,15 +391,15 @@ void tst_popup::activeFocusOnClose1() // Test that a popup that never sets focus: true (e.g. ToolTip) doesn't affect // the active focus item when it closes. QQuickApplicationHelper helper(this, QStringLiteral("activeFocusOnClose1.qml")); - QQuickApplicationWindow *window = helper.window; + QQuickApplicationWindow *window = helper.appWindow; window->show(); window->requestActivate(); QVERIFY(QTest::qWaitForWindowActive(window)); - QQuickPopup *focusedPopup = helper.window->property("focusedPopup").value<QQuickPopup*>(); + QQuickPopup *focusedPopup = helper.appWindow->property("focusedPopup").value<QQuickPopup*>(); QVERIFY(focusedPopup); - QQuickPopup *nonFocusedPopup = helper.window->property("nonFocusedPopup").value<QQuickPopup*>(); + QQuickPopup *nonFocusedPopup = helper.appWindow->property("nonFocusedPopup").value<QQuickPopup*>(); QVERIFY(nonFocusedPopup); focusedPopup->open(); @@ -338,18 +421,18 @@ void tst_popup::activeFocusOnClose2() // calling forceActiveFocus() on another item) before it closes doesn't // affect the active focus item when it closes. QQuickApplicationHelper helper(this, QStringLiteral("activeFocusOnClose2.qml")); - QQuickApplicationWindow *window = helper.window; + QQuickApplicationWindow *window = helper.appWindow; window->show(); window->requestActivate(); QVERIFY(QTest::qWaitForWindowActive(window)); - QQuickPopup *popup1 = helper.window->property("popup1").value<QQuickPopup*>(); + QQuickPopup *popup1 = helper.appWindow->property("popup1").value<QQuickPopup*>(); QVERIFY(popup1); - QQuickPopup *popup2 = helper.window->property("popup2").value<QQuickPopup*>(); + QQuickPopup *popup2 = helper.appWindow->property("popup2").value<QQuickPopup*>(); QVERIFY(popup2); - QQuickButton *closePopup2Button = helper.window->property("closePopup2Button").value<QQuickButton*>(); + QQuickButton *closePopup2Button = helper.appWindow->property("closePopup2Button").value<QQuickButton*>(); QVERIFY(closePopup2Button); popup1->open(); @@ -369,31 +452,35 @@ void tst_popup::activeFocusOnClose2() void tst_popup::hover_data() { + QTest::addColumn<QString>("source"); QTest::addColumn<bool>("modal"); - QTest::newRow("modal") << true; - QTest::newRow("modeless") << false; + QTest::newRow("Window:modal") << "window-hover.qml" << true; + QTest::newRow("Window:modeless") << "window-hover.qml" << false; + QTest::newRow("ApplicationWindow:modal") << "applicationwindow-hover.qml" << true; + QTest::newRow("ApplicationWindow:modeless") << "applicationwindow-hover.qml" << false; } void tst_popup::hover() { + QFETCH(QString, source); QFETCH(bool, modal); - QQuickApplicationHelper helper(this, QStringLiteral("hover.qml")); - QQuickApplicationWindow *window = helper.window; + QQuickApplicationHelper helper(this, source); + QQuickWindow *window = helper.window; window->show(); window->requestActivate(); QVERIFY(QTest::qWaitForWindowActive(window)); - QQuickPopup *popup = helper.window->property("popup").value<QQuickPopup*>(); + QQuickPopup *popup = window->property("popup").value<QQuickPopup*>(); QVERIFY(popup); popup->setModal(modal); - QQuickButton *parentButton = helper.window->property("parentButton").value<QQuickButton*>(); + QQuickButton *parentButton = window->property("parentButton").value<QQuickButton*>(); QVERIFY(parentButton); parentButton->setHoverEnabled(true); - QQuickButton *childButton = helper.window->property("childButton").value<QQuickButton*>(); + QQuickButton *childButton = window->property("childButton").value<QQuickButton*>(); QVERIFY(childButton); childButton->setHoverEnabled(true); diff --git a/tests/auto/qquickmaterialstyle/data/tst_material.qml b/tests/auto/qquickmaterialstyle/data/tst_material.qml index 8eb6933c..63385a6e 100644 --- a/tests/auto/qquickmaterialstyle/data/tst_material.qml +++ b/tests/auto/qquickmaterialstyle/data/tst_material.qml @@ -74,6 +74,11 @@ TestCase { } Component { + id: applicationWindow + ApplicationWindow { } + } + + Component { id: styledWindow Window { Material.theme: Material.Dark @@ -600,4 +605,76 @@ TestCase { window.destroy() } + + Component { + id: backgroundControls + ApplicationWindow { + id: window + property Button button: Button { } + property ComboBox combobox: ComboBox { } + property Drawer drawer: Drawer { } + property GroupBox groupbox: GroupBox { Material.elevation: 10 } + property Frame frame: Frame { Material.elevation: 10 } + property Menu menu: Menu { } + property Page page: Page { } + property Pane pane: Pane { } + property Popup popup: Popup { } + property TabBar tabbar: TabBar { } + property ToolBar toolbar: ToolBar { } + property ToolTip tooltip: ToolTip { } + } + } + + function test_background_data() { + return [ + { tag: "button", inherit: false, wait: 400 }, + { tag: "combobox", inherit: false, wait: 400 }, + { tag: "drawer", inherit: true }, + { tag: "groupbox", inherit: true }, + { tag: "frame", inherit: true }, + { tag: "menu", inherit: true }, + { tag: "page", inherit: true }, + { tag: "pane", inherit: true }, + { tag: "popup", inherit: true }, + { tag: "tabbar", inherit: true }, + { tag: "toolbar", inherit: false }, + { tag: "tooltip", inherit: false } + ] + } + + function test_background(data) { + var window = backgroundControls.createObject(testCase) + verify(window) + + var control = window[data.tag] + verify(control) + + control.parent = window.contentItem + control.visible = true + + var defaultBackground = control.background.color + + window.Material.background = "#ff0000" + compare(window.color, "#ff0000") + + // For controls that have an animated background color, we wait the length + // of the color animation to be sure that the color hasn't actually changed. + if (data.wait) + wait(data.wait) + + // We want the control's background color to be equal to the window's background + // color, because we want the color to propagate to items that might actually use + // it... Button, ComboBox, ToolBar and ToolTip have a special background color, + // so they don't use the generic background color unless explicitly set, so we + // compare the actual background rect color instead. + if (data.inherit) + compare(control.background.color, "#ff0000") + else + compare(control.background.color, defaultBackground) + + control.Material.background = "#0000ff" + tryCompare(control.background, "color", "#0000ff") + + window.destroy() + } } diff --git a/tests/auto/qquickmaterialstyleconf/tst_qquickmaterialstyleconf.cpp b/tests/auto/qquickmaterialstyleconf/tst_qquickmaterialstyleconf.cpp index c7670c21..c0db7c49 100644 --- a/tests/auto/qquickmaterialstyleconf/tst_qquickmaterialstyleconf.cpp +++ b/tests/auto/qquickmaterialstyleconf/tst_qquickmaterialstyleconf.cpp @@ -55,7 +55,7 @@ void tst_qquickmaterialstyleconf::conf() { QQuickApplicationHelper helper(this, QLatin1String("applicationwindow.qml")); - QQuickApplicationWindow *window = helper.window; + QQuickApplicationWindow *window = helper.appWindow; window->show(); QVERIFY(QTest::qWaitForWindowExposed(window)); // We specified a custom background color, so the window should have it. diff --git a/tests/auto/qquickuniversalstyleconf/tst_qquickuniversalstyleconf.cpp b/tests/auto/qquickuniversalstyleconf/tst_qquickuniversalstyleconf.cpp index c676ae6d..ce018a05 100644 --- a/tests/auto/qquickuniversalstyleconf/tst_qquickuniversalstyleconf.cpp +++ b/tests/auto/qquickuniversalstyleconf/tst_qquickuniversalstyleconf.cpp @@ -55,7 +55,7 @@ void tst_qquickuniversalstyleconf::conf() { QQuickApplicationHelper helper(this, QLatin1String("applicationwindow.qml")); - QQuickApplicationWindow *window = helper.window; + QQuickApplicationWindow *window = helper.appWindow; window->show(); QVERIFY(QTest::qWaitForWindowExposed(window)); // We specified a custom background color, so the window should have it. diff --git a/tests/auto/sanity/tst_sanity.cpp b/tests/auto/sanity/tst_sanity.cpp index 6b9e05ba..10348ba4 100644 --- a/tests/auto/sanity/tst_sanity.cpp +++ b/tests/auto/sanity/tst_sanity.cpp @@ -114,7 +114,8 @@ public: QQmlJS::Parser parser(&engine); if (!parser.parse()) { - foreach (const QQmlJS::DiagnosticMessage &msg, parser.diagnosticMessages()) + const auto diagnosticMessages = parser.diagnosticMessages(); + for (const QQmlJS::DiagnosticMessage &msg : diagnosticMessages) m_errors += QString("%s:%d : %s").arg(m_fileName).arg(msg.loc.startLine).arg(msg.message); return false; } @@ -267,11 +268,12 @@ static void addTestRows(QQmlEngine *engine, const QString &sourcePath, const QSt // the engine's import path. This way we can use QQmlComponent to load each QML file // for benchmarking. - QFileInfoList entries = QDir(QQC2_IMPORT_PATH "/" + sourcePath).entryInfoList(QStringList("*.qml"), QDir::Files); - foreach (const QFileInfo &entry, entries) { + const QFileInfoList entries = QDir(QQC2_IMPORT_PATH "/" + sourcePath).entryInfoList(QStringList("*.qml"), QDir::Files); + for (const QFileInfo &entry : entries) { QString name = entry.baseName(); if (!skiplist.contains(name)) { - foreach (const QString &importPath, engine->importPathList()) { + const auto importPathList = engine->importPathList(); + for (const QString &importPath : importPathList) { QString name = entry.dir().dirName() + "/" + entry.fileName(); QString filePath = importPath + "/" + targetPath + "/" + entry.fileName(); if (QFile::exists(filePath)) { @@ -296,7 +298,7 @@ void tst_Sanity::attachedObjects() QSet<QString> classNames; QScopedPointer<QObject> object(component.create()); QVERIFY2(object.data(), qPrintable(component.errorString())); - foreach (QObject *object, *qt_qobjects) { + for (QObject *object : qAsConst(*qt_qobjects)) { if (object->parent() == &engine) continue; // allow "global" instances QString className = object->metaObject()->className(); diff --git a/tests/auto/shared/visualtestutil.h b/tests/auto/shared/visualtestutil.h index c1cc9c7d..c67e5bdc 100644 --- a/tests/auto/shared/visualtestutil.h +++ b/tests/auto/shared/visualtestutil.h @@ -118,9 +118,10 @@ namespace QQuickVisualTestUtil component.loadUrl(testCase->testFileUrl(testFilePath)); QObject *rootObject = component.create(); cleanup.reset(rootObject); - QVERIFY2(rootObject, qPrintable(QString::fromLatin1("Failed to create ApplicationWindow: %1").arg(component.errorString()))); + QVERIFY2(rootObject, qPrintable(QString::fromLatin1("Failed to create window: %1").arg(component.errorString()))); - window = qobject_cast<QQuickApplicationWindow*>(rootObject); + window = qobject_cast<QQuickWindow*>(rootObject); + appWindow = qobject_cast<QQuickApplicationWindow*>(rootObject); QVERIFY(window); QVERIFY(!window->isVisible()); } @@ -128,7 +129,8 @@ namespace QQuickVisualTestUtil QQmlEngine engine; QQmlComponent component; QScopedPointer<QObject> cleanup; - QQuickApplicationWindow *window; + QQuickApplicationWindow *appWindow; + QQuickWindow *window; }; } diff --git a/tests/auto/snippets/tst_snippets.cpp b/tests/auto/snippets/tst_snippets.cpp index fb581b90..6ca9e72c 100644 --- a/tests/auto/snippets/tst_snippets.cpp +++ b/tests/auto/snippets/tst_snippets.cpp @@ -101,7 +101,7 @@ void tst_Snippets::screenshots() QVERIFY(QTest::qWaitForWindowActive(&view)); bool generateScreenshot = true; - foreach (const QString &baseName, nonVisualSnippets) { + for (const QString &baseName : qAsConst(nonVisualSnippets)) { if (input.contains(baseName)) { generateScreenshot = false; break; diff --git a/tests/benchmarks/creationtime/tst_creationtime.cpp b/tests/benchmarks/creationtime/tst_creationtime.cpp index 877cf273..3eec3825 100644 --- a/tests/benchmarks/creationtime/tst_creationtime.cpp +++ b/tests/benchmarks/creationtime/tst_creationtime.cpp @@ -78,11 +78,12 @@ static void addTestRows(QQmlEngine *engine, const QString &sourcePath, const QSt // the engine's import path. This way we can use QQmlComponent to load each QML file // for benchmarking. - QFileInfoList entries = QDir(QQC2_IMPORT_PATH "/" + sourcePath).entryInfoList(QStringList("*.qml"), QDir::Files); - foreach (const QFileInfo &entry, entries) { + const QFileInfoList entries = QDir(QQC2_IMPORT_PATH "/" + sourcePath).entryInfoList(QStringList("*.qml"), QDir::Files); + for (const QFileInfo &entry : entries) { QString name = entry.baseName(); if (!skiplist.contains(name)) { - foreach (const QString &importPath, engine->importPathList()) { + const auto importPathList = engine->importPathList(); + for (const QString &importPath : importPathList) { QString name = entry.dir().dirName() + "/" + entry.fileName(); QString filePath = importPath + "/" + targetPath + "/" + entry.fileName(); if (QFile::exists(filePath)) { diff --git a/tests/benchmarks/objectcount/tst_objectcount.cpp b/tests/benchmarks/objectcount/tst_objectcount.cpp index 91c17fcc..14a950f3 100644 --- a/tests/benchmarks/objectcount/tst_objectcount.cpp +++ b/tests/benchmarks/objectcount/tst_objectcount.cpp @@ -100,7 +100,7 @@ static void printItems(const QList<QQuickItem *> &items) std::cout << " QQuickItems: " << items.count() << " (total of QObjects: " << qt_qobjects->count() << ")" << std::endl; if (qt_verbose) { - foreach (QObject *object, *qt_qobjects) + for (QObject *object : qAsConst(*qt_qobjects)) qInfo() << "\t" << object; } } @@ -118,11 +118,12 @@ static void addTestRows(QQmlEngine *engine, const QString &sourcePath, const QSt // the engine's import path. This way we can use QQmlComponent to load each QML file // for benchmarking. - QFileInfoList entries = QDir(QQC2_IMPORT_PATH "/" + sourcePath).entryInfoList(QStringList("*.qml"), QDir::Files); - foreach (const QFileInfo &entry, entries) { + const QFileInfoList entries = QDir(QQC2_IMPORT_PATH "/" + sourcePath).entryInfoList(QStringList("*.qml"), QDir::Files); + for (const QFileInfo &entry : entries) { QString name = entry.baseName(); if (!skiplist.contains(name)) { - foreach (const QString &importPath, engine->importPathList()) { + const auto importPathList = engine->importPathList(); + for (const QString &importPath : importPathList) { QString name = entry.dir().dirName() + "/" + entry.fileName(); QString filePath = importPath + "/" + targetPath + "/" + entry.fileName(); if (QFile::exists(filePath)) { @@ -148,7 +149,7 @@ static void doBenchmark(QQmlEngine *engine, const QUrl &url) QVERIFY2(object.data(), qPrintable(component.errorString())); QList<QQuickItem *> items; - foreach (QObject *object, *qt_qobjects()) { + for (QObject *object : qAsConst(*qt_qobjects)) { QQuickItem *item = qobject_cast<QQuickItem *>(object); if (item) items += item; diff --git a/tests/manual/buttons/ButtonLoader.qml b/tests/manual/buttons/ButtonLoader.qml new file mode 100644 index 00000000..8793d98f --- /dev/null +++ b/tests/manual/buttons/ButtonLoader.qml @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.8 +import QtQuick.Controls 2.1 + +Item { + id: root + implicitWidth: activeButton.implicitWidth + implicitHeight: activeButton.implicitHeight + + property bool round: false + + property string text + property bool flat + property bool hoverEnabled + property bool highlighted + property bool checked + property var down: undefined + + property AbstractButton activeButton: round ? roundButton : button + + Button { + id: button + visible: !round + text: root.text + flat: root.flat + hoverEnabled: root.hoverEnabled + highlighted: root.highlighted + checked: root.checked + down: root.down + enabled: root.enabled + } + + RoundButton { + id: roundButton + visible: round + text: "\u2713" + flat: root.flat + hoverEnabled: root.hoverEnabled + highlighted: root.highlighted + checked: root.checked + down: root.down + enabled: root.enabled + + Label { + text: root.text + font.pixelSize: roundButton.contentItem.font.pixelSize * 0.5 + anchors.top: parent.bottom + anchors.topMargin: 2 + anchors.horizontalCenter: parent.horizontalCenter + } + } +} diff --git a/tests/manual/buttons/buttons.pro b/tests/manual/buttons/buttons.pro index d0a6eb28..2cb1c14c 100644 --- a/tests/manual/buttons/buttons.pro +++ b/tests/manual/buttons/buttons.pro @@ -3,4 +3,4 @@ TARGET = buttons QT += qml quickcontrols2 SOURCES += buttons.cpp -RESOURCES += buttons.qml +RESOURCES += $$files(*.qml) diff --git a/tests/manual/buttons/buttons.qml b/tests/manual/buttons/buttons.qml index e1a393fd..b3aba775 100644 --- a/tests/manual/buttons/buttons.qml +++ b/tests/manual/buttons/buttons.qml @@ -62,6 +62,11 @@ ApplicationWindow { text: "Hover" checked: true } + CheckBox { + id: roundBox + text: "Round" + checked: false + } } } @@ -105,25 +110,25 @@ ApplicationWindow { spacing: 20 padding: 20 - Button { text: "Normal"; flat: modelData.flat; hoverEnabled: hoverBox.checked } - Button { text: "Disabled"; flat: modelData.flat; hoverEnabled: hoverBox.checked; enabled: false } - Button { text: "Down"; flat: modelData.flat; hoverEnabled: hoverBox.checked; down: true } - Button { text: "Disabled"; flat: modelData.flat; hoverEnabled: hoverBox.checked; down: true; enabled: false } - - Button { text: "Checked"; flat: modelData.flat; hoverEnabled: hoverBox.checked; checked: true } - Button { text: "Disabled"; flat: modelData.flat; hoverEnabled: hoverBox.checked; checked: true; enabled: false } - Button { text: "Down"; flat: modelData.flat; hoverEnabled: hoverBox.checked; checked: true; down: true } - Button { text: "Disabled"; flat: modelData.flat; hoverEnabled: hoverBox.checked; checked: true; down: true; enabled: false } - - Button { text: "Highlighted"; flat: modelData.flat; hoverEnabled: hoverBox.checked; highlighted: true } - Button { text: "Disabled"; flat: modelData.flat; hoverEnabled: hoverBox.checked; highlighted: true; enabled: false } - Button { text: "Down"; flat: modelData.flat; hoverEnabled: hoverBox.checked; highlighted: true; down: true } - Button { text: "Disabled"; flat: modelData.flat; hoverEnabled: hoverBox.checked; highlighted: true; down: true; enabled: false } - - Button { text: "Hi-checked"; flat: modelData.flat; hoverEnabled: hoverBox.checked; highlighted: true; checked: true } - Button { text: "Disabled"; flat: modelData.flat; hoverEnabled: hoverBox.checked; highlighted: true; checked: true; enabled: false } - Button { text: "Down"; flat: modelData.flat; hoverEnabled: hoverBox.checked; highlighted: true; checked: true; down: true } - Button { text: "Disabled"; flat: modelData.flat; hoverEnabled: hoverBox.checked; highlighted: true; checked: true; down: true; enabled: false } + ButtonLoader { text: "Normal"; flat: modelData.flat; hoverEnabled: hoverBox.checked; round: roundBox.checked } + ButtonLoader { text: "Disabled"; flat: modelData.flat; hoverEnabled: hoverBox.checked; enabled: false; round: roundBox.checked } + ButtonLoader { text: "Down"; flat: modelData.flat; hoverEnabled: hoverBox.checked; down: true; round: roundBox.checked } + ButtonLoader { text: "Disabled"; flat: modelData.flat; hoverEnabled: hoverBox.checked; down: true; enabled: false; round: roundBox.checked } + + ButtonLoader { text: "Checked"; flat: modelData.flat; hoverEnabled: hoverBox.checked; checked: true; round: roundBox.checked } + ButtonLoader { text: "Disabled"; flat: modelData.flat; hoverEnabled: hoverBox.checked; checked: true; enabled: false; round: roundBox.checked } + ButtonLoader { text: "Down"; flat: modelData.flat; hoverEnabled: hoverBox.checked; checked: true; down: true; round: roundBox.checked } + ButtonLoader { text: "Disabled"; flat: modelData.flat; hoverEnabled: hoverBox.checked; checked: true; down: true; enabled: false; round: roundBox.checked } + + ButtonLoader { text: "Highlighted"; flat: modelData.flat; hoverEnabled: hoverBox.checked; highlighted: true; round: roundBox.checked } + ButtonLoader { text: "Disabled"; flat: modelData.flat; hoverEnabled: hoverBox.checked; highlighted: true; enabled: false; round: roundBox.checked } + ButtonLoader { text: "Down"; flat: modelData.flat; hoverEnabled: hoverBox.checked; highlighted: true; down: true; round: roundBox.checked } + ButtonLoader { text: "Disabled"; flat: modelData.flat; hoverEnabled: hoverBox.checked; highlighted: true; down: true; enabled: false; round: roundBox.checked } + + ButtonLoader { text: "Hi-checked"; flat: modelData.flat; hoverEnabled: hoverBox.checked; highlighted: true; checked: true; round: roundBox.checked } + ButtonLoader { text: "Disabled"; flat: modelData.flat; hoverEnabled: hoverBox.checked; highlighted: true; checked: true; enabled: false; round: roundBox.checked } + ButtonLoader { text: "Down"; flat: modelData.flat; hoverEnabled: hoverBox.checked; highlighted: true; checked: true; down: true; round: roundBox.checked } + ButtonLoader { text: "Disabled"; flat: modelData.flat; hoverEnabled: hoverBox.checked; highlighted: true; checked: true; down: true; enabled: false; round: roundBox.checked } } } } diff --git a/tests/manual/gifs/eventcapturer.cpp b/tests/manual/gifs/eventcapturer.cpp index 83e1c76b..3c355f59 100644 --- a/tests/manual/gifs/eventcapturer.cpp +++ b/tests/manual/gifs/eventcapturer.cpp @@ -57,7 +57,8 @@ // interact with the view here, in order for the events to be captured qDebug() << "\n"; - foreach (CapturedEvent event, eventCapturer.capturedEvents()) + const auto capturedEvents = eventCapturer.capturedEvents(); + for (CapturedEvent event : capturedEvents) qDebug().noquote() << event.cppCommand(); \endcode diff --git a/tests/manual/gifs/tst_gifs.cpp b/tests/manual/gifs/tst_gifs.cpp index 94400e04..919ba104 100644 --- a/tests/manual/gifs/tst_gifs.cpp +++ b/tests/manual/gifs/tst_gifs.cpp @@ -191,7 +191,8 @@ void tst_Gifs::tumblerWrap() gifRecorder.waitForFinish(); - foreach (CapturedEvent event, eventCapturer.capturedEvents()) + const auto capturedEvents = eventCapturer.capturedEvents(); + for (CapturedEvent event : capturedEvents) qDebug().noquote() << event.cppCommand(); } diff --git a/tests/manual/testbench/main.qml b/tests/manual/testbench/main.qml index dd1a1de8..ea59d5b5 100644 --- a/tests/manual/testbench/main.qml +++ b/tests/manual/testbench/main.qml @@ -251,6 +251,108 @@ ApplicationWindow { } RowLayout { + spacing: window.controlSpacing * 2 + + Button { + text: "Normal" + } + Button { + text: "Pressed" + down: true + } + Button { + text: "Checked" + checked: true + } + Button { + text: "CH + PR" + checked: true + down: true + } + Button { + text: "Disabled" + enabled: false + } + Button { + text: "CH + DIS" + enabled: false + checked: true + } + } + + RowLayout { + spacing: window.controlSpacing * 2 + + ColumnLayout { + RoundButton { + highlighted: true + Layout.alignment: Qt.AlignHCenter + } + Label { + text: "HI" + Layout.alignment: Qt.AlignHCenter + } + } + ColumnLayout { + RoundButton { + highlighted: true + down: true + Layout.alignment: Qt.AlignHCenter + } + Label { + text: "HI + PR" + Layout.alignment: Qt.AlignHCenter + } + } + ColumnLayout { + RoundButton { + highlighted: true + checked: true + Layout.alignment: Qt.AlignHCenter + } + Label { + text: "HI + CH" + Layout.alignment: Qt.AlignHCenter + } + } + ColumnLayout { + RoundButton { + highlighted: true + down: true + checked: true + Layout.alignment: Qt.AlignHCenter + } + Label { + text: "HI+CH+PR" + Layout.alignment: Qt.AlignHCenter + } + } + ColumnLayout { + RoundButton { + highlighted: true + enabled: false + Layout.alignment: Qt.AlignHCenter + } + Label { + text: "HI + DIS" + Layout.alignment: Qt.AlignHCenter + } + } + ColumnLayout { + RoundButton { + highlighted: true + enabled: false + checked: true + Layout.alignment: Qt.AlignHCenter + } + Label { + text: "HI+CH+DIS" + Layout.alignment: Qt.AlignHCenter + } + } + } + + RowLayout { CheckBox { text: "Normal" } |