diff options
author | Oswald Buddenhagen <oswald.buddenhagen@qt.io> | 2017-02-01 20:44:51 +0100 |
---|---|---|
committer | Oswald Buddenhagen <oswald.buddenhagen@qt.io> | 2017-02-01 20:44:51 +0100 |
commit | 855633758577750e44482843b0836cc0437ef1b2 (patch) | |
tree | 097f298789631d5b1b2562af39aab5d280dd4d04 | |
parent | 283e95aaf299b6bb320dbacabb099d91f070f116 (diff) | |
parent | 9351df782b513a5e4dbdd2780ef376a0a08e9800 (diff) |
Merge dev into 5.9
Change-Id: I816cc71d81ee6435ff9a58aff2d538698f9f8072
31 files changed, 1489 insertions, 35 deletions
diff --git a/src/imports/controls/ScrollView.qml b/src/imports/controls/ScrollView.qml new file mode 100644 index 00000000..e10c5328 --- /dev/null +++ b/src/imports/controls/ScrollView.qml @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.9 +import QtQuick.Controls 2.2 +import QtQuick.Controls.impl 2.2 +import QtQuick.Templates 2.2 as T + +T.ScrollView { + id: control + + implicitWidth: Math.max(background ? background.implicitWidth : 0, contentWidth + leftPadding + rightPadding) + implicitHeight: Math.max(background ? background.implicitHeight : 0, contentHeight + topPadding + bottomPadding) + + contentWidth: contentItem.implicitWidth || (contentChildren.length === 1 ? contentChildren[0].implicitWidth : -1) + contentHeight: contentItem.implicitHeight || (contentChildren.length === 1 ? contentChildren[0].implicitHeight : -1) + + ScrollBar.vertical: ScrollBar { + parent: control + x: control.mirrored ? 0 : control.width - width + y: control.topPadding + height: control.availableHeight + active: control.ScrollBar.horizontal.active + } + + ScrollBar.horizontal: ScrollBar { + parent: control + x: control.leftPadding + y: control.height - height + width: control.availableWidth + active: control.ScrollBar.vertical.active + } +} diff --git a/src/imports/controls/controls.pri b/src/imports/controls/controls.pri index 04e31103..5aa56418 100644 --- a/src/imports/controls/controls.pri +++ b/src/imports/controls/controls.pri @@ -45,6 +45,7 @@ QML_CONTROLS = \ RoundButton.qml \ ScrollBar.qml \ ScrollIndicator.qml \ + ScrollView.qml \ Slider.qml \ SpinBox.qml \ StackView.qml \ diff --git a/src/imports/controls/doc/images/qtquickcontrols2-scrollview-custom.png b/src/imports/controls/doc/images/qtquickcontrols2-scrollview-custom.png Binary files differnew file mode 100644 index 00000000..9c2790c9 --- /dev/null +++ b/src/imports/controls/doc/images/qtquickcontrols2-scrollview-custom.png diff --git a/src/imports/controls/doc/images/qtquickcontrols2-scrollview-wireframe.png b/src/imports/controls/doc/images/qtquickcontrols2-scrollview-wireframe.png Binary files differnew file mode 100644 index 00000000..afdc681d --- /dev/null +++ b/src/imports/controls/doc/images/qtquickcontrols2-scrollview-wireframe.png diff --git a/src/imports/controls/doc/images/qtquickcontrols2-scrollview.png b/src/imports/controls/doc/images/qtquickcontrols2-scrollview.png Binary files differnew file mode 100644 index 00000000..2a1807ee --- /dev/null +++ b/src/imports/controls/doc/images/qtquickcontrols2-scrollview.png diff --git a/src/imports/controls/doc/images/qtquickcontrols2-textarea-flickable.png b/src/imports/controls/doc/images/qtquickcontrols2-textarea-scrollable.png Binary files differindex 39578f71..39578f71 100644 --- a/src/imports/controls/doc/images/qtquickcontrols2-textarea-flickable.png +++ b/src/imports/controls/doc/images/qtquickcontrols2-textarea-scrollable.png diff --git a/src/imports/controls/doc/snippets/qtquickcontrols2-scrollview-interactive.qml b/src/imports/controls/doc/snippets/qtquickcontrols2-scrollview-interactive.qml new file mode 100644 index 00000000..8bb7be29 --- /dev/null +++ b/src/imports/controls/doc/snippets/qtquickcontrols2-scrollview-interactive.qml @@ -0,0 +1,38 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: http://www.gnu.org/copyleft/fdl.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.9 +import QtQuick.Controls 2.2 + +//! [file] +ScrollView { + // ... + ScrollBar.horizontal.interactive: true + ScrollBar.vertical.interactive: true +} +//! [file] + diff --git a/src/imports/controls/doc/snippets/qtquickcontrols2-scrollview-listview.qml b/src/imports/controls/doc/snippets/qtquickcontrols2-scrollview-listview.qml new file mode 100644 index 00000000..bce00cde --- /dev/null +++ b/src/imports/controls/doc/snippets/qtquickcontrols2-scrollview-listview.qml @@ -0,0 +1,61 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: http://www.gnu.org/copyleft/fdl.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.9 +import QtQuick.Controls 2.2 + +Item { + id: root + width: 200 + height: 200 + + Binding { + target: root.children[0].ScrollBar.horizontal + property: "active" + value: true + } + + Binding { + target: root.children[0].ScrollBar.vertical + property: "active" + value: true + } + +//! [file] +ScrollView { + width: 200 + height: 200 + + ListView { + model: 20 + delegate: ItemDelegate { + text: "Item " + index + } + } +} +//! [file] +} diff --git a/src/imports/controls/doc/snippets/qtquickcontrols2-scrollview-policy.qml b/src/imports/controls/doc/snippets/qtquickcontrols2-scrollview-policy.qml new file mode 100644 index 00000000..ce41950d --- /dev/null +++ b/src/imports/controls/doc/snippets/qtquickcontrols2-scrollview-policy.qml @@ -0,0 +1,38 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: http://www.gnu.org/copyleft/fdl.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.9 +import QtQuick.Controls 2.2 + +//! [file] +ScrollView { + // ... + ScrollBar.horizontal.policy: ScrollBar.AlwaysOff + ScrollBar.vertical.policy: ScrollBar.AlwaysOn +} +//! [file] + diff --git a/src/imports/controls/doc/snippets/screenshots/qtquickcontrols2-scrollview-custom.qml b/src/imports/controls/doc/snippets/screenshots/qtquickcontrols2-scrollview-custom.qml new file mode 100644 index 00000000..82347a5d --- /dev/null +++ b/src/imports/controls/doc/snippets/screenshots/qtquickcontrols2-scrollview-custom.qml @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: http://www.gnu.org/copyleft/fdl.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.9 +import QtQuick.Controls 2.2 + +Item { + width: 200 + height: 200 + + Binding { + target: control.ScrollBar.horizontal + property: "active" + value: true + } + + Binding { + target: control.ScrollBar.vertical + property: "active" + value: true + } + +//! [file] +ScrollView { + id: control + + width: 200 + height: 200 + focus: true + + Label { + text: "ABC" + font.pixelSize: 224 + } + + ScrollBar.vertical: ScrollBar { + parent: control + x: control.mirrored ? 0 : control.width - width + y: control.topPadding + height: control.availableHeight + active: control.ScrollBar.horizontal.active + } + + ScrollBar.horizontal: ScrollBar { + parent: control + x: control.leftPadding + y: control.height - height + width: control.availableWidth + active: control.ScrollBar.vertical.active + } + + background: Rectangle { + border.color: control.activeFocus ? "#21be2b" : "#bdbebf" + } +} +//! [file] +} diff --git a/src/imports/controls/doc/snippets/screenshots/qtquickcontrols2-scrollview.qml b/src/imports/controls/doc/snippets/screenshots/qtquickcontrols2-scrollview.qml new file mode 100644 index 00000000..8900cf9e --- /dev/null +++ b/src/imports/controls/doc/snippets/screenshots/qtquickcontrols2-scrollview.qml @@ -0,0 +1,61 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: http://www.gnu.org/copyleft/fdl.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.9 +import QtQuick.Controls 2.2 + +Rectangle { + id: root + width: 200 + height: 200 + border.color: "#ddd" + + Binding { + target: root.children[0].ScrollBar.horizontal + property: "active" + value: true + } + + Binding { + target: root.children[0].ScrollBar.vertical + property: "active" + value: true + } + +//! [file] +ScrollView { + width: 200 + height: 200 + clip: true + + Label { + text: "ABC" + font.pixelSize: 224 + } +} +//! [file] +} diff --git a/src/imports/controls/doc/snippets/screenshots/qtquickcontrols2-textarea-flickable.qml b/src/imports/controls/doc/snippets/screenshots/qtquickcontrols2-textarea-scrollable.qml index 6fa64390..aa4d05a8 100644 --- a/src/imports/controls/doc/snippets/screenshots/qtquickcontrols2-textarea-flickable.qml +++ b/src/imports/controls/doc/snippets/screenshots/qtquickcontrols2-textarea-scrollable.qml @@ -26,25 +26,22 @@ ****************************************************************************/ import QtQuick 2.0 -import QtQuick.Controls 2.1 +import QtQuick.Controls 2.2 Item { width: 100 height: 100 - Binding { target: flickable.ScrollBar.vertical; property: "active"; value: true } + Binding { target: view.ScrollBar.vertical; property: "active"; value: true } //! [1] - Flickable { - id: flickable + ScrollView { + id: view anchors.fill: parent - TextArea.flickable: TextArea { + TextArea { text: "TextArea\n...\n...\n...\n...\n...\n...\n" - wrapMode: TextArea.Wrap } - - ScrollBar.vertical: ScrollBar { } } //! [1] } diff --git a/src/imports/controls/doc/src/qtquickcontrols2-containers.qdoc b/src/imports/controls/doc/src/qtquickcontrols2-containers.qdoc index 139958e0..73cfd989 100644 --- a/src/imports/controls/doc/src/qtquickcontrols2-containers.qdoc +++ b/src/imports/controls/doc/src/qtquickcontrols2-containers.qdoc @@ -78,6 +78,12 @@ you to position its contents, for instance by using a \l RowLayout or a \l ColumnLayout. + \section1 ScrollView Control + + \image qtquickcontrols2-scrollview-wireframe.png + + \l ScrollView provides scrolling for user-defined content. + \section1 StackView Control \image qtquickcontrols2-stackview-wireframe.png diff --git a/src/imports/controls/doc/src/qtquickcontrols2-customize.qdoc b/src/imports/controls/doc/src/qtquickcontrols2-customize.qdoc index 6ef019f6..487c5d6f 100644 --- a/src/imports/controls/doc/src/qtquickcontrols2-customize.qdoc +++ b/src/imports/controls/doc/src/qtquickcontrols2-customize.qdoc @@ -636,6 +636,16 @@ \snippet qtquickcontrols2-scrollindicator-custom.qml file + \section2 Customizing ScrollView + + ScrollView consists of a \l {Control::background}{background} item, + and horizontal and vertical scroll bars. + + \image qtquickcontrols2-scrollview-custom.png + + \snippet qtquickcontrols2-scrollview-custom.qml file + + \section2 Customizing Slider Slider consists of two visual items: \l {Control::background}{background}, diff --git a/src/imports/controls/doc/src/qtquickcontrols2-differences.qdoc b/src/imports/controls/doc/src/qtquickcontrols2-differences.qdoc index 3423e423..dd86d2ed 100644 --- a/src/imports/controls/doc/src/qtquickcontrols2-differences.qdoc +++ b/src/imports/controls/doc/src/qtquickcontrols2-differences.qdoc @@ -328,14 +328,9 @@ \li \row \li \l [QML QtQuickControls] {ScrollView} - \li \mdash - \li \l [QML QtQuickControls2] {ScrollBar},\br - \l [QML QtQuickControls2] {ScrollIndicator} \br\sup {(Qt Quick Controls 2)} - \li \list - \li \b {Qt Quick Controls 2}: \c ScrollBar and \c ScrollIndicator offer - similar functionality. They can be attached to any \c Flickable to - build scrollable views. - \endlist + \li \l [QML QtQuickControls2] {ScrollView} + \li + \li \row \li \l [QML QtQuickControls] {Slider} \li \l [QML QtQuickControls2] {Slider} diff --git a/src/imports/controls/plugins.qmltypes b/src/imports/controls/plugins.qmltypes index c3e01172..affdfb96 100644 --- a/src/imports/controls/plugins.qmltypes +++ b/src/imports/controls/plugins.qmltypes @@ -376,6 +376,14 @@ Module { defaultProperty: "data" } Component { + prototype: "QQuickScrollView" + name: "QtQuick.Controls/ScrollView 2.2" + exports: ["QtQuick.Controls/ScrollView 2.2"] + exportMetaObjectRevisions: [2] + isComposite: true + defaultProperty: "contentData" + } + Component { prototype: "QQuickSlider" name: "QtQuick.Controls/Slider 2.0" exports: ["QtQuick.Controls/Slider 2.0"] @@ -1258,6 +1266,17 @@ Module { Property { name: "vertical"; type: "QQuickScrollIndicator"; isPointer: true } } Component { + name: "QQuickScrollView" + defaultProperty: "contentData" + prototype: "QQuickControl" + exports: ["QtQuick.Templates/ScrollView 2.2"] + exportMetaObjectRevisions: [0] + Property { name: "contentWidth"; type: "double" } + Property { name: "contentHeight"; type: "double" } + Property { name: "contentData"; type: "QObject"; isList: true; isReadonly: true } + Property { name: "contentChildren"; type: "QQuickItem"; isList: true; isReadonly: true } + } + Component { name: "QQuickSlider" defaultProperty: "data" prototype: "QQuickControl" diff --git a/src/imports/controls/qtquickcontrols2plugin.cpp b/src/imports/controls/qtquickcontrols2plugin.cpp index e7bedc73..cd81eb62 100644 --- a/src/imports/controls/qtquickcontrols2plugin.cpp +++ b/src/imports/controls/qtquickcontrols2plugin.cpp @@ -143,6 +143,7 @@ void QtQuickControls2Plugin::registerTypes(const char *uri) // QtQuick.Controls 2.2 (new types in Qt 5.9) qmlRegisterType(selector.select(QStringLiteral("DelayButton.qml")), uri, 2, 2, "DelayButton"); + qmlRegisterType(selector.select(QStringLiteral("ScrollView.qml")), uri, 2, 2, "ScrollView"); } static QObject *styleSingleton(QQmlEngine *engine, QJSEngine *scriptEngine) diff --git a/src/imports/templates/plugins.qmltypes b/src/imports/templates/plugins.qmltypes index d2151685..0ff02f22 100644 --- a/src/imports/templates/plugins.qmltypes +++ b/src/imports/templates/plugins.qmltypes @@ -753,6 +753,17 @@ Module { Property { name: "vertical"; type: "QQuickScrollIndicator"; isPointer: true } } Component { + name: "QQuickScrollView" + defaultProperty: "contentData" + prototype: "QQuickControl" + exports: ["QtQuick.Templates/ScrollView 2.2"] + exportMetaObjectRevisions: [0] + Property { name: "contentWidth"; type: "double" } + Property { name: "contentHeight"; type: "double" } + Property { name: "contentData"; type: "QObject"; isList: true; isReadonly: true } + Property { name: "contentChildren"; type: "QQuickItem"; isList: true; isReadonly: true } + } + Component { name: "QQuickSlider" defaultProperty: "data" prototype: "QQuickControl" diff --git a/src/imports/templates/qtquicktemplates2plugin.cpp b/src/imports/templates/qtquicktemplates2plugin.cpp index 52529c3b..b17b916b 100644 --- a/src/imports/templates/qtquicktemplates2plugin.cpp +++ b/src/imports/templates/qtquicktemplates2plugin.cpp @@ -70,6 +70,7 @@ #include <QtQuickTemplates2/private/qquickroundbutton_p.h> #include <QtQuickTemplates2/private/qquickscrollbar_p.h> #include <QtQuickTemplates2/private/qquickscrollindicator_p.h> +#include <QtQuickTemplates2/private/qquickscrollview_p.h> #include <QtQuickTemplates2/private/qquickshortcutcontext_p_p.h> #include <QtQuickTemplates2/private/qquickslider_p.h> #include <QtQuickTemplates2/private/qquickspinbox_p.h> @@ -231,6 +232,7 @@ void QtQuickTemplates2Plugin::registerTypes(const char *uri) qmlRegisterType<QQuickDrawer, 2>(uri, 2, 2, "Drawer"); qmlRegisterType<QQuickRangeSlider, 2>(uri, 2, 2, "RangeSlider"); qmlRegisterType<QQuickScrollBar, 2>(uri, 2, 2, "ScrollBar"); + qmlRegisterType<QQuickScrollView>(uri, 2, 2, "ScrollView"); qmlRegisterType<QQuickSlider, 2>(uri, 2, 2, "Slider"); qmlRegisterType<QQuickSpinBox, 2>(uri, 2, 2, "SpinBox"); qmlRegisterType<QQuickSwipeDelegate, 2>(uri, 2, 2, "SwipeDelegate"); diff --git a/src/quicktemplates2/qquickpopup.cpp b/src/quicktemplates2/qquickpopup.cpp index 3a09bf84..80c530eb 100644 --- a/src/quicktemplates2/qquickpopup.cpp +++ b/src/quicktemplates2/qquickpopup.cpp @@ -1729,8 +1729,12 @@ void QQuickPopup::componentComplete() { Q_D(QQuickPopup); d->complete = true; - if (!parentItem()) - setParentItem(qobject_cast<QQuickItem *>(parent())); + if (!parentItem()) { + if (QQuickItem *item = qobject_cast<QQuickItem *>(parent())) + setParentItem(item); + else if (QQuickWindow *window = qobject_cast<QQuickWindow *>(parent())) + setParentItem(window->contentItem()); + } if (d->visible) d->transitionManager.transitionEnter(); d->popupItem->componentComplete(); diff --git a/src/quicktemplates2/qquickscrollbar.cpp b/src/quicktemplates2/qquickscrollbar.cpp index ac786649..1ad14e9d 100644 --- a/src/quicktemplates2/qquickscrollbar.cpp +++ b/src/quicktemplates2/qquickscrollbar.cpp @@ -36,6 +36,7 @@ #include "qquickscrollbar_p.h" #include "qquickscrollbar_p_p.h" +#include "qquickscrollview_p.h" #include <QtQml/qqmlinfo.h> #include <QtQuick/private/qquickflickable_p.h> @@ -163,6 +164,7 @@ QQuickScrollBarPrivate::QQuickScrollBarPrivate() pressed(false), moving(false), interactive(true), + explicitInteractive(false), orientation(Qt::Vertical), snapMode(QQuickScrollBar::NoSnap), policy(QQuickScrollBar::AsNeeded) @@ -187,6 +189,19 @@ qreal QQuickScrollBarPrivate::positionAt(const QPointF &point) const return (point.y() - q->topPadding()) / q->availableHeight(); } +void QQuickScrollBarPrivate::setInteractive(bool enabled) +{ + Q_Q(QQuickScrollBar); + if (interactive == enabled) + return; + + interactive = enabled; + q->setAcceptedMouseButtons(interactive ? Qt::LeftButton : Qt::NoButton); + if (!interactive) + q->ungrabMouse(); + emit q->interactiveChanged(); +} + void QQuickScrollBarPrivate::updateActive() { Q_Q(QQuickScrollBar); @@ -480,14 +495,15 @@ bool QQuickScrollBar::isInteractive() const void QQuickScrollBar::setInteractive(bool interactive) { Q_D(QQuickScrollBar); - if (d->interactive == interactive) - return; + d->explicitInteractive = true; + d->setInteractive(interactive); +} - d->interactive = interactive; - setAcceptedMouseButtons(interactive ? Qt::LeftButton : Qt::NoButton); - if (!interactive) - ungrabMouse(); - emit interactiveChanged(); +void QQuickScrollBar::resetInteractive() +{ + Q_D(QQuickScrollBar); + d->explicitInteractive = false; + d->setInteractive(true); } /*! @@ -615,7 +631,10 @@ QQuickScrollBarAttachedPrivate::QQuickScrollBarAttachedPrivate() void QQuickScrollBarAttachedPrivate::setFlickable(QQuickFlickable *item) { if (flickable) { - QQuickItemPrivate::get(flickable)->updateOrRemoveGeometryChangeListener(this, QQuickItemPrivate::Size); + // NOTE: Use removeItemChangeListener(Geometry) instead of updateOrRemoveGeometryChangeListener(Size). + // The latter doesn't remove the listener but only resets its types. Thus, it leaves behind a dangling + // pointer on destruction. + QQuickItemPrivate::get(flickable)->removeItemChangeListener(this, QQuickItemPrivate::Geometry); if (horizontal) cleanupHorizontal(); if (vertical) @@ -644,6 +663,11 @@ void QQuickScrollBarAttachedPrivate::initHorizontal() QObject::connect(area, SIGNAL(widthRatioChanged(qreal)), horizontal, SLOT(setSize(qreal))); QObject::connect(area, SIGNAL(xPositionChanged(qreal)), horizontal, SLOT(setPosition(qreal))); + // ensure that the ScrollBar is stacked above the Flickable in a ScrollView + QQuickItem *parent = horizontal->parentItem(); + if (parent && parent == flickable->parentItem()) + horizontal->stackAfter(flickable); + layoutHorizontal(); horizontal->setSize(area->property("widthRatio").toReal()); horizontal->setPosition(area->property("xPosition").toReal()); @@ -660,6 +684,11 @@ void QQuickScrollBarAttachedPrivate::initVertical() QObject::connect(area, SIGNAL(heightRatioChanged(qreal)), vertical, SLOT(setSize(qreal))); QObject::connect(area, SIGNAL(yPositionChanged(qreal)), vertical, SLOT(setPosition(qreal))); + // ensure that the ScrollBar is stacked above the Flickable in a ScrollView + QQuickItem *parent = vertical->parentItem(); + if (parent && parent == flickable->parentItem()) + vertical->stackAfter(flickable); + layoutVertical(); vertical->setSize(area->property("heightRatio").toReal()); vertical->setPosition(area->property("yPosition").toReal()); @@ -806,8 +835,8 @@ QQuickScrollBarAttached::QQuickScrollBarAttached(QObject *parent) Q_D(QQuickScrollBarAttached); d->setFlickable(qobject_cast<QQuickFlickable *>(parent)); - if (parent && !d->flickable) - qmlWarning(parent) << "ScrollBar must be attached to a Flickable"; + if (parent && !d->flickable && !qobject_cast<QQuickScrollView *>(parent)) + qmlWarning(parent) << "ScrollBar must be attached to a Flickable or ScrollView"; } QQuickScrollBarAttached::~QQuickScrollBarAttached() diff --git a/src/quicktemplates2/qquickscrollbar_p.h b/src/quicktemplates2/qquickscrollbar_p.h index dabc860b..868cb5d8 100644 --- a/src/quicktemplates2/qquickscrollbar_p.h +++ b/src/quicktemplates2/qquickscrollbar_p.h @@ -65,7 +65,7 @@ class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickScrollBar : public QQuickControl Q_PROPERTY(bool pressed READ isPressed WRITE setPressed NOTIFY pressedChanged FINAL) Q_PROPERTY(Qt::Orientation orientation READ orientation WRITE setOrientation NOTIFY orientationChanged FINAL) Q_PROPERTY(SnapMode snapMode READ snapMode WRITE setSnapMode NOTIFY snapModeChanged FINAL REVISION 2) - Q_PROPERTY(bool interactive READ isInteractive WRITE setInteractive NOTIFY interactiveChanged FINAL REVISION 2) + Q_PROPERTY(bool interactive READ isInteractive WRITE setInteractive RESET resetInteractive NOTIFY interactiveChanged FINAL REVISION 2) Q_PROPERTY(Policy policy READ policy WRITE setPolicy NOTIFY policyChanged FINAL REVISION 2) public: @@ -100,6 +100,7 @@ public: bool isInteractive() const; void setInteractive(bool interactive); + void resetInteractive(); enum Policy { AsNeeded = Qt::ScrollBarAsNeeded, diff --git a/src/quicktemplates2/qquickscrollbar_p_p.h b/src/quicktemplates2/qquickscrollbar_p_p.h index fa5bd0b5..cbbef2c8 100644 --- a/src/quicktemplates2/qquickscrollbar_p_p.h +++ b/src/quicktemplates2/qquickscrollbar_p_p.h @@ -70,6 +70,7 @@ public: qreal snapPosition(qreal position) const; qreal positionAt(const QPointF &point) const; + void setInteractive(bool interactive); void updateActive(); void resizeContent() override; @@ -86,6 +87,7 @@ public: bool pressed; bool moving; bool interactive; + bool explicitInteractive; Qt::Orientation orientation; QQuickScrollBar::SnapMode snapMode; QQuickScrollBar::Policy policy; diff --git a/src/quicktemplates2/qquickscrollindicator.cpp b/src/quicktemplates2/qquickscrollindicator.cpp index c98a1956..a57cfbb6 100644 --- a/src/quicktemplates2/qquickscrollindicator.cpp +++ b/src/quicktemplates2/qquickscrollindicator.cpp @@ -412,6 +412,9 @@ QQuickScrollIndicatorAttached::~QQuickScrollIndicatorAttached() QQuickItemPrivate::get(d->horizontal)->removeItemChangeListener(d, horizontalChangeTypes); if (d->vertical) QQuickItemPrivate::get(d->vertical)->removeItemChangeListener(d,verticalChangeTypes); + // NOTE: Use removeItemChangeListener(Geometry) instead of updateOrRemoveGeometryChangeListener(Size). + // The latter doesn't remove the listener but only resets its types. Thus, it leaves behind a dangling + // pointer on destruction. QQuickItemPrivate::get(d->flickable)->removeItemChangeListener(d, QQuickItemPrivate::Geometry); } } diff --git a/src/quicktemplates2/qquickscrollview.cpp b/src/quicktemplates2/qquickscrollview.cpp new file mode 100644 index 00000000..cf1fac48 --- /dev/null +++ b/src/quicktemplates2/qquickscrollview.cpp @@ -0,0 +1,572 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickscrollview_p.h" +#include "qquickcontrol_p_p.h" +#include "qquickscrollbar_p_p.h" + +#include <QtQuick/private/qquickflickable_p.h> + +QT_BEGIN_NAMESPACE + +/*! + \qmltype ScrollView + \inherits Control + \instantiates QQuickScrollView + \inqmlmodule QtQuick.Controls + \since 5.9 + \ingroup qtquickcontrols2-containers + \brief Scrollable view. + + ScrollView provides scrolling for user-defined content. It can be used to + either replace a \l Flickable, or to decorate an existing one. + + \image qtquickcontrols2-scrollview.png + + The first example demonstrates the simplest usage of ScrollView. + + \snippet qtquickcontrols2-scrollview.qml file + + \note ScrollView does not automatically clip its contents. If it is not used as + a full-screen item, you should consider setting the \l {Item::}{clip} property + to \c true, as shown above. + + The second example illustrates using an existing \l Flickable, that is, + a \l ListView. + + \snippet qtquickcontrols2-scrollview-listview.qml file + + \section2 Scroll Bars + + The horizontal and vertical scroll bars can be accessed and customized using + the \l {ScrollBar::horizontal}{ScrollBar.horizontal} and \l {ScrollBar::vertical} + {ScrollBar.vertical} attached properties. The following example adjusts the scroll + bar policies so that the horizontal scroll bar is always off, and the vertical + scroll bar is always on. + + \snippet qtquickcontrols2-scrollview-policy.qml file + + \section2 Touch vs. Mouse Interaction + + On touch, ScrollView enables flicking and makes the scroll bars non-interactive. + + \image qtquickcontrols2-scrollindicator.gif + + When interacted with a mouse device, flicking is disabled and the scroll bars + are interactive. + + \image qtquickcontrols2-scrollbar.gif + + Scroll bars can be made interactive on touch, or non-interactive when interacted + with a mouse device, by setting the \l {ScrollBar::}{interactive} property explicitly + to \c true or \c false, respectively. + + \snippet qtquickcontrols2-scrollview-interactive.qml file + + \sa ScrollBar, ScrollIndicator, {Customizing ScrollView}, {Container Controls}, +*/ + +class QQuickScrollViewPrivate : public QQuickControlPrivate +{ + Q_DECLARE_PUBLIC(QQuickScrollView) + +public: + QQuickScrollViewPrivate(); + + QQuickItem *getContentItem() override; + + QQuickFlickable *ensureFlickable(); + bool setFlickable(QQuickFlickable *flickable, bool content); + + void updateContentWidth(); + void updateContentHeight(); + + QQuickScrollBar *verticalScrollBar() const; + QQuickScrollBar *horizontalScrollBar() const; + + void setScrollBarsInteractive(bool interactive); + + static void contentData_append(QQmlListProperty<QObject> *prop, QObject *obj); + static int contentData_count(QQmlListProperty<QObject> *prop); + static QObject *contentData_at(QQmlListProperty<QObject> *prop, int index); + static void contentData_clear(QQmlListProperty<QObject> *prop); + + static void contentChildren_append(QQmlListProperty<QQuickItem> *prop, QQuickItem *obj); + static int contentChildren_count(QQmlListProperty<QQuickItem> *prop); + static QQuickItem *contentChildren_at(QQmlListProperty<QQuickItem> *prop, int index); + static void contentChildren_clear(QQmlListProperty<QQuickItem> *prop); + + bool wasTouched; + qreal contentWidth; + qreal contentHeight; + QQuickFlickable *flickable; +}; + +QQuickScrollViewPrivate::QQuickScrollViewPrivate() + : wasTouched(false), + contentWidth(-1), + contentHeight(-1), + flickable(nullptr) +{ + wheelEnabled = true; +} + +QQuickItem *QQuickScrollViewPrivate::getContentItem() +{ + return ensureFlickable(); +} + +QQuickFlickable *QQuickScrollViewPrivate::ensureFlickable() +{ + Q_Q(QQuickScrollView); + if (!flickable) + setFlickable(new QQuickFlickable(q), true); + return flickable; +} + +bool QQuickScrollViewPrivate::setFlickable(QQuickFlickable *item, bool content) +{ + Q_Q(QQuickScrollView); + if (item == flickable) + return false; + + QQuickScrollBarAttached *attached = qobject_cast<QQuickScrollBarAttached *>(qmlAttachedPropertiesObject<QQuickScrollBar>(q, false)); + + if (flickable) { + flickable->removeEventFilter(q); + + if (attached) + QQuickScrollBarAttachedPrivate::get(attached)->setFlickable(nullptr); + + QObject::disconnect(flickable->contentItem(), &QQuickItem::childrenChanged, q, &QQuickScrollView::contentChildrenChanged); + QObjectPrivate::disconnect(flickable, &QQuickFlickable::contentWidthChanged, this, &QQuickScrollViewPrivate::updateContentWidth); + QObjectPrivate::disconnect(flickable, &QQuickFlickable::contentHeightChanged, this, &QQuickScrollViewPrivate::updateContentHeight); + } + + flickable = item; + if (content) + q->setContentItem(flickable); + + if (flickable) { + flickable->installEventFilter(q); + if (contentWidth > 0) + item->setContentWidth(contentWidth); + else + updateContentWidth(); + if (contentHeight > 0) + item->setContentHeight(contentHeight); + else + updateContentHeight(); + + if (attached) + QQuickScrollBarAttachedPrivate::get(attached)->setFlickable(flickable); + + QObject::connect(flickable->contentItem(), &QQuickItem::childrenChanged, q, &QQuickScrollView::contentChildrenChanged); + QObjectPrivate::connect(flickable, &QQuickFlickable::contentWidthChanged, this, &QQuickScrollViewPrivate::updateContentWidth); + QObjectPrivate::connect(flickable, &QQuickFlickable::contentHeightChanged, this, &QQuickScrollViewPrivate::updateContentHeight); + } + + return true; +} + +void QQuickScrollViewPrivate::updateContentWidth() +{ + Q_Q(QQuickScrollView); + if (!flickable) + return; + + const qreal cw = flickable->contentWidth(); + if (qFuzzyCompare(cw, contentWidth)) + return; + + contentWidth = cw; + emit q->contentWidthChanged(); +} + +void QQuickScrollViewPrivate::updateContentHeight() +{ + Q_Q(QQuickScrollView); + if (!flickable) + return; + + const qreal ch = flickable->contentHeight(); + if (qFuzzyCompare(ch, contentHeight)) + return; + + contentHeight = ch; + emit q->contentHeightChanged(); +} + +QQuickScrollBar *QQuickScrollViewPrivate::verticalScrollBar() const +{ + Q_Q(const QQuickScrollView); + QQuickScrollBarAttached *attached = qobject_cast<QQuickScrollBarAttached *>(qmlAttachedPropertiesObject<QQuickScrollBar>(q, false)); + if (!attached) + return nullptr; + return attached->vertical(); +} + +QQuickScrollBar *QQuickScrollViewPrivate::horizontalScrollBar() const +{ + Q_Q(const QQuickScrollView); + QQuickScrollBarAttached *attached = qobject_cast<QQuickScrollBarAttached *>(qmlAttachedPropertiesObject<QQuickScrollBar>(q, false)); + if (!attached) + return nullptr; + return attached->horizontal(); +} + +void QQuickScrollViewPrivate::setScrollBarsInteractive(bool interactive) +{ + QQuickScrollBar *hbar = horizontalScrollBar(); + if (hbar) { + QQuickScrollBarPrivate *p = QQuickScrollBarPrivate::get(hbar); + if (!p->explicitInteractive) + p->setInteractive(interactive); + } + + QQuickScrollBar *vbar = verticalScrollBar(); + if (vbar) { + QQuickScrollBarPrivate *p = QQuickScrollBarPrivate::get(vbar); + if (!p->explicitInteractive) + p->setInteractive(interactive); + } +} + +void QQuickScrollViewPrivate::contentData_append(QQmlListProperty<QObject> *prop, QObject *obj) +{ + QQuickScrollViewPrivate *p = static_cast<QQuickScrollViewPrivate *>(prop->data); + if (!p->flickable && p->setFlickable(qobject_cast<QQuickFlickable *>(obj), true)) + return; + + QQuickFlickable *flickable = p->ensureFlickable(); + Q_ASSERT(flickable); + QQmlListProperty<QObject> data = flickable->flickableData(); + data.append(&data, obj); +} + +int QQuickScrollViewPrivate::contentData_count(QQmlListProperty<QObject> *prop) +{ + QQuickScrollViewPrivate *p = static_cast<QQuickScrollViewPrivate *>(prop->data); + if (!p->flickable) + return 0; + + QQmlListProperty<QObject> data = p->flickable->flickableData(); + return data.count(&data); +} + +QObject *QQuickScrollViewPrivate::contentData_at(QQmlListProperty<QObject> *prop, int index) +{ + QQuickScrollViewPrivate *p = static_cast<QQuickScrollViewPrivate *>(prop->data); + if (!p->flickable) + return nullptr; + + QQmlListProperty<QObject> data = p->flickable->flickableData(); + return data.at(&data, index); +} + +void QQuickScrollViewPrivate::contentData_clear(QQmlListProperty<QObject> *prop) +{ + QQuickScrollViewPrivate *p = static_cast<QQuickScrollViewPrivate *>(prop->data); + if (!p->flickable) + return; + + QQmlListProperty<QObject> data = p->flickable->flickableData(); + return data.clear(&data); +} + +void QQuickScrollViewPrivate::contentChildren_append(QQmlListProperty<QQuickItem> *prop, QQuickItem *item) +{ + QQuickScrollViewPrivate *p = static_cast<QQuickScrollViewPrivate *>(prop->data); + if (!p->flickable) + p->setFlickable(qobject_cast<QQuickFlickable *>(item), true); + + QQuickFlickable *flickable = p->ensureFlickable(); + Q_ASSERT(flickable); + QQmlListProperty<QQuickItem> children = flickable->flickableChildren(); + children.append(&children, item); +} + +int QQuickScrollViewPrivate::contentChildren_count(QQmlListProperty<QQuickItem> *prop) +{ + QQuickScrollViewPrivate *p = static_cast<QQuickScrollViewPrivate *>(prop->data); + if (!p->flickable) + return 0; + + QQmlListProperty<QQuickItem> children = p->flickable->flickableChildren(); + return children.count(&children); +} + +QQuickItem *QQuickScrollViewPrivate::contentChildren_at(QQmlListProperty<QQuickItem> *prop, int index) +{ + QQuickScrollViewPrivate *p = static_cast<QQuickScrollViewPrivate *>(prop->data); + if (!p->flickable) + return nullptr; + + QQmlListProperty<QQuickItem> children = p->flickable->flickableChildren(); + return children.at(&children, index); +} + +void QQuickScrollViewPrivate::contentChildren_clear(QQmlListProperty<QQuickItem> *prop) +{ + QQuickScrollViewPrivate *p = static_cast<QQuickScrollViewPrivate *>(prop->data); + if (!p->flickable) + return; + + QQmlListProperty<QQuickItem> children = p->flickable->flickableChildren(); + children.clear(&children); +} + +QQuickScrollView::QQuickScrollView(QQuickItem *parent) + : QQuickControl(*(new QQuickScrollViewPrivate), parent) +{ + setFlag(ItemIsFocusScope); + setActiveFocusOnTab(true); + setFiltersChildMouseEvents(true); +} + +/*! + \qmlproperty real QtQuick.Controls::ScrollView::contentWidth + + This property holds the width of the scrollable content. + + If only a single item is used within a ScrollView, the content size is + automatically calculated based on the implicit size of its contained item. + + \sa contentHeight +*/ +qreal QQuickScrollView::contentWidth() const +{ + Q_D(const QQuickScrollView); + return d->contentWidth; +} + +void QQuickScrollView::setContentWidth(qreal width) +{ + Q_D(QQuickScrollView); + if (qFuzzyCompare(d->contentWidth, width)) + return; + + if (d->flickable) { + d->flickable->setContentWidth(width); + } else { + d->contentWidth = width; + emit contentWidthChanged(); + } +} + +/*! + \qmlproperty real QtQuick.Controls::ScrollView::contentHeight + + This property holds the height of the scrollable content. + + If only a single item is used within a ScrollView, the content size is + automatically calculated based on the implicit size of its contained item. + + \sa contentWidth +*/ +qreal QQuickScrollView::contentHeight() const +{ + Q_D(const QQuickScrollView); + return d->contentHeight; +} + +void QQuickScrollView::setContentHeight(qreal height) +{ + Q_D(QQuickScrollView); + if (qFuzzyCompare(d->contentHeight, height)) + return; + + if (d->flickable) { + d->flickable->setContentHeight(height); + } else { + d->contentHeight = height; + emit contentHeightChanged(); + } +} + +/*! + \qmlproperty list<Object> QtQuick.Controls::ScrollView::contentData + \default + + This property holds the list of content data. + + The list contains all objects that have been declared in QML as children of the view. + + \note Unlike \c contentChildren, \c contentData does include non-visual QML objects. + + \sa Item::data, contentChildren +*/ +QQmlListProperty<QObject> QQuickScrollView::contentData() +{ + Q_D(QQuickScrollView); + return QQmlListProperty<QObject>(this, d, + QQuickScrollViewPrivate::contentData_append, + QQuickScrollViewPrivate::contentData_count, + QQuickScrollViewPrivate::contentData_at, + QQuickScrollViewPrivate::contentData_clear); +} + +/*! + \qmlproperty list<Item> QtQuick.Controls::ScrollView::contentChildren + + This property holds the list of content children. + + The list contains all items that have been declared in QML as children of the view. + + \note Unlike \c contentData, \c contentChildren does not include non-visual QML objects. + + \sa Item::children, contentData +*/ +QQmlListProperty<QQuickItem> QQuickScrollView::contentChildren() +{ + Q_D(QQuickScrollView); + return QQmlListProperty<QQuickItem>(this, d, + QQuickScrollViewPrivate::contentChildren_append, + QQuickScrollViewPrivate::contentChildren_count, + QQuickScrollViewPrivate::contentChildren_at, + QQuickScrollViewPrivate::contentChildren_clear); +} + +bool QQuickScrollView::childMouseEventFilter(QQuickItem *item, QEvent *event) +{ + Q_D(QQuickScrollView); + switch (event->type()) { + case QEvent::TouchBegin: + d->wasTouched = true; + d->setScrollBarsInteractive(false); + return false; + + case QEvent::TouchEnd: + d->wasTouched = false; + return false; + + case QEvent::MouseButtonPress: + // NOTE: Flickable does not handle touch events, only synthesized mouse events + if (static_cast<QMouseEvent *>(event)->source() == Qt::MouseEventNotSynthesized) { + d->wasTouched = false; + d->setScrollBarsInteractive(true); + return false; + } + return !d->wasTouched && item == d->flickable; + + case QEvent::MouseMove: + case QEvent::MouseButtonRelease: + if (static_cast<QMouseEvent *>(event)->source() == Qt::MouseEventNotSynthesized) + return item == d->flickable; + break; + + case QEvent::HoverEnter: + case QEvent::HoverMove: + if (d->wasTouched && (item == d->verticalScrollBar() || item == d->horizontalScrollBar())) + d->setScrollBarsInteractive(true); + break; + + default: + break; + } + + return false; +} + +bool QQuickScrollView::eventFilter(QObject *object, QEvent *event) +{ + Q_D(QQuickScrollView); + if (event->type() == QEvent::Wheel) { + d->setScrollBarsInteractive(true); + if (!d->wheelEnabled) + return true; + } + return QQuickControl::eventFilter(object, event); +} + +void QQuickScrollView::keyPressEvent(QKeyEvent *event) +{ + Q_D(QQuickScrollView); + QQuickControl::keyPressEvent(event); + switch (event->key()) { + case Qt::Key_Up: + if (QQuickScrollBar *vbar = d->verticalScrollBar()) { + vbar->decrease(); + event->accept(); + } + break; + case Qt::Key_Down: + if (QQuickScrollBar *vbar = d->verticalScrollBar()) { + vbar->increase(); + event->accept(); + } + break; + case Qt::Key_Left: + if (QQuickScrollBar *hbar = d->horizontalScrollBar()) { + hbar->decrease(); + event->accept(); + } + break; + case Qt::Key_Right: + if (QQuickScrollBar *hbar = d->horizontalScrollBar()) { + hbar->increase(); + event->accept(); + } + break; + default: + event->ignore(); + break; + } +} + +void QQuickScrollView::componentComplete() +{ + Q_D(QQuickScrollView); + QQuickControl::componentComplete(); + if (!d->contentItem) + d->ensureFlickable(); +} + +void QQuickScrollView::contentItemChange(QQuickItem *newItem, QQuickItem *oldItem) +{ + Q_D(QQuickScrollView); + QQuickControl::contentItemChange(newItem, oldItem); + d->setFlickable(qobject_cast<QQuickFlickable *>(newItem), false); +} + +#ifndef QT_NO_ACCESSIBILITY +QAccessible::Role QQuickScrollView::accessibleRole() const +{ + return QAccessible::Pane; +} +#endif + +QT_END_NAMESPACE diff --git a/src/quicktemplates2/qquickscrollview_p.h b/src/quicktemplates2/qquickscrollview_p.h new file mode 100644 index 00000000..353f2fc9 --- /dev/null +++ b/src/quicktemplates2/qquickscrollview_p.h @@ -0,0 +1,105 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKSCROLLVIEW_P_H +#define QQUICKSCROLLVIEW_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtQuickTemplates2/private/qquickcontrol_p.h> +#include <QtQml/qqmllist.h> + +QT_BEGIN_NAMESPACE + +class QQuickScrollViewPrivate; + +class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickScrollView : public QQuickControl +{ + Q_OBJECT + Q_PROPERTY(qreal contentWidth READ contentWidth WRITE setContentWidth NOTIFY contentWidthChanged FINAL) + Q_PROPERTY(qreal contentHeight READ contentHeight WRITE setContentHeight NOTIFY contentHeightChanged FINAL) + Q_PROPERTY(QQmlListProperty<QObject> contentData READ contentData FINAL) + Q_PROPERTY(QQmlListProperty<QQuickItem> contentChildren READ contentChildren NOTIFY contentChildrenChanged FINAL) + Q_CLASSINFO("DefaultProperty", "contentData") + +public: + explicit QQuickScrollView(QQuickItem *parent = nullptr); + + qreal contentWidth() const; + void setContentWidth(qreal width); + + qreal contentHeight() const; + void setContentHeight(qreal height); + + QQmlListProperty<QObject> contentData(); + QQmlListProperty<QQuickItem> contentChildren(); + +Q_SIGNALS: + void contentWidthChanged(); + void contentHeightChanged(); + void contentChildrenChanged(); + +protected: + bool childMouseEventFilter(QQuickItem *item, QEvent *event) override; + bool eventFilter(QObject *object, QEvent *event) override; + void keyPressEvent(QKeyEvent *event) override; + + void componentComplete() override; + void contentItemChange(QQuickItem *newItem, QQuickItem *oldItem) override; + +#ifndef QT_NO_ACCESSIBILITY + QAccessible::Role accessibleRole() const override; +#endif + +private: + Q_DISABLE_COPY(QQuickScrollView) + Q_DECLARE_PRIVATE(QQuickScrollView) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickScrollView) + +#endif // QQUICKSCROLLVIEW_P_H diff --git a/src/quicktemplates2/qquicktextarea.cpp b/src/quicktemplates2/qquicktextarea.cpp index fb006c1b..573521cf 100644 --- a/src/quicktemplates2/qquicktextarea.cpp +++ b/src/quicktemplates2/qquicktextarea.cpp @@ -38,6 +38,7 @@ #include "qquicktextarea_p_p.h" #include "qquickcontrol_p.h" #include "qquickcontrol_p_p.h" +#include "qquickscrollview_p.h" #include <QtQml/qqmlinfo.h> #include <QtQuick/private/qquickitem_p.h> @@ -80,14 +81,13 @@ QT_BEGIN_NAMESPACE \section2 Scrollable TextArea If you want to make a TextArea scrollable, for example, when it covers - an entire application page, attach it to a \l Flickable and combine with a - \l ScrollBar or \l ScrollIndicator. + an entire application page, it can be placed inside a \l ScrollView. - \image qtquickcontrols2-textarea-flickable.png + \image qtquickcontrols2-textarea-scrollable.png - \snippet qtquickcontrols2-textarea-flickable.qml 1 + \snippet qtquickcontrols2-textarea-scrollable.qml 1 - A TextArea that is attached to a \l Flickable does the following: + A TextArea that is placed inside a \l ScrollView does the following: \list \li Sets the content size automatically @@ -603,6 +603,13 @@ void QQuickTextArea::itemChange(QQuickItem::ItemChange change, const QQuickItem: d->resolveFont(); if (!d->explicitHoverEnabled) d->updateHoverEnabled(QQuickControlPrivate::calcHoverEnabled(d->parentItem), false); // explicit=false + + QQuickFlickable *flickable = qobject_cast<QQuickFlickable *>(value.item->parentItem()); + if (flickable) { + QQuickScrollView *scrollView = qobject_cast<QQuickScrollView *>(flickable->parentItem()); + if (scrollView) + d->attachFlickable(flickable); + } } } diff --git a/src/quicktemplates2/quicktemplates2.pri b/src/quicktemplates2/quicktemplates2.pri index 8ef1ef28..dfeb899d 100644 --- a/src/quicktemplates2/quicktemplates2.pri +++ b/src/quicktemplates2/quicktemplates2.pri @@ -54,6 +54,7 @@ HEADERS += \ $$PWD/qquickscrollbar_p.h \ $$PWD/qquickscrollbar_p_p.h \ $$PWD/qquickscrollindicator_p.h \ + $$PWD/qquickscrollview_p.h \ $$PWD/qquickshortcutcontext_p_p.h \ $$PWD/qquickslider_p.h \ $$PWD/qquickspinbox_p.h \ @@ -120,6 +121,7 @@ SOURCES += \ $$PWD/qquickroundbutton.cpp \ $$PWD/qquickscrollbar.cpp \ $$PWD/qquickscrollindicator.cpp \ + $$PWD/qquickscrollview.cpp \ $$PWD/qquickshortcutcontext.cpp \ $$PWD/qquickslider.cpp \ $$PWD/qquickspinbox.cpp \ diff --git a/tests/auto/controls/data/tst_popup.qml b/tests/auto/controls/data/tst_popup.qml index 6294250e..3d88e62a 100644 --- a/tests/auto/controls/data/tst_popup.qml +++ b/tests/auto/controls/data/tst_popup.qml @@ -1230,4 +1230,14 @@ TestCase { compare(control.x, (control.parent.width - control.width) / 2) compare(control.y, (control.parent.height - control.height) / 2) } + + function test_windowParent() { + var control = popupControl.createObject(applicationWindow, {width: 100, height: 100}) + verify(control) + + control.open() + verify(control.visible) + + control.destroy() + } } diff --git a/tests/auto/controls/data/tst_scrollbar.qml b/tests/auto/controls/data/tst_scrollbar.qml index cf1a8559..ab2c8cb6 100644 --- a/tests/auto/controls/data/tst_scrollbar.qml +++ b/tests/auto/controls/data/tst_scrollbar.qml @@ -307,7 +307,7 @@ TestCase { } function test_warning() { - ignoreWarning(Qt.resolvedUrl("tst_scrollbar.qml") + ":45:1: QML TestCase: ScrollBar must be attached to a Flickable") + ignoreWarning(Qt.resolvedUrl("tst_scrollbar.qml") + ":45:1: QML TestCase: ScrollBar must be attached to a Flickable or ScrollView") testCase.ScrollBar.vertical = null } diff --git a/tests/auto/controls/data/tst_scrollview.qml b/tests/auto/controls/data/tst_scrollview.qml new file mode 100644 index 00000000..fc9507a5 --- /dev/null +++ b/tests/auto/controls/data/tst_scrollview.qml @@ -0,0 +1,332 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.2 +import QtTest 1.0 +import QtQuick.Controls 2.2 + +TestCase { + id: testCase + width: 400 + height: 400 + visible: true + when: windowShown + name: "ScrollView" + + Component { + id: signalSpy + SignalSpy { } + } + + Component { + id: scrollView + ScrollView { } + } + + Component { + id: scrollableLabel + ScrollView { + Label { + text: "ABC" + font.pixelSize: 512 + } + } + } + + Component { + id: scrollableLabels + ScrollView { + contentHeight: label1.implicitHeight + label2.implicitHeight + label3.implicitHeight + Label { + id: label1 + text: "First" + font.pixelSize: 96 + } + Label { + id: label2 + text: "Second" + font.pixelSize: 96 + } + Label { + id: label3 + text: "Third" + font.pixelSize: 96 + } + } + } + + Component { + id: flickableLabel + ScrollView { + Flickable { + contentWidth: label.implicitWidth + contentHeight: label.implicitHeight + Label { + id: label + text: "ABC" + font.pixelSize: 512 + } + } + } + } + + Component { + id: scrollableListView + ScrollView { + ListView { + model: 3 + delegate: Label { + text: modelData + } + } + } + } + + function test_scrollBars() { + var control = createTemporaryObject(scrollView, testCase, {width: 200, height: 200}) + verify(control) + + var vertical = control.ScrollBar.vertical + verify(vertical) + + var horizontal = control.ScrollBar.horizontal + verify(horizontal) + + control.contentHeight = 400 + verify(vertical.size > 0) + compare(control.contentItem.visibleArea.heightRatio, vertical.size) + + control.contentWidth = 400 + verify(horizontal.size > 0) + compare(control.contentItem.visibleArea.widthRatio, horizontal.size) + + vertical.increase() + verify(vertical.position > 0) + compare(control.contentItem.visibleArea.yPosition, vertical.position) + + horizontal.increase() + verify(horizontal.position > 0) + compare(control.contentItem.visibleArea.xPosition, horizontal.position) + } + + function test_oneChild_data() { + return [ + { tag: "label", component: scrollableLabel }, + { tag: "flickable", component: flickableLabel } + ] + } + + function test_oneChild(data) { + var control = createTemporaryObject(data.component, testCase) + verify(control) + + var flickable = control.contentItem + verify(flickable.hasOwnProperty("contentX")) + verify(flickable.hasOwnProperty("contentY")) + + var label = flickable.contentItem.children[0] + compare(label.text, "ABC") + + compare(control.implicitWidth, label.implicitWidth) + compare(control.implicitHeight, label.implicitHeight) + + compare(control.contentWidth, label.implicitWidth) + compare(control.contentHeight, label.implicitHeight) + + compare(flickable.contentWidth, label.implicitWidth) + compare(flickable.contentHeight, label.implicitHeight) + + control.contentWidth = 200 + compare(control.implicitWidth, 200) + compare(control.contentWidth, 200) + compare(flickable.contentWidth, 200) + + control.contentHeight = 100 + compare(control.implicitHeight, 100) + compare(control.contentHeight, 100) + compare(flickable.contentHeight, 100) + } + + function test_multipleChildren() { + var control = createTemporaryObject(scrollableLabels, testCase) + verify(control) + + var flickable = control.contentItem + verify(flickable.hasOwnProperty("contentX")) + verify(flickable.hasOwnProperty("contentY")) + + compare(control.contentChildren, flickable.contentItem.children) + + var label1 = control.contentChildren[0] + compare(label1.text, "First") + + var label2 = control.contentChildren[1] + compare(label2.text, "Second") + + var label3 = control.contentChildren[2] + compare(label3.text, "Third") + + var expectedContentHeight = label1.implicitHeight + label2.implicitHeight + label3.implicitHeight + compare(control.contentHeight, expectedContentHeight) + compare(flickable.contentHeight, expectedContentHeight) + } + + function test_listView() { + var control = createTemporaryObject(scrollableListView, testCase) + verify(control) + + var listview = control.contentItem + verify(listview.hasOwnProperty("contentX")) + verify(listview.hasOwnProperty("contentY")) + verify(listview.hasOwnProperty("model")) + + compare(control.contentWidth, listview.contentWidth) + compare(control.contentHeight, listview.contentHeight) + } + + function test_mouse() { + var control = createTemporaryObject(scrollView, testCase, {width: 200, height: 200, contentHeight: 400}) + verify(control) + + mousePress(control, control.width / 2, control.height / 2, Qt.LeftButton) + compare(control.contentItem.contentY, 0) + + for (var y = control.height / 2; y >= 0; --y) { + mouseMove(control, control.width / 2, y, 10) + compare(control.contentItem.contentY, 0) + } + + mouseRelease(control, control.width / 2, 0, Qt.LeftButton) + compare(control.contentItem.contentY, 0) + } + + function test_hover() { + var control = createTemporaryObject(scrollView, testCase, {width: 200, height: 200, contentHeight: 400}) + verify(control) + + var vertical = control.ScrollBar.vertical + verify(vertical) + vertical.hoverEnabled = true + + mouseMove(vertical, vertical.width / 2, vertical.height / 2) + compare(vertical.visible, true) + compare(vertical.hovered, true) + compare(vertical.active, true) + compare(vertical.interactive, true) + } + + function test_wheel() { + var control = createTemporaryObject(scrollView, testCase, {width: 200, height: 200, contentHeight: 400}) + verify(control) + + var vertical = control.ScrollBar.vertical + verify(vertical) + + mouseWheel(control, control.width / 2, control.height / 2, 0, -120) + compare(vertical.visible, true) + compare(vertical.active, true) + compare(vertical.interactive, true) + } + + function test_touch() { + var control = createTemporaryObject(scrollView, testCase, {width: 200, height: 200, contentHeight: 400}) + verify(control) + + var vertical = control.ScrollBar.vertical + verify(vertical) + + var touch = touchEvent(control) + touch.press(0, control, control.width / 2, control.height / 2).commit() + compare(control.contentItem.contentY, 0) + + compare(vertical.active, false) + compare(vertical.interactive, false) + + var maxContentY = 0 + for (var y = control.height / 2; y >= 0; --y) { + touch.move(0, control, control.width / 2, y).commit() + maxContentY = Math.max(maxContentY, control.contentItem.contentY) + } + verify(maxContentY > 0) + + compare(vertical.active, true) + compare(vertical.interactive, false) + + touch.release(0, control, control.width / 2, 0).commit() + } + + function test_keys() { + var control = createTemporaryObject(scrollView, testCase, {width: 200, height: 200, contentWidth: 400, contentHeight: 400}) + verify(control) + + control.forceActiveFocus() + verify(control.activeFocus) + + var vertical = control.ScrollBar.vertical + verify(vertical) + + compare(vertical.position, 0.0) + for (var i = 1; i <= 10; ++i) { + keyClick(Qt.Key_Down) + compare(vertical.position, Math.min(0.5, i * 0.1)) + } + compare(vertical.position, 0.5) + for (i = 1; i <= 10; ++i) { + keyClick(Qt.Key_Up) + compare(vertical.position, Math.max(0.0, 0.5 - i * 0.1)) + } + compare(vertical.position, 0.0) + + var horizontal = control.ScrollBar.horizontal + verify(horizontal) + + compare(horizontal.position, 0.0) + for (i = 1; i <= 10; ++i) { + keyClick(Qt.Key_Right) + compare(horizontal.position, Math.min(0.5, i * 0.1)) + } + compare(horizontal.position, 0.5) + for (i = 1; i <= 10; ++i) { + keyClick(Qt.Key_Left) + compare(horizontal.position, Math.max(0.0, 0.5 - i * 0.1)) + } + compare(horizontal.position, 0.0) + } +} |