diff options
author | J-P Nurmi <jpnurmi@theqtcompany.com> | 2016-01-25 11:02:44 +0100 |
---|---|---|
committer | J-P Nurmi <jpnurmi@theqtcompany.com> | 2016-01-25 11:03:53 +0100 |
commit | 5ada1525e03116326a480fb05611904d3484c296 (patch) | |
tree | f23822e6b0482012065b9586d95cb8ab5d70c4bf | |
parent | f4f5e857447d42738b1b60b5c4184f39df2f1593 (diff) | |
parent | 4f1fb09ed70c69e73f2e19f28f4299f4cfbc90dc (diff) |
Merge remote-tracking branch 'origin/5.6' into dev
Conflicts:
.gitignore
src/templates/qquickcombobox_p.h
src/templates/qquickmenu_p.h
src/templates/qquickmenu_p_p.h
src/templates/qquickoverlay_p.h
src/templates/qquickpopup.cpp
src/templates/qquickpopup_p.h
src/templates/qquickstackview_p.cpp
Change-Id: I89c7d518697beec0b81ef3a12205286a4f3ccf89
84 files changed, 2036 insertions, 419 deletions
@@ -96,3 +96,4 @@ android-*.so-deployment-settings.json *.directory *_plugin_import.cpp *_wrapper.sh +*_wrapper.bat diff --git a/examples/controls/gallery/gallery.qml b/examples/controls/gallery/gallery.qml index 2bd7d40f..d40e7f9b 100644 --- a/examples/controls/gallery/gallery.qml +++ b/examples/controls/gallery/gallery.qml @@ -86,6 +86,20 @@ ApplicationWindow { source: "qrc:/images/menu.png" } onClicked: optionsMenu.open() + + Menu { + id: optionsMenu + x: parent.width - width + + MenuItem { + text: "Settings" + onTriggered: settingsPopup.open() + } + MenuItem { + text: "About" + onTriggered: aboutDialog.open() + } + } } } } @@ -193,83 +207,76 @@ ApplicationWindow { Popup { id: settingsPopup + x: (window.width - width) / 2 + y: window.height / 6 + width: Math.min(window.width, window.height) / 3 * 2 + height: settingsColumn.implicitHeight + topPadding + bottomPadding modal: true focus: true onPressedOutside: close() - contentItem: Pane { - id: settingsPane - x: (window.width - width) / 2 - y: window.height / 6 - width: Math.min(window.width, window.height) / 3 * 2 - contentHeight: settingsColumn.implicitHeight - - Keys.onEscapePressed: settingsPopup.close() + contentItem: ColumnLayout { + id: settingsColumn + spacing: 20 + Keys.onEscapePressed: settingsPopup.close() // TODO: Popup::closePolicy + Label { + text: "Settings" + font.bold: true + } - ColumnLayout { - id: settingsColumn - spacing: 20 - anchors.fill: parent + RowLayout { + spacing: 10 Label { - text: "Settings" - font.bold: true + text: "Style:" } - RowLayout { - spacing: 10 - - Label { - text: "Style:" - } - - ComboBox { - id: styleBox - property int styleIndex: -1 - model: ["Default", "Material", "Universal"] - Component.onCompleted: { - styleIndex = find(settings.style) - if (styleIndex !== -1) - currentIndex = styleIndex - } - Layout.fillWidth: true + ComboBox { + id: styleBox + property int styleIndex: -1 + model: ["Default", "Material", "Universal"] + Component.onCompleted: { + styleIndex = find(settings.style) + if (styleIndex !== -1) + currentIndex = styleIndex } - } - - Label { - text: "Restart required" - opacity: styleBox.currentIndex !== styleBox.styleIndex ? 1.0 : 0.0 - horizontalAlignment: Label.AlignHCenter - verticalAlignment: Label.AlignVCenter Layout.fillWidth: true - Layout.fillHeight: true } + } + + Label { + text: "Restart required" + opacity: styleBox.currentIndex !== styleBox.styleIndex ? 1.0 : 0.0 + horizontalAlignment: Label.AlignHCenter + verticalAlignment: Label.AlignVCenter + Layout.fillWidth: true + Layout.fillHeight: true + } - RowLayout { - spacing: 10 + RowLayout { + spacing: 10 - Button { - id: okButton - text: "Ok" - onClicked: { - settings.style = styleBox.displayText - settingsPopup.close() - } - Layout.preferredWidth: 0 - Layout.fillWidth: true + Button { + id: okButton + text: "Ok" + onClicked: { + settings.style = styleBox.displayText + settingsPopup.close() } + Layout.preferredWidth: 0 + Layout.fillWidth: true + } - Button { - id: cancelButton - text: "Cancel" - onClicked: { - styleBox.currentIndex = styleBox.styleIndex - settingsPopup.close() - } - Layout.preferredWidth: 0 - Layout.fillWidth: true + Button { + id: cancelButton + text: "Cancel" + onClicked: { + styleBox.currentIndex = styleBox.styleIndex + settingsPopup.close() } + Layout.preferredWidth: 0 + Layout.fillWidth: true } } } @@ -279,57 +286,37 @@ ApplicationWindow { id: aboutDialog modal: true focus: true + x: (window.width - width) / 2 + y: window.height / 6 + width: Math.min(window.width, window.height) / 3 * 2 + contentHeight: aboutColumn.height onPressedOutside: close() - contentItem: Pane { - x: (window.width - width) / 2 - y: (window.height - height) / 2 - width: Math.min(window.width, window.height) / 3 * 2 - contentHeight: aboutColumn.implicitHeight - - Keys.onEscapePressed: aboutDialog.close() - - Column { - id: aboutColumn - - spacing: 20 - anchors.fill: parent - - Label { - text: "About" - font.bold: true - } - - Label { - width: parent.width - text: "The Qt Labs Controls module is a technology preview of the next generation user interface controls based on Qt Quick." - wrapMode: Label.Wrap - font.pixelSize: 12 - } + Column { + id: aboutColumn + spacing: 20 + Keys.onEscapePressed: aboutDialog.close() // TODO: Popup::closePolicy - Label { - width: parent.width - text: "In comparison to the desktop oriented Qt Quick Controls 1, the experimental Qt Labs " - + "Controls are an order of magnitude simpler, lighter and faster, and are primarily targeting embedded " - + "and mobile platforms." - wrapMode: Label.Wrap - font.pixelSize: 12 - } + Label { + text: "About" + font.bold: true } - } - } - Menu { - id: optionsMenu - contentItem.x: contentItem.parent ? (contentItem.parent.width - contentItem.width) : 0 + Label { + width: aboutDialog.availableWidth + text: "The Qt Labs Controls module is a technology preview of the next generation user interface controls based on Qt Quick." + wrapMode: Label.Wrap + font.pixelSize: 12 + } - MenuItem { - text: "Settings" - onTriggered: settingsPopup.open() - } - MenuItem { - text: "About" - onTriggered: aboutDialog.open() + Label { + width: aboutDialog.availableWidth + text: "In comparison to the desktop oriented Qt Quick Controls 1, the experimental Qt Labs " + + "Controls are an order of magnitude simpler, lighter and faster, and are primarily targeting embedded " + + "and mobile platforms." + wrapMode: Label.Wrap + font.pixelSize: 12 + } } } } diff --git a/examples/controls/gallery/qtlabscontrols.conf b/examples/controls/gallery/qtlabscontrols.conf index 80041052..8f176563 100644 --- a/examples/controls/gallery/qtlabscontrols.conf +++ b/examples/controls/gallery/qtlabscontrols.conf @@ -2,6 +2,7 @@ Style=Universal [Material] +Primary=LightGreen Accent=LightGreen Theme=Light diff --git a/src/controls/qquickstyle.cpp b/src/controls/qquickstyle.cpp index 6728667e..576506fb 100644 --- a/src/controls/qquickstyle.cpp +++ b/src/controls/qquickstyle.cpp @@ -40,7 +40,7 @@ #include <QtCore/qsettings.h> #include <QtCore/qfileselector.h> #include <QtQuick/private/qquickitem_p.h> -#include <QtLabsTemplates/private/qquickpopup_p.h> +#include <QtLabsTemplates/private/qquickpopup_p_p.h> QT_BEGIN_NAMESPACE @@ -119,7 +119,7 @@ static QList<QQuickStyle *> findChildStyles(const QMetaObject *type, QObject *ob } } } else if (QQuickPopup *popup = qobject_cast<QQuickPopup *>(object)) { - item = popup->contentItem(); + item = QQuickPopupPrivate::get(popup)->popupItem; QQuickStyle *style = attachedStyle(type, popup); if (style) diff --git a/src/imports/calendar/qquickcalendarmodel.cpp b/src/imports/calendar/qquickcalendarmodel.cpp index 832513d7..6b59d45a 100644 --- a/src/imports/calendar/qquickcalendarmodel.cpp +++ b/src/imports/calendar/qquickcalendarmodel.cpp @@ -60,6 +60,8 @@ QT_BEGIN_NAMESPACE \row \li \b model.year : int \li The number of the year \endtable + \labs + \sa MonthGrid */ diff --git a/src/imports/calendar/qquickdayofweekrow.cpp b/src/imports/calendar/qquickdayofweekrow.cpp index e87c3d45..1f5e4387 100644 --- a/src/imports/calendar/qquickdayofweekrow.cpp +++ b/src/imports/calendar/qquickdayofweekrow.cpp @@ -64,6 +64,8 @@ QT_BEGIN_NAMESPACE The visual appearance of DayOfWeekRow can be changed by implementing a \l {delegate}{custom delegate}. + \labs + \sa MonthGrid, WeekNumberColumn */ diff --git a/src/imports/calendar/qquickmonthgrid.cpp b/src/imports/calendar/qquickmonthgrid.cpp index 07e3448a..8fe9ad63 100644 --- a/src/imports/calendar/qquickmonthgrid.cpp +++ b/src/imports/calendar/qquickmonthgrid.cpp @@ -49,7 +49,7 @@ QT_BEGIN_NAMESPACE \inherits Control \instantiates QQuickMonthGrid \inqmlmodule Qt.labs.calendar - \brief A calendar view. + \brief A grid of days for a calendar month. MonthGrid presents a calendar month in a grid. The contents are calculated for a given \l month and \l year, using the specified @@ -68,6 +68,8 @@ QT_BEGIN_NAMESPACE The visual appearance of MonthGrid can be changed by implementing a \l {delegate}{custom delegate}. + \labs + \sa DayOfWeekRow, WeekNumberColumn, CalendarModel */ diff --git a/src/imports/calendar/qquickweeknumbercolumn.cpp b/src/imports/calendar/qquickweeknumbercolumn.cpp index 1a796ce0..031b0555 100644 --- a/src/imports/calendar/qquickweeknumbercolumn.cpp +++ b/src/imports/calendar/qquickweeknumbercolumn.cpp @@ -66,6 +66,8 @@ QT_BEGIN_NAMESPACE The visual appearance of WeekNumberColumn can be changed by implementing a \l {delegate}{custom delegate}. + \labs + \sa MonthGrid, DayOfWeekRow */ diff --git a/src/imports/controls/Button.qml b/src/imports/controls/Button.qml index 1196f56b..290f4ce4 100644 --- a/src/imports/controls/Button.qml +++ b/src/imports/controls/Button.qml @@ -71,7 +71,7 @@ T.Button { implicitWidth: 100 implicitHeight: 40 opacity: enabled ? 1 : 0.3 - color: control.pressed ? (control.highlighted ? "#585a5c" : "#e4e4e4") : (control.highlighted ? "#353637" : "#ffffff") + color: control.pressed ? (control.highlighted ? "#585a5c" : "#e4e4e4") : (control.highlighted ? "#353637" : "#f6f6f6") border.color: control.pressed ? "#26282a" : "#353637" } //! [background] diff --git a/src/imports/controls/CheckBox.qml b/src/imports/controls/CheckBox.qml index b7d67fe6..20d8ef1c 100644 --- a/src/imports/controls/CheckBox.qml +++ b/src/imports/controls/CheckBox.qml @@ -60,7 +60,7 @@ T.CheckBox { x: text ? (control.mirrored ? control.width - width - control.rightPadding : control.leftPadding) : control.leftPadding + (control.availableWidth - width) / 2 y: control.topPadding + (control.availableHeight - height) / 2 - color: control.enabled ? (control.pressed ? "#e4e4e4" : "#ffffff") : "#353637" + color: control.enabled ? (control.pressed ? "#e4e4e4" : "#f6f6f6") : "#353637" border.color: control.enabled ? (control.pressed ? "#26282a" : "#353637") : "transparent" Image { diff --git a/src/imports/controls/ComboBox.qml b/src/imports/controls/ComboBox.qml index 5be26304..33568699 100644 --- a/src/imports/controls/ComboBox.qml +++ b/src/imports/controls/ComboBox.qml @@ -99,35 +99,29 @@ T.ComboBox { //! [popup] popup: T.Popup { - contentItem: Rectangle { - // TODO: Popup::anchors - readonly property var above: popup.visible ? control.mapToItem(null, 0, -height + 1) : Qt.point(0, 0) - readonly property var below: popup.visible ? control.mapToItem(null, 0, control.height - 1) : Qt.point(0, 0) + y: control.height - 1 + implicitWidth: control.width + implicitHeight: Math.min(200, listview.contentHeight) - x: below.x - y: above.y >= 0 && below.y + height > control.Window.height ? above.y : below.y - width: control.width - height: listview.height - - ListView { - id: listview - width: control.width - height: Math.min(200, contentHeight) - - clip: true - model: control.delegateModel - currentIndex: control.highlightedIndex - -// ScrollIndicator.vertical: ScrollIndicator { } - } + contentItem: ListView { + id: listview + clip: true + model: control.delegateModel + currentIndex: control.highlightedIndex Rectangle { - width: parent.width - height: parent.height - color: "transparent" + z: 10 + parent: listview + width: listview.width + height: listview.height border.color: "#353637" + color: "transparent" } + +// ScrollIndicator.vertical: ScrollIndicator { } } + + background: Rectangle { } } //! [popup] } diff --git a/src/imports/controls/Menu.qml b/src/imports/controls/Menu.qml index f4a4243e..6692ef08 100644 --- a/src/imports/controls/Menu.qml +++ b/src/imports/controls/Menu.qml @@ -41,10 +41,14 @@ import Qt.labs.templates 1.0 as T T.Menu { id: control + implicitWidth: Math.max(background ? background.implicitWidth : 0, + contentItem ? contentItem.implicitWidth + leftPadding + rightPadding : 0) + implicitHeight: Math.min(background ? background.implicitHeight : 0, + contentItem ? contentItem.implicitHeight + topPadding + bottomPadding : 0) + //! [contentItem] contentItem: ListView { - implicitWidth: 200 - implicitHeight: Math.min(contentHeight, 200) + implicitHeight: contentHeight model: control.contentModel // TODO: improve this? interactive: ApplicationWindow.window ? contentHeight > ApplicationWindow.window.height : false @@ -53,14 +57,15 @@ T.Menu { currentIndex: -1 ScrollIndicator.vertical: ScrollIndicator {} - - Rectangle { - width: parent.width - height: parent.height - color: "#ffffff" - border.color: "#353637" - z: -1 - } } //! [contentItem] + + //! [background] + background: Rectangle { + implicitWidth: 200 + implicitHeight: 200 + color: "#ffffff" + border.color: "#353637" + } + //! [background] } diff --git a/src/imports/controls/Popup.qml b/src/imports/controls/Popup.qml index b7c6fe08..bc7c610c 100644 --- a/src/imports/controls/Popup.qml +++ b/src/imports/controls/Popup.qml @@ -39,4 +39,18 @@ import Qt.labs.templates 1.0 as T T.Popup { id: control + + implicitWidth: Math.max(background ? background.implicitWidth : 0, contentWidth + leftPadding + rightPadding) + implicitHeight: Math.max(background ? background.implicitHeight : 0, contentHeight + topPadding + bottomPadding) + + contentWidth: contentChildren.length === 1 ? contentChildren[0].implicitWidth : 0 + contentHeight: contentChildren.length === 1 ? contentChildren[0].implicitHeight : 0 + + padding: 6 + + contentItem: Item { } + + background: Rectangle { + border.color: "#353637" + } } diff --git a/src/imports/controls/RadioButton.qml b/src/imports/controls/RadioButton.qml index 82d74b88..8e1e0bce 100644 --- a/src/imports/controls/RadioButton.qml +++ b/src/imports/controls/RadioButton.qml @@ -64,7 +64,7 @@ T.RadioButton { radius: width / 2 border.width: 1 border.color: (control.pressed ? "#26282a" : "#353637") - color: control.pressed ? "#e4e4e4" : "#ffffff" + color: control.pressed ? "#e4e4e4" : "#f6f6f6" Rectangle { x: (parent.width - width) / 2 diff --git a/src/imports/controls/Slider.qml b/src/imports/controls/Slider.qml index 250dbd3a..95cfc54e 100644 --- a/src/imports/controls/Slider.qml +++ b/src/imports/controls/Slider.qml @@ -57,7 +57,7 @@ T.Slider { implicitHeight: 28 radius: width / 2 border.color: "#353637" - color: control.pressed ? "#bdbebf" : "#ffffff" + color: control.pressed ? "#bdbebf" : "#f6f6f6" readonly property bool horizontal: control.orientation === Qt.Horizontal } diff --git a/src/imports/controls/SpinBox.qml b/src/imports/controls/SpinBox.qml index 4b42a839..c84a2e3e 100644 --- a/src/imports/controls/SpinBox.qml +++ b/src/imports/controls/SpinBox.qml @@ -84,7 +84,7 @@ T.SpinBox { x: control.mirrored ? 0 : parent.width - width implicitWidth: 40 implicitHeight: 40 - color: up.pressed ? "#e4e4e4" : "#ffffff" + color: up.pressed ? "#e4e4e4" : "#f6f6f6" border.color: control.enabled ? "#353637" : "#bdbebf" Rectangle { @@ -109,7 +109,7 @@ T.SpinBox { x: control.mirrored ? parent.width - width : 0 implicitWidth: 40 implicitHeight: 40 - color: down.pressed ? "#e4e4e4" : "#ffffff" + color: down.pressed ? "#e4e4e4" : "#f6f6f6" border.color: control.enabled ? "#353637" : "#bdbebf" Rectangle { diff --git a/src/imports/controls/Switch.qml b/src/imports/controls/Switch.qml index e1627d32..51348e77 100644 --- a/src/imports/controls/Switch.qml +++ b/src/imports/controls/Switch.qml @@ -75,7 +75,7 @@ T.Switch { width: 28 height: 28 radius: 16 - color: control.pressed ? "#e4e4e4" : "#ffffff" + color: control.pressed ? "#e4e4e4" : "#f6f6f6" border.width: 1 border.color: control.pressed ? "#26282a" : "#353637" diff --git a/src/imports/controls/doc/qtlabscontrols.qdocconf b/src/imports/controls/doc/qtlabscontrols.qdocconf index df004294..cfd5ec55 100644 --- a/src/imports/controls/doc/qtlabscontrols.qdocconf +++ b/src/imports/controls/doc/qtlabscontrols.qdocconf @@ -60,3 +60,5 @@ macro.styleimport.HTML = "<table class=\"alignedsummary\"><tbody><tr><td class=\ # \endstyleproperty macro.styleproperty.HTML = "<div class=\"qmlproto\"><table class=\"qmlname\"><tbody><tr valign=\"top\" class=\"odd\" id=\"\3\"><td class=\"tblQmlPropNode\"><p><span class=\"name\">\1</span> : <span class=\"type\">\2</span></p></td></tr></tbody></table></div>" macro.endstyleproperty = "\\br" + +macro.labs = "\\note \\e{Types in the Qt.labs module are not guaranteed to remain compatible in future versions.}" diff --git a/src/imports/controls/doc/src/qtlabscontrols-gettingstarted.qdoc b/src/imports/controls/doc/src/qtlabscontrols-gettingstarted.qdoc new file mode 100644 index 00000000..3f6d2898 --- /dev/null +++ b/src/imports/controls/doc/src/qtlabscontrols-gettingstarted.qdoc @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Copyright (C) 2015 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$ +** +****************************************************************************/ + +/*! + \page qtlabscontrols-gettingstarted.html + \title Getting Started with Qt Labs Controls + + A basic example of a QML file that makes use of controls is shown here: + + \snippet basic-example.qml 0 + + \section1 Setting Up Controls from C++ + + Although QQuickView has traditionally been used to display QML files in a + C++ application, doing this means you can only set window properties from + C++. + + With Qt Labs Controls, declare an ApplicationWindow as the root item of + your application and launch it by using QQmlApplicationEngine instead. + This ensures that you can control top level window properties from QML. + + A basic example of a source file that makes use of controls is shown here: + + \code + #include <QGuiApplication> + #include <QQmlApplicationEngine> + + int main(int argc, char *argv[]) + { + QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling); + QGuiApplication app(argc, argv); + QQmlApplicationEngine engine("main.qml"); + return app.exec(); + } + \endcode + + \section2 Using C++ Data From QML + + If you need to register a C++ class to use from QML, you can call + qmlRegisterType() before declaring your QQmlApplicationEngine. + See \l [QtQml] {Defining QML Types from C++} for more information. + + If you need to expose data to QML components, you need to make them + available to the context of the current QML engine. See QQmlContext for + more information. +*/ diff --git a/src/imports/controls/doc/src/qtlabscontrols-index.qdoc b/src/imports/controls/doc/src/qtlabscontrols-index.qdoc index dc198135..93040e3f 100644 --- a/src/imports/controls/doc/src/qtlabscontrols-index.qdoc +++ b/src/imports/controls/doc/src/qtlabscontrols-index.qdoc @@ -42,50 +42,10 @@ \section2 Qt.labs.calendar Module \generatelist {qmltypesbymodule Qt.labs.calendar} - \section1 Getting Started - - A basic example of a QML file that makes use of controls is shown here: - - \snippet basic-example.qml 0 - - \section1 Setting Up Controls from C++ - - Although QQuickView has traditionally been used to display QML files in a - C++ application, doing this means you can only set window properties from - C++. - - With Qt Labs Controls, declare an ApplicationWindow as the root item of - your application and launch it by using QQmlApplicationEngine instead. - This ensures that you can control top level window properties from QML. - - A basic example of a source file that makes use of controls is shown here: - - \code - #include <QGuiApplication> - #include <QQmlApplicationEngine> - - int main(int argc, char *argv[]) - { - QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling); - QGuiApplication app(argc, argv); - QQmlApplicationEngine engine("main.qml"); - return app.exec(); - } - \endcode - - \section2 Using C++ Data From QML - - If you need to register a C++ class to use from QML, you can call - qmlRegisterType() before declaring your QQmlApplicationEngine. - See \l [QtQml] {Defining QML Types from C++} for more information. - - If you need to expose data to QML components, you need to make them - available to the context of the current QML engine. See QQmlContext for - more information. - \section1 Important Concepts in Qt Labs Controls \list + \li \l{Getting Started with Qt Labs Controls} \li \l{Styling Qt Labs Controls} \li \l{High-DPI Support in Qt Labs Controls} \li \l{Differences between Qt Quick Controls} diff --git a/src/imports/controls/doc/src/qtlabscontrols-material.qdoc b/src/imports/controls/doc/src/qtlabscontrols-material.qdoc index f9b6a008..e2cadbda 100644 --- a/src/imports/controls/doc/src/qtlabscontrols-material.qdoc +++ b/src/imports/controls/doc/src/qtlabscontrols-material.qdoc @@ -38,6 +38,7 @@ \list \li \l {accent-attached-prop}{\b accent} : color + \li \l {primary-attached-prop}{\b primary} : color \li \l {theme-attached-prop}{\b theme} : enumeration \endlist @@ -59,9 +60,9 @@ \section2 Customization - The Material style allows customizing two attributes, \l {theme-attached-prop}{theme} - and \l {accent-attached-prop}{accent}. The following example illustrates how to create - a red \e stop button with light text: + The Material style allows customizing three attributes, \l {theme-attached-prop}{theme}, + \l {primary-attached-prop}{primary} and \l {accent-attached-prop}{accent}. The following + example illustrates how to create a red \e stop button with light text: \table \row @@ -123,18 +124,13 @@ of explicit Material style-specific references, the Material style must be deployed with the application. - \section1 Attached Property Documentation - - \styleproperty {Material.accent} {color} {accent-attached-prop} - \target accent-attached-prop - This attached property holds the accent color of the theme. The property - can be attached to any window or item. The value is propagated to children. + \section2 Pre-defined Colors - Even though the accent can be any \l {colorbasictypedocs}{color}, it is - recommended to use one of the pre-defined accents that have been designed + Even though primary and accent can be any \l {colorbasictypedocs}{color}, it + is recommended to use one of the pre-defined colors that have been designed to work well with the rest of the Material style palette: - Available accents: + Available pre-defined colors: \value Material.Red Red (#F44336) \value Material.Pink Pink (#E91E63) \value Material.Purple Purple (#9C27B0) @@ -143,7 +139,7 @@ \value Material.Blue Blue (#2196F3) \value Material.LightBlue Light Blue (#03A9F4) \value Material.Cyan Cyan (#00BCD4) - \value Material.Teal Teal (#009688, default) + \value Material.Teal Teal (#009688) \value Material.Green Green (#4CAF50) \value Material.LightGreen Light Green (#8BC34A) \value Material.Lime Lime (#CDDC39) @@ -155,6 +151,34 @@ \value Material.Grey Grey (#9E9E9E) \value Material.BlueGrey Blue Grey (#607D8B) + \labs + + \section1 Attached Property Documentation + + \styleproperty {Material.accent} {color} {accent-attached-prop} + \target accent-attached-prop + This attached property holds the accent color of the theme. The property + can be attached to any window or item. The value is propagated to children. + + The default value is \c Material.Teal. + + \note Even though the accent can be any \l {colorbasictypedocs}{color}, it is + recommended to use one of the \l {pre-defined colors} that have been designed + to work well with the rest of the Material style palette. + + \endstyleproperty + + \styleproperty {Material.primary} {color} {primary-attached-prop} + \target primary-attached-prop + This attached property holds the primary color of the theme. The property + can be attached to any window or item. The value is propagated to children. + + The default value is \c Material.BlueGray. + + \note Even though the primary can be any \l {colorbasictypedocs}{color}, it is + recommended to use one of the \l {pre-defined colors} that have been designed + to work well with the rest of the Material style palette. + \endstyleproperty \styleproperty {Material.theme} {enumeration} {theme-attached-prop} diff --git a/src/imports/controls/doc/src/qtlabscontrols-universal.qdoc b/src/imports/controls/doc/src/qtlabscontrols-universal.qdoc index 3c616d84..bf3dcfdc 100644 --- a/src/imports/controls/doc/src/qtlabscontrols-universal.qdoc +++ b/src/imports/controls/doc/src/qtlabscontrols-universal.qdoc @@ -124,6 +124,8 @@ of explicit Universal style-specific references, the Universal style must be deployed with the application. + \labs + \section1 Attached Property Documentation \styleproperty {Universal.accent} {color} {accent-attached-prop} diff --git a/src/imports/controls/doc/src/qtlabscontrols.qdoc b/src/imports/controls/doc/src/qtlabscontrols.qdoc index 10476f08..17e93a44 100644 --- a/src/imports/controls/doc/src/qtlabscontrols.qdoc +++ b/src/imports/controls/doc/src/qtlabscontrols.qdoc @@ -49,6 +49,8 @@ import Qt.labs.calendar 1.0 \endcode + \labs + \section1 QML Types \section2 Qt.labs.controls Module diff --git a/src/imports/controls/doc/src/templates/qtlabstemplates.qdoc b/src/imports/controls/doc/src/templates/qtlabstemplates.qdoc index 84eabe44..0f7f7d86 100644 --- a/src/imports/controls/doc/src/templates/qtlabstemplates.qdoc +++ b/src/imports/controls/doc/src/templates/qtlabstemplates.qdoc @@ -41,6 +41,8 @@ import Qt.labs.templates 1.0 as T \endcode + \labs + For the sake of clarity, there is a one-to-one mapping between the types provided by the \c Qt.labs.templates and \c Qt.labs.controls imports. For every type available in the \c Qt.labs.controls import, a non-visual template diff --git a/src/imports/controls/material/ComboBox.qml b/src/imports/controls/material/ComboBox.qml index 4b154182..fc284be1 100644 --- a/src/imports/controls/material/ComboBox.qml +++ b/src/imports/controls/material/ComboBox.qml @@ -115,6 +115,10 @@ T.ComboBox { //! [popup] popup: T.Popup { + y: control.height + implicitWidth: control.width + implicitHeight: Math.min(200, listview.contentHeight) + enter: Transition { // grow_fade_in NumberAnimation { property: "scale"; from: 0.9; to: 1.0; easing.type: Easing.OutQuint; duration: 220 } @@ -127,18 +131,17 @@ T.ComboBox { NumberAnimation { property: "opacity"; from: 1.0; to: 0.0; easing.type: Easing.OutCubic; duration: 150 } } - contentItem: Item { - // TODO: Popup::anchors - readonly property var above: popup.visible ? control.mapToItem(null, 0, -height) : Qt.point(0, 0) - readonly property var below: popup.visible ? control.mapToItem(null, 0, control.height) : Qt.point(0, 0) - readonly property bool showAbove: above.y >= 0 && below.y + height > control.Window.height + contentItem: ListView { + id: listview + clip: true + model: control.delegateModel + currentIndex: control.highlightedIndex + transformOrigin: popup.showAbove ? Item.Bottom : Item.Top - x: below.x - y: showAbove ? above.y : below.y - width: control.width - height: listview.height - transformOrigin: showAbove ? Item.Bottom : Item.Top +// ScrollIndicator.vertical: ScrollIndicator { } + } + background: Item { Rectangle { id: panel width: parent.width @@ -156,18 +159,6 @@ T.ComboBox { samples: 15 spread: 0.5 } - - ListView { - id: listview - width: control.width - height: Math.min(200, contentHeight) // TODO: 396 - - clip: true - model: control.delegateModel - currentIndex: control.highlightedIndex - -// ScrollIndicator.vertical: ScrollIndicator { } - } } } //! [popup] diff --git a/src/imports/controls/material/Menu.qml b/src/imports/controls/material/Menu.qml index 4523fc26..6a5c6f61 100644 --- a/src/imports/controls/material/Menu.qml +++ b/src/imports/controls/material/Menu.qml @@ -43,6 +43,11 @@ import QtGraphicalEffects 1.0 T.Menu { id: control + implicitWidth: Math.max(background ? background.implicitWidth : 0, + contentItem ? contentItem.implicitWidth + leftPadding + rightPadding : 0) + implicitHeight: Math.min(background ? background.implicitHeight : 0, + contentItem ? contentItem.implicitHeight + topPadding + bottomPadding : 0) + enter: Transition { // grow_fade_in NumberAnimation { property: "scale"; from: 0.9; to: 1.0; easing.type: Easing.OutQuint; duration: 220 } @@ -56,11 +61,26 @@ T.Menu { } //! [contentItem] - contentItem: Item { - implicitWidth: 200 - implicitHeight: Math.min(listview.contentHeight, 200) + contentItem: ListView { + implicitHeight: contentHeight transformOrigin: Item.Top + model: control.contentModel + // TODO: improve this? + interactive: ApplicationWindow.window ? contentHeight > ApplicationWindow.window.height : false + clip: true + keyNavigationWraps: false + currentIndex: -1 + + ScrollIndicator.vertical: ScrollIndicator {} + } + //! [contentItem] + + //! [background] + background: Item { + implicitWidth: 200 + implicitHeight: 200 + Rectangle { id: panel width: parent.width @@ -77,20 +97,6 @@ T.Menu { samples: 15 spread: 0.5 } - - ListView { - id: listview - width: parent.width - height: parent.height - model: control.contentModel - // TODO: improve this? - interactive: ApplicationWindow.window ? contentHeight > ApplicationWindow.window.height : false - clip: true - keyNavigationWraps: false - currentIndex: -1 - - ScrollIndicator.vertical: ScrollIndicator {} - } } - //! [contentItem] + //! [background] } diff --git a/src/imports/controls/material/Popup.qml b/src/imports/controls/material/Popup.qml index 40531c61..b1c73b0c 100644 --- a/src/imports/controls/material/Popup.qml +++ b/src/imports/controls/material/Popup.qml @@ -35,11 +35,21 @@ ****************************************************************************/ import QtQuick 2.6 +import QtGraphicalEffects 1.0 import Qt.labs.templates 1.0 as T +import Qt.labs.controls.material 1.0 T.Popup { id: control + implicitWidth: Math.max(background ? background.implicitWidth : 0, contentWidth + leftPadding + rightPadding) + implicitHeight: Math.max(background ? background.implicitHeight : 0, contentHeight + topPadding + bottomPadding) + + contentWidth: contentChildren.length === 1 ? contentChildren[0].implicitWidth : 0 + contentHeight: contentChildren.length === 1 ? contentChildren[0].implicitHeight : 0 + + padding: 6 + enter: Transition { // grow_fade_in NumberAnimation { property: "scale"; from: 0.9; to: 1.0; easing.type: Easing.OutQuint; duration: 220 } @@ -51,4 +61,18 @@ T.Popup { NumberAnimation { property: "scale"; from: 1.0; to: 0.9; easing.type: Easing.OutQuint; duration: 220 } NumberAnimation { property: "opacity"; from: 1.0; to: 0.0; easing.type: Easing.OutCubic; duration: 150 } } + + contentItem: Item { } + + background: Rectangle { + radius: 3 + color: control.Material.dialogColor + + layer.effect: DropShadow { + verticalOffset: 1 + color: control.Material.dropShadowColor + samples: 15 + spread: 0.5 + } + } } diff --git a/src/imports/controls/material/ToolBar.qml b/src/imports/controls/material/ToolBar.qml index 83f07be1..ad34e1a0 100644 --- a/src/imports/controls/material/ToolBar.qml +++ b/src/imports/controls/material/ToolBar.qml @@ -54,7 +54,7 @@ T.ToolBar { //! [background] background: Rectangle { implicitHeight: 48 - color: control.Material.accentColor + color: control.Material.primaryColor } //! [background] } diff --git a/src/imports/controls/material/qquickmaterialstyle.cpp b/src/imports/controls/material/qquickmaterialstyle.cpp index 04e96480..71b48a2b 100644 --- a/src/imports/controls/material/qquickmaterialstyle.cpp +++ b/src/imports/controls/material/qquickmaterialstyle.cpp @@ -43,21 +43,6 @@ QT_BEGIN_NAMESPACE -/*! - \qmltype Material - \inherits QtObject - \instantiates QQuickMaterialStyleAttached - \inqmlmodule QtQuick.Controls.Material - \ingroup utilities - \brief A style interface. - - TODO -*/ - -/*! - \qmlattachedproperty color QtQuickControls2::Material::textColorPrimaray -*/ - static const QRgb colors[][14] = { // Red { @@ -385,8 +370,10 @@ static const QRgb colors[][14] = { }; static QQuickMaterialStyle::Theme defaultTheme = QQuickMaterialStyle::Light; -static uint defaultAccent = QQuickMaterialStyle::Teal; -static bool defaultCustom = false; +static uint defaultPrimary = QQuickMaterialStyle::Indigo; +static uint defaultAccent = QQuickMaterialStyle::Pink; +static bool defaultPrimaryCustom = false; +static bool defaultAccentCustom = false; static const QRgb backgroundColorLight = 0xFFFAFAFA; static const QRgb backgroundColorDark = 0xFF303030; static const QRgb dialogColorLight = 0xFFFFFFFF; @@ -420,9 +407,12 @@ static const QRgb checkBoxUncheckedRippleColorDark = 0x20FFFFFF; QQuickMaterialStyle::QQuickMaterialStyle(QObject *parent) : QQuickStyle(parent), m_explicitTheme(false), + m_explicitPrimary(false), m_explicitAccent(false), - m_customAccent(defaultCustom), + m_customPrimary(defaultPrimaryCustom), + m_customAccent(defaultAccentCustom), m_theme(defaultTheme), + m_primary(defaultPrimary), m_accent(defaultAccent) { init(); @@ -478,6 +468,76 @@ void QQuickMaterialStyle::resetTheme() } } +QVariant QQuickMaterialStyle::primary() const +{ + return primaryColor(); +} + +void QQuickMaterialStyle::setPrimary(const QVariant &var) +{ + QRgb primary = 0; + bool custom = false; + if (var.type() == QVariant::Int) { + int val = var.toInt(); + if (val > BlueGrey) { + qmlInfo(parent()) << "unknown Material.primary value: " << val; + return; + } + primary = val; + } else { + int val = QMetaEnum::fromType<Color>().keyToValue(var.toByteArray()); + if (val != -1) { + primary = val; + } else { + QColor color(var.toString()); + if (!color.isValid()) { + qmlInfo(parent()) << "unknown Material.primary value: " << var.toString(); + return; + } + custom = true; + primary = color.rgba(); + } + } + + m_explicitPrimary = true; + if (m_primary != primary) { + m_customPrimary = custom; + m_primary = primary; + propagatePrimary(); + emit primaryChanged(); + emit paletteChanged(); + } +} + +void QQuickMaterialStyle::inheritPrimary(uint primary, bool custom) +{ + if (!m_explicitPrimary && m_primary != primary) { + m_customPrimary = custom; + m_primary = primary; + propagatePrimary(); + emit primaryChanged(); + } +} + +void QQuickMaterialStyle::propagatePrimary() +{ + foreach (QQuickStyle *child, childStyles()) { + QQuickMaterialStyle *material = qobject_cast<QQuickMaterialStyle *>(child); + if (material) + material->inheritPrimary(m_primary, m_customPrimary); + } +} + +void QQuickMaterialStyle::resetPrimary() +{ + if (m_explicitPrimary) { + m_customPrimary = false; + m_explicitPrimary = false; + QQuickMaterialStyle *material = qobject_cast<QQuickMaterialStyle *>(parentStyle()); + inheritPrimary(material ? material->m_primary : defaultPrimary, true); + } +} + QVariant QQuickMaterialStyle::accent() const { return accentColor(); @@ -549,6 +609,15 @@ void QQuickMaterialStyle::resetAccent() } } +QColor QQuickMaterialStyle::primaryColor() const +{ + if (m_customPrimary) + return QColor::fromRgba(m_primary); + if (m_primary > BlueGrey) + return QColor(); + return colors[m_primary][Shade500]; +} + QColor QQuickMaterialStyle::accentColor() const { if (m_customAccent) @@ -820,6 +889,7 @@ void QQuickMaterialStyle::parentStyleChange(QQuickStyle *newParent, QQuickStyle Q_UNUSED(oldParent); QQuickMaterialStyle *material = qobject_cast<QQuickMaterialStyle *>(newParent); if (material) { + inheritPrimary(material->m_primary, material->m_customPrimary); inheritAccent(material->m_accent, material->m_customAccent); inheritTheme(material->theme()); } @@ -846,15 +916,30 @@ void QQuickMaterialStyle::init() else if (!value.isEmpty()) qWarning().nospace().noquote() << settings->fileName() << ": unknown Material theme value: " << value; + value = settings->value(QStringLiteral("Primary")).toByteArray(); + Color primary = toEnumValue<Color>(value, &ok); + if (ok) { + defaultPrimaryCustom = m_customPrimary = false; + defaultPrimary = m_primary = primary; + } else { + QColor color(value.constData()); + if (color.isValid()) { + defaultPrimaryCustom = m_customPrimary = true; + defaultPrimary = m_primary = color.rgba(); + } else if (!value.isEmpty()) { + qWarning().nospace().noquote() << settings->fileName() << ": unknown Material primary value: " << value; + } + } + value = settings->value(QStringLiteral("Accent")).toByteArray(); Color accent = toEnumValue<Color>(value, &ok); if (ok) { - defaultCustom = m_customAccent = false; + defaultAccentCustom = m_customAccent = false; defaultAccent = m_accent = accent; } else { QColor color(value.constData()); if (color.isValid()) { - defaultCustom = m_customAccent = true; + defaultAccentCustom = m_customAccent = true; defaultAccent = m_accent = color.rgba(); } else if (!value.isEmpty()) { qWarning().nospace().noquote() << settings->fileName() << ": unknown Material accent value: " << value; diff --git a/src/imports/controls/material/qquickmaterialstyle_p.h b/src/imports/controls/material/qquickmaterialstyle_p.h index b9236216..00103361 100644 --- a/src/imports/controls/material/qquickmaterialstyle_p.h +++ b/src/imports/controls/material/qquickmaterialstyle_p.h @@ -59,7 +59,9 @@ class QQuickMaterialStyle : public QQuickStyle { Q_OBJECT Q_PROPERTY(Theme theme READ theme WRITE setTheme RESET resetTheme NOTIFY themeChanged FINAL) + Q_PROPERTY(QVariant primary READ primary WRITE setPrimary RESET resetPrimary NOTIFY primaryChanged FINAL) Q_PROPERTY(QVariant accent READ accent WRITE setAccent RESET resetAccent NOTIFY accentChanged FINAL) + Q_PROPERTY(QColor primaryColor READ primaryColor NOTIFY primaryChanged FINAL) // TODO: remove? Q_PROPERTY(QColor accentColor READ accentColor NOTIFY accentChanged FINAL) // TODO: remove? Q_PROPERTY(QColor backgroundColor READ backgroundColor NOTIFY paletteChanged FINAL) Q_PROPERTY(QColor primaryTextColor READ primaryTextColor NOTIFY paletteChanged FINAL) @@ -153,12 +155,19 @@ public: void propagateTheme(); void resetTheme(); + QVariant primary() const; + void setPrimary(const QVariant &accent); + void inheritPrimary(uint primary, bool custom); + void propagatePrimary(); + void resetPrimary(); + QVariant accent() const; void setAccent(const QVariant &accent); void inheritAccent(uint accent, bool custom); void propagateAccent(); void resetAccent(); + QColor primaryColor() const; QColor accentColor() const; QColor backgroundColor() const; QColor primaryTextColor() const; @@ -198,6 +207,7 @@ public: Q_SIGNALS: void themeChanged(); + void primaryChanged(); void accentChanged(); void paletteChanged(); @@ -208,9 +218,12 @@ private: void init(); bool m_explicitTheme; + bool m_explicitPrimary; bool m_explicitAccent; + bool m_customPrimary; bool m_customAccent; Theme m_theme; + uint m_primary; uint m_accent; }; diff --git a/src/imports/controls/universal/ComboBox.qml b/src/imports/controls/universal/ComboBox.qml index 38c8e6e2..f61ab634 100644 --- a/src/imports/controls/universal/ComboBox.qml +++ b/src/imports/controls/universal/ComboBox.qml @@ -110,29 +110,22 @@ T.ComboBox { //! [popup] popup: T.Popup { - contentItem: Rectangle { - // TODO: Popup::anchors - readonly property var above: popup.visible ? control.mapToItem(null, 0, control.height - height) : Qt.point(0, 0) - readonly property var below: popup.visible ? control.mapToItem(null, 0, 0) : Qt.point(0, 0) + implicitWidth: control.width + implicitHeight: Math.min(200, listview.contentHeight) // TODO: 396 - x: below.x - y: above.y >= 0 && below.y + height > control.Window.height ? above.y : below.y - width: control.width - height: listview.height + contentItem: ListView { + id: listview + clip: true + model: control.delegateModel + currentIndex: control.highlightedIndex - color: control.Universal.chromeMediumLowColor - - ListView { - id: listview - width: control.width - height: Math.min(200, contentHeight) // TODO: 396 - - clip: true - model: control.delegateModel - currentIndex: control.highlightedIndex +// ScrollIndicator.vertical: ScrollIndicator { } + } -// ScrollIndicator.vertical: ScrollIndicator { } - } + background: Rectangle { + color: control.Universal.chromeMediumLowColor + border.color: control.Universal.chromeHighColor + border.width: 1 // FlyoutBorderThemeThickness } } //! [popup] diff --git a/src/imports/controls/universal/Menu.qml b/src/imports/controls/universal/Menu.qml index fec16c16..7cae65f1 100644 --- a/src/imports/controls/universal/Menu.qml +++ b/src/imports/controls/universal/Menu.qml @@ -42,10 +42,14 @@ import Qt.labs.controls.universal 1.0 T.Menu { id: control + implicitWidth: Math.max(background ? background.implicitWidth : 0, + contentItem ? contentItem.implicitWidth + leftPadding + rightPadding : 0) + implicitHeight: Math.min(background ? background.implicitHeight : 0, + contentItem ? contentItem.implicitHeight + topPadding + bottomPadding : 0) + //! [contentItem] contentItem: ListView { - implicitWidth: 200 - implicitHeight: Math.min(contentHeight, 200) + implicitHeight: contentHeight model: control.contentModel // TODO: improve this? interactive: ApplicationWindow.window ? contentHeight > ApplicationWindow.window.height : false @@ -54,15 +58,16 @@ T.Menu { currentIndex: -1 ScrollIndicator.vertical: ScrollIndicator {} - - Rectangle { - z: -1 - width: parent.width - height: parent.height - color: control.Universal.chromeMediumLowColor - border.color: control.Universal.chromeHighColor - border.width: 1 // FlyoutBorderThemeThickness - } } //! [contentItem] + + //! [background] + background: Rectangle { + implicitWidth: 200 + implicitHeight: 200 + color: control.Universal.chromeMediumLowColor + border.color: control.Universal.chromeHighColor + border.width: 1 // FlyoutBorderThemeThickness + } + //! [background] } diff --git a/src/imports/controls/universal/Popup.qml b/src/imports/controls/universal/Popup.qml new file mode 100644 index 00000000..16ed5451 --- /dev/null +++ b/src/imports/controls/universal/Popup.qml @@ -0,0 +1,59 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Labs Controls 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 Qt.labs.templates 1.0 as T +import Qt.labs.controls.universal 1.0 + +T.Popup { + id: control + + implicitWidth: Math.max(background ? background.implicitWidth : 0, contentWidth + leftPadding + rightPadding) + implicitHeight: Math.max(background ? background.implicitHeight : 0, contentHeight + topPadding + bottomPadding) + + contentWidth: contentChildren.length === 1 ? contentChildren[0].implicitWidth : 0 + contentHeight: contentChildren.length === 1 ? contentChildren[0].implicitHeight : 0 + + padding: 12 + + contentItem: Item { } + + background: Rectangle { + color: control.Universal.chromeMediumLowColor + border.color: control.Universal.chromeHighColor + border.width: 1 // FlyoutBorderThemeThickness + } +} diff --git a/src/imports/controls/universal/universal.pri b/src/imports/controls/universal/universal.pri index 73547f4d..965228cb 100644 --- a/src/imports/controls/universal/universal.pri +++ b/src/imports/controls/universal/universal.pri @@ -14,6 +14,7 @@ QML_FILES += \ $$PWD/Page.qml \ $$PWD/PageIndicator.qml \ $$PWD/Pane.qml \ + $$PWD/Popup.qml \ $$PWD/ProgressBar.qml \ $$PWD/RadioButton.qml \ $$PWD/RangeSlider.qml \ diff --git a/src/templates/qquickabstractbutton.cpp b/src/templates/qquickabstractbutton.cpp index 5fde4be1..6c42d7d8 100644 --- a/src/templates/qquickabstractbutton.cpp +++ b/src/templates/qquickabstractbutton.cpp @@ -62,6 +62,8 @@ static const int AUTO_REPEAT_INTERVAL = 100; implementations, leaving them to the types that derive from it. TODO: ButtonGroup usage + + \labs */ /*! diff --git a/src/templates/qquickapplicationwindow.cpp b/src/templates/qquickapplicationwindow.cpp index b49f80af..80e347c9 100644 --- a/src/templates/qquickapplicationwindow.cpp +++ b/src/templates/qquickapplicationwindow.cpp @@ -81,6 +81,8 @@ QT_BEGIN_NAMESPACE \note By default, an ApplicationWindow is not visible. + \labs + \sa Page, {Container Controls} */ diff --git a/src/templates/qquickbusyindicator.cpp b/src/templates/qquickbusyindicator.cpp index 8751ef7c..381ac5c7 100644 --- a/src/templates/qquickbusyindicator.cpp +++ b/src/templates/qquickbusyindicator.cpp @@ -60,6 +60,8 @@ QT_BEGIN_NAMESPACE } \endqml + \labs + \sa {Customizing BusyIndicator}, {Indicator Controls} */ diff --git a/src/templates/qquickbutton.cpp b/src/templates/qquickbutton.cpp index b323cb0b..1e40ec05 100644 --- a/src/templates/qquickbutton.cpp +++ b/src/templates/qquickbutton.cpp @@ -80,6 +80,8 @@ QT_BEGIN_NAMESPACE } \endcode + \labs + \sa {Customizing Button}, {Button Controls} */ diff --git a/src/templates/qquickbuttongroup.cpp b/src/templates/qquickbuttongroup.cpp index af975cad..c1822183 100644 --- a/src/templates/qquickbuttongroup.cpp +++ b/src/templates/qquickbuttongroup.cpp @@ -119,6 +119,8 @@ QT_BEGIN_NAMESPACE More advanced use cases can be handled using the addButton() and removeButton() methods. + \labs + \sa RadioButton */ diff --git a/src/templates/qquickcheckbox.cpp b/src/templates/qquickcheckbox.cpp index 9a416419..644e151e 100644 --- a/src/templates/qquickcheckbox.cpp +++ b/src/templates/qquickcheckbox.cpp @@ -80,6 +80,8 @@ QT_BEGIN_NAMESPACE } \endcode + \labs + \sa {Customizing CheckBox}, {Button Controls} */ diff --git a/src/templates/qquickcombobox.cpp b/src/templates/qquickcombobox.cpp index 9bf1c4e6..3283a46e 100644 --- a/src/templates/qquickcombobox.cpp +++ b/src/templates/qquickcombobox.cpp @@ -98,6 +98,8 @@ QT_BEGIN_NAMESPACE \l textRole is not defined, ComboBox is unable to visualize it and throws a \c {ReferenceError: modelData is not defined}. + \labs + \sa {Customizing ComboBox}, {Input Controls} */ @@ -330,6 +332,13 @@ QQuickComboBox::QQuickComboBox(QQuickItem *parent) : setAcceptedMouseButtons(Qt::LeftButton); } +QQuickComboBox::~QQuickComboBox() +{ + Q_D(QQuickComboBox); + delete d->popup; + d->popup = nullptr; +} + /*! \readonly \qmlproperty int Qt.labs.controls::ComboBox::count diff --git a/src/templates/qquickcombobox_p.h b/src/templates/qquickcombobox_p.h index 30bc7760..3381791e 100644 --- a/src/templates/qquickcombobox_p.h +++ b/src/templates/qquickcombobox_p.h @@ -73,6 +73,7 @@ class Q_LABSTEMPLATES_EXPORT QQuickComboBox : public QQuickControl public: explicit QQuickComboBox(QQuickItem *parent = nullptr); + ~QQuickComboBox(); int count() const; diff --git a/src/templates/qquickcontainer.cpp b/src/templates/qquickcontainer.cpp index c6638768..d5c387fb 100644 --- a/src/templates/qquickcontainer.cpp +++ b/src/templates/qquickcontainer.cpp @@ -51,6 +51,8 @@ QT_BEGIN_NAMESPACE Container is the base type of container-like user interface controls. + \labs + \sa {Container Controls} */ diff --git a/src/templates/qquickcontrol.cpp b/src/templates/qquickcontrol.cpp index 6914037d..42ab4c70 100644 --- a/src/templates/qquickcontrol.cpp +++ b/src/templates/qquickcontrol.cpp @@ -63,6 +63,8 @@ QT_BEGIN_NAMESPACE \brief A user interface control. Control is the base type of user interface controls. + + \labs */ QQuickControlPrivate::QQuickControlPrivate() : diff --git a/src/templates/qquickdial.cpp b/src/templates/qquickdial.cpp index 3c354be4..2ad86f45 100644 --- a/src/templates/qquickdial.cpp +++ b/src/templates/qquickdial.cpp @@ -70,6 +70,8 @@ QT_BEGIN_NAMESPACE \row \li Set \l value to \l to \li \c Qt.Key_End \endtable + \labs + \sa {Customizing Dial}, {Input Controls} */ @@ -427,27 +429,39 @@ void QQuickDial::setHandle(QQuickItem *handle) void QQuickDial::keyPressEvent(QKeyEvent *event) { Q_D(QQuickDial); - if (event->key() == Qt::Key_Left || event->key() == Qt::Key_Down) { + switch (event->key()) { + case Qt::Key_Left: + case Qt::Key_Down: setPressed(true); if (isMirrored()) increase(); else decrease(); - } else if (event->key() == Qt::Key_Right || event->key() == Qt::Key_Up) { + break; + + case Qt::Key_Right: + case Qt::Key_Up: setPressed(true); if (isMirrored()) decrease(); else increase(); - } else if (event->key() == Qt::Key_Home) { + break; + + case Qt::Key_Home: setPressed(true); setValue(isMirrored() ? d->to : d->from); - } else if (event->key() == Qt::Key_End) { + break; + + case Qt::Key_End: setPressed(true); setValue(isMirrored() ? d->from : d->to); - } else { + break; + + default: event->ignore(); QQuickControl::keyPressEvent(event); + break; } } diff --git a/src/templates/qquickdrawer.cpp b/src/templates/qquickdrawer.cpp index e0216872..2eb12676 100644 --- a/src/templates/qquickdrawer.cpp +++ b/src/templates/qquickdrawer.cpp @@ -80,6 +80,8 @@ QT_BEGIN_NAMESPACE If you would like the application's contents to stay where they are when the drawer is opened, don't apply a translation. + \labs + \sa SwipeView, {Customizing Drawer}, {Navigation Controls}, {Container Controls} */ diff --git a/src/templates/qquickframe.cpp b/src/templates/qquickframe.cpp index 68be30a2..6ca9da0e 100644 --- a/src/templates/qquickframe.cpp +++ b/src/templates/qquickframe.cpp @@ -60,6 +60,8 @@ QT_BEGIN_NAMESPACE \snippet qtlabscontrols-frame.qml 1 + \labs + \sa {Customizing Frame}, {Container Controls} */ diff --git a/src/templates/qquickgroupbox.cpp b/src/templates/qquickgroupbox.cpp index d324bb15..011245b1 100644 --- a/src/templates/qquickgroupbox.cpp +++ b/src/templates/qquickgroupbox.cpp @@ -75,6 +75,8 @@ QT_BEGIN_NAMESPACE \snippet qtlabscontrols-groupbox-checkable.qml 1 + \labs + \sa CheckBox, {Customizing GroupBox}, {Container Controls} */ diff --git a/src/templates/qquickitemdelegate.cpp b/src/templates/qquickitemdelegate.cpp index 7e13b638..f25a117f 100644 --- a/src/templates/qquickitemdelegate.cpp +++ b/src/templates/qquickitemdelegate.cpp @@ -54,6 +54,8 @@ QT_BEGIN_NAMESPACE \snippet qtlabscontrols-itemdelegate.qml 1 + \labs + \sa {Customizing ItemDelegate} */ diff --git a/src/templates/qquicklabel.cpp b/src/templates/qquicklabel.cpp index e749645c..8420deb1 100644 --- a/src/templates/qquicklabel.cpp +++ b/src/templates/qquicklabel.cpp @@ -65,6 +65,8 @@ QT_BEGIN_NAMESPACE \snippet qtlabscontrols-label.qml 1 + \labs + \sa {Customizing Label} */ diff --git a/src/templates/qquickmenu.cpp b/src/templates/qquickmenu.cpp index 69a409eb..beda1489 100644 --- a/src/templates/qquickmenu.cpp +++ b/src/templates/qquickmenu.cpp @@ -85,6 +85,8 @@ QT_BEGIN_NAMESPACE } \endcode + \labs + \sa {Customizing Menu}, {Menu Controls} */ @@ -106,6 +108,8 @@ void QQuickMenuPrivate::insertItem(int index, QQuickItem *item) { contentData.append(item); item->setParentItem(contentItem); + if (complete) + resizeItem(item); QQuickItemPrivate::get(item)->addItemChangeListener(this, QQuickItemPrivate::Destroyed | QQuickItemPrivate::Parent); contentModel->insert(index, item); } @@ -124,6 +128,27 @@ void QQuickMenuPrivate::removeItem(int index, QQuickItem *item) contentModel->remove(index); } +void QQuickMenuPrivate::resizeItem(QQuickItem *item) +{ + if (!item || !contentItem) + return; + + QQuickItemPrivate *p = QQuickItemPrivate::get(item); + if (!p->widthValid) { + item->setWidth(contentItem->width()); + p->widthValid = false; + } +} + +void QQuickMenuPrivate::resizeItems() +{ + if (!contentModel) + return; + + for (int i = 0; i < contentModel->count(); ++i) + resizeItem(itemAt(i)); +} + void QQuickMenuPrivate::itemChildAdded(QQuickItem *, QQuickItem *child) { // add dynamically reparented items (eg. by a Repeater) @@ -157,32 +182,10 @@ void QQuickMenuPrivate::itemDestroyed(QQuickItem *item) removeItem(index, item); } -void QQuickMenuPrivate::onContentItemChanged() +void QQuickMenuPrivate::itemGeometryChanged(QQuickItem *, const QRectF &, const QRectF &) { - Q_Q(QQuickMenu); - if (contentItem) { - contentItem->installEventFilter(q); - contentItem->setFlag(QQuickItem::ItemIsFocusScope); - contentItem->setActiveFocusOnTab(true); - - // Trying to give active focus to the contentItem (ListView, by default) - // when the menu first opens, without also giving it to the first delegate item - // doesn't seem to be possible, but this is what we need to do. QMenu behaves - // similarly to this; it receives focus if a button that has it as a menu is clicked, - // and only after pressing tab is the first menu item then given active focus. - if (!dummyFocusItem) { - dummyFocusItem = new QQuickItem(contentItem); - dummyFocusItem->setObjectName(QStringLiteral("dummyMenuFocusItem")); - } else { - dummyFocusItem->setParentItem(contentItem); - } - - dummyFocusItem->setActiveFocusOnTab(true); - dummyFocusItem->stackBefore(contentItem->childItems().first()); - - QObjectPrivate::connect(q, &QQuickMenu::visibleChanged, this, &QQuickMenuPrivate::onMenuVisibleChanged); - QObjectPrivate::connect(dummyFocusItem, &QQuickItem::activeFocusChanged, this, &QQuickMenuPrivate::maybeUnsetDummyFocusOnTab); - } + if (complete) + resizeItems(); } void QQuickMenuPrivate::onItemPressed() @@ -300,7 +303,7 @@ QQuickMenu::QQuickMenu(QObject *parent) : Q_D(QQuickMenu); connect(this, &QQuickMenu::pressedOutside, this, &QQuickMenu::close); connect(this, &QQuickMenu::releasedOutside, this, &QQuickMenu::close); - QObjectPrivate::connect(this, &QQuickMenu::contentItemChanged, d, &QQuickMenuPrivate::onContentItemChanged); + QObjectPrivate::connect(this, &QQuickMenu::visibleChanged, d, &QQuickMenuPrivate::onMenuVisibleChanged); } /*! @@ -442,6 +445,47 @@ void QQuickMenu::setTitle(QString &title) emit titleChanged(); } +void QQuickMenu::componentComplete() +{ + Q_D(QQuickMenu); + QQuickPopup::componentComplete(); + d->resizeItems(); +} + +void QQuickMenu::contentItemChange(QQuickItem *newItem, QQuickItem *oldItem) +{ + Q_D(QQuickMenu); + QQuickPopup::contentItemChange(newItem, oldItem); + if (oldItem) { + oldItem->removeEventFilter(this); + if (d->dummyFocusItem) + QObjectPrivate::disconnect(d->dummyFocusItem.data(), &QQuickItem::activeFocusChanged, d, &QQuickMenuPrivate::maybeUnsetDummyFocusOnTab); + } + + if (newItem) { + newItem->installEventFilter(this); + newItem->setFlag(QQuickItem::ItemIsFocusScope); + newItem->setActiveFocusOnTab(true); + + // Trying to give active focus to the contentItem (ListView, by default) + // when the menu first opens, without also giving it to the first delegate item + // doesn't seem to be possible, but this is what we need to do. QMenu behaves + // similarly to this; it receives focus if a button that has it as a menu is clicked, + // and only after pressing tab is the first menu item then given active focus. + if (!d->dummyFocusItem) { + d->dummyFocusItem = new QQuickItem(newItem); + d->dummyFocusItem->setObjectName(QStringLiteral("dummyMenuFocusItem")); + } else { + d->dummyFocusItem->setParentItem(newItem); + } + + d->dummyFocusItem->setActiveFocusOnTab(true); + d->dummyFocusItem->stackBefore(newItem->childItems().first()); + + QObjectPrivate::connect(d->dummyFocusItem.data(), &QQuickItem::activeFocusChanged, d, &QQuickMenuPrivate::maybeUnsetDummyFocusOnTab); + } +} + bool QQuickMenu::eventFilter(QObject *object, QEvent *event) { Q_D(QQuickMenu); @@ -458,17 +502,23 @@ bool QQuickMenu::eventFilter(QObject *object, QEvent *event) // only allow flicking with the mouse when there are too many menu items to be // shown at once. QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event); - if (keyEvent->key() == Qt::Key_Up) { + switch (keyEvent->key()) { + case Qt::Key_Up: if (d->contentItem->metaObject()->indexOfMethod("decrementCurrentIndex()") != -1) QMetaObject::invokeMethod(d->contentItem, "decrementCurrentIndex"); return true; - } else if (keyEvent->key() == Qt::Key_Down) { + + case Qt::Key_Down: if (d->contentItem->metaObject()->indexOfMethod("incrementCurrentIndex()") != -1) QMetaObject::invokeMethod(d->contentItem, "incrementCurrentIndex"); return true; - } else if (keyEvent->key() == Qt::Key_Escape) { + + case Qt::Key_Escape: close(); return true; + + default: + break; } return false; diff --git a/src/templates/qquickmenu_p.h b/src/templates/qquickmenu_p.h index a54cafbd..ec417d16 100644 --- a/src/templates/qquickmenu_p.h +++ b/src/templates/qquickmenu_p.h @@ -82,6 +82,8 @@ public: void setTitle(QString &title); protected: + void componentComplete() override; + void contentItemChange(QQuickItem *newItem, QQuickItem *oldItem) override; bool eventFilter(QObject *object, QEvent *event) override; Q_SIGNALS: diff --git a/src/templates/qquickmenu_p_p.h b/src/templates/qquickmenu_p_p.h index e06c0299..505f3cb9 100644 --- a/src/templates/qquickmenu_p_p.h +++ b/src/templates/qquickmenu_p_p.h @@ -49,6 +49,7 @@ // #include <QtCore/qvector.h> +#include <QtCore/qpointer.h> #include <QtQuick/private/qquickitemchangelistener_p.h> #include <QtLabsTemplates/private/qquickpopup_p_p.h> @@ -69,12 +70,15 @@ public: void moveItem(int from, int to); void removeItem(int index, QQuickItem *item); + void resizeItem(QQuickItem *item); + void resizeItems(); + void itemChildAdded(QQuickItem *item, QQuickItem *child) override; void itemSiblingOrderChanged(QQuickItem *item) override; void itemParentChanged(QQuickItem *item, QQuickItem *parent) override; void itemDestroyed(QQuickItem *item) override; + void itemGeometryChanged(QQuickItem *, const QRectF &newGeometry, const QRectF &oldGeometry) override; - void onContentItemChanged(); void onItemPressed(); void onItemActiveFocusChanged(); void onMenuVisibleChanged(); @@ -87,7 +91,7 @@ public: QVector<QObject *> contentData; QQmlObjectModel *contentModel; - QQuickItem *dummyFocusItem; + QPointer<QQuickItem> dummyFocusItem; bool ignoreActiveFocusChanges; QString title; }; diff --git a/src/templates/qquickmenuitem.cpp b/src/templates/qquickmenuitem.cpp index f6cfda4c..d784af02 100644 --- a/src/templates/qquickmenuitem.cpp +++ b/src/templates/qquickmenuitem.cpp @@ -75,6 +75,8 @@ QT_BEGIN_NAMESPACE } \endcode + \labs + \sa {Customizing MenuItem}, {Menu Controls} */ diff --git a/src/templates/qquickoverlay.cpp b/src/templates/qquickoverlay.cpp index 5455676f..8b19e8e9 100644 --- a/src/templates/qquickoverlay.cpp +++ b/src/templates/qquickoverlay.cpp @@ -240,6 +240,12 @@ void QQuickOverlay::mouseReleaseEvent(QMouseEvent *event) emit released(); } +void QQuickOverlay::wheelEvent(QWheelEvent *event) +{ + Q_D(QQuickOverlay); + event->setAccepted(d->modalPopups > 0); +} + bool QQuickOverlay::childMouseEventFilter(QQuickItem *item, QEvent *event) { Q_D(QQuickOverlay); @@ -255,11 +261,11 @@ bool QQuickOverlay::childMouseEventFilter(QQuickItem *item, QEvent *event) const QQuickItemPrivate *priv = QQuickItemPrivate::get(this); const QList<QQuickItem *> &sortedChildren = priv->paintOrderChildItems(); for (int i = sortedChildren.count() - 1; i >= 0; --i) { - QQuickItem *contentItem = sortedChildren[i]; - if (contentItem == item) + QQuickItem *popupItem = sortedChildren[i]; + if (popupItem == item) break; - QQuickPopup *popup = d->popups.value(contentItem); + QQuickPopup *popup = d->popups.value(popupItem); if (popup) { emit popup->pressedOutside(); diff --git a/src/templates/qquickoverlay_p.h b/src/templates/qquickoverlay_p.h index b532720f..f5ce54f6 100644 --- a/src/templates/qquickoverlay_p.h +++ b/src/templates/qquickoverlay_p.h @@ -80,6 +80,7 @@ protected: void mousePressEvent(QMouseEvent *event) override; void mouseMoveEvent(QMouseEvent *event) override; void mouseReleaseEvent(QMouseEvent *event) override; + void wheelEvent(QWheelEvent *event) override; bool childMouseEventFilter(QQuickItem *item, QEvent *event) override; private: diff --git a/src/templates/qquickpageindicator.cpp b/src/templates/qquickpageindicator.cpp index 400a5237..1333c245 100644 --- a/src/templates/qquickpageindicator.cpp +++ b/src/templates/qquickpageindicator.cpp @@ -59,6 +59,8 @@ QT_BEGIN_NAMESPACE \snippet qtlabscontrols-pageindicator.qml 1 + \labs + \sa SwipeView, {Customizing PageIndicator}, {Indicator Controls} */ diff --git a/src/templates/qquickpane.cpp b/src/templates/qquickpane.cpp index bbec8e42..c660220c 100644 --- a/src/templates/qquickpane.cpp +++ b/src/templates/qquickpane.cpp @@ -60,6 +60,8 @@ QT_BEGIN_NAMESPACE \snippet qtlabscontrols-pane.qml 1 + \labs + \sa {Customizing Pane}, {Container Controls} */ diff --git a/src/templates/qquickpopup.cpp b/src/templates/qquickpopup.cpp index 87c63f8d..93e5a63a 100644 --- a/src/templates/qquickpopup.cpp +++ b/src/templates/qquickpopup.cpp @@ -55,34 +55,400 @@ QT_BEGIN_NAMESPACE \brief A popup control. Popup is the base type of popup-like user interface controls. + + \labs */ +static const QQuickItemPrivate::ChangeTypes AncestorChangeTypes = QQuickItemPrivate::Geometry + | QQuickItemPrivate::Parent + | QQuickItemPrivate::Children; + +static const QQuickItemPrivate::ChangeTypes ItemChangeTypes = QQuickItemPrivate::Geometry + | QQuickItemPrivate::Parent + | QQuickItemPrivate::Destroyed; + QQuickPopupPrivate::QQuickPopupPrivate() : QObjectPrivate() , focus(false) , modal(false) + , hasTopPadding(false) + , hasLeftPadding(false) + , hasRightPadding(false) + , hasBottomPadding(false) + , padding(0) + , topPadding(0) + , leftPadding(0) + , rightPadding(0) + , bottomPadding(0) + , contentWidth(0) + , contentHeight(0) + , parentItem(nullptr) + , background(nullptr) , contentItem(nullptr) , overlay(nullptr) , enter(nullptr) , exit(nullptr) + , popupItem(nullptr) + , positioner(this) , transitionManager(this) { } +void QQuickPopupPrivate::init() +{ + Q_Q(QQuickPopup); + popupItem = new QQuickPopupItem(q); + popupItem->setParent(q); + q->setParentItem(qobject_cast<QQuickItem *>(parent)); +} + void QQuickPopupPrivate::finalizeEnterTransition() { if (focus) - contentItem->setFocus(true); + popupItem->setFocus(true); } void QQuickPopupPrivate::finalizeExitTransition() { Q_Q(QQuickPopup); overlay = nullptr; - contentItem->setParentItem(nullptr); + positioner.setParentItem(nullptr); + popupItem->setParentItem(nullptr); emit q->visibleChanged(); } +void QQuickPopupPrivate::resizeBackground() +{ + Q_Q(QQuickPopup); + if (background) { + QQuickItemPrivate *p = QQuickItemPrivate::get(background); + if (!p->widthValid && qFuzzyIsNull(background->x())) { + background->setWidth(q->width()); + p->widthValid = false; + } + if (!p->heightValid && qFuzzyIsNull(background->y())) { + background->setHeight(q->height()); + p->heightValid = false; + } + } +} + +void QQuickPopupPrivate::resizeContent() +{ + Q_Q(QQuickPopup); + if (contentItem) { + contentItem->setPosition(QPointF(q->leftPadding(), q->topPadding())); + contentItem->setSize(QSizeF(q->availableWidth(), q->availableHeight())); + } +} + +void QQuickPopupPrivate::setTopPadding(qreal value, bool reset) +{ + Q_Q(QQuickPopup); + qreal oldPadding = q->topPadding(); + topPadding = value; + hasTopPadding = !reset; + if ((!reset && !qFuzzyCompare(oldPadding, value)) || (reset && !qFuzzyCompare(oldPadding, padding))) { + emit q->topPaddingChanged(); + emit q->availableHeightChanged(); + q->paddingChange(QMarginsF(leftPadding, topPadding, rightPadding, bottomPadding), + QMarginsF(leftPadding, oldPadding, rightPadding, bottomPadding)); + } +} + +void QQuickPopupPrivate::setLeftPadding(qreal value, bool reset) +{ + Q_Q(QQuickPopup); + qreal oldPadding = q->leftPadding(); + leftPadding = value; + hasLeftPadding = !reset; + if ((!reset && !qFuzzyCompare(oldPadding, value)) || (reset && !qFuzzyCompare(oldPadding, padding))) { + emit q->leftPaddingChanged(); + emit q->availableWidthChanged(); + q->paddingChange(QMarginsF(leftPadding, topPadding, rightPadding, bottomPadding), + QMarginsF(oldPadding, topPadding, rightPadding, bottomPadding)); + } +} + +void QQuickPopupPrivate::setRightPadding(qreal value, bool reset) +{ + Q_Q(QQuickPopup); + qreal oldPadding = q->rightPadding(); + rightPadding = value; + hasRightPadding = !reset; + if ((!reset && !qFuzzyCompare(oldPadding, value)) || (reset && !qFuzzyCompare(oldPadding, padding))) { + emit q->rightPaddingChanged(); + emit q->availableWidthChanged(); + q->paddingChange(QMarginsF(leftPadding, topPadding, rightPadding, bottomPadding), + QMarginsF(leftPadding, topPadding, oldPadding, bottomPadding)); + } +} + +void QQuickPopupPrivate::setBottomPadding(qreal value, bool reset) +{ + Q_Q(QQuickPopup); + qreal oldPadding = q->bottomPadding(); + bottomPadding = value; + hasBottomPadding = !reset; + if ((!reset && !qFuzzyCompare(oldPadding, value)) || (reset && !qFuzzyCompare(oldPadding, padding))) { + emit q->bottomPaddingChanged(); + emit q->availableHeightChanged(); + q->paddingChange(QMarginsF(leftPadding, topPadding, rightPadding, bottomPadding), + QMarginsF(leftPadding, topPadding, rightPadding, oldPadding)); + } +} + +class QQuickPopupItemPrivate : public QQuickItemPrivate +{ + Q_DECLARE_PUBLIC(QQuickPopupItem) + +public: + QQuickPopupItemPrivate(QQuickPopup *popup); + + void implicitWidthChanged() override; + void implicitHeightChanged() override; + + QQuickPopup *popup; +}; + +QQuickPopupItemPrivate::QQuickPopupItemPrivate(QQuickPopup *popup) : popup(popup) +{ +} + +void QQuickPopupItemPrivate::implicitWidthChanged() +{ + emit popup->implicitHeightChanged(); +} + +void QQuickPopupItemPrivate::implicitHeightChanged() +{ + emit popup->implicitHeightChanged(); +} + +QQuickPopupItem::QQuickPopupItem(QQuickPopup *popup) : + QQuickItem(*(new QQuickPopupItemPrivate(popup))) +{ + setAcceptedMouseButtons(Qt::AllButtons); +} + +void QQuickPopupItem::focusInEvent(QFocusEvent *event) +{ + Q_D(QQuickPopupItem); + d->popup->focusInEvent(event); +} + +void QQuickPopupItem::focusOutEvent(QFocusEvent *event) +{ + Q_D(QQuickPopupItem); + d->popup->focusOutEvent(event); +} + +void QQuickPopupItem::keyPressEvent(QKeyEvent *event) +{ + Q_D(QQuickPopupItem); + d->popup->keyPressEvent(event); +} + +void QQuickPopupItem::keyReleaseEvent(QKeyEvent *event) +{ + Q_D(QQuickPopupItem); + d->popup->keyReleaseEvent(event); +} + +void QQuickPopupItem::mousePressEvent(QMouseEvent *event) +{ + Q_D(QQuickPopupItem); + d->popup->mousePressEvent(event); +} + +void QQuickPopupItem::mouseMoveEvent(QMouseEvent *event) +{ + Q_D(QQuickPopupItem); + d->popup->mouseMoveEvent(event); +} + +void QQuickPopupItem::mouseReleaseEvent(QMouseEvent *event) +{ + Q_D(QQuickPopupItem); + d->popup->mouseReleaseEvent(event); +} + +void QQuickPopupItem::mouseDoubleClickEvent(QMouseEvent *event) +{ + Q_D(QQuickPopupItem); + d->popup->mouseDoubleClickEvent(event); +} + +void QQuickPopupItem::mouseUngrabEvent() +{ + Q_D(QQuickPopupItem); + d->popup->mouseUngrabEvent(); +} + +void QQuickPopupItem::wheelEvent(QWheelEvent *event) +{ + Q_D(QQuickPopupItem); + d->popup->wheelEvent(event); +} + +void QQuickPopupItem::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) +{ + Q_D(QQuickPopupItem); + d->popup->geometryChanged(newGeometry, oldGeometry); +} + +QQuickPopupPositioner::QQuickPopupPositioner(QQuickPopupPrivate *popup) : + m_x(0), + m_y(0), + m_parentItem(nullptr), + m_popup(popup) +{ +} + +QQuickPopupPositioner::~QQuickPopupPositioner() +{ + if (m_parentItem) { + QQuickItemPrivate::get(m_parentItem)->removeItemChangeListener(this, ItemChangeTypes); + removeAncestorListeners(m_parentItem->parentItem()); + } +} + +qreal QQuickPopupPositioner::x() const +{ + return m_x; +} + +void QQuickPopupPositioner::setX(qreal x) +{ + if (m_x != x) { + m_x = x; + repositionPopup(); + } +} + +qreal QQuickPopupPositioner::y() const +{ + return m_y; +} + +void QQuickPopupPositioner::setY(qreal y) +{ + if (m_y != y) { + m_y = y; + repositionPopup(); + } +} + +QQuickItem *QQuickPopupPositioner::parentItem() const +{ + return m_parentItem; +} + +void QQuickPopupPositioner::setParentItem(QQuickItem *parent) +{ + if (m_parentItem == parent) + return; + + if (m_parentItem) { + QQuickItemPrivate::get(m_parentItem)->removeItemChangeListener(this, ItemChangeTypes); + removeAncestorListeners(m_parentItem->parentItem()); + } + + m_parentItem = parent; + + if (!parent) + return; + + QQuickItemPrivate::get(parent)->addItemChangeListener(this, ItemChangeTypes); + addAncestorListeners(parent->parentItem()); + + repositionPopup(); +} + +void QQuickPopupPositioner::itemGeometryChanged(QQuickItem *, const QRectF &, const QRectF &) +{ + repositionPopup(); +} + +void QQuickPopupPositioner::itemParentChanged(QQuickItem *, QQuickItem *parent) +{ + addAncestorListeners(parent); +} + +void QQuickPopupPositioner::itemChildRemoved(QQuickItem *, QQuickItem *child) +{ + if (isAncestor(child)) + removeAncestorListeners(child); +} + +void QQuickPopupPositioner::itemDestroyed(QQuickItem *item) +{ + Q_ASSERT(m_parentItem == item); + + m_parentItem = nullptr; + QQuickItemPrivate::get(item)->removeItemChangeListener(this, ItemChangeTypes); + removeAncestorListeners(item->parentItem()); +} + +void QQuickPopupPositioner::repositionPopup() +{ + QRectF rect(m_x, m_y, m_popup->popupItem->width(), m_popup->popupItem->height()); + if (m_parentItem) { + rect = m_parentItem->mapRectToScene(rect); + + QQuickWindow *window = m_parentItem->window(); + if (window) { + if (rect.top() < 0 || rect.bottom() > window->height()) { + // if the popup doesn't fit on the screen, try flipping it around (below <-> above) + QRectF flipped = m_parentItem->mapRectToScene(QRectF(m_x, m_parentItem->height() - m_y - rect.height(), rect.width(), rect.height())); + if (flipped.y() >= 0 && flipped.bottom() < window->height()) + rect = flipped; + } + } + } + + m_popup->popupItem->setPosition(rect.topLeft()); +} + +void QQuickPopupPositioner::removeAncestorListeners(QQuickItem *item) +{ + if (item == m_parentItem) + return; + + QQuickItem *p = item; + while (p) { + QQuickItemPrivate::get(p)->removeItemChangeListener(this, AncestorChangeTypes); + p = p->parentItem(); + } +} + +void QQuickPopupPositioner::addAncestorListeners(QQuickItem *item) +{ + if (item == m_parentItem) + return; + + QQuickItem *p = item; + while (p) { + QQuickItemPrivate::get(p)->addItemChangeListener(this, AncestorChangeTypes); + p = p->parentItem(); + } +} + +// TODO: use QQuickItem::isAncestorOf() in dev/5.7 +bool QQuickPopupPositioner::isAncestor(QQuickItem *item) const +{ + if (!m_parentItem) + return false; + + QQuickItem *parent = m_parentItem->parentItem(); + while (parent) { + if (parent == item) + return true; + parent = parent->parentItem(); + } + return false; +} + QQuickPopupTransitionManager::QQuickPopupTransitionManager(QQuickPopupPrivate *popup) : QQuickTransitionManager() , state(Off) @@ -96,7 +462,7 @@ void QQuickPopupTransitionManager::transitionEnter() return; QList<QQuickStateAction> actions; state = Enter; - transition(actions, popup->enter, popup->contentItem); + transition(actions, popup->enter, popup->popupItem); } void QQuickPopupTransitionManager::transitionExit() @@ -105,7 +471,7 @@ void QQuickPopupTransitionManager::transitionExit() return; QList<QQuickStateAction> actions; state = Exit; - transition(actions, popup->exit, popup->contentItem); + transition(actions, popup->exit, popup->popupItem); } void QQuickPopupTransitionManager::finished() @@ -121,11 +487,21 @@ void QQuickPopupTransitionManager::finished() QQuickPopup::QQuickPopup(QObject *parent) : QObject(*(new QQuickPopupPrivate), parent) { + Q_D(QQuickPopup); + d->init(); } QQuickPopup::QQuickPopup(QQuickPopupPrivate &dd, QObject *parent) : QObject(dd, parent) { + Q_D(QQuickPopup); + d->init(); +} + +QQuickPopup::~QQuickPopup() +{ + Q_D(QQuickPopup); + d->positioner.setParentItem(nullptr); } /*! @@ -136,13 +512,8 @@ QQuickPopup::QQuickPopup(QQuickPopupPrivate &dd, QObject *parent) void QQuickPopup::open() { Q_D(QQuickPopup); - if (!d->contentItem) { - qmlInfo(this) << "no popup content to show."; - return; - } if (d->overlay) { - // FIXME qmlInfo needs to know about QQuickWindow and/or QObject - static_cast<QDebug>(qmlInfo(this) << "popup already open in window") << d->overlay->window(); + // popup already open return; } @@ -172,7 +543,11 @@ void QQuickPopup::open() } d->overlay = static_cast<QQuickOverlay *>(applicationWindow->overlay()); - d->contentItem->setParentItem(d->overlay); + d->popupItem->setParentItem(d->overlay); + d->positioner.setParentItem(d->parentItem); + // TODO: add Popup::transformOrigin? + if (d->contentItem) + d->popupItem->setTransformOrigin(d->contentItem->transformOrigin()); emit aboutToShow(); d->transitionManager.transitionEnter(); emit visibleChanged(); @@ -187,17 +562,406 @@ void QQuickPopup::close() { Q_D(QQuickPopup); if (!d->overlay) { - // TODO This could mean we opened the popup item in a plain QQuickWindow - qmlInfo(this) << "trying to close non-visible Popup."; + // popup already closed return; } - d->contentItem->setFocus(false); + d->popupItem->setFocus(false); emit aboutToHide(); d->transitionManager.transitionExit(); } /*! + \qmlproperty real Qt.labs.controls::Popup::x + + This property holds the x-coordinate of the popup. +*/ +qreal QQuickPopup::x() const +{ + Q_D(const QQuickPopup); + return d->positioner.x(); +} + +void QQuickPopup::setX(qreal x) +{ + Q_D(QQuickPopup); + d->positioner.setX(x); +} + +/*! + \qmlproperty real Qt.labs.controls::Popup::y + + This property holds the y-coordinate of the popup. +*/ +qreal QQuickPopup::y() const +{ + Q_D(const QQuickPopup); + return d->positioner.y(); +} + +void QQuickPopup::setY(qreal y) +{ + Q_D(QQuickPopup); + d->positioner.setY(y); +} + +/*! + \qmlproperty real Qt.labs.controls::Popup::width + + This property holds the width of the popup. +*/ +qreal QQuickPopup::width() const +{ + Q_D(const QQuickPopup); + return d->popupItem->width(); +} + +void QQuickPopup::setWidth(qreal width) +{ + Q_D(QQuickPopup); + d->popupItem->setWidth(width); +} + +void QQuickPopup::resetWidth() +{ + Q_D(QQuickPopup); + d->popupItem->resetWidth(); +} + +/*! + \qmlproperty real Qt.labs.controls::Popup::height + + This property holds the height of the popup. +*/ +qreal QQuickPopup::height() const +{ + Q_D(const QQuickPopup); + return d->popupItem->height(); +} + +void QQuickPopup::setHeight(qreal height) +{ + Q_D(QQuickPopup); + d->popupItem->setHeight(height); +} + +void QQuickPopup::resetHeight() +{ + Q_D(QQuickPopup); + d->popupItem->resetHeight(); +} + +/*! + \qmlproperty real Qt.labs.controls::Popup::implicitWidth + + This property holds the implicit width of the popup. +*/ +qreal QQuickPopup::implicitWidth() const +{ + Q_D(const QQuickPopup); + return d->popupItem->implicitWidth(); +} + +void QQuickPopup::setImplicitWidth(qreal width) +{ + Q_D(QQuickPopup); + d->popupItem->setImplicitWidth(width); +} + +/*! + \qmlproperty real Qt.labs.controls::Popup::implicitHeight + + This property holds the implicit height of the popup. +*/ +qreal QQuickPopup::implicitHeight() const +{ + Q_D(const QQuickPopup); + return d->popupItem->implicitHeight(); +} + +void QQuickPopup::setImplicitHeight(qreal height) +{ + Q_D(QQuickPopup); + d->popupItem->setImplicitHeight(height); +} + +/*! + \qmlproperty real Qt.labs.controls::Popup::contentWidth + + This property holds the content width. It is used for calculating the + total implicit width of the Popup. + + \note If only a single item is used within the Popup, the implicit width + of its contained item is used as the content width. +*/ +qreal QQuickPopup::contentWidth() const +{ + Q_D(const QQuickPopup); + return d->contentWidth; +} + +void QQuickPopup::setContentWidth(qreal width) +{ + Q_D(QQuickPopup); + if (d->contentWidth != width) { + d->contentWidth = width; + emit contentWidthChanged(); + } +} + +/*! + \qmlproperty real Qt.labs.controls::Popup::contentHeight + + This property holds the content height. It is used for calculating the + total implicit height of the Popup. + + \note If only a single item is used within the Popup, the implicit height + of its contained item is used as the content height. +*/ +qreal QQuickPopup::contentHeight() const +{ + Q_D(const QQuickPopup); + return d->contentHeight; +} + +void QQuickPopup::setContentHeight(qreal height) +{ + Q_D(QQuickPopup); + if (d->contentHeight != height) { + d->contentHeight = height; + emit contentHeightChanged(); + } +} + +/*! + \qmlproperty real Qt.labs.controls::Popup::availableWidth + + This property holds the width available after deducting horizontal padding. + + \sa padding, leftPadding, rightPadding +*/ +qreal QQuickPopup::availableWidth() const +{ + return qMax<qreal>(0.0, width() - leftPadding() - rightPadding()); +} + +/*! + \qmlproperty real Qt.labs.controls::Popup::availableHeight + + This property holds the height available after deducting vertical padding. + + \sa padding, topPadding, bottomPadding +*/ +qreal QQuickPopup::availableHeight() const +{ + return qMax<qreal>(0.0, height() - topPadding() - bottomPadding()); +} + +/*! + \qmlproperty real Qt.labs.controls::Popup::padding + + This property holds the default padding. + + \sa availableWidth, availableHeight, topPadding, leftPadding, rightPadding, bottomPadding +*/ +qreal QQuickPopup::padding() const +{ + Q_D(const QQuickPopup); + return d->padding; +} + +void QQuickPopup::setPadding(qreal padding) +{ + Q_D(QQuickPopup); + if (qFuzzyCompare(d->padding, padding)) + return; + QMarginsF oldPadding(leftPadding(), topPadding(), rightPadding(), bottomPadding()); + d->padding = padding; + emit paddingChanged(); + QMarginsF newPadding(leftPadding(), topPadding(), rightPadding(), bottomPadding()); + if (!qFuzzyCompare(newPadding.top(), oldPadding.top())) + emit topPaddingChanged(); + if (!qFuzzyCompare(newPadding.left(), oldPadding.left())) + emit leftPaddingChanged(); + if (!qFuzzyCompare(newPadding.right(), oldPadding.right())) + emit rightPaddingChanged(); + if (!qFuzzyCompare(newPadding.bottom(), oldPadding.bottom())) + emit bottomPaddingChanged(); + if (!qFuzzyCompare(newPadding.top(), oldPadding.top()) || !qFuzzyCompare(newPadding.bottom(), oldPadding.bottom())) + emit availableHeightChanged(); + if (!qFuzzyCompare(newPadding.left(), oldPadding.left()) || !qFuzzyCompare(newPadding.right(), oldPadding.right())) + emit availableWidthChanged(); + paddingChange(newPadding, oldPadding); +} + +void QQuickPopup::resetPadding() +{ + setPadding(0); +} + +/*! + \qmlproperty real Qt.labs.controls::Popup::topPadding + + This property holds the top padding. + + \sa padding, bottomPadding, availableHeight +*/ +qreal QQuickPopup::topPadding() const +{ + Q_D(const QQuickPopup); + if (d->hasTopPadding) + return d->topPadding; + return d->padding; +} + +void QQuickPopup::setTopPadding(qreal padding) +{ + Q_D(QQuickPopup); + d->setTopPadding(padding); +} + +void QQuickPopup::resetTopPadding() +{ + Q_D(QQuickPopup); + d->setTopPadding(0, true); +} + +/*! + \qmlproperty real Qt.labs.controls::Popup::leftPadding + + This property holds the left padding. + + \sa padding, rightPadding, availableWidth +*/ +qreal QQuickPopup::leftPadding() const +{ + Q_D(const QQuickPopup); + if (d->hasLeftPadding) + return d->leftPadding; + return d->padding; +} + +void QQuickPopup::setLeftPadding(qreal padding) +{ + Q_D(QQuickPopup); + d->setLeftPadding(padding); +} + +void QQuickPopup::resetLeftPadding() +{ + Q_D(QQuickPopup); + d->setLeftPadding(0, true); +} + +/*! + \qmlproperty real Qt.labs.controls::Popup::rightPadding + + This property holds the right padding. + + \sa padding, leftPadding, availableWidth +*/ +qreal QQuickPopup::rightPadding() const +{ + Q_D(const QQuickPopup); + if (d->hasRightPadding) + return d->rightPadding; + return d->padding; +} + +void QQuickPopup::setRightPadding(qreal padding) +{ + Q_D(QQuickPopup); + d->setRightPadding(padding); +} + +void QQuickPopup::resetRightPadding() +{ + Q_D(QQuickPopup); + d->setRightPadding(0, true); +} + +/*! + \qmlproperty real Qt.labs.controls::Popup::bottomPadding + + This property holds the bottom padding. + + \sa padding, topPadding, availableHeight +*/ +qreal QQuickPopup::bottomPadding() const +{ + Q_D(const QQuickPopup); + if (d->hasBottomPadding) + return d->bottomPadding; + return d->padding; +} + +void QQuickPopup::setBottomPadding(qreal padding) +{ + Q_D(QQuickPopup); + d->setBottomPadding(padding); +} + +void QQuickPopup::resetBottomPadding() +{ + Q_D(QQuickPopup); + d->setBottomPadding(0, true); +} + +/*! + \qmlproperty Item Qt.labs.popups::Popup::parent + + This property holds the parent item. +*/ +QQuickItem *QQuickPopup::parentItem() const +{ + Q_D(const QQuickPopup); + return d->parentItem; +} + +void QQuickPopup::setParentItem(QQuickItem *parent) +{ + Q_D(QQuickPopup); + if (d->parentItem != parent) { + d->parentItem = parent; + if (d->positioner.parentItem()) + d->positioner.setParentItem(parent); + emit parentChanged(); + } +} + +/*! + \qmlproperty Item Qt.labs.popups::Popup::background + + This property holds the background item. + + \note If the background item has no explicit size specified, it automatically + follows the popup's size. In most cases, there is no need to specify + width or height for a background item. +*/ +QQuickItem *QQuickPopup::background() const +{ + Q_D(const QQuickPopup); + return d->background; +} + +void QQuickPopup::setBackground(QQuickItem *background) +{ + Q_D(QQuickPopup); + if (d->background != background) { + delete d->background; + d->background = background; + if (background) { + background->setParentItem(d->popupItem); + if (qFuzzyIsNull(background->z())) + background->setZ(-1); + if (isComponentComplete()) + d->resizeBackground(); + } + emit backgroundChanged(); + } +} + +/*! \qmlproperty Item Qt.labs.controls::Popup::contentItem This property holds the content item of the popup. @@ -222,15 +986,55 @@ void QQuickPopup::setContentItem(QQuickItem *item) return; } if (d->contentItem != item) { + contentItemChange(item, d->contentItem); delete d->contentItem; d->contentItem = item; - if (item) + if (item) { + item->setParentItem(d->popupItem); QQuickItemPrivate::get(item)->isTabFence = true; + if (isComponentComplete()) + d->resizeContent(); + } emit contentItemChanged(); } } /*! + \qmlproperty list<Object> Qt.labs.controls::Popup::contentData + \default + + This property holds the list of content data. + + \sa Item::data +*/ +QQmlListProperty<QObject> QQuickPopup::contentData() +{ + Q_D(QQuickPopup); + return QQmlListProperty<QObject>(d->contentItem, nullptr, + QQuickItemPrivate::data_append, + QQuickItemPrivate::data_count, + QQuickItemPrivate::data_at, + QQuickItemPrivate::data_clear); +} + +/*! + \qmlproperty list<Item> Qt.labs.controls::Popup::contentChildren + + This property holds the list of content children. + + \sa Item::children +*/ +QQmlListProperty<QQuickItem> QQuickPopup::contentChildren() +{ + Q_D(QQuickPopup); + return QQmlListProperty<QQuickItem>(d->contentItem, nullptr, + QQuickItemPrivate::children_append, + QQuickItemPrivate::children_count, + QQuickItemPrivate::children_at, + QQuickItemPrivate::children_clear); +} + +/*! \qmlproperty bool Qt.labs.controls::Popup::focus This property holds whether the popup has focus. @@ -281,6 +1085,14 @@ bool QQuickPopup::isVisible() const return d->overlay != nullptr /*&& !d->transitionManager.isRunning()*/; } +void QQuickPopup::setVisible(bool visible) +{ + if (visible) + open(); + else + close(); +} + /*! \qmlproperty Transition Qt.labs.controls::Popup::enter @@ -323,6 +1135,102 @@ void QQuickPopup::setExit(QQuickTransition *transition) emit exitChanged(); } +void QQuickPopup::classBegin() +{ +} + +void QQuickPopup::componentComplete() +{ + Q_D(QQuickPopup); + d->complete = true; + if (!parentItem()) + setParentItem(qobject_cast<QQuickItem *>(parent())); +} + +bool QQuickPopup::isComponentComplete() const +{ + Q_D(const QQuickPopup); + return d->complete; +} + +void QQuickPopup::focusInEvent(QFocusEvent *event) +{ + event->accept(); +} + +void QQuickPopup::focusOutEvent(QFocusEvent *event) +{ + event->accept(); +} + +void QQuickPopup::keyPressEvent(QKeyEvent *event) +{ + event->accept(); +} + +void QQuickPopup::keyReleaseEvent(QKeyEvent *event) +{ + event->accept(); +} + +void QQuickPopup::mousePressEvent(QMouseEvent *event) +{ + event->accept(); +} + +void QQuickPopup::mouseMoveEvent(QMouseEvent *event) +{ + event->accept(); +} + +void QQuickPopup::mouseReleaseEvent(QMouseEvent *event) +{ + event->accept(); +} + +void QQuickPopup::mouseDoubleClickEvent(QMouseEvent *event) +{ + event->accept(); +} + +void QQuickPopup::mouseUngrabEvent() +{ +} + +void QQuickPopup::wheelEvent(QWheelEvent *event) +{ + event->accept(); +} + +void QQuickPopup::contentItemChange(QQuickItem *newItem, QQuickItem *oldItem) +{ + Q_UNUSED(newItem); + Q_UNUSED(oldItem); +} + +void QQuickPopup::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) +{ + Q_D(QQuickPopup); + d->resizeBackground(); + d->resizeContent(); + if (!qFuzzyCompare(newGeometry.width(), oldGeometry.width())) { + emit widthChanged(); + emit availableWidthChanged(); + } + if (!qFuzzyCompare(newGeometry.height(), oldGeometry.height())) { + emit heightChanged(); + emit availableHeightChanged(); + } +} + +void QQuickPopup::paddingChange(const QMarginsF &newPadding, const QMarginsF &oldPadding) +{ + Q_D(QQuickPopup); + Q_UNUSED(newPadding); + Q_UNUSED(oldPadding); + d->resizeContent(); +} + QT_END_NAMESPACE #include "moc_qquickpopup_p.cpp" diff --git a/src/templates/qquickpopup_p.h b/src/templates/qquickpopup_p.h index 8eb5e150..e94b63f1 100644 --- a/src/templates/qquickpopup_p.h +++ b/src/templates/qquickpopup_p.h @@ -49,31 +49,116 @@ // #include <QtCore/qobject.h> +#include <QtCore/qmargins.h> +#include <QtGui/qevent.h> #include <QtLabsTemplates/private/qtlabstemplatesglobal_p.h> #include <QtQml/qqml.h> +#include <QtQml/qqmllist.h> +#include <QtQml/qqmlparserstatus.h> QT_BEGIN_NAMESPACE class QQuickItem; class QQuickPopupPrivate; class QQuickTransition; +class QQuickTransform; -class Q_LABSTEMPLATES_EXPORT QQuickPopup : public QObject +class Q_LABSTEMPLATES_EXPORT QQuickPopup : public QObject, public QQmlParserStatus { Q_OBJECT + Q_INTERFACES(QQmlParserStatus) + Q_PROPERTY(qreal x READ x WRITE setX NOTIFY xChanged FINAL) + Q_PROPERTY(qreal y READ y WRITE setY NOTIFY yChanged FINAL) + Q_PROPERTY(qreal width READ width WRITE setWidth RESET resetWidth NOTIFY widthChanged FINAL) + Q_PROPERTY(qreal height READ height WRITE setHeight RESET resetHeight NOTIFY heightChanged FINAL) + Q_PROPERTY(qreal implicitWidth READ implicitWidth WRITE setImplicitWidth NOTIFY implicitWidthChanged FINAL) + Q_PROPERTY(qreal implicitHeight READ implicitHeight WRITE setImplicitHeight NOTIFY implicitHeightChanged FINAL) + Q_PROPERTY(qreal contentWidth READ contentWidth WRITE setContentWidth NOTIFY contentWidthChanged FINAL) + Q_PROPERTY(qreal contentHeight READ contentHeight WRITE setContentHeight NOTIFY contentHeightChanged FINAL) + Q_PROPERTY(qreal availableWidth READ availableWidth NOTIFY availableWidthChanged FINAL) + Q_PROPERTY(qreal availableHeight READ availableHeight NOTIFY availableHeightChanged FINAL) + Q_PROPERTY(qreal padding READ padding WRITE setPadding RESET resetPadding NOTIFY paddingChanged FINAL) + Q_PROPERTY(qreal topPadding READ topPadding WRITE setTopPadding RESET resetTopPadding NOTIFY topPaddingChanged FINAL) + Q_PROPERTY(qreal leftPadding READ leftPadding WRITE setLeftPadding RESET resetLeftPadding NOTIFY leftPaddingChanged FINAL) + Q_PROPERTY(qreal rightPadding READ rightPadding WRITE setRightPadding RESET resetRightPadding NOTIFY rightPaddingChanged FINAL) + Q_PROPERTY(qreal bottomPadding READ bottomPadding WRITE setBottomPadding RESET resetBottomPadding NOTIFY bottomPaddingChanged FINAL) + Q_PROPERTY(QQuickItem *parent READ parentItem WRITE setParentItem NOTIFY parentChanged FINAL) + Q_PROPERTY(QQuickItem *background READ background WRITE setBackground NOTIFY backgroundChanged FINAL) Q_PROPERTY(QQuickItem *contentItem READ contentItem WRITE setContentItem NOTIFY contentItemChanged FINAL) + Q_PROPERTY(QQmlListProperty<QObject> contentData READ contentData FINAL) + Q_PROPERTY(QQmlListProperty<QQuickItem> contentChildren READ contentChildren NOTIFY contentChildrenChanged FINAL) Q_PROPERTY(bool focus READ hasFocus WRITE setFocus NOTIFY focusChanged) Q_PROPERTY(bool modal READ isModal WRITE setModal NOTIFY modalChanged) - Q_PROPERTY(bool visible READ isVisible NOTIFY visibleChanged) + Q_PROPERTY(bool visible READ isVisible WRITE setVisible NOTIFY visibleChanged) Q_PROPERTY(QQuickTransition *enter READ enter WRITE setEnter NOTIFY enterChanged FINAL) Q_PROPERTY(QQuickTransition *exit READ exit WRITE setExit NOTIFY exitChanged FINAL) + Q_CLASSINFO("DefaultProperty", "contentData") public: explicit QQuickPopup(QObject *parent = nullptr); + ~QQuickPopup(); + + qreal x() const; + void setX(qreal x); + + qreal y() const; + void setY(qreal y); + + qreal width() const; + void setWidth(qreal width); + void resetWidth(); + + qreal height() const; + void setHeight(qreal height); + void resetHeight(); + + qreal implicitWidth() const; + void setImplicitWidth(qreal width); + + qreal implicitHeight() const; + void setImplicitHeight(qreal height); + + qreal contentWidth() const; + void setContentWidth(qreal width); + + qreal contentHeight() const; + void setContentHeight(qreal height); + + qreal availableWidth() const; + qreal availableHeight() const; + + qreal padding() const; + void setPadding(qreal padding); + void resetPadding(); + + qreal topPadding() const; + void setTopPadding(qreal padding); + void resetTopPadding(); + + qreal leftPadding() const; + void setLeftPadding(qreal padding); + void resetLeftPadding(); + + qreal rightPadding() const; + void setRightPadding(qreal padding); + void resetRightPadding(); + + qreal bottomPadding() const; + void setBottomPadding(qreal padding); + void resetBottomPadding(); + + QQuickItem *parentItem() const; + void setParentItem(QQuickItem *parent); + + QQuickItem *background() const; + void setBackground(QQuickItem *background); QQuickItem *contentItem() const; void setContentItem(QQuickItem *item); + QQmlListProperty<QObject> contentData(); + QQmlListProperty<QQuickItem> contentChildren(); + bool hasFocus() const; void setFocus(bool focus); @@ -81,6 +166,7 @@ public: void setModal(bool modal); bool isVisible() const; + void setVisible(bool visible); QQuickTransition *enter() const; void setEnter(QQuickTransition *transition); @@ -93,7 +179,25 @@ public Q_SLOTS: void close(); Q_SIGNALS: + void xChanged(); + void yChanged(); + void widthChanged(); + void heightChanged(); + void implicitWidthChanged(); + void implicitHeightChanged(); + void contentWidthChanged(); + void contentHeightChanged(); + void availableWidthChanged(); + void availableHeightChanged(); + void paddingChanged(); + void topPaddingChanged(); + void leftPaddingChanged(); + void rightPaddingChanged(); + void bottomPaddingChanged(); + void parentChanged(); + void backgroundChanged(); void contentItemChanged(); + void contentChildrenChanged(); void focusChanged(); void modalChanged(); void visibleChanged(); @@ -110,9 +214,29 @@ Q_SIGNALS: protected: QQuickPopup(QQuickPopupPrivate &dd, QObject *parent); + void classBegin() override; + void componentComplete() override; + bool isComponentComplete() const; + + virtual void focusInEvent(QFocusEvent *event); + virtual void focusOutEvent(QFocusEvent *event); + virtual void keyPressEvent(QKeyEvent *event); + virtual void keyReleaseEvent(QKeyEvent *event); + virtual void mousePressEvent(QMouseEvent *event); + virtual void mouseMoveEvent(QMouseEvent *event); + virtual void mouseReleaseEvent(QMouseEvent *event); + virtual void mouseDoubleClickEvent(QMouseEvent *event); + virtual void mouseUngrabEvent(); + virtual void wheelEvent(QWheelEvent *event); + + virtual void contentItemChange(QQuickItem *newItem, QQuickItem *oldItem); + virtual void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry); + virtual void paddingChange(const QMarginsF &newPadding, const QMarginsF &oldPadding); + private: Q_DISABLE_COPY(QQuickPopup) Q_DECLARE_PRIVATE(QQuickPopup) + friend class QQuickPopupItem; }; QT_END_NAMESPACE diff --git a/src/templates/qquickpopup_p_p.h b/src/templates/qquickpopup_p_p.h index 3a5909b7..910fca9e 100644 --- a/src/templates/qquickpopup_p_p.h +++ b/src/templates/qquickpopup_p_p.h @@ -48,17 +48,21 @@ // We mean it. // +#include "qquickpopup_p.h" + #include <QtCore/private/qobject_p.h> +#include <QtQuick/qquickitem.h> +#include <QtQuick/private/qquickitemchangelistener_p.h> #include <QtQuick/private/qquicktransitionmanager_p_p.h> QT_BEGIN_NAMESPACE -class QQuickItem; class QQuickTransition; class QQuickTransitionManager; class QQuickPopup; -class QQuickPopupPrivate; class QQuickOverlay; +class QQuickPopupPrivate; +class QQuickPopupItemPrivate; class QQuickPopupTransitionManager : public QQuickTransitionManager { @@ -80,6 +84,66 @@ private: QQuickPopupPrivate *popup; }; +class QQuickPopupItem : public QQuickItem +{ + Q_OBJECT + +public: + explicit QQuickPopupItem(QQuickPopup *popup); + +protected: + void focusInEvent(QFocusEvent *event) override; + void focusOutEvent(QFocusEvent *event) override; + void keyPressEvent(QKeyEvent *event) override; + void keyReleaseEvent(QKeyEvent *event) override; + void mousePressEvent(QMouseEvent *event) override; + void mouseMoveEvent(QMouseEvent *event) override; + void mouseReleaseEvent(QMouseEvent *event) override; + void mouseDoubleClickEvent(QMouseEvent *event) override; + void mouseUngrabEvent() override; + void wheelEvent(QWheelEvent *event) override; + + void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) override; + +private: + Q_DECLARE_PRIVATE(QQuickPopupItem) +}; + +class QQuickPopupPositioner : public QQuickItemChangeListener +{ +public: + explicit QQuickPopupPositioner(QQuickPopupPrivate *popup); + ~QQuickPopupPositioner(); + + qreal x() const; + void setX(qreal x); + + qreal y() const; + void setY(qreal y); + + QQuickItem *parentItem() const; + void setParentItem(QQuickItem *parent); + +protected: + void itemGeometryChanged(QQuickItem *, const QRectF &, const QRectF &); + void itemParentChanged(QQuickItem *, QQuickItem *parent); + void itemChildRemoved(QQuickItem *, QQuickItem *child); + void itemDestroyed(QQuickItem *item); + +private: + void repositionPopup(); + + void removeAncestorListeners(QQuickItem *item); + void addAncestorListeners(QQuickItem *item); + + bool isAncestor(QQuickItem *item) const; + + qreal m_x; + qreal m_y; + QQuickItem *m_parentItem; + QQuickPopupPrivate *m_popup; +}; + class QQuickPopupPrivate : public QObjectPrivate { Q_DECLARE_PUBLIC(QQuickPopup) @@ -87,15 +151,46 @@ class QQuickPopupPrivate : public QObjectPrivate public: QQuickPopupPrivate(); + static QQuickPopupPrivate *get(QQuickPopup *popup) + { + return popup->d_func(); + } + + void init(); + void finalizeEnterTransition(); void finalizeExitTransition(); + void resizeBackground(); + void resizeContent(); + + void setTopPadding(qreal value, bool reset = false); + void setLeftPadding(qreal value, bool reset = false); + void setRightPadding(qreal value, bool reset = false); + void setBottomPadding(qreal value, bool reset = false); + bool focus; bool modal; + bool complete; + bool hasTopPadding; + bool hasLeftPadding; + bool hasRightPadding; + bool hasBottomPadding; + qreal padding; + qreal topPadding; + qreal leftPadding; + qreal rightPadding; + qreal bottomPadding; + qreal contentWidth; + qreal contentHeight; + QQuickItem *parentItem; + QQuickItem *background; QQuickItem *contentItem; QQuickOverlay *overlay; QQuickTransition *enter; QQuickTransition *exit; + QQuickPopupItem *popupItem; + QQuickPopupPositioner positioner; QQuickPopupTransitionManager transitionManager; }; diff --git a/src/templates/qquickprogressbar.cpp b/src/templates/qquickprogressbar.cpp index 114b117e..cd490f50 100644 --- a/src/templates/qquickprogressbar.cpp +++ b/src/templates/qquickprogressbar.cpp @@ -62,6 +62,8 @@ QT_BEGIN_NAMESPACE } \endcode + \labs + \sa {Customizing ProgressBar} */ diff --git a/src/templates/qquickradiobutton.cpp b/src/templates/qquickradiobutton.cpp index 9aca5808..7d7a64aa 100644 --- a/src/templates/qquickradiobutton.cpp +++ b/src/templates/qquickradiobutton.cpp @@ -84,6 +84,8 @@ QT_BEGIN_NAMESPACE } \endcode + \labs + \sa ButtonGroup, {Customizing RadioButton}, {Button Controls} */ diff --git a/src/templates/qquickrangeslider.cpp b/src/templates/qquickrangeslider.cpp index 0a16cafc..0dec63cb 100644 --- a/src/templates/qquickrangeslider.cpp +++ b/src/templates/qquickrangeslider.cpp @@ -73,6 +73,8 @@ QT_BEGIN_NAMESPACE } \endcode + \labs + \sa {Customizing RangeSlider}, {Input Controls} */ diff --git a/src/templates/qquickscrollbar.cpp b/src/templates/qquickscrollbar.cpp index caabbcf7..ad7b0a6d 100644 --- a/src/templates/qquickscrollbar.cpp +++ b/src/templates/qquickscrollbar.cpp @@ -73,6 +73,8 @@ QT_BEGIN_NAMESPACE \li \l active \endlist + \labs + \sa ScrollIndicator, {Customizing ScrollBar}, {Indicator Controls} */ diff --git a/src/templates/qquickscrollindicator.cpp b/src/templates/qquickscrollindicator.cpp index 59f2acc0..5e210f09 100644 --- a/src/templates/qquickscrollindicator.cpp +++ b/src/templates/qquickscrollindicator.cpp @@ -73,6 +73,8 @@ QT_BEGIN_NAMESPACE \li \l active \endlist + \labs + \sa ScrollBar, {Customizing ScrollIndicator}, {Indicator Controls} */ diff --git a/src/templates/qquickslider.cpp b/src/templates/qquickslider.cpp index f1eca648..b109fc5a 100644 --- a/src/templates/qquickslider.cpp +++ b/src/templates/qquickslider.cpp @@ -68,6 +68,8 @@ QT_BEGIN_NAMESPACE } \endcode + \labs + \sa {Customizing Slider}, {Input Controls} */ diff --git a/src/templates/qquickspinbox.cpp b/src/templates/qquickspinbox.cpp index 83eefbf3..31368a1a 100644 --- a/src/templates/qquickspinbox.cpp +++ b/src/templates/qquickspinbox.cpp @@ -80,6 +80,8 @@ static const int AUTO_REPEAT_INTERVAL = 100; \snippet qtlabscontrols-spinbox-textual.qml 1 + \labs + \sa Tumbler, {Customizing SpinBox} */ @@ -181,7 +183,7 @@ bool QQuickSpinBoxPrivate::handleMousePressEvent(QQuickItem *child, QMouseEvent q->setAccessibleProperty("pressed", pressed); if (pressed) startRepeatDelay(); - return up->isPressed() || down->isPressed(); + return pressed; } bool QQuickSpinBoxPrivate::handleMouseMoveEvent(QQuickItem *child, QMouseEvent *event) @@ -195,7 +197,7 @@ bool QQuickSpinBoxPrivate::handleMouseMoveEvent(QQuickItem *child, QMouseEvent * bool pressed = up->isPressed() || down->isPressed(); q->setAccessibleProperty("pressed", pressed); stopPressRepeat(); - return up->isPressed() || down->isPressed(); + return pressed; } bool QQuickSpinBoxPrivate::handleMouseReleaseEvent(QQuickItem *child, QMouseEvent *event) diff --git a/src/templates/qquickstackview.cpp b/src/templates/qquickstackview.cpp index 29b631dc..b338f3bf 100644 --- a/src/templates/qquickstackview.cpp +++ b/src/templates/qquickstackview.cpp @@ -257,6 +257,8 @@ QT_BEGIN_NAMESPACE } \endqml + \labs + \sa {Customizing StackView}, {Navigation Controls}, {Container Controls} */ @@ -877,10 +879,18 @@ void QQuickStackView::geometryChanged(const QRectF &newGeometry, const QRectF &o } } -bool QQuickStackView::childMouseEventFilter(QQuickItem *, QEvent *) +bool QQuickStackView::childMouseEventFilter(QQuickItem *item, QEvent *event) { - // busy should be true if this function gets called - return true; + // in order to block accidental user interaction while busy/transitioning, + // StackView filters out childrens' mouse events. therefore we block all + // press events. however, since push() may be called from signal handlers + // such as onPressed or onDoubleClicked, we must let the current mouse + // grabber item receive the respective mouse release event to avoid + // breaking its state (QTBUG-50305). + if (event->type() == QEvent::MouseButtonPress) + return true; + QQuickWindow *window = item->window(); + return window && !window->mouseGrabberItem(); } void QQuickStackAttachedPrivate::itemParentChanged(QQuickItem *item, QQuickItem *parent) diff --git a/src/templates/qquickstackview_p.cpp b/src/templates/qquickstackview_p.cpp index a34b62e9..5d967a0e 100644 --- a/src/templates/qquickstackview_p.cpp +++ b/src/templates/qquickstackview_p.cpp @@ -83,18 +83,20 @@ QQuickStackElement::~QQuickStackElement() if (ownComponent) delete component; - if (ownItem && item) { - item->setParentItem(nullptr); - item->deleteLater(); - item = nullptr; - } else if (item) { - item->setVisible(false); - if (item->parentItem() != originalParent) { - item->setParentItem(originalParent); + if (item) { + if (ownItem) { + item->setParentItem(nullptr); + item->deleteLater(); + item = nullptr; } else { - QQuickStackAttached *attached = attachedStackObject(this); - if (attached) - QQuickStackAttachedPrivate::get(attached)->itemParentChanged(item, nullptr); + item->setVisible(false); + if (item->parentItem() != originalParent) { + item->setParentItem(originalParent); + } else { + QQuickStackAttached *attached = attachedStackObject(this); + if (attached) + QQuickStackAttachedPrivate::get(attached)->itemParentChanged(item, nullptr); + } } } diff --git a/src/templates/qquickswipeview.cpp b/src/templates/qquickswipeview.cpp index 40366af1..b73a8ac0 100644 --- a/src/templates/qquickswipeview.cpp +++ b/src/templates/qquickswipeview.cpp @@ -67,6 +67,8 @@ QT_BEGIN_NAMESPACE \l {Container::moveItem()}{move}, and \l {Container::removeItem()}{remove} pages dynamically at run time. + \labs + \sa TabBar, PageIndicator, {Customizing SwipeView}, {Navigation Controls}, {Container Controls} */ diff --git a/src/templates/qquickswitch.cpp b/src/templates/qquickswitch.cpp index a867ec70..8544d7ca 100644 --- a/src/templates/qquickswitch.cpp +++ b/src/templates/qquickswitch.cpp @@ -77,6 +77,8 @@ QT_BEGIN_NAMESPACE } \endcode + \labs + \sa {Customizing Switch}, {Button Controls} */ diff --git a/src/templates/qquicktabbar.cpp b/src/templates/qquicktabbar.cpp index d2362f0c..880a89e2 100644 --- a/src/templates/qquicktabbar.cpp +++ b/src/templates/qquicktabbar.cpp @@ -59,6 +59,8 @@ QT_BEGIN_NAMESPACE \snippet qtlabscontrols-tabbar.qml 1 + \labs + \sa TabButton, {Customizing TabBar}, {Navigation Controls}, {Container Controls} */ diff --git a/src/templates/qquicktabbutton.cpp b/src/templates/qquicktabbutton.cpp index 00d4d8b4..a70dcbe1 100644 --- a/src/templates/qquicktabbutton.cpp +++ b/src/templates/qquicktabbutton.cpp @@ -55,6 +55,8 @@ QT_BEGIN_NAMESPACE \snippet qtlabscontrols-tabbutton.qml 1 + \labs + \sa TabBar, {Customizing TabButton}, {Navigation Controls} */ diff --git a/src/templates/qquicktextarea.cpp b/src/templates/qquicktextarea.cpp index 98fd7648..e83f8bf1 100644 --- a/src/templates/qquicktextarea.cpp +++ b/src/templates/qquicktextarea.cpp @@ -67,6 +67,8 @@ QT_BEGIN_NAMESPACE } \endcode + \labs + \sa TextField, {Customizing TextArea}, {Input Controls} */ diff --git a/src/templates/qquicktextfield.cpp b/src/templates/qquicktextfield.cpp index a8a6895e..782ff148 100644 --- a/src/templates/qquicktextfield.cpp +++ b/src/templates/qquicktextfield.cpp @@ -77,6 +77,8 @@ QT_BEGIN_NAMESPACE } \endcode + \labs + \sa TextArea, {Customizing TextField}, {Input Controls} */ diff --git a/src/templates/qquicktoolbar.cpp b/src/templates/qquicktoolbar.cpp index c20303e8..882834e3 100644 --- a/src/templates/qquicktoolbar.cpp +++ b/src/templates/qquicktoolbar.cpp @@ -86,6 +86,8 @@ QT_BEGIN_NAMESPACE } \endcode + \labs + \sa ApplicationWindow, ToolButton, {Customizing ToolBar}, {Container Controls} */ diff --git a/src/templates/qquicktoolbutton.cpp b/src/templates/qquicktoolbutton.cpp index 3505346c..775eedd1 100644 --- a/src/templates/qquicktoolbutton.cpp +++ b/src/templates/qquicktoolbutton.cpp @@ -73,6 +73,8 @@ QT_BEGIN_NAMESPACE } \endcode + \labs + \sa ToolBar, {Customizing ToolButton}, {Button Controls} */ diff --git a/src/templates/qquicktumbler.cpp b/src/templates/qquicktumbler.cpp index b1a64051..fcc443df 100644 --- a/src/templates/qquicktumbler.cpp +++ b/src/templates/qquicktumbler.cpp @@ -66,6 +66,8 @@ QT_BEGIN_NAMESPACE \image qtlabscontrols-tumbler-wrap.gif + \labs + \sa {Customizing Tumbler}, {Input Controls} */ diff --git a/tests/auto/controls/data/tst_stackview.qml b/tests/auto/controls/data/tst_stackview.qml index 2eb802bf..762be4cc 100644 --- a/tests/auto/controls/data/tst_stackview.qml +++ b/tests/auto/controls/data/tst_stackview.qml @@ -761,6 +761,45 @@ TestCase { control.destroy() } + Component { + id: mouseArea + MouseArea { + property int presses: 0 + property int releases: 0 + property int clicks: 0 + property int doubleClicks: 0 + property int cancels: 0 + onPressed: ++presses + onReleased: ++releases + onClicked: ++clicks + onDoubleClicked: ++doubleClicks + onCanceled: ++cancels + } + } + + // QTBUG-50305 + function test_events() { + var control = stackView.createObject(testCase, {initialItem: mouseArea, width: testCase.width, height: testCase.height}) + verify(control) + + var testItem = control.currentItem + verify(testItem) + + testItem.doubleClicked.connect(function() { + control.push(mouseArea) // ungrab -> cancel + }) + + mouseDoubleClickSequence(testItem) + compare(testItem.presses, 2) + compare(testItem.releases, 2) + compare(testItem.clicks, 1) + compare(testItem.doubleClicks, 1) + compare(testItem.pressed, false) + compare(testItem.cancels, 0) + + control.destroy() + } + function test_failures() { var control = stackView.createObject(testCase, {initialItem: component}) verify(control) diff --git a/tests/auto/material/data/tst_material.qml b/tests/auto/material/data/tst_material.qml index e29fea3b..6cd704eb 100644 --- a/tests/auto/material/data/tst_material.qml +++ b/tests/auto/material/data/tst_material.qml @@ -61,6 +61,7 @@ TestCase { id: styledButton Button { Material.theme: Material.Dark + Material.primary: Material.DeepOrange Material.accent: Material.DeepPurple } } @@ -74,6 +75,7 @@ TestCase { id: styledWindow Window { Material.theme: Material.Dark + Material.primary: Material.Brown Material.accent: Material.Green } } @@ -97,6 +99,7 @@ TestCase { Component { id: menu Item { + Material.primary: Material.Blue Material.accent: Material.Red property alias menu: popup Menu { @@ -113,6 +116,7 @@ TestCase { width: 200 height: 200 visible: true + Material.primary: Material.Blue Material.accent: Material.Red property alias combo: box ComboBox { @@ -127,7 +131,8 @@ TestCase { var control = button.createObject(testCase) verify(control) verify(control.Material) - compare(control.Material.accent, Material.color(Material.Teal)) + compare(control.Material.primary, Material.color(Material.Indigo)) + compare(control.Material.accent, Material.color(Material.Pink)) compare(control.Material.theme, Material.Light) control.destroy() } @@ -135,8 +140,10 @@ TestCase { function test_set() { var control = button.createObject(testCase) verify(control) + control.Material.primary = Material.Green control.Material.accent = Material.Brown control.Material.theme = Material.Dark + compare(control.Material.primary, Material.color(Material.Green)) compare(control.Material.accent, Material.color(Material.Brown)) compare(control.Material.theme, Material.Dark) control.destroy() @@ -145,10 +152,13 @@ TestCase { function test_reset() { var control = styledButton.createObject(testCase) verify(control) + compare(control.Material.primary, Material.color(Material.DeepOrange)) compare(control.Material.accent, Material.color(Material.DeepPurple)) compare(control.Material.theme, Material.Dark) + control.Material.primary = undefined control.Material.accent = undefined control.Material.theme = undefined + compare(control.Material.primary, testCase.Material.primary) compare(control.Material.accent, testCase.Material.accent) compare(control.Material.theme, testCase.Material.theme) control.destroy() @@ -156,6 +166,7 @@ TestCase { function test_inheritance_data() { return [ + { tag: "primary", value1: Material.color(Material.Amber), value2: Material.color(Material.Indigo) }, { tag: "accent", value1: Material.color(Material.Amber), value2: Material.color(Material.Indigo) }, { tag: "theme", value1: Material.Dark, value2: Material.Light }, ] @@ -209,17 +220,25 @@ TestCase { var parent = window.createObject() var control = button.createObject(parent.contentItem) + compare(control.Material.primary, parent.Material.primary) compare(control.Material.accent, parent.Material.accent) compare(control.Material.theme, parent.Material.theme) var styledChild = styledWindow.createObject(window) + verify(styledChild.Material.primary !== parent.Material.primary) verify(styledChild.Material.accent !== parent.Material.accent) verify(styledChild.Material.theme !== parent.Material.theme) var unstyledChild = window.createObject(window) + compare(unstyledChild.Material.primary, parent.Material.primary) compare(unstyledChild.Material.accent, parent.Material.accent) compare(unstyledChild.Material.theme, parent.Material.theme) + parent.Material.primary = Material.Lime + compare(control.Material.primary, Material.color(Material.Lime)) + verify(styledChild.Material.primary !== Material.color(Material.Lime)) + // ### TODO: compare(unstyledChild.Material.primary, Material.color(Material.Lime)) + parent.Material.accent = Material.Cyan compare(control.Material.accent, Material.color(Material.Cyan)) verify(styledChild.Material.accent !== Material.color(Material.Cyan)) @@ -230,14 +249,20 @@ TestCase { function test_loader() { var control = loader.createObject(testCase) + control.Material.primary = Material.Yellow control.Material.accent = Material.Lime control.active = true + compare(control.item.Material.primary, Material.color(Material.Yellow)) compare(control.item.Material.accent, Material.color(Material.Lime)) + control.Material.primary = Material.Red control.Material.accent = Material.Pink + compare(control.item.Material.primary, Material.color(Material.Red)) compare(control.item.Material.accent, Material.color(Material.Pink)) control.active = false + control.Material.primary = Material.Orange control.Material.accent = Material.Brown control.active = true + compare(control.item.Material.primary, Material.color(Material.Orange)) compare(control.item.Material.accent, Material.color(Material.Brown)) control.destroy() } @@ -261,6 +286,9 @@ TestCase { compare(container.Material.theme, Material.Light) compare(container.menu.Material.theme, Material.Dark) compare(child.Material.theme, Material.Dark) + compare(container.Material.primary, Material.color(Material.Blue)) + compare(container.menu.Material.primary, Material.color(Material.Blue)) + compare(child.Material.primary, Material.color(Material.Blue)) compare(container.Material.accent, Material.color(Material.Red)) compare(container.menu.Material.accent, Material.color(Material.Red)) compare(child.Material.accent, Material.color(Material.Red)) @@ -276,58 +304,70 @@ TestCase { verify(window.combo.activeFocus) keyClick(Qt.Key_Space) verify(window.combo.popup.visible) - var listView = window.combo.popup.contentItem.children[0] + var listView = window.combo.popup.contentItem verify(listView) var child = listView.contentItem.children[0] verify(child) compare(window.Material.theme, Material.Light) compare(window.combo.Material.theme, Material.Dark) compare(child.Material.theme, Material.Dark) + compare(window.Material.primary, Material.color(Material.Blue)) + compare(window.combo.Material.primary, Material.color(Material.Blue)) + compare(child.Material.primary, Material.color(Material.Blue)) compare(window.Material.accent, Material.color(Material.Red)) compare(window.combo.Material.accent, Material.color(Material.Red)) compare(child.Material.accent, Material.color(Material.Red)) window.destroy() } - function test_colors() { + + function test_colors_data() { + return [ + { tag: "primary" }, { tag: "accent" } + ] + } + + function test_colors(data) { var control = button.createObject(testCase) verify(control) - // Material.Accent - enum - control.Material.accent = Material.Red - compare(control.Material.accent, "#f44336") + var prop = data.tag + + // Material.Color - enum + control.Material[prop] = Material.Red + compare(control.Material[prop], "#f44336") - // Material.Accent - string - control.Material.accent = "BlueGrey" - compare(control.Material.accent, "#607d8b") + // Material.Color - string + control.Material[prop] = "BlueGrey" + compare(control.Material[prop], "#607d8b") // SVG named color - control.Material.accent = "tomato" - compare(control.Material.accent, "#ff6347") + control.Material[prop] = "tomato" + compare(control.Material[prop], "#ff6347") // #rrggbb - control.Material.accent = "#123456" - compare(control.Material.accent, "#123456") + control.Material[prop] = "#123456" + compare(control.Material[prop], "#123456") // #aarrggbb - control.Material.accent = "#12345678" - compare(control.Material.accent, "#12345678") + control.Material[prop] = "#12345678" + compare(control.Material[prop], "#12345678") // Qt.rgba() - no alpha - control.Material.accent = Qt.rgba(0.5, 0.5, 0.5) - compare(control.Material.accent, "#808080") + control.Material[prop] = Qt.rgba(0.5, 0.5, 0.5) + compare(control.Material[prop], "#808080") // Qt.rgba() - with alpha - control.Material.accent = Qt.rgba(0.5, 0.5, 0.5, 0.5) - compare(control.Material.accent, "#80808080") + control.Material[prop] = Qt.rgba(0.5, 0.5, 0.5, 0.5) + compare(control.Material[prop], "#80808080") // unknown - ignoreWarning(Qt.resolvedUrl("tst_material.qml") + ":57:9: QML Button: unknown Material.accent value: 123") - control.Material.accent = 123 - ignoreWarning(Qt.resolvedUrl("tst_material.qml") + ":57:9: QML Button: unknown Material.accent value: foo") - control.Material.accent = "foo" - ignoreWarning(Qt.resolvedUrl("tst_material.qml") + ":57:9: QML Button: unknown Material.accent value: #1") - control.Material.accent = "#1" + ignoreWarning(Qt.resolvedUrl("tst_material.qml") + ":57:9: QML Button: unknown Material." + prop + " value: 123") + control.Material[prop] = 123 + ignoreWarning(Qt.resolvedUrl("tst_material.qml") + ":57:9: QML Button: unknown Material." + prop + " value: foo") + control.Material[prop] = "foo" + ignoreWarning(Qt.resolvedUrl("tst_material.qml") + ":57:9: QML Button: unknown Material." + prop + " value: #1") + control.Material[prop] = "#1" control.destroy() } diff --git a/tests/auto/menu/tst_menu.cpp b/tests/auto/menu/tst_menu.cpp index 3a2d9c61..d072af4b 100644 --- a/tests/auto/menu/tst_menu.cpp +++ b/tests/auto/menu/tst_menu.cpp @@ -86,7 +86,7 @@ void tst_menu::mouse() QQuickMenu *menu = window->property("menu").value<QQuickMenu*>(); menu->open(); QVERIFY(menu->isVisible()); - QVERIFY(window->overlay()->childItems().contains(menu->contentItem())); + QVERIFY(window->overlay()->childItems().contains(menu->contentItem()->parentItem())); QQuickItem *firstItem = menu->itemAt(0); QSignalSpy clickedSpy(firstItem, SIGNAL(clicked(QQuickMouseEvent*))); @@ -111,7 +111,7 @@ void tst_menu::mouse() menu->open(); QCOMPARE(visibleSpy.count(), 2); QVERIFY(menu->isVisible()); - QVERIFY(window->overlay()->childItems().contains(menu->contentItem())); + QVERIFY(window->overlay()->childItems().contains(menu->contentItem()->parentItem())); // Ensure that we have enough space to click outside of the menu. QVERIFY(window->width() > menu->contentItem()->width()); @@ -120,12 +120,12 @@ void tst_menu::mouse() QPoint(menu->contentItem()->width() + 1, menu->contentItem()->height() + 1)); QCOMPARE(visibleSpy.count(), 3); QVERIFY(!menu->isVisible()); - QVERIFY(!window->overlay()->childItems().contains(menu->contentItem())); + QVERIFY(!window->overlay()->childItems().contains(menu->contentItem()->parentItem())); menu->open(); QCOMPARE(visibleSpy.count(), 4); QVERIFY(menu->isVisible()); - QVERIFY(window->overlay()->childItems().contains(menu->contentItem())); + QVERIFY(window->overlay()->childItems().contains(menu->contentItem()->parentItem())); // Try pressing within the menu and releasing outside of it; it should close. // TODO: won't work until QQuickPopup::releasedOutside() actually gets emitted @@ -167,7 +167,7 @@ void tst_menu::contextMenuKeyboard() menu->open(); QCOMPARE(visibleSpy.count(), 1); QVERIFY(menu->isVisible()); - QVERIFY(window->overlay()->childItems().contains(menu->contentItem())); + QVERIFY(window->overlay()->childItems().contains(menu->contentItem()->parentItem())); QVERIFY(!firstItem->hasActiveFocus()); QCOMPARE(menu->contentItem()->property("currentIndex"), QVariant(-1)); @@ -194,7 +194,7 @@ void tst_menu::contextMenuKeyboard() menu->open(); QCOMPARE(visibleSpy.count(), 3); QVERIFY(menu->isVisible()); - QVERIFY(window->overlay()->childItems().contains(menu->contentItem())); + QVERIFY(window->overlay()->childItems().contains(menu->contentItem()->parentItem())); QVERIFY(!firstItem->hasActiveFocus()); QVERIFY(!secondItem->hasActiveFocus()); QCOMPARE(menu->contentItem()->property("currentIndex"), QVariant(-1)); diff --git a/tests/auto/universal/data/tst_universal.qml b/tests/auto/universal/data/tst_universal.qml index af70ad0e..36e8b0b2 100644 --- a/tests/auto/universal/data/tst_universal.qml +++ b/tests/auto/universal/data/tst_universal.qml @@ -276,7 +276,7 @@ TestCase { verify(window.combo.activeFocus) keyClick(Qt.Key_Space) verify(window.combo.popup.visible) - var listView = window.combo.popup.contentItem.children[0] + var listView = window.combo.popup.contentItem verify(listView) var child = listView.contentItem.children[0] verify(child) diff --git a/tests/manual/testbench/main.qml b/tests/manual/testbench/main.qml index 76008497..6ace3620 100644 --- a/tests/manual/testbench/main.qml +++ b/tests/manual/testbench/main.qml @@ -53,7 +53,6 @@ ApplicationWindow { Material.theme: themeSwitch.checked ? Material.Dark : Material.Light Universal.theme: themeSwitch.checked ? Universal.Dark : Universal.Light - Material.accent: Material.LightGreen property int controlSpacing: 10 @@ -71,6 +70,25 @@ ApplicationWindow { ToolButton { text: "Normal" onClicked: menu.visible ? menu.close() : menu.open() + + Menu { + id: menu + x: 1 + y: 1 + parent.height + + MenuItem { + text: "Option 1" + checkable: true + } + MenuItem { + text: "Option 2" + checkable: true + } + MenuItem { + text: "Option 3" + checkable: true + } + } } ToolButton { text: "Pressed" @@ -115,25 +133,6 @@ ApplicationWindow { } } - Menu { - id: menu - contentItem.x: 1 - contentItem.y: header.height - - MenuItem { - text: "Option 1" - checkable: true - } - MenuItem { - text: "Option 2" - checkable: true - } - MenuItem { - text: "Option 3" - checkable: true - } - } - Pane { anchors.fill: parent |