aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMitch Curtis <mitch.curtis@qt.io>2018-08-02 14:49:13 +0200
committerMitch Curtis <mitch.curtis@qt.io>2018-11-13 12:51:04 +0000
commited87e837ffae06a5770891aa49e1f82c8b58eeec (patch)
tree857bd7d0026b37fd6e0a741791fdfc280108163d
parent1425995a8b0e7e35b90bf93c3f9d4fe4e7e2b887 (diff)
Add SplitView
SplitView is an important tool for desktop applications that do not want to use a dock widget-style approach for their user interface. It allows users to have some degree of control over the sizing of elements in the UI, as well as the ability to conveniently serialize those sizes so that they're remembered across sessions. The main differences between this and the SplitView in Qt Quick Controls 1 are: - Has its own SplitView attached properties, rather than relying on the Layout attached properties (which required an additional import). - Uses the attached preferredWidth and preferredHeight properties as well as Item's implicitWidth/implicitHeight properties for the preferred size of items, rather than using the width and height properties. - Inherits from Container, so supports most of its API (though some parts of the API, like the currentIndex-related stuff, make no sense for SplitView). - Uses attached SplitHandle properties for the handle delegate to visualize hovered/pressed effects. - Offers convenience API for serializing the user's preferred sizes. [ChangeLog][Controls][SplitView] Introduced SplitView, a control that lays out items horizontally or vertically with a draggable splitter between each item. Task-number: QTBUG-56318 Change-Id: I3da91643ab312eb9ef5b0567da4e758f17747192 Reviewed-by: Richard Moe Gustavsen <richard.gustavsen@qt.io>
-rw-r--r--src/imports/controls/SplitView.qml55
-rw-r--r--src/imports/controls/controls.pri1
-rw-r--r--src/imports/controls/doc/images/qtquickcontrols2-splitview-custom.pngbin0 -> 383 bytes
-rw-r--r--src/imports/controls/doc/snippets/qtquickcontrols2-splitview-custom.qml57
-rw-r--r--src/imports/controls/doc/src/qtquickcontrols2-customize.qdoc9
-rw-r--r--src/imports/controls/fusion/SplitView.qml56
-rw-r--r--src/imports/controls/fusion/fusion.pri1
-rw-r--r--src/imports/controls/imagine/SplitView.qml63
-rw-r--r--src/imports/controls/imagine/design/imagine.sketchbin293192 -> 164993 bytes
-rw-r--r--src/imports/controls/imagine/images/splitview-handle-disabled.pngbin0 -> 93 bytes
-rw-r--r--src/imports/controls/imagine/images/splitview-handle-disabled@2x.pngbin0 -> 99 bytes
-rw-r--r--src/imports/controls/imagine/images/splitview-handle-disabled@3x.pngbin0 -> 112 bytes
-rw-r--r--src/imports/controls/imagine/images/splitview-handle-disabled@4x.pngbin0 -> 130 bytes
-rw-r--r--src/imports/controls/imagine/images/splitview-handle-hovered.pngbin0 -> 93 bytes
-rw-r--r--src/imports/controls/imagine/images/splitview-handle-hovered@2x.pngbin0 -> 99 bytes
-rw-r--r--src/imports/controls/imagine/images/splitview-handle-hovered@3x.pngbin0 -> 112 bytes
-rw-r--r--src/imports/controls/imagine/images/splitview-handle-hovered@4x.pngbin0 -> 130 bytes
-rw-r--r--src/imports/controls/imagine/images/splitview-handle-pressed.pngbin0 -> 93 bytes
-rw-r--r--src/imports/controls/imagine/images/splitview-handle-pressed@2x.pngbin0 -> 99 bytes
-rw-r--r--src/imports/controls/imagine/images/splitview-handle-pressed@3x.pngbin0 -> 112 bytes
-rw-r--r--src/imports/controls/imagine/images/splitview-handle-pressed@4x.pngbin0 -> 129 bytes
-rw-r--r--src/imports/controls/imagine/images/splitview-handle.pngbin0 -> 93 bytes
-rw-r--r--src/imports/controls/imagine/images/splitview-handle@2x.pngbin0 -> 99 bytes
-rw-r--r--src/imports/controls/imagine/images/splitview-handle@3x.pngbin0 -> 112 bytes
-rw-r--r--src/imports/controls/imagine/images/splitview-handle@4x.pngbin0 -> 130 bytes
-rw-r--r--src/imports/controls/imagine/imagine.pri1
-rw-r--r--src/imports/controls/material/SplitView.qml74
-rw-r--r--src/imports/controls/material/material.pri1
-rw-r--r--src/imports/controls/qtquickcontrols2plugin.cpp6
-rw-r--r--src/imports/controls/universal/SplitView.qml56
-rw-r--r--src/imports/controls/universal/universal.pri1
-rw-r--r--src/imports/templates/qtquicktemplates2plugin.cpp6
-rw-r--r--src/quicktemplates2/qquicksplitview.cpp1970
-rw-r--r--src/quicktemplates2/qquicksplitview_p.h218
-rw-r--r--src/quicktemplates2/qquicksplitview_p_p.h173
-rw-r--r--src/quicktemplates2/quicktemplates2.pri2
-rw-r--r--tests/auto/controls/data/splitview/fillItemInMiddle.qml77
-rw-r--r--tests/auto/controls/data/splitview/fillItemOnLeft.qml77
-rw-r--r--tests/auto/controls/data/splitview/fillItemOnTop.qml78
-rw-r--r--tests/auto/controls/data/tst_splitview.qml1749
-rw-r--r--tests/manual/testbench/controls/SplitView.qml79
-rw-r--r--tests/manual/testbench/qml.qrc1
42 files changed, 4811 insertions, 0 deletions
diff --git a/src/imports/controls/SplitView.qml b/src/imports/controls/SplitView.qml
new file mode 100644
index 00000000..1953cf69
--- /dev/null
+++ b/src/imports/controls/SplitView.qml
@@ -0,0 +1,55 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.12
+import QtQuick.Templates 2.5 as T
+import QtQuick.Controls 2.5
+import QtQuick.Controls.impl 2.5
+
+T.SplitView {
+ id: control
+ implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
+ implicitContentWidth + leftPadding + rightPadding)
+ implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
+ implicitContentHeight + topPadding + bottomPadding)
+
+ handle: Rectangle {
+ implicitWidth: control.orientation === Qt.Horizontal ? 6 : control.width
+ implicitHeight: control.orientation === Qt.Horizontal ? control.height : 6
+ color: T.SplitHandle.pressed ? control.palette.mid
+ : (T.SplitHandle.hovered ? control.palette.midlight : control.palette.button)
+ }
+}
diff --git a/src/imports/controls/controls.pri b/src/imports/controls/controls.pri
index 675fcf39..fbf5d075 100644
--- a/src/imports/controls/controls.pri
+++ b/src/imports/controls/controls.pri
@@ -53,6 +53,7 @@ QML_FILES += \
$$PWD/ScrollView.qml \
$$PWD/Slider.qml \
$$PWD/SpinBox.qml \
+ $$PWD/SplitView.qml \
$$PWD/StackView.qml \
$$PWD/SwipeDelegate.qml \
$$PWD/Switch.qml \
diff --git a/src/imports/controls/doc/images/qtquickcontrols2-splitview-custom.png b/src/imports/controls/doc/images/qtquickcontrols2-splitview-custom.png
new file mode 100644
index 00000000..7afd3ff8
--- /dev/null
+++ b/src/imports/controls/doc/images/qtquickcontrols2-splitview-custom.png
Binary files differ
diff --git a/src/imports/controls/doc/snippets/qtquickcontrols2-splitview-custom.qml b/src/imports/controls/doc/snippets/qtquickcontrols2-splitview-custom.qml
new file mode 100644
index 00000000..e202e836
--- /dev/null
+++ b/src/imports/controls/doc/snippets/qtquickcontrols2-splitview-custom.qml
@@ -0,0 +1,57 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Free Documentation License Usage
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of
+** this file. Please review the following information to ensure
+** the GNU Free Documentation License version 1.3 requirements
+** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.12
+import QtQuick.Controls 2.5
+
+Item {
+ width: 200
+ height: 100
+
+ //! [1]
+ SplitView {
+ id: splitView
+ anchors.fill: parent
+
+ handle: Rectangle {
+ implicitWidth: 4
+ implicitHeight: 4
+ color: SplitHandle.pressed ? "#81e889"
+ : (SplitHandle.hovered ? Qt.lighter("#c2f4c6", 1.1) : "#c2f4c6")
+ }
+
+ Rectangle {
+ implicitWidth: 150
+ color: "#444"
+ }
+ Rectangle {
+ implicitWidth: 50
+ color: "#666"
+ }
+ }
+ //! [1]
+}
diff --git a/src/imports/controls/doc/src/qtquickcontrols2-customize.qdoc b/src/imports/controls/doc/src/qtquickcontrols2-customize.qdoc
index cabeb528..de13be9d 100644
--- a/src/imports/controls/doc/src/qtquickcontrols2-customize.qdoc
+++ b/src/imports/controls/doc/src/qtquickcontrols2-customize.qdoc
@@ -736,6 +736,15 @@
\snippet qtquickcontrols2-spinbox-custom.qml file
+ \section2 Customizing SplitView
+
+ SplitView consists of a visual \l {SplitView::handle}{handle} delegate.
+
+ \image qtquickcontrols2-splitview-custom.png
+
+ \snippet qtquickcontrols2-splitview-custom.qml 1
+
+
\section2 Customizing StackView
StackView can have a visual \l {Control::background}{background}
diff --git a/src/imports/controls/fusion/SplitView.qml b/src/imports/controls/fusion/SplitView.qml
new file mode 100644
index 00000000..4050b28a
--- /dev/null
+++ b/src/imports/controls/fusion/SplitView.qml
@@ -0,0 +1,56 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.12
+import QtQuick.Templates 2.5 as T
+import QtQuick.Controls 2.5
+import QtQuick.Controls.impl 2.5
+import QtQuick.Controls.Fusion 2.5
+
+T.SplitView {
+ id: control
+ implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
+ implicitContentWidth + leftPadding + rightPadding)
+ implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
+ implicitContentHeight + topPadding + bottomPadding)
+
+ handle: Rectangle {
+ implicitWidth: control.orientation === Qt.Horizontal ? 2 : control.width
+ implicitHeight: control.orientation === Qt.Horizontal ? control.height : 2
+ color: T.SplitHandle.pressed ? palette.dark
+ : (T.SplitHandle.hovered ? control.palette.midlight : control.palette.mid)
+ }
+}
diff --git a/src/imports/controls/fusion/fusion.pri b/src/imports/controls/fusion/fusion.pri
index c4493c09..72978db5 100644
--- a/src/imports/controls/fusion/fusion.pri
+++ b/src/imports/controls/fusion/fusion.pri
@@ -51,6 +51,7 @@ QML_FILES += \
$$PWD/SliderGroove.qml \
$$PWD/SliderHandle.qml \
$$PWD/SpinBox.qml \
+ $$PWD/SplitView.qml \
$$PWD/SwipeDelegate.qml \
$$PWD/SwitchDelegate.qml \
$$PWD/SwitchIndicator.qml \
diff --git a/src/imports/controls/imagine/SplitView.qml b/src/imports/controls/imagine/SplitView.qml
new file mode 100644
index 00000000..a90b3a86
--- /dev/null
+++ b/src/imports/controls/imagine/SplitView.qml
@@ -0,0 +1,63 @@
+/****************************************************************************
+**
+** 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.12
+import QtQuick.Templates 2.5 as T
+import QtQuick.Controls.Imagine 2.5
+import QtQuick.Controls.Imagine.impl 2.5
+
+T.SplitView {
+ id: control
+
+ implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
+ implicitContentWidth + leftPadding + rightPadding)
+ implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
+ implicitContentHeight + topPadding + bottomPadding)
+
+ handle: NinePatchImage {
+ source: Imagine.url + "splitview-handle"
+ NinePatchImageSelector on source {
+ states: [
+ {"vertical": control.orientation === Qt.Vertical},
+ {"horizontal":control.orientation === Qt.Horizontal},
+ {"disabled": !control.enabled},
+ {"pressed": T.SplitHandle.pressed},
+ {"mirrored": control.mirrored},
+ {"hovered": T.SplitHandle.hovered}
+ ]
+ }
+ }
+}
diff --git a/src/imports/controls/imagine/design/imagine.sketch b/src/imports/controls/imagine/design/imagine.sketch
index ecb437f7..f3535b37 100644
--- a/src/imports/controls/imagine/design/imagine.sketch
+++ b/src/imports/controls/imagine/design/imagine.sketch
Binary files differ
diff --git a/src/imports/controls/imagine/images/splitview-handle-disabled.png b/src/imports/controls/imagine/images/splitview-handle-disabled.png
new file mode 100644
index 00000000..0071c196
--- /dev/null
+++ b/src/imports/controls/imagine/images/splitview-handle-disabled.png
Binary files differ
diff --git a/src/imports/controls/imagine/images/splitview-handle-disabled@2x.png b/src/imports/controls/imagine/images/splitview-handle-disabled@2x.png
new file mode 100644
index 00000000..67cee407
--- /dev/null
+++ b/src/imports/controls/imagine/images/splitview-handle-disabled@2x.png
Binary files differ
diff --git a/src/imports/controls/imagine/images/splitview-handle-disabled@3x.png b/src/imports/controls/imagine/images/splitview-handle-disabled@3x.png
new file mode 100644
index 00000000..84752ba9
--- /dev/null
+++ b/src/imports/controls/imagine/images/splitview-handle-disabled@3x.png
Binary files differ
diff --git a/src/imports/controls/imagine/images/splitview-handle-disabled@4x.png b/src/imports/controls/imagine/images/splitview-handle-disabled@4x.png
new file mode 100644
index 00000000..e4be8597
--- /dev/null
+++ b/src/imports/controls/imagine/images/splitview-handle-disabled@4x.png
Binary files differ
diff --git a/src/imports/controls/imagine/images/splitview-handle-hovered.png b/src/imports/controls/imagine/images/splitview-handle-hovered.png
new file mode 100644
index 00000000..1386d213
--- /dev/null
+++ b/src/imports/controls/imagine/images/splitview-handle-hovered.png
Binary files differ
diff --git a/src/imports/controls/imagine/images/splitview-handle-hovered@2x.png b/src/imports/controls/imagine/images/splitview-handle-hovered@2x.png
new file mode 100644
index 00000000..4708a47b
--- /dev/null
+++ b/src/imports/controls/imagine/images/splitview-handle-hovered@2x.png
Binary files differ
diff --git a/src/imports/controls/imagine/images/splitview-handle-hovered@3x.png b/src/imports/controls/imagine/images/splitview-handle-hovered@3x.png
new file mode 100644
index 00000000..2ccc1ff5
--- /dev/null
+++ b/src/imports/controls/imagine/images/splitview-handle-hovered@3x.png
Binary files differ
diff --git a/src/imports/controls/imagine/images/splitview-handle-hovered@4x.png b/src/imports/controls/imagine/images/splitview-handle-hovered@4x.png
new file mode 100644
index 00000000..6929da70
--- /dev/null
+++ b/src/imports/controls/imagine/images/splitview-handle-hovered@4x.png
Binary files differ
diff --git a/src/imports/controls/imagine/images/splitview-handle-pressed.png b/src/imports/controls/imagine/images/splitview-handle-pressed.png
new file mode 100644
index 00000000..7face6b4
--- /dev/null
+++ b/src/imports/controls/imagine/images/splitview-handle-pressed.png
Binary files differ
diff --git a/src/imports/controls/imagine/images/splitview-handle-pressed@2x.png b/src/imports/controls/imagine/images/splitview-handle-pressed@2x.png
new file mode 100644
index 00000000..ad940d0f
--- /dev/null
+++ b/src/imports/controls/imagine/images/splitview-handle-pressed@2x.png
Binary files differ
diff --git a/src/imports/controls/imagine/images/splitview-handle-pressed@3x.png b/src/imports/controls/imagine/images/splitview-handle-pressed@3x.png
new file mode 100644
index 00000000..d4e19dc2
--- /dev/null
+++ b/src/imports/controls/imagine/images/splitview-handle-pressed@3x.png
Binary files differ
diff --git a/src/imports/controls/imagine/images/splitview-handle-pressed@4x.png b/src/imports/controls/imagine/images/splitview-handle-pressed@4x.png
new file mode 100644
index 00000000..8ccbbebc
--- /dev/null
+++ b/src/imports/controls/imagine/images/splitview-handle-pressed@4x.png
Binary files differ
diff --git a/src/imports/controls/imagine/images/splitview-handle.png b/src/imports/controls/imagine/images/splitview-handle.png
new file mode 100644
index 00000000..c1dffa67
--- /dev/null
+++ b/src/imports/controls/imagine/images/splitview-handle.png
Binary files differ
diff --git a/src/imports/controls/imagine/images/splitview-handle@2x.png b/src/imports/controls/imagine/images/splitview-handle@2x.png
new file mode 100644
index 00000000..180e266a
--- /dev/null
+++ b/src/imports/controls/imagine/images/splitview-handle@2x.png
Binary files differ
diff --git a/src/imports/controls/imagine/images/splitview-handle@3x.png b/src/imports/controls/imagine/images/splitview-handle@3x.png
new file mode 100644
index 00000000..35ea51db
--- /dev/null
+++ b/src/imports/controls/imagine/images/splitview-handle@3x.png
Binary files differ
diff --git a/src/imports/controls/imagine/images/splitview-handle@4x.png b/src/imports/controls/imagine/images/splitview-handle@4x.png
new file mode 100644
index 00000000..3a1e7e9f
--- /dev/null
+++ b/src/imports/controls/imagine/images/splitview-handle@4x.png
Binary files differ
diff --git a/src/imports/controls/imagine/imagine.pri b/src/imports/controls/imagine/imagine.pri
index cb6857a5..081a509e 100644
--- a/src/imports/controls/imagine/imagine.pri
+++ b/src/imports/controls/imagine/imagine.pri
@@ -30,6 +30,7 @@ QML_FILES += \
$$PWD/ScrollIndicator.qml \
$$PWD/Slider.qml \
$$PWD/SpinBox.qml \
+ $$PWD/SplitView.qml \
$$PWD/StackView.qml \
$$PWD/SwipeDelegate.qml \
$$PWD/SwipeView.qml \
diff --git a/src/imports/controls/material/SplitView.qml b/src/imports/controls/material/SplitView.qml
new file mode 100644
index 00000000..6e5eb54d
--- /dev/null
+++ b/src/imports/controls/material/SplitView.qml
@@ -0,0 +1,74 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.12
+import QtQuick.Templates 2.5 as T
+import QtQuick.Controls 2.5
+import QtQuick.Controls.impl 2.5
+import QtQuick.Controls.Material 2.5
+
+T.SplitView {
+ id: control
+ implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
+ implicitContentWidth + leftPadding + rightPadding)
+ implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
+ implicitContentHeight + topPadding + bottomPadding)
+
+ handle: Rectangle {
+ implicitWidth: control.orientation === Qt.Horizontal ? 6 : control.width
+ implicitHeight: control.orientation === Qt.Horizontal ? control.height : 6
+ color: T.SplitHandle.pressed ? control.Material.background
+ : Qt.lighter(control.Material.background, T.SplitHandle.hovered ? 1.2 : 1.1)
+
+ Rectangle {
+ color: control.Material.secondaryTextColor
+ width: control.orientation === Qt.Horizontal ? thickness : length
+ height: control.orientation === Qt.Horizontal ? length : thickness
+ radius: thickness
+ x: (parent.width - width) / 2
+ y: (parent.height - height) / 2
+
+ property int length: parent.T.SplitHandle.pressed ? 3 : 8
+ readonly property int thickness: parent.T.SplitHandle.pressed ? 3 : 1
+
+ Behavior on length {
+ NumberAnimation {
+ duration: 100
+ }
+ }
+ }
+ }
+}
diff --git a/src/imports/controls/material/material.pri b/src/imports/controls/material/material.pri
index ab925aa2..bda1fb21 100644
--- a/src/imports/controls/material/material.pri
+++ b/src/imports/controls/material/material.pri
@@ -53,6 +53,7 @@ QML_FILES += \
$$PWD/Slider.qml \
$$PWD/SliderHandle.qml \
$$PWD/SpinBox.qml \
+ $$PWD/SplitView.qml \
$$PWD/StackView.qml \
$$PWD/SwipeDelegate.qml \
$$PWD/SwipeView.qml \
diff --git a/src/imports/controls/qtquickcontrols2plugin.cpp b/src/imports/controls/qtquickcontrols2plugin.cpp
index 754e76ab..0dd787b4 100644
--- a/src/imports/controls/qtquickcontrols2plugin.cpp
+++ b/src/imports/controls/qtquickcontrols2plugin.cpp
@@ -56,6 +56,7 @@
#include <QtQuickControls2/private/qquicktumblerview_p.h>
#endif
#include <QtQuickTemplates2/private/qquickoverlay_p.h>
+#include <QtQuickTemplates2/private/qquicksplitview_p.h>
#include <QtQuickControls2/private/qquickclippedtext_p.h>
#include <QtQuickControls2/private/qquickitemgroup_p.h>
#include <QtQuickTemplates2/private/qquicktheme_p_p.h>
@@ -186,6 +187,11 @@ void QtQuickControls2Plugin::registerTypes(const char *uri)
qmlRegisterType(resolvedUrl(QStringLiteral("MenuBarItem.qml")), uri, 2, 3, "MenuBarItem");
qmlRegisterUncreatableType<QQuickOverlay>(uri, 2, 3, "Overlay", QStringLiteral("Overlay is only available as an attached property."));
+ // QtQuick.Controls 2.5 (new types in Qt 5.12)
+ qmlRegisterType(resolvedUrl(QStringLiteral("SplitView.qml")), uri, 2, 5, "SplitView");
+ qmlRegisterUncreatableType<QQuickSplitHandleAttached>(uri, 2, 5, "SplitHandle",
+ QStringLiteral("SplitHandle is only available as an attached property."));
+
const QByteArray import = QByteArray(uri) + ".impl";
qmlRegisterModule(import, 2, QT_VERSION_MINOR - 7); // Qt 5.7->2.0, 5.8->2.1, 5.9->2.2...
diff --git a/src/imports/controls/universal/SplitView.qml b/src/imports/controls/universal/SplitView.qml
new file mode 100644
index 00000000..bbcdba9d
--- /dev/null
+++ b/src/imports/controls/universal/SplitView.qml
@@ -0,0 +1,56 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.12
+import QtQuick.Templates 2.5 as T
+import QtQuick.Controls 2.5
+import QtQuick.Controls.impl 2.5
+import QtQuick.Controls.Universal 2.5
+
+T.SplitView {
+ id: control
+ implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
+ implicitContentWidth + leftPadding + rightPadding)
+ implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
+ implicitContentHeight + topPadding + bottomPadding)
+
+ handle: Rectangle {
+ implicitWidth: control.orientation === Qt.Horizontal ? 6 : control.width
+ implicitHeight: control.orientation === Qt.Horizontal ? control.height : 6
+ color: T.SplitHandle.pressed ? control.Universal.baseMediumColor
+ : (T.SplitHandle.hovered ? control.Universal.baseMediumLowColor : control.Universal.chromeHighColor)
+ }
+}
diff --git a/src/imports/controls/universal/universal.pri b/src/imports/controls/universal/universal.pri
index 33d0dcb0..4440acbf 100644
--- a/src/imports/controls/universal/universal.pri
+++ b/src/imports/controls/universal/universal.pri
@@ -34,6 +34,7 @@ QML_FILES += \
$$PWD/ScrollIndicator.qml \
$$PWD/Slider.qml \
$$PWD/SpinBox.qml \
+ $$PWD/SplitView.qml \
$$PWD/StackView.qml \
$$PWD/SwipeDelegate.qml \
$$PWD/SwitchDelegate.qml \
diff --git a/src/imports/templates/qtquicktemplates2plugin.cpp b/src/imports/templates/qtquicktemplates2plugin.cpp
index ef7a646b..0d5765be 100644
--- a/src/imports/templates/qtquicktemplates2plugin.cpp
+++ b/src/imports/templates/qtquicktemplates2plugin.cpp
@@ -82,6 +82,7 @@
#include <QtQuickTemplates2/private/qquickshortcutcontext_p_p.h>
#include <QtQuickTemplates2/private/qquickslider_p.h>
#include <QtQuickTemplates2/private/qquickspinbox_p.h>
+#include <QtQuickTemplates2/private/qquicksplitview_p.h>
#include <QtQuickTemplates2/private/qquickstackview_p.h>
#include <QtQuickTemplates2/private/qquickswipe_p.h>
#include <QtQuickTemplates2/private/qquickswipedelegate_p.h>
@@ -337,6 +338,11 @@ void QtQuickTemplates2Plugin::registerTypes(const char *uri)
qmlRegisterType<QQuickPopupAnchors>();
qmlRegisterType<QQuickRangeSlider, 5>(uri, 2, 5, "RangeSlider");
qmlRegisterType<QQuickSlider, 5>(uri, 2, 5, "Slider");
+ qmlRegisterType<QQuickSplitView, 5>(uri, 2, 5, "SplitView");
+ qmlRegisterType<QQuickSplitViewAttached>();
+ qmlRegisterUncreatableType<QQuickSplitHandleAttached>(uri, 2, 5, "SplitHandle",
+ QStringLiteral("SplitHandle is only available as an attached property."));
+ qmlRegisterType<QQuickSplitHandleAttached>();
qmlRegisterType<QQuickSpinBox, 5>(uri, 2, 5, "SpinBox");
qmlRegisterType<QQuickTextArea, 5>(uri, 2, 5, "TextArea");
qmlRegisterType<QQuickTextField, 5>(uri, 2, 5, "TextField");
diff --git a/src/quicktemplates2/qquicksplitview.cpp b/src/quicktemplates2/qquicksplitview.cpp
new file mode 100644
index 00000000..2d75f801
--- /dev/null
+++ b/src/quicktemplates2/qquicksplitview.cpp
@@ -0,0 +1,1970 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 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 "qquicksplitview_p.h"
+#include "qquicksplitview_p_p.h"
+#include "qquickcontentitem_p.h"
+
+#include <QtCore/qdebug.h>
+#include <QtCore/qloggingcategory.h>
+#include <QtCore/qcborarray.h>
+#include <QtCore/qcbormap.h>
+#include <QtCore/qcborvalue.h>
+#include <QtQml/QQmlInfo>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \qmltype SplitView
+ \inherits Control
+ \instantiates QQuickSplitView
+ \inqmlmodule QtQuick.Controls
+ \since 5.12
+ \ingroup qtquickcontrols2-containers
+ \ingroup qtquickcontrols2-focusscopes
+ \brief Lays out items with a draggable splitter between each item
+
+ SplitView is a control that lays out items horizontally or vertically with
+ a draggable splitter between each item.
+
+ SplitView supports the following attached properties on items it manages:
+
+ \list
+ \li \l SplitView.minimumWidth
+ \li \l SplitView.minimumHeight
+ \li \l SplitView.preferredWidth
+ \li \l SplitView.preferredHeight
+ \li \l SplitView.maximumWidth
+ \li \l SplitView.maximumHeight
+ \li \l SplitView.fillWidth (true for only one child)
+ \li \l SplitView.fillHeight (true for only one child)
+ \endlist
+
+ In addition, each handle has the following read-only attached properties:
+
+ \list
+ \li \l SplitHandle.hovered
+ \li \l SplitHandle.pressed
+ \endlist
+
+ The preferred size of items in a SplitView can be specified via
+ \l {Item::}{implicitWidth} and \l {Item::}{implicitHeight} or
+ \c SplitView.preferredWidth and \c SplitView.preferredHeight:
+
+ \code
+ SplitView {
+ anchors.fill: parent
+
+ Item {
+ SplitView.preferredWidth: 50
+ }
+
+ // ...
+ }
+ \endcode
+
+ For a horizontal SplitView, it's not necessary to specify the preferred
+ height of each item, as they will be resized to the height of the view.
+ This applies in reverse for vertical views.
+
+ When a split handle is dragged, the \c SplitView.preferredWidth or
+ \c SplitView.preferredHeight property is overwritten, depending on the
+ \l orientation of the view.
+
+ To limit the size of items in a horizontal view, use the following
+ properties:
+
+ \code
+ SplitView {
+ anchors.fill: parent
+
+ Item {
+ SplitView.minimumWidth: 25
+ SplitView.preferredWidth: 50
+ SplitView.maximumWidth: 100
+ }
+
+ // ...
+ }
+ \endcode
+
+ To limit the size of items in a vertical view, use the following
+ properties:
+
+ \code
+ SplitView {
+ anchors.fill: parent
+ orientation: Qt.Vertical
+
+ Item {
+ SplitView.minimumHeight: 25
+ SplitView.preferredHeight: 50
+ SplitView.maximumHeight: 100
+ }
+
+ // ...
+ }
+ \endcode
+
+ There will always be one item (the fill item) in the SplitView that has
+ \c SplitView.fillWidth set to \c true (or \c SplitView.fillHeight, if
+ \l orientation is \c Qt.Vertical). This means that the item will get all
+ leftover space when other items have been laid out. By default, the last
+ visible child of the SplitView will have this set, but it can be changed by
+ explicitly setting \c fillWidth to \c true on another item.
+
+ A handle can belong to the item either on the left or top side, or on the
+ right or bottom side:
+
+ \list
+ \li If the fill item is to the right: the handle belongs to the left
+ item.
+ \li If the fill item is on the left: the handle belongs to the right
+ item.
+ \endlist
+
+ To create a SplitView with three items, and let the center item get
+ superfluous space, one could do the following:
+
+ \code
+ SplitView {
+ anchors.fill: parent
+ orientation: Qt.Horizontal
+
+ Rectangle {
+ width: 200
+ SplitView.maximumWidth: 400
+ color: "lightblue"
+ Label {
+ text: "View 1"
+ anchors.centerIn: parent
+ }
+ }
+ Rectangle {
+ id: centerItem
+ SplitView.minimumWidth: 50
+ SplitView.fillWidth: true
+ color: "lightgray"
+ Label {
+ text: "View 2"
+ anchors.centerIn: parent
+ }
+ }
+ Rectangle {
+ width: 200
+ color: "lightgreen"
+ Label {
+ text: "View 3"
+ anchors.centerIn: parent
+ }
+ }
+ }
+ \endcode
+
+ \section1 Serializing SplitView's State
+
+ The main purpose of SplitView is to allow users to easily configure the
+ size of various UI elements. In addition, the user's preferred sizes should
+ be remembered across sessions. To achieve this, the values of the \c
+ SplitView.preferredWidth and \c SplitView.preferredHeight properties can be
+ serialized using the \l saveState() and \l restoreState() functions:
+
+ \code
+ import QtQuick.Controls 2.5
+ import Qt.labs.settings 1.0
+
+ ApplicationWindow {
+ // ...
+
+ Component.onCompleted: splitView.restoreState(settings.splitView)
+ Component.onDestruction: settings.splitView = splitView.saveState()
+
+ Settings {
+ id: settings
+ property var splitView
+ }
+
+ SplitView {
+ id: splitView
+ // ...
+ }
+ }
+ \endcode
+
+ Alternatively, the \l {Settings::}{value()} and \l {Settings::}{setValue()}
+ functions of \l Settings can be used:
+
+ \code
+ import QtQuick.Controls 2.5
+ import Qt.labs.settings 1.0
+
+ ApplicationWindow {
+ // ...
+
+ Component.onCompleted: splitView.restoreState(settings.value("ui/splitview"))
+ Component.onDestruction: settings.setValue("ui/splitview", splitView.saveState())
+
+ Settings {
+ id: settings
+ }
+
+ SplitView {
+ id: splitView
+ // ...
+ }
+ }
+ \endcode
+
+ As the state is saved into a string, it can be written to any kind of file.
+
+ \sa SplitHandle, {Customizing SplitView}, {Container Controls}
+*/
+
+Q_LOGGING_CATEGORY(qlcQQuickSplitView, "qt.quick.controls.splitview")
+Q_LOGGING_CATEGORY(qlcQQuickSplitViewMouse, "qt.quick.controls.splitview.mouse")
+Q_LOGGING_CATEGORY(qlcQQuickSplitViewState, "qt.quick.controls.splitview.state")
+
+void QQuickSplitViewPrivate::updateFillIndex()
+{
+ const int count = contentModel->count();
+ const bool horizontal = isHorizontal();
+
+ qCDebug(qlcQQuickSplitView) << "looking for fillWidth/Height item amongst" << count << "items";
+
+ m_fillIndex = -1;
+ int i = 0;
+ int lastVisibleIndex = -1;
+ for (; i < count; ++i) {
+ QQuickItem *item = qobject_cast<QQuickItem*>(contentModel->object(i));
+ if (!item->isVisible())
+ continue;
+
+ lastVisibleIndex = i;
+
+ const QQuickSplitViewAttached *attached = qobject_cast<QQuickSplitViewAttached*>(
+ qmlAttachedPropertiesObject<QQuickSplitView>(item, false));
+ if (!attached)
+ continue;
+
+ if ((horizontal && attached->fillWidth()) || (!horizontal && attached->fillHeight())) {
+ m_fillIndex = i;
+ qCDebug(qlcQQuickSplitView) << "found fillWidth/Height item at index" << m_fillIndex;
+ break;
+ }
+ }
+
+ if (m_fillIndex == -1) {
+ // If there was no item with fillWidth/fillHeight set, m_fillIndex will be -1,
+ // and we'll set it to the last visible item.
+ // If there was an item with fillWidth/fillHeight set, we were already done and this will be skipped.
+ m_fillIndex = lastVisibleIndex != -1 ? lastVisibleIndex : count - 1;
+ qCDebug(qlcQQuickSplitView) << "found no fillWidth/Height item; using last item at index" << m_fillIndex;
+ }
+}
+
+/*
+ Resizes split items according to their preferred size and any constraints.
+
+ If a split item is being resized due to a split handle being dragged,
+ it will be resized accordingly.
+
+ Items that aren't visible are skipped.
+*/
+void QQuickSplitViewPrivate::layoutResizeSplitItems(qreal &usedWidth, qreal &usedHeight, int &indexBeingResizedDueToDrag)
+{
+ const int count = contentModel->count();
+ const bool horizontal = isHorizontal();
+ for (int index = 0; index < count; ++index) {
+ QQuickItem *item = qobject_cast<QQuickItem*>(contentModel->object(index));
+ if (!item->isVisible()) {
+ // The item is not visible, so skip it.
+ qCDebug(qlcQQuickSplitView).nospace() << " - " << index << ": split item " << item
+ << " at index " << index << " is not visible; skipping it and its handles (if any)";
+ continue;
+ }
+
+ const QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
+ QQuickSplitViewAttached *attached = qobject_cast<QQuickSplitViewAttached*>(
+ qmlAttachedPropertiesObject<QQuickSplitView>(item, false));
+ const auto sizeData = effectiveSizeData(itemPrivate, attached);
+
+ const bool resizeLeftItem = m_fillIndex > m_pressedHandleIndex;
+ // True if any handle is pressed.
+ const bool isAHandlePressed = m_pressedHandleIndex != -1;
+ // True if this particular item is being resized as a result of a handle being dragged.
+ const bool isBeingResized = isAHandlePressed && ((resizeLeftItem && index == m_pressedHandleIndex)
+ || (!resizeLeftItem && index == m_pressedHandleIndex + 1));
+ if (isBeingResized) {
+ indexBeingResizedDueToDrag = index;
+ qCDebug(qlcQQuickSplitView).nospace() << " - " << index << ": dragging handle for item";
+ }
+
+ const qreal size = horizontal ? width : height;
+ qreal requestedSize = 0;
+ if (isBeingResized) {
+ // Don't let the mouse go past either edge of the SplitView.
+ const qreal clampedMousePos = horizontal
+ ? qBound(0.0, m_mousePos.x(), width)
+ : qBound(0.0, m_mousePos.y(), height);
+
+ // We also need to ensure that the item's edge doesn't go too far
+ // out and hence give the item more space than is available.
+ const int firstIndex = resizeLeftItem ? m_pressedHandleIndex + 1 : 0;
+ const int lastIndex = resizeLeftItem ? contentModel->count() - 1 : m_pressedHandleIndex;
+ const qreal accumulated = accumulatedSize(firstIndex, lastIndex);
+
+ const qreal mousePosRelativeToLeftHandleEdge = horizontal
+ ? m_pressPos.x() - m_handlePosBeforePress.x()
+ : m_pressPos.y() - m_handlePosBeforePress.y();
+
+ const QQuickItem *pressedHandleItem = m_handleItems.at(m_pressedHandleIndex);
+ const qreal pressedHandleSize = horizontal ? pressedHandleItem->width() : pressedHandleItem->height();
+
+ if (resizeLeftItem) {
+ // The handle shouldn't cross other handles, so use the right edge of
+ // the first handle to the left as the left edge.
+ qreal leftEdge = 0;
+ if (m_pressedHandleIndex - 1 >= 0) {
+ const QQuickItem *leftHandle = m_handleItems.at(m_pressedHandleIndex - 1);
+ leftEdge = horizontal
+ ? leftHandle->x() + leftHandle->width()
+ : leftHandle->y() + leftHandle->height();
+ }
+
+ // The mouse can be clicked anywhere in the handle, and if we don't account for
+ // its position within the handle, the handle will jump when dragged.
+ const qreal pressedHandlePos = clampedMousePos - mousePosRelativeToLeftHandleEdge;
+
+ const qreal rightStop = size - accumulated - pressedHandleSize;
+ qreal leftStop = qMax(leftEdge, pressedHandlePos);
+ // qBound() doesn't care if min is greater than max, but we do.
+ if (leftStop > rightStop)
+ leftStop = rightStop;
+ const qreal newHandlePos = qBound(leftStop, pressedHandlePos, rightStop);
+ const qreal newItemSize = newHandlePos - leftEdge;
+
+ // Modify the preferredWidth, otherwise the original implicitWidth/preferredWidth
+ // will be used on the next layout (when it's no longer being resized).
+ if (!attached) {
+ // Force the attached object to be created since we rely on it.
+ attached = qobject_cast<QQuickSplitViewAttached*>(
+ qmlAttachedPropertiesObject<QQuickSplitView>(item, true));
+ }
+ if (horizontal)
+ attached->setPreferredWidth(newItemSize);
+ else
+ attached->setPreferredHeight(newItemSize);
+
+ // We still need to use requestedWidth in the setWidth() call below,
+ // because sizeData has already been calculated and now contains an old
+ // effectivePreferredWidth value.
+ requestedSize = newItemSize;
+
+ qCDebug(qlcQQuickSplitView).nospace() << " - " << index << ": resized (dragged) " << item
+ << " (clampedMousePos=" << clampedMousePos
+ << " pressedHandlePos=" << pressedHandlePos
+ << " accumulated=" << accumulated
+ << " leftEdge=" << leftEdge
+ << " leftStop=" << leftStop
+ << " rightStop=" << rightStop
+ << " newHandlePos=" << newHandlePos
+ << " newItemSize=" << newItemSize << ")";
+ } else { // Resizing the item on the right.
+ // The handle shouldn't cross other handles, so use the left edge of
+ // the first handle to the right as the right edge.
+ qreal rightEdge = size;
+ if (m_pressedHandleIndex + 1 < m_handleItems.size()) {
+ const QQuickItem *rightHandle = m_handleItems.at(m_pressedHandleIndex + 1);
+ rightEdge = horizontal ? rightHandle->x() : rightHandle->y();
+ }
+
+ // The mouse can be clicked anywhere in the handle, and if we don't account for
+ // its position within the handle, the handle will jump when dragged.
+ const qreal pressedHandlePos = clampedMousePos - mousePosRelativeToLeftHandleEdge;
+
+ const qreal leftStop = accumulated - pressedHandleSize;
+ qreal rightStop = qMin(rightEdge - pressedHandleSize, pressedHandlePos);
+ // qBound() doesn't care if min is greater than max, but we do.
+ if (rightStop < leftStop)
+ rightStop = leftStop;
+ const qreal newHandlePos = qBound(leftStop, pressedHandlePos, rightStop);
+ const qreal newItemSize = rightEdge - (newHandlePos + pressedHandleSize);
+
+ // Modify the preferredWidth, otherwise the original implicitWidth/preferredWidth
+ // will be used on the next layout (when it's no longer being resized).
+ if (!attached) {
+ // Force the attached object to be created since we rely on it.
+ attached = qobject_cast<QQuickSplitViewAttached*>(
+ qmlAttachedPropertiesObject<QQuickSplitView>(item, true));
+ }
+ if (horizontal)
+ attached->setPreferredWidth(newItemSize);
+ else
+ attached->setPreferredHeight(newItemSize);
+
+ // We still need to use requestedWidth in the setWidth() call below,
+ // because sizeData has already been calculated and now contains an old
+ // effectivePreferredWidth value.
+ requestedSize = newItemSize;
+
+ qCDebug(qlcQQuickSplitView).nospace() << " - " << index << ": resized (dragged) " << item
+ << " (clampedMousePos=" << clampedMousePos
+ << " pressedHandlePos=" << pressedHandlePos
+ << " accumulated=" << accumulated
+ << " leftEdge=" << rightEdge
+ << " leftStop=" << leftStop
+ << " rightStop=" << rightStop
+ << " newHandlePos=" << newHandlePos
+ << " newItemSize=" << newItemSize << ")";
+ }
+ } else if (index != m_fillIndex) {
+ // No handle is being dragged and we're not the fill item,
+ // so set our preferred size as we normally would.
+ requestedSize = horizontal
+ ? sizeData.effectivePreferredWidth : sizeData.effectivePreferredHeight;
+ }
+
+ if (index != m_fillIndex) {
+ if (horizontal) {
+ item->setWidth(qBound(
+ sizeData.effectiveMinimumWidth,
+ requestedSize,
+ sizeData.effectiveMaximumWidth));
+ item->setHeight(height);
+ } else {
+ item->setWidth(width);
+ item->setHeight(qBound(
+ sizeData.effectiveMinimumHeight,
+ requestedSize,
+ sizeData.effectiveMaximumHeight));
+ }
+
+ qCDebug(qlcQQuickSplitView).nospace() << " - " << index << ": resized split item " << item
+ << " (effective"
+ << " minW=" << sizeData.effectiveMinimumWidth
+ << ", minH=" << sizeData.effectiveMinimumHeight
+ << ", prfW=" << sizeData.effectivePreferredWidth
+ << ", prfH=" << sizeData.effectivePreferredHeight
+ << ", maxW=" << sizeData.effectiveMaximumWidth
+ << ", maxH=" << sizeData.effectiveMaximumHeight << ")";
+
+ // Keep track of how much space has been used so far.
+ if (horizontal)
+ usedWidth += item->width();
+ else
+ usedHeight += item->height();
+ } else if (indexBeingResizedDueToDrag != m_fillIndex) {
+ // The fill item is resized afterwards, outside of the loop.
+ qCDebug(qlcQQuickSplitView).nospace() << " - " << index << ": skipping fill item as we resize it last";
+ }
+
+ // Also account for the size of the handle for this item (if any).
+ // We do this for the fill item too, which is why it's outside of the check above.
+ if (index < count - 1 && m_handle) {
+ QQuickItem *handleItem = m_handleItems.at(index);
+ // The handle for an item that's not visible will usually already be skipped
+ // with the item visibility check higher up, but if the view looks like this
+ // [ visible ] | [ visible (fill) ] | [ hidden ]
+ // ^
+ // hidden
+ // and we're iterating over the second item (which is visible but has no handle),
+ // we need to add an extra check for it to avoid it still taking up space.
+ if (handleItem->isVisible()) {
+ if (horizontal) {
+ qCDebug(qlcQQuickSplitView).nospace() << " - " << index
+ << ": handle takes up " << handleItem->width() << " width";
+ usedWidth += handleItem->width();
+ } else {
+ qCDebug(qlcQQuickSplitView).nospace() << " - " << index
+ << ": handle takes up " << handleItem->height() << " height";
+ usedHeight += handleItem->height();
+ }
+ } else {
+ qCDebug(qlcQQuickSplitView).nospace() << " - " << index << ": handle is not visible; skipping it";
+ }
+ }
+ }
+}
+
+/*
+ Resizes the fill item by giving it the remaining space
+ after all other items have been resized.
+
+ Items that aren't visible are skipped.
+*/
+void QQuickSplitViewPrivate::layoutResizeFillItem(QQuickItem *fillItem,
+ qreal &usedWidth, qreal &usedHeight, int indexBeingResizedDueToDrag)
+{
+ // Only bother resizing if it it's visible. Also, if it's being resized due to a drag,
+ // then we've already set its size in layoutResizeSplitItems(), so no need to do it here.
+ if (!fillItem->isVisible() || indexBeingResizedDueToDrag == m_fillIndex) {
+ qCDebug(qlcQQuickSplitView).nospace() << m_fillIndex << ": - fill item " << fillItem
+ << " is not visible or was already resized due to a drag;"
+ << " skipping it and its handles (if any)";
+ return;
+ }
+
+ const QQuickItemPrivate *fillItemPrivate = QQuickItemPrivate::get(fillItem);
+ const QQuickSplitViewAttached *attached = qobject_cast<QQuickSplitViewAttached*>(
+ qmlAttachedPropertiesObject<QQuickSplitView>(fillItem, false));
+ const auto fillSizeData = effectiveSizeData(fillItemPrivate, attached);
+ if (isHorizontal()) {
+ fillItem->setWidth(qBound(
+ fillSizeData.effectiveMinimumWidth,
+ width - usedWidth,
+ fillSizeData.effectiveMaximumWidth));
+ fillItem->setHeight(height);
+ } else {
+ fillItem->setWidth(width);
+ fillItem->setHeight(qBound(
+ fillSizeData.effectiveMinimumHeight,
+ height - usedHeight,
+ fillSizeData.effectiveMaximumHeight));
+ }
+
+ qCDebug(qlcQQuickSplitView).nospace() << " - " << m_fillIndex
+ << ": resized split fill item " << fillItem << " (effective"
+ << " minW=" << fillSizeData.effectiveMinimumWidth
+ << ", minH=" << fillSizeData.effectiveMinimumHeight
+ << ", maxW=" << fillSizeData.effectiveMaximumWidth
+ << ", maxH=" << fillSizeData.effectiveMaximumHeight << ")";
+}
+
+/*
+ Positions items by laying them out in a row or column.
+
+ Items that aren't visible are skipped.
+*/
+void QQuickSplitViewPrivate::layoutPositionItems(const QQuickItem *fillItem)
+{
+ const bool horizontal = isHorizontal();
+ const int count = contentModel->count();
+ qreal usedWidth = 0;
+ qreal usedHeight = 0;
+
+ for (int i = 0; i < count; ++i) {
+ QQuickItem *item = qobject_cast<QQuickItem*>(contentModel->object(i));
+ if (!item->isVisible()) {
+ qCDebug(qlcQQuickSplitView).nospace() << " - " << i << ": split item " << item
+ << " is not visible; skipping it and its handles (if any)";
+ continue;
+ }
+
+ // Position the item.
+ if (horizontal) {
+ item->setX(usedWidth);
+ item->setY(0);
+ } else {
+ item->setX(0);
+ item->setY(usedHeight);
+ }
+
+ // Keep track of how much space has been used so far.
+ if (horizontal)
+ usedWidth += item->width();
+ else
+ usedHeight += item->height();
+
+ if (Q_UNLIKELY(qlcQQuickSplitView().isDebugEnabled())) {
+ const QQuickItemPrivate *fillItemPrivate = QQuickItemPrivate::get(fillItem);
+ const QQuickSplitViewAttached *attached = qobject_cast<QQuickSplitViewAttached*>(
+ qmlAttachedPropertiesObject<QQuickSplitView>(fillItem, false));
+ const auto sizeData = effectiveSizeData(fillItemPrivate, attached);
+ qCDebug(qlcQQuickSplitView).nospace() << " - " << i << ": positioned "
+ << (i == m_fillIndex ? "fill item " : "item ") << item << " (effective"
+ << " minW=" << sizeData.effectiveMinimumWidth
+ << ", minH=" << sizeData.effectiveMinimumHeight
+ << ", prfW=" << sizeData.effectivePreferredWidth
+ << ", prfH=" << sizeData.effectivePreferredHeight
+ << ", maxW=" << sizeData.effectiveMaximumWidth
+ << ", maxH=" << sizeData.effectiveMaximumHeight << ")";
+ }
+
+ // Position the handle for this item (if any).
+ if (i < count - 1 && m_handle) {
+ // Position the handle.
+ QQuickItem *handleItem = m_handleItems.at(i);
+ handleItem->setX(horizontal ? usedWidth : 0);
+ handleItem->setY(horizontal ? 0 : usedHeight);
+
+ if (horizontal)
+ usedWidth += handleItem->width();
+ else
+ usedHeight += handleItem->height();
+
+ qCDebug(qlcQQuickSplitView).nospace() << " - " << i << ": positioned handle " << handleItem;
+ }
+ }
+}
+
+void QQuickSplitViewPrivate::layout()
+{
+ if (!componentComplete)
+ return;
+
+ if (m_layingOut)
+ return;
+
+ const int count = contentModel->count();
+ if (count <= 0)
+ return;
+
+ Q_ASSERT_X(m_fillIndex < count, Q_FUNC_INFO, qPrintable(
+ QString::fromLatin1("m_fillIndex is %1 but our count is %2").arg(m_fillIndex).arg(count)));
+
+ Q_ASSERT_X(!m_handle || m_handleItems.size() == count - 1, Q_FUNC_INFO, qPrintable(QString::fromLatin1(
+ "Expected %1 handle items, but there are %2").arg(count - 1).arg(m_handleItems.size())));
+
+ // If a split item is being resized, its preferredWidth/Height will be changed,
+ // which will in turn call layout(). This flag avoids recursive calls.
+ QBoolBlocker guard(m_layingOut, true);
+
+ const bool horizontal = isHorizontal();
+ qCDebug(qlcQQuickSplitView) << "laying out" << count << "split items"
+ << (horizontal ? "horizontally" : "vertically") << "in SplitView" << q_func();
+
+ qreal usedWidth = 0;
+ qreal usedHeight = 0;
+ int indexBeingResizedDueToDrag = -1;
+
+ qCDebug(qlcQQuickSplitView) << " resizing:";
+
+ // First, resize the items. We need to do this first because otherwise fill
+ // items would take up all of the remaining space as soon as they are encountered.
+ layoutResizeSplitItems(usedWidth, usedHeight, indexBeingResizedDueToDrag);
+
+ qCDebug(qlcQQuickSplitView).nospace()
+ << " - (remaining width=" << width - usedWidth
+ << " remaining height=" << height - usedHeight << ")";
+
+ // Give the fill item the remaining space.
+ QQuickItem *fillItem = qobject_cast<QQuickItem*>(contentModel->object(m_fillIndex));
+ layoutResizeFillItem(fillItem, usedWidth, usedHeight, indexBeingResizedDueToDrag);
+
+ qCDebug(qlcQQuickSplitView) << " positioning:";
+
+ // Position the items.
+ layoutPositionItems(fillItem);
+
+ qCDebug(qlcQQuickSplitView).nospace() << "finished layouting";
+}
+
+void QQuickSplitViewPrivate::createHandles()
+{
+ Q_ASSERT(m_handle);
+ // A handle only makes sense if there are two items on either side.
+ if (contentModel->count() <= 1)
+ return;
+
+ // Create new handle items if there aren't enough.
+ const int count = contentModel->count() - 1;
+ qCDebug(qlcQQuickSplitView) << "creating" << count << "handles";
+ m_handleItems.reserve(count);
+ for (int i = 0; i < count; ++i)
+ createHandleItem(i);
+}
+
+void QQuickSplitViewPrivate::createHandleItem(int index)
+{
+ Q_Q(QQuickSplitView);
+ if (contentModel->count() <= 1)
+ return;
+
+ qCDebug(qlcQQuickSplitView) << "- creating handle for split item at index" << index
+ << "from handle component" << m_handle;
+
+ // If we don't use the correct context, it won't be possible to refer to
+ // the control's id from within the delegate.
+ QQmlContext *creationContext = m_handle->creationContext();
+ // The component might not have been created in QML, in which case
+ // the creation context will be null and we have to create it ourselves.
+ if (!creationContext)
+ creationContext = qmlContext(q);
+ QQmlContext *context = new QQmlContext(creationContext, q);
+ context->setContextObject(q);
+ QQuickItem *item = qobject_cast<QQuickItem*>(m_handle->beginCreate(context));
+ if (item) {
+ // Insert the item to our list of items *before* its parent is set to us,
+ // so that we can avoid it being added as a content item by checking
+ // if it is in the list in isContent().
+ m_handleItems.insert(index, item);
+
+ item->setParentItem(q);
+
+ m_handle->completeCreate();
+ resizeHandle(item);
+ }
+}
+
+void QQuickSplitViewPrivate::removeExcessHandles()
+{
+ int excess = m_handleItems.size() - qMax(0, contentModel->count() - 1);
+ for (; excess > 0; --excess) {
+ QQuickItem *handleItem = m_handleItems.takeLast();
+ delete handleItem;
+ }
+}
+
+qreal QQuickSplitViewPrivate::accumulatedSize(int firstIndex, int lastIndex) const
+{
+ qreal size = 0.0;
+ const bool horizontal = isHorizontal();
+ for (int i = firstIndex; i <= lastIndex; ++i) {
+ QQuickItem *item = qobject_cast<QQuickItem*>(contentModel->object(i));
+ if (item->isVisible()) {
+ if (i != m_fillIndex) {
+ size += horizontal ? item->width() : item->height();
+ } else {
+ // If the fill item has a minimum size specified, we must respect it.
+ const QQuickSplitViewAttached *attached = qobject_cast<QQuickSplitViewAttached*>(
+ qmlAttachedPropertiesObject<QQuickSplitView>(item, false));
+ if (attached) {
+ const QQuickSplitViewAttachedPrivate *attachedPrivate
+ = QQuickSplitViewAttachedPrivate::get(attached);
+ if (horizontal && attachedPrivate->m_isMinimumWidthSet)
+ size += attachedPrivate->m_minimumWidth;
+ else if (!horizontal && attachedPrivate->m_isMinimumHeightSet)
+ size += attachedPrivate->m_minimumHeight;
+ }
+ }
+ }
+
+ // Only add the handle's width if there's actually a handle for this split item index.
+ if (i < lastIndex || lastIndex < contentModel->count() - 1) {
+ const QQuickItem *handleItem = m_handleItems.at(i);
+ if (handleItem->isVisible())
+ size += horizontal ? handleItem->width() : handleItem->height();
+ }
+ }
+ return size;
+}
+
+qreal effectiveMinimumWidth(const QQuickSplitViewAttachedPrivate *attachedPrivate)
+{
+ return attachedPrivate && attachedPrivate->m_isMinimumWidthSet ? attachedPrivate->m_minimumWidth : 0;
+}
+
+qreal effectiveMinimumHeight(const QQuickSplitViewAttachedPrivate *attachedPrivate)
+{
+ return attachedPrivate && attachedPrivate->m_isMinimumHeightSet ? attachedPrivate->m_minimumHeight : 0;
+}
+
+qreal effectivePreferredWidth(const QQuickSplitViewAttachedPrivate *attachedPrivate,
+ const QQuickItemPrivate *itemPrivate)
+{
+ return attachedPrivate && attachedPrivate->m_isPreferredWidthSet
+ ? attachedPrivate->m_preferredWidth : itemPrivate->implicitWidth;
+}
+
+qreal effectivePreferredHeight(const QQuickSplitViewAttachedPrivate *attachedPrivate,
+ const QQuickItemPrivate *itemPrivate)
+{
+ return attachedPrivate && attachedPrivate->m_isPreferredHeightSet
+ ? attachedPrivate->m_preferredHeight : itemPrivate->implicitHeight;
+}
+
+qreal effectiveMaximumWidth(const QQuickSplitViewAttachedPrivate *attachedPrivate)
+{
+ return attachedPrivate && attachedPrivate->m_isMaximumWidthSet
+ ? attachedPrivate->m_maximumWidth : std::numeric_limits<qreal>::infinity();
+}
+
+qreal effectiveMaximumHeight(const QQuickSplitViewAttachedPrivate *attachedPrivate)
+{
+ return attachedPrivate && attachedPrivate->m_isMaximumHeightSet
+ ? attachedPrivate->m_maximumHeight : std::numeric_limits<qreal>::infinity();
+}
+
+// We don't just take an index, because the item and attached properties object
+// will both be used outside of this function by calling code, so save some
+// time by not accessing them twice.
+QQuickSplitViewPrivate::EffectiveSizeData QQuickSplitViewPrivate::effectiveSizeData(
+ const QQuickItemPrivate *itemPrivate, const QQuickSplitViewAttached *attached) const
+{
+ EffectiveSizeData data;
+ const QQuickSplitViewAttachedPrivate *attachedPrivate = attached ? QQuickSplitViewAttachedPrivate::get(attached) : nullptr;
+ data.effectiveMinimumWidth = effectiveMinimumWidth(attachedPrivate);
+ data.effectiveMinimumHeight = effectiveMinimumHeight(attachedPrivate);
+ data.effectivePreferredWidth = effectivePreferredWidth(attachedPrivate, itemPrivate);
+ data.effectivePreferredHeight = effectivePreferredHeight(attachedPrivate, itemPrivate);
+ data.effectiveMaximumWidth = effectiveMaximumWidth(attachedPrivate);
+ data.effectiveMaximumHeight = effectiveMaximumHeight(attachedPrivate);
+ return data;
+}
+
+int QQuickSplitViewPrivate::handleIndexForSplitIndex(int splitIndex) const
+{
+ // If it's the last item in the view, it doesn't have a handle, so use
+ // the handle for the previous item.
+ return splitIndex == contentModel->count() - 1 ? splitIndex - 1 : splitIndex;
+}
+
+void QQuickSplitViewPrivate::destroyHandles()
+{
+ qDeleteAll(m_handleItems);
+ m_handleItems.clear();
+}
+
+void QQuickSplitViewPrivate::resizeHandle(QQuickItem *handleItem)
+{
+ const bool horizontal = isHorizontal();
+ handleItem->setWidth(horizontal ? handleItem->implicitWidth() : width);
+ handleItem->setHeight(horizontal ? height : handleItem->implicitHeight());
+}
+
+void QQuickSplitViewPrivate::resizeHandles()
+{
+ for (QQuickItem *handleItem : m_handleItems)
+ resizeHandle(handleItem);
+}
+
+void QQuickSplitViewPrivate::updateHandleVisibilities()
+{
+ // If this is the first item that is visible, we won't have any
+ // handles yet, because we don't create a handle if we only have one item.
+ if (m_handleItems.isEmpty())
+ return;
+
+ // If the visibility/children change makes any item the last (right/bottom-most)
+ // visible item, we don't want to display a handle for it either:
+ // [ visible (fill) ] | [ hidden ] | [ hidden ]
+ // ^ ^
+ // hidden hidden
+ const int count = contentModel->count();
+ int lastVisibleItemIndex = -1;
+ for (int i = count - 1; i >= 0; --i) {
+ const QQuickItem *item = qobject_cast<QQuickItem*>(contentModel->object(i));
+ if (item->isVisible()) {
+ lastVisibleItemIndex = i;
+ break;
+ }
+ }
+
+ for (int i = 0; i < count - 1; ++i) {
+ const QQuickItem *item = qobject_cast<QQuickItem*>(contentModel->object(i));
+ QQuickItem *handleItem = m_handleItems.at(i);
+ if (i != lastVisibleItemIndex)
+ handleItem->setVisible(item->isVisible());
+ else
+ handleItem->setVisible(false);
+ qCDebug(qlcQQuickSplitView) << "set visible property of handle at index"
+ << i << "to" << handleItem->isVisible();
+ }
+}
+
+void QQuickSplitViewPrivate::setResizing(bool resizing)
+{
+ Q_Q(QQuickSplitView);
+ if (resizing == m_resizing)
+ return;
+
+ m_resizing = resizing;
+ emit q->resizingChanged();
+}
+
+bool QQuickSplitViewPrivate::isHorizontal() const
+{
+ return m_orientation == Qt::Horizontal;
+}
+
+QQuickItem *QQuickSplitViewPrivate::getContentItem()
+{
+ Q_Q(QQuickSplitView);
+ if (QQuickItem *item = QQuickContainerPrivate::getContentItem())
+ return item;
+
+ // TODO: why are several created?
+ return new QQuickContentItem(q);
+}
+
+void QQuickSplitViewPrivate::handlePress(const QPointF &point)
+{
+ Q_Q(QQuickSplitView);
+ QQuickContainerPrivate::handlePress(point);
+
+ QQuickItem *pressedItem = q->childAt(point.x(), point.y());
+ const int pressedHandleIndex = m_handleItems.indexOf(pressedItem);
+ if (pressedHandleIndex != -1) {
+ m_pressedHandleIndex = pressedHandleIndex;
+ m_pressPos = point;
+ m_mousePos = point;
+
+ const QQuickItem *leftOrTopItem = qobject_cast<QQuickItem*>(contentModel->object(m_pressedHandleIndex));
+ const QQuickItem *rightOrBottomItem = qobject_cast<QQuickItem*>(contentModel->object(m_pressedHandleIndex + 1));
+ const bool isHorizontal = m_orientation == Qt::Horizontal;
+ m_leftOrTopItemSizeBeforePress = isHorizontal ? leftOrTopItem->width() : leftOrTopItem->height();
+ m_rightOrBottomItemSizeBeforePress = isHorizontal ? rightOrBottomItem->width() : rightOrBottomItem->height();
+ m_handlePosBeforePress = pressedItem->position();
+
+ // Avoid e.g. Flickable stealing our drag if we're inside it.
+ q->setKeepMouseGrab(true);
+
+ // Force the attached object to be created since we rely on it.
+ QQuickSplitHandleAttached *handleAttached = qobject_cast<QQuickSplitHandleAttached*>(
+ qmlAttachedPropertiesObject<QQuickSplitHandleAttached>(pressedItem, true));
+ QQuickSplitHandleAttachedPrivate::get(handleAttached)->setPressed(true);
+
+ setResizing(true);
+
+ qCDebug(qlcQQuickSplitViewMouse).nospace() << "handled press -"
+ << " left/top index=" << m_pressedHandleIndex << ","
+ << " size before press=" << m_leftOrTopItemSizeBeforePress << ","
+ << " right/bottom index=" << m_pressedHandleIndex + 1 << ","
+ << " size before press=" << m_rightOrBottomItemSizeBeforePress;
+ }
+}
+
+void QQuickSplitViewPrivate::handleMove(const QPointF &point)
+{
+ QQuickContainerPrivate::handleMove(point);
+
+ if (m_pressedHandleIndex != -1) {
+ m_mousePos = point;
+ layout();
+ }
+}
+
+void QQuickSplitViewPrivate::handleRelease(const QPointF &point)
+{
+ Q_Q(QQuickSplitView);
+ QQuickContainerPrivate::handleRelease(point);
+
+ if (m_pressedHandleIndex != -1) {
+ QQuickItem *pressedHandle = m_handleItems.at(m_pressedHandleIndex);
+ QQuickSplitHandleAttached *handleAttached = qobject_cast<QQuickSplitHandleAttached*>(
+ qmlAttachedPropertiesObject<QQuickSplitHandleAttached>(pressedHandle, true));
+ QQuickSplitHandleAttachedPrivate::get(handleAttached)->setPressed(false);
+ }
+
+ setResizing(false);
+
+ m_pressedHandleIndex = -1;
+ m_pressPos = QPointF();
+ m_mousePos = QPointF();
+ m_handlePosBeforePress = QPointF();
+ m_leftOrTopItemSizeBeforePress = 0.0;
+ m_rightOrBottomItemSizeBeforePress = 0.0;
+ q->setKeepMouseGrab(false);
+}
+
+void QQuickSplitViewPrivate::itemVisibilityChanged(QQuickItem *item)
+{
+ const int itemIndex = contentModel->indexOf(item, nullptr);
+ Q_ASSERT(itemIndex != -1);
+
+ qCDebug(qlcQQuickSplitView) << "visible property of split item"
+ << item << "at index" << itemIndex << "changed to" << item->isVisible();
+
+ // The visibility of an item just changed, so we need to update the visibility
+ // of the corresponding handle (if one exists).
+
+ const int handleIndex = handleIndexForSplitIndex(itemIndex);
+ QQuickItem *handleItem = m_handleItems.at(handleIndex);
+ handleItem->setVisible(item->isVisible());
+
+ qCDebug(qlcQQuickSplitView) << "set visible property of handle item"
+ << handleItem << "at index" << handleIndex << "to" << item->isVisible();
+
+ updateHandleVisibilities();
+ updateFillIndex();
+ layout();
+}
+
+QQuickSplitViewPrivate *QQuickSplitViewPrivate::get(QQuickSplitView *splitView)
+{
+ return splitView->d_func();
+}
+
+QQuickSplitView::QQuickSplitView(QQuickItem *parent)
+ : QQuickContainer(*(new QQuickSplitViewPrivate), parent)
+{
+ Q_D(QQuickSplitView);
+ d->changeTypes |= QQuickItemPrivate::Visibility;
+
+ setAcceptedMouseButtons(Qt::LeftButton);
+}
+
+QQuickSplitView::QQuickSplitView(QQuickSplitViewPrivate &dd, QQuickItem *parent)
+ : QQuickContainer(dd, parent)
+{
+ Q_D(QQuickSplitView);
+ d->changeTypes |= QQuickItemPrivate::Visibility;
+
+ setAcceptedMouseButtons(Qt::LeftButton);
+}
+
+QQuickSplitView::~QQuickSplitView()
+{
+}
+
+/*!
+ \qmlproperty enumeration QtQuick.Controls::SplitView::orientation
+
+ This property holds the orientation of the SplitView.
+
+ The orientation determines how the split items are laid out:
+
+ Possible values:
+ \value Qt.Horizontal The items are laid out horizontally (default).
+ \value Qt.Vertical The items are laid out vertically (default).
+*/
+Qt::Orientation QQuickSplitView::orientation() const
+{
+ Q_D(const QQuickSplitView);
+ return d->m_orientation;
+}
+
+void QQuickSplitView::setOrientation(Qt::Orientation orientation)
+{
+ Q_D(QQuickSplitView);
+ if (orientation == d->m_orientation)
+ return;
+
+ d->m_orientation = orientation;
+ d->resizeHandles();
+ d->layout();
+ emit orientationChanged();
+}
+
+/*!
+ \qmlproperty bool QtQuick.Controls::SplitView::resizing
+ \readonly
+
+ This property is \c true when the user is resizing
+ split items by dragging on the splitter handles.
+*/
+bool QQuickSplitView::isResizing() const
+{
+ Q_D(const QQuickSplitView);
+ return d->m_resizing;
+}
+
+/*!
+ \qmlproperty Component QtQuick.Controls::SplitView::handle
+
+ This property holds the handle component.
+
+ An instance of this component will be instantiated \c {count - 1}
+ times, as long as \l count is greater than than \c {1}.
+
+ The following table explains how each handle will be resized
+ depending on the orientation of the split view:
+
+ \table
+ \header
+ \li Orientation
+ \li Handle Width
+ \li Handle Height
+ \row
+ \li \c Qt.Horizontal
+ \li \c implicitWidth
+ \li The \l height of the SplitView.
+ \row
+ \li \c Qt.Vertical
+ \li The \l width of the SplitView.
+ \li \c implicitHeight
+ \endtable
+
+ \sa {Customizing SplitView}
+*/
+QQmlComponent *QQuickSplitView::handle()
+{
+ Q_D(const QQuickSplitView);
+ return d->m_handle;
+}
+
+void QQuickSplitView::setHandle(QQmlComponent *handle)
+{
+ Q_D(QQuickSplitView);
+ if (handle == d->m_handle)
+ return;
+
+ qCDebug(qlcQQuickSplitView) << "setting handle" << handle;
+
+ if (d->m_handle)
+ d->destroyHandles();
+
+ d->m_handle = handle;
+
+ if (d->m_handle)
+ d->createHandles();
+
+ d->layout();
+
+ emit handleChanged();
+}
+
+bool QQuickSplitView::isContent(QQuickItem *item) const
+{
+ Q_D(const QQuickSplitView);
+ if (!qmlContext(item))
+ return false;
+
+ if (QQuickItemPrivate::get(item)->isTransparentForPositioner())
+ return false;
+
+ return !d->m_handleItems.contains(item);
+}
+
+QQuickSplitViewAttached *QQuickSplitView::qmlAttachedProperties(QObject *object)
+{
+ return new QQuickSplitViewAttached(object);
+}
+
+/*!
+ \qmlmethod var QtQuick.Controls::SplitView::saveState()
+
+ Saves the preferred sizes of split items into a byte array and returns it.
+
+ \sa {Serializing SplitView's State}, restoreState()
+*/
+QVariant QQuickSplitView::saveState()
+{
+ Q_D(QQuickSplitView);
+ qCDebug(qlcQQuickSplitViewState) << "saving state for split items in" << this;
+
+ // Save the preferred sizes of each split item.
+ QCborArray cborArray;
+ for (int i = 0; i < d->contentModel->count(); ++i) {
+ const QQuickItem *item = qobject_cast<QQuickItem*>(d->contentModel->object(i));
+ const QQuickSplitViewAttached *attached = qobject_cast<QQuickSplitViewAttached*>(
+ qmlAttachedPropertiesObject<QQuickSplitView>(item, false));
+ // Don't serialise stuff if we don't need to. If a split item was given a preferred
+ // size in QML or it was dragged, it will have an attached object and either
+ // m_isPreferredWidthSet or m_isPreferredHeightSet (or both) will be true,
+ // so items without these can be skipped. We write the index of each item
+ // that has data so that we know which item to set it on when restoring.
+ if (!attached)
+ continue;
+
+ const QQuickSplitViewAttachedPrivate *attachedPrivate = QQuickSplitViewAttachedPrivate::get(attached);
+ if (!attachedPrivate->m_isPreferredWidthSet && !attachedPrivate->m_isPreferredHeightSet)
+ continue;
+
+ QCborMap cborMap;
+ cborMap[QLatin1String("index")] = i;
+ if (attachedPrivate->m_isPreferredWidthSet) {
+ cborMap[QLatin1String("preferredWidth")] = static_cast<double>(attachedPrivate->m_preferredWidth);
+
+ qCDebug(qlcQQuickSplitViewState).nospace() << "- wrote preferredWidth of "
+ << attachedPrivate->m_preferredWidth << " for split item at index " << i;
+ }
+ if (attachedPrivate->m_isPreferredHeightSet) {
+ cborMap[QLatin1String("preferredHeight")] = static_cast<double>(attachedPrivate->m_preferredHeight);
+
+ qCDebug(qlcQQuickSplitViewState).nospace() << "- wrote preferredHeight of "
+ << attachedPrivate->m_preferredHeight << " for split item at index " << i;
+ }
+
+ cborArray.append(cborMap);
+ }
+
+ const QByteArray byteArray = cborArray.toCborValue().toCbor();
+ qCDebug(qlcQQuickSplitViewState) << "the resulting byte array is:" << byteArray;
+ return QVariant(byteArray);
+}
+
+/*!
+ \qmlmethod bool QtQuick.Controls::SplitView::restoreState(state)
+
+ Reads the preferred sizes from \a state and applies them to the split items.
+
+ Returns \c true if the state was successfully restored, otherwise \c false.
+
+ \sa {Serializing SplitView's State}, saveState()
+*/
+bool QQuickSplitView::restoreState(const QVariant &state)
+{
+ const QByteArray cborByteArray = state.toByteArray();
+ Q_D(QQuickSplitView);
+ if (cborByteArray.isEmpty())
+ return false;
+
+ QCborParserError parserError;
+ const QCborValue cborValue(QCborValue::fromCbor(cborByteArray, &parserError));
+ if (parserError.error != QCborError::NoError) {
+ qmlWarning(this) << "Error reading SplitView state:" << parserError.errorString();
+ return false;
+ }
+
+ qCDebug(qlcQQuickSplitViewState) << "restoring state for split items of" << this
+ << "from the following string:" << state;
+
+ const QCborArray cborArray(cborValue.toArray());
+ const int ourCount = d->contentModel->count();
+ // This could conceivably happen if items were removed from the SplitView since the state was last saved.
+ if (cborArray.size() > ourCount) {
+ qmlWarning(this) << "Error reading SplitView state: expected "
+ << ourCount << " or less split items but got " << cborArray.size();
+ return false;
+ }
+
+ for (auto it = cborArray.constBegin(); it != cborArray.constEnd(); ++it) {
+ QCborMap cborMap(it->toMap());
+ const int splitItemIndex = cborMap.value(QLatin1String("index")).toInteger();
+ const bool isPreferredWidthSet = cborMap.contains(QLatin1String("preferredWidth"));
+ const bool isPreferredHeightSet = cborMap.contains(QLatin1String("preferredWidth"));
+
+ QQuickItem *item = qobject_cast<QQuickItem*>(d->contentModel->object(splitItemIndex));
+ // If the split item does not have a preferred size specified in QML, it could still have
+ // been resized via dragging before it was saved. In this case, it won't have an
+ // attached object upon application startup, so we create it.
+ QQuickSplitViewAttached *attached = qobject_cast<QQuickSplitViewAttached*>(
+ qmlAttachedPropertiesObject<QQuickSplitView>(item, true));
+ if (isPreferredWidthSet) {
+ const qreal preferredWidth = cborMap.value(QLatin1String("preferredWidth")).toDouble();
+ attached->setPreferredWidth(preferredWidth);
+ }
+ if (isPreferredHeightSet) {
+ const qreal preferredHeight = cborMap.value(QLatin1String("preferredHeight")).toDouble();
+ attached->setPreferredHeight(preferredHeight);
+ }
+
+ const QQuickSplitViewAttachedPrivate *attachedPrivate = QQuickSplitViewAttachedPrivate::get(attached);
+ qCDebug(qlcQQuickSplitViewState).nospace()
+ << "- restored the following state for split item at index " << splitItemIndex
+ << ": preferredWidthSet=" << attachedPrivate->m_isPreferredWidthSet
+ << " preferredWidth=" << attachedPrivate->m_preferredWidth
+ << " preferredHeightSet=" << attachedPrivate->m_isPreferredHeightSet
+ << " preferredHeight=" << attachedPrivate->m_preferredHeight;
+ }
+
+ return true;
+}
+
+void QQuickSplitView::componentComplete()
+{
+ Q_D(QQuickSplitView);
+ QQuickControl::componentComplete();
+ d->resizeHandles();
+ d->updateFillIndex();
+ d->layout();
+}
+
+void QQuickSplitView::hoverMoveEvent(QHoverEvent *event)
+{
+ Q_D(QQuickSplitView);
+ QQuickContainer::hoverMoveEvent(event);
+
+ QQuickItem *hoveredItem = childAt(event->pos().x(), event->pos().y());
+ if (!hoveredItem) {
+ // No handle is hovered.
+ if (d->m_hoveredHandleIndex != -1) {
+ // The previously-hovered handle is no longer hovered.
+ QQuickItem *oldHoveredHandle = d->m_handleItems.at(d->m_hoveredHandleIndex);
+ QQuickSplitHandleAttached *handleAttached = qobject_cast<QQuickSplitHandleAttached*>(
+ qmlAttachedPropertiesObject<QQuickSplitHandleAttached>(oldHoveredHandle, true));
+ QQuickSplitHandleAttachedPrivate::get(handleAttached)->setHovered(false);
+ }
+
+ qCDebug(qlcQQuickSplitViewMouse) << "handle item at index" << d->m_hoveredHandleIndex << "is no longer hovered";
+
+ d->m_hoveredHandleIndex = -1;
+
+#if QT_CONFIG(cursor)
+ setCursor(Qt::ArrowCursor);
+#endif
+ } else {
+ // A child item of ours is hovered.
+
+ // First, clear the hovered flag of any previously-hovered handle.
+ if (d->m_hoveredHandleIndex != -1) {
+ QQuickItem *oldHoveredHandle = d->m_handleItems.at(d->m_hoveredHandleIndex);
+ QQuickSplitHandleAttached *handleAttached = qobject_cast<QQuickSplitHandleAttached*>(
+ qmlAttachedPropertiesObject<QQuickSplitHandleAttached>(oldHoveredHandle, true));
+ QQuickSplitHandleAttachedPrivate::get(handleAttached)->setHovered(false);
+ }
+
+ // Now check if the newly hovered item is actually a handle.
+ const int hoveredHandleIndex = d->m_handleItems.indexOf(hoveredItem);
+ if (hoveredHandleIndex == -1)
+ return;
+
+ // It's a handle, so it's now hovered.
+ d->m_hoveredHandleIndex = hoveredHandleIndex;
+
+ QQuickSplitHandleAttached *handleAttached = qobject_cast<QQuickSplitHandleAttached*>(
+ qmlAttachedPropertiesObject<QQuickSplitHandleAttached>(hoveredItem, true));
+ QQuickSplitHandleAttachedPrivate::get(handleAttached)->setHovered(true);
+
+#if QT_CONFIG(cursor)
+ setCursor(d->m_orientation == Qt::Horizontal ? Qt::SplitHCursor : Qt::SplitVCursor);
+#endif
+
+ qCDebug(qlcQQuickSplitViewMouse) << "handle item at index" << d->m_hoveredHandleIndex << "is now hovered";
+ }
+}
+
+void QQuickSplitView::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
+{
+ Q_D(QQuickSplitView);
+ QQuickControl::geometryChanged(newGeometry, oldGeometry);
+ d->resizeHandles();
+ d->layout();
+}
+
+void QQuickSplitView::itemAdded(int index, QQuickItem *item)
+{
+ Q_D(QQuickSplitView);
+ if (QQuickItemPrivate::get(item)->isTransparentForPositioner())
+ return;
+
+ const int count = d->contentModel->count();
+ qCDebug(qlcQQuickSplitView).nospace() << "split item " << item << " added at index " << index
+ << "; there are now " << count << " items";
+
+ QQuickSplitViewAttached *attached = qobject_cast<QQuickSplitViewAttached*>(
+ qmlAttachedPropertiesObject<QQuickSplitView>(item, false));
+ if (attached)
+ QQuickSplitViewAttachedPrivate::get(attached)->setView(this);
+
+ // Only need to add handles if we have more than one split item.
+ if (count > 1) {
+ // If the item was added at the end, it shouldn't get a handle;
+ // the handle always goes to the split item on the left.
+ d->createHandleItem(index < count - 1 ? index : index - 1);
+ }
+
+ d->updateHandleVisibilities();
+ d->updateFillIndex();
+ d->layout();
+}
+
+void QQuickSplitView::itemMoved(int index, QQuickItem *item)
+{
+ Q_D(QQuickSplitView);
+ if (QQuickItemPrivate::get(item)->isTransparentForPositioner())
+ return;
+
+ qCDebug(qlcQQuickSplitView) << "split item" << item << "moved to index" << index;
+
+ d->updateHandleVisibilities();
+ d->updateFillIndex();
+ d->layout();
+}
+
+void QQuickSplitView::itemRemoved(int index, QQuickItem *item)
+{
+ Q_D(QQuickSplitView);
+ if (QQuickItemPrivate::get(item)->isTransparentForPositioner())
+ return;
+
+ qCDebug(qlcQQuickSplitView).nospace() << "split item " << item << " removed from index " << index
+ << "; there are now " << d->contentModel->count() << " items";
+
+ // Clear hovered/pressed handle if there are any.
+ if (d->m_hoveredHandleIndex != -1 || d->m_pressedHandleIndex != -1) {
+ const int handleIndex = d->m_hoveredHandleIndex != -1 ? d->m_hoveredHandleIndex : d->m_pressedHandleIndex;
+ QQuickItem *itemHandle = d->m_handleItems.at(handleIndex);
+ QQuickSplitHandleAttached *handleAttached = qobject_cast<QQuickSplitHandleAttached*>(
+ qmlAttachedPropertiesObject<QQuickSplitHandleAttached>(itemHandle, false));
+ if (handleAttached) {
+ auto handleAttachedPrivate = QQuickSplitHandleAttachedPrivate::get(handleAttached);
+ handleAttachedPrivate->setHovered(false);
+ handleAttachedPrivate->setPressed(false);
+ }
+
+ setKeepMouseGrab(false);
+ d->m_hoveredHandleIndex = -1;
+ d->m_pressedHandleIndex = -1;
+ }
+
+ // Unset any attached properties since the item is no longer owned by us.
+ QQuickSplitViewAttached *attached = qobject_cast<QQuickSplitViewAttached*>(
+ qmlAttachedPropertiesObject<QQuickSplitView>(item, false));
+ if (attached)
+ QQuickSplitViewAttachedPrivate::get(attached)->setView(this);
+
+ d->removeExcessHandles();
+ d->updateHandleVisibilities();
+ d->updateFillIndex();
+ d->layout();
+}
+
+#if QT_CONFIG(accessibility)
+QAccessible::Role QQuickSplitView::accessibleRole() const
+{
+ return QAccessible::Pane;
+}
+#endif
+
+QQuickSplitViewAttached::QQuickSplitViewAttached(QObject *parent)
+ : QObject(*(new QQuickSplitViewAttachedPrivate), parent)
+{
+ Q_D(QQuickSplitViewAttached);
+ QQuickItem *item = qobject_cast<QQuickItem *>(parent);
+ if (!item || QQuickItemPrivate::get(item)->isTransparentForPositioner())
+ return;
+
+ d->m_splitItem = item;
+
+ // Child items get added to SplitView's contentItem, so we have to ensure
+ // that exists first before trying to set m_splitView.
+ // Apparently, in some cases it's normal for the parent item
+ // to not exist until shortly after this constructor has run.
+ if (!item->parentItem())
+ return;
+
+ // This will get hit when attached SplitView properties are imperatively set
+ // on an item that previously had none set, for example.
+ QQuickSplitView *splitView = qobject_cast<QQuickSplitView*>(item->parentItem()->parentItem());
+ if (!splitView) {
+ qmlWarning(parent) << "SplitView: attached properties must be accessed through a direct child of SplitView";
+ return;
+ }
+
+ d->setView(splitView);
+}
+
+/*!
+ \qmlattachedproperty SplitView QtQuick.Controls::SplitView::view
+
+ This attached property holds the split view of the item it is
+ attached to, or \c null if the item is not in a split view.
+*/
+QQuickSplitView *QQuickSplitViewAttached::view() const
+{
+ Q_D(const QQuickSplitViewAttached);
+ return d->m_splitView;
+}
+
+/*!
+ \qmlattachedproperty real QtQuick.Controls::SplitView::minimumWidth
+
+ This attached property controls the minimum width of the split item.
+ The \l preferredWidth is bound within the \l minimumWidth and
+ \l maximumWidth. A split item cannot be dragged to be smaller than
+ its \c minimumWidth.
+
+ The default value is \c 0. To reset this property to its default value,
+ set it to \c undefined.
+
+ \sa maximumWidth, preferredWidth, fillWidth, minimumHeight
+*/
+qreal QQuickSplitViewAttached::minimumWidth() const
+{
+ Q_D(const QQuickSplitViewAttached);
+ return d->m_minimumWidth;
+}
+
+void QQuickSplitViewAttached::setMinimumWidth(qreal width)
+{
+ Q_D(QQuickSplitViewAttached);
+ d->m_isMinimumWidthSet = true;
+ if (qFuzzyCompare(width, d->m_minimumWidth))
+ return;
+
+ d->m_minimumWidth = width;
+ d->layoutView();
+ emit minimumWidthChanged();
+}
+
+void QQuickSplitViewAttached::resetMinimumWidth()
+{
+ Q_D(QQuickSplitViewAttached);
+ const qreal oldEffectiveMinimumWidth = effectiveMinimumWidth(d);
+
+ d->m_isMinimumWidthSet = false;
+ d->m_minimumWidth = -1;
+
+ const qreal newEffectiveMinimumWidth = effectiveMinimumWidth(d);
+ if (qFuzzyCompare(newEffectiveMinimumWidth, oldEffectiveMinimumWidth))
+ return;
+
+ d->layoutView();
+ emit minimumWidthChanged();
+}
+
+/*!
+ \qmlattachedproperty real QtQuick.Controls::SplitView::minimumHeight
+
+ This attached property controls the minimum height of the split item.
+ The \l preferredHeight is bound within the \l minimumHeight and
+ \l maximumHeight. A split item cannot be dragged to be smaller than
+ its \c minimumHeight.
+
+ The default value is \c 0. To reset this property to its default value,
+ set it to \c undefined.
+
+ \sa maximumHeight, preferredHeight, fillHeight, minimumWidth
+*/
+qreal QQuickSplitViewAttached::minimumHeight() const
+{
+ Q_D(const QQuickSplitViewAttached);
+ return d->m_minimumHeight;
+}
+
+void QQuickSplitViewAttached::setMinimumHeight(qreal height)
+{
+ Q_D(QQuickSplitViewAttached);
+ d->m_isMinimumHeightSet = true;
+ if (qFuzzyCompare(height, d->m_minimumHeight))
+ return;
+
+ d->m_minimumHeight = height;
+ d->layoutView();
+ emit minimumHeightChanged();
+}
+
+void QQuickSplitViewAttached::resetMinimumHeight()
+{
+ Q_D(QQuickSplitViewAttached);
+ const qreal oldEffectiveMinimumHeight = effectiveMinimumHeight(d);
+
+ d->m_isMinimumHeightSet = false;
+ d->m_minimumHeight = -1;
+
+ const qreal newEffectiveMinimumHeight = effectiveMinimumHeight(d);
+ if (qFuzzyCompare(newEffectiveMinimumHeight, oldEffectiveMinimumHeight))
+ return;
+
+ d->layoutView();
+ emit minimumHeightChanged();
+}
+
+/*!
+ \qmlattachedproperty real QtQuick.Controls::SplitView::preferredWidth
+
+ This attached property controls the preferred width of the split item. The
+ preferred width will be used as the size of the item, and will be bound
+ within the \l minimumWidth and \l maximumWidth. If the preferred width
+ is not set, the item's \l {Item::}{implicitWidth} will be used.
+
+ When a split item is resized, the preferredWidth will be set in order
+ to keep track of the new size.
+
+ By default, this property is not set, and therefore
+ \l {Item::}{implicitWidth} will be used instead. To reset this property to
+ its default value, set it to \c undefined.
+
+ \note Do not set the \l width property of a split item, as it will be
+ overwritten upon each layout of the SplitView.
+
+ \sa minimumWidth, maximumWidth, fillWidth, preferredHeight
+*/
+qreal QQuickSplitViewAttached::preferredWidth() const
+{
+ Q_D(const QQuickSplitViewAttached);
+ return d->m_preferredWidth;
+}
+
+void QQuickSplitViewAttached::setPreferredWidth(qreal width)
+{
+ Q_D(QQuickSplitViewAttached);
+ d->m_isPreferredWidthSet = true;
+ if (qFuzzyCompare(width, d->m_preferredWidth))
+ return;
+
+ d->m_preferredWidth = width;
+ d->layoutView();
+ emit preferredWidthChanged();
+}
+
+void QQuickSplitViewAttached::resetPreferredWidth()
+{
+ Q_D(QQuickSplitViewAttached);
+ const qreal oldEffectivePreferredWidth = effectivePreferredWidth(
+ d, QQuickItemPrivate::get(d->m_splitItem));
+
+ d->m_isPreferredWidthSet = false;
+ d->m_preferredWidth = -1;
+
+ const qreal newEffectivePreferredWidth = effectivePreferredWidth(
+ d, QQuickItemPrivate::get(d->m_splitItem));
+ if (qFuzzyCompare(newEffectivePreferredWidth, oldEffectivePreferredWidth))
+ return;
+
+ d->layoutView();
+ emit preferredWidthChanged();
+}
+
+/*!
+ \qmlattachedproperty real QtQuick.Controls::SplitView::preferredHeight
+
+ This attached property controls the preferred height of the split item. The
+ preferred height will be used as the size of the item, and will be bound
+ within the \l minimumHeight and \l maximumHeight. If the preferred height
+ is not set, the item's \l {Item::}{implicitHeight} will be used.
+
+ When a split item is resized, the preferredHeight will be set in order
+ to keep track of the new size.
+
+ By default, this property is not set, and therefore
+ \l {Item::}{implicitHeight} will be used instead. To reset this property to
+ its default value, set it to \c undefined.
+
+ \note Do not set the \l height property of a split item, as it will be
+ overwritten upon each layout of the SplitView.
+
+ \sa minimumHeight, maximumHeight, fillHeight, preferredWidth
+*/
+qreal QQuickSplitViewAttached::preferredHeight() const
+{
+ Q_D(const QQuickSplitViewAttached);
+ return d->m_preferredHeight;
+}
+
+void QQuickSplitViewAttached::setPreferredHeight(qreal height)
+{
+ Q_D(QQuickSplitViewAttached);
+ d->m_isPreferredHeightSet = true;
+ if (qFuzzyCompare(height, d->m_preferredHeight))
+ return;
+
+ d->m_preferredHeight = height;
+ d->layoutView();
+ emit preferredHeightChanged();
+}
+
+void QQuickSplitViewAttached::resetPreferredHeight()
+{
+ Q_D(QQuickSplitViewAttached);
+ const qreal oldEffectivePreferredHeight = effectivePreferredHeight(
+ d, QQuickItemPrivate::get(d->m_splitItem));
+
+ d->m_isPreferredHeightSet = false;
+ d->m_preferredHeight = -1;
+
+ const qreal newEffectivePreferredHeight = effectivePreferredHeight(
+ d, QQuickItemPrivate::get(d->m_splitItem));
+ if (qFuzzyCompare(newEffectivePreferredHeight, oldEffectivePreferredHeight))
+ return;
+
+ d->layoutView();
+ emit preferredHeightChanged();
+}
+
+/*!
+ \qmlattachedproperty real QtQuick.Controls::SplitView::maximumWidth
+
+ This attached property controls the maximum width of the split item.
+ The \l preferredWidth is bound within the \l minimumWidth and
+ \l maximumWidth. A split item cannot be dragged to be larger than
+ its \c maximumWidth.
+
+ The default value is \c Infinity. To reset this property to its default
+ value, set it to \c undefined.
+
+ \sa minimumWidth, preferredWidth, fillWidth, maximumHeight
+*/
+qreal QQuickSplitViewAttached::maximumWidth() const
+{
+ Q_D(const QQuickSplitViewAttached);
+ return d->m_maximumWidth;
+}
+
+void QQuickSplitViewAttached::setMaximumWidth(qreal width)
+{
+ Q_D(QQuickSplitViewAttached);
+ d->m_isMaximumWidthSet = true;
+ if (qFuzzyCompare(width, d->m_maximumWidth))
+ return;
+
+ d->m_maximumWidth = width;
+ d->layoutView();
+ emit maximumWidthChanged();
+}
+
+void QQuickSplitViewAttached::resetMaximumWidth()
+{
+ Q_D(QQuickSplitViewAttached);
+ const qreal oldEffectiveMaximumWidth = effectiveMaximumWidth(d);
+
+ d->m_isMaximumWidthSet = false;
+ d->m_maximumWidth = -1;
+
+ const qreal newEffectiveMaximumWidth = effectiveMaximumWidth(d);
+ if (qFuzzyCompare(newEffectiveMaximumWidth, oldEffectiveMaximumWidth))
+ return;
+
+ d->layoutView();
+ emit maximumWidthChanged();
+}
+
+/*!
+ \qmlattachedproperty real QtQuick.Controls::SplitView::maximumHeight
+
+ This attached property controls the maximum height of the split item.
+ The \l preferredHeight is bound within the \l minimumHeight and
+ \l maximumHeight. A split item cannot be dragged to be larger than
+ its \c maximumHeight.
+
+ The default value is \c Infinity. To reset this property to its default
+ value, set it to \c undefined.
+
+ \sa minimumHeight, preferredHeight, fillHeight, maximumWidth
+*/
+qreal QQuickSplitViewAttached::maximumHeight() const
+{
+ Q_D(const QQuickSplitViewAttached);
+ return d->m_maximumHeight;
+}
+
+void QQuickSplitViewAttached::setMaximumHeight(qreal height)
+{
+ Q_D(QQuickSplitViewAttached);
+ d->m_isMaximumHeightSet = true;
+ if (qFuzzyCompare(height, d->m_maximumHeight))
+ return;
+
+ d->m_maximumHeight = height;
+ d->layoutView();
+ emit maximumHeightChanged();
+}
+
+void QQuickSplitViewAttached::resetMaximumHeight()
+{
+ Q_D(QQuickSplitViewAttached);
+ const qreal oldEffectiveMaximumHeight = effectiveMaximumHeight(d);
+
+ d->m_isMaximumHeightSet = false;
+ d->m_maximumHeight = -1;
+
+ const qreal newEffectiveMaximumHeight = effectiveMaximumHeight(d);
+ if (qFuzzyCompare(newEffectiveMaximumHeight, oldEffectiveMaximumHeight))
+ return;
+
+ d->layoutView();
+ emit maximumHeightChanged();
+}
+
+/*!
+ \qmlattachedproperty bool QtQuick.Controls::SplitView::fillWidth
+
+ This attached property controls whether the item takes the remaining space
+ in the split view after all other items have been laid out.
+
+ By default, the last visible child of the split view will have this set,
+ but it can be changed by explicitly setting \c fillWidth to \c true on
+ another item.
+
+ The width of a split item with \c fillWidth set to \c true is still
+ restricted within its \l minimumWidth and \l maximumWidth.
+
+ \sa minimumWidth, preferredWidth, maximumWidth, fillHeight
+*/
+bool QQuickSplitViewAttached::fillWidth() const
+{
+ Q_D(const QQuickSplitViewAttached);
+ return d->m_fillWidth;
+}
+
+void QQuickSplitViewAttached::setFillWidth(bool fill)
+{
+ Q_D(QQuickSplitViewAttached);
+ d->m_isFillWidthSet = true;
+ if (fill == d->m_fillWidth)
+ return;
+
+ d->m_fillWidth = fill;
+ if (d->m_splitView && d->m_splitView->orientation() == Qt::Horizontal)
+ QQuickSplitViewPrivate::get(d->m_splitView)->updateFillIndex();
+ d->layoutView();
+ emit fillWidthChanged();
+}
+
+/*!
+ \qmlattachedproperty bool QtQuick.Controls::SplitView::fillHeight
+
+ This attached property controls whether the item takes the remaining space
+ in the split view after all other items have been laid out.
+
+ By default, the last visible child of the split view will have this set,
+ but it can be changed by explicitly setting \c fillHeight to \c true on
+ another item.
+
+ The height of a split item with \c fillHeight set to \c true is still
+ restricted within its \l minimumHeight and \l maximumHeight.
+
+ \sa minimumHeight, preferredHeight, maximumHeight, fillWidth
+*/
+bool QQuickSplitViewAttached::fillHeight() const
+{
+ Q_D(const QQuickSplitViewAttached);
+ return d->m_fillHeight;
+}
+
+void QQuickSplitViewAttached::setFillHeight(bool fill)
+{
+ Q_D(QQuickSplitViewAttached);
+ d->m_isFillHeightSet = true;
+ if (fill == d->m_fillHeight)
+ return;
+
+ d->m_fillHeight = fill;
+ if (d->m_splitView && d->m_splitView->orientation() == Qt::Vertical)
+ QQuickSplitViewPrivate::get(d->m_splitView)->updateFillIndex();
+ d->layoutView();
+ emit fillHeightChanged();
+}
+
+QQuickSplitViewAttachedPrivate::QQuickSplitViewAttachedPrivate()
+ : m_fillWidth(false)
+ , m_fillHeight(false)
+ , m_isFillWidthSet(false)
+ , m_isFillHeightSet(false)
+ , m_isMinimumWidthSet(false)
+ , m_isMinimumHeightSet(false)
+ , m_isPreferredWidthSet(false)
+ , m_isPreferredHeightSet(false)
+ , m_isMaximumWidthSet(false)
+ , m_isMaximumHeightSet(false)
+ , m_minimumWidth(0)
+ , m_minimumHeight(0)
+ , m_preferredWidth(-1)
+ , m_preferredHeight(-1)
+ , m_maximumWidth(std::numeric_limits<qreal>::infinity())
+ , m_maximumHeight(std::numeric_limits<qreal>::infinity())
+{
+}
+
+void QQuickSplitViewAttachedPrivate::setView(QQuickSplitView *newView)
+{
+ Q_Q(QQuickSplitViewAttached);
+ if (newView == m_splitView)
+ return;
+
+ m_splitView = newView;
+ qCDebug(qlcQQuickSplitView) << "set SplitView" << newView << "on attached object" << this;
+ emit q->viewChanged();
+}
+
+void QQuickSplitViewAttachedPrivate::layoutView()
+{
+ if (m_splitView)
+ QQuickSplitViewPrivate::get(m_splitView)->layout();
+}
+
+QQuickSplitViewAttachedPrivate *QQuickSplitViewAttachedPrivate::get(QQuickSplitViewAttached *attached)
+{
+ return attached->d_func();
+}
+
+const QQuickSplitViewAttachedPrivate *QQuickSplitViewAttachedPrivate::get(const QQuickSplitViewAttached *attached)
+{
+ return attached->d_func();
+}
+
+QQuickSplitHandleAttachedPrivate::QQuickSplitHandleAttachedPrivate()
+ : m_hovered(false)
+ , m_pressed(false)
+{
+}
+
+void QQuickSplitHandleAttachedPrivate::setHovered(bool hovered)
+{
+ Q_Q(QQuickSplitHandleAttached);
+ if (hovered == m_hovered)
+ return;
+
+ m_hovered = hovered;
+ emit q->hoveredChanged();
+}
+
+void QQuickSplitHandleAttachedPrivate::setPressed(bool pressed)
+{
+ Q_Q(QQuickSplitHandleAttached);
+ if (pressed == m_pressed)
+ return;
+
+ m_pressed = pressed;
+ emit q->pressedChanged();
+}
+
+QQuickSplitHandleAttachedPrivate *QQuickSplitHandleAttachedPrivate::get(QQuickSplitHandleAttached *attached)
+{
+ return attached->d_func();
+}
+
+const QQuickSplitHandleAttachedPrivate *QQuickSplitHandleAttachedPrivate::get(const QQuickSplitHandleAttached *attached)
+{
+ return attached->d_func();
+}
+
+QQuickSplitHandleAttached::QQuickSplitHandleAttached(QObject *parent)
+ : QObject(*(new QQuickSplitViewAttachedPrivate), parent)
+{
+}
+
+/*!
+ \qmltype SplitHandle
+ \inherits QtObject
+ \instantiates QQuickSplitHandleAttached
+ \inqmlmodule QtQuick.Controls
+ \since 5.12
+ \brief Provides attached properties for SplitView handles
+
+ SplitHandle provides attached properties for \l SplitView handles.
+
+ For split items themselves, use the attached \l SplitView properties.
+
+ \sa SplitView
+*/
+
+/*!
+ \qmlattachedproperty bool QtQuick.Controls::SplitHandle::hovered
+
+ This attached property holds whether the split handle is hovered.
+
+ \sa pressed
+*/
+bool QQuickSplitHandleAttached::isHovered() const
+{
+ Q_D(const QQuickSplitHandleAttached);
+ return d->m_hovered;
+}
+
+/*!
+ \qmlattachedproperty bool QtQuick.Controls::SplitHandle::pressed
+
+ This attached property holds whether the split handle is pressed.
+
+ \sa hovered
+*/
+bool QQuickSplitHandleAttached::isPressed() const
+{
+ Q_D(const QQuickSplitHandleAttached);
+ return d->m_pressed;
+}
+
+QQuickSplitHandleAttached *QQuickSplitHandleAttached::qmlAttachedProperties(QObject *object)
+{
+ return new QQuickSplitHandleAttached(object);
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qquicksplitview_p.cpp"
diff --git a/src/quicktemplates2/qquicksplitview_p.h b/src/quicktemplates2/qquicksplitview_p.h
new file mode 100644
index 00000000..99001615
--- /dev/null
+++ b/src/quicktemplates2/qquicksplitview_p.h
@@ -0,0 +1,218 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 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 QQUICKSPLITVIEW_P_H
+#define QQUICKSPLITVIEW_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/qquickcontainer_p.h>
+#include <QtQml/qqmllist.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQuickSplitViewPrivate;
+class QQuickSplitViewAttached;
+class QQuickSplitViewAttachedPrivate;
+class QQuickSplitHandleAttached;
+class QQuickSplitHandleAttachedPrivate;
+
+class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickSplitView : public QQuickContainer
+{
+ Q_OBJECT
+ Q_PROPERTY(Qt::Orientation orientation READ orientation WRITE setOrientation NOTIFY orientationChanged FINAL)
+ Q_PROPERTY(bool resizing READ isResizing NOTIFY resizingChanged)
+ Q_PROPERTY(QQmlComponent *handle READ handle WRITE setHandle NOTIFY handleChanged FINAL)
+
+public:
+ explicit QQuickSplitView(QQuickItem *parent = nullptr);
+ ~QQuickSplitView() override;
+
+ Qt::Orientation orientation() const;
+ void setOrientation(Qt::Orientation orientation);
+
+ bool isResizing() const;
+
+ QQmlComponent *handle();
+ void setHandle(QQmlComponent *handle);
+
+ bool isContent(QQuickItem *item) const override;
+
+ static QQuickSplitViewAttached *qmlAttachedProperties(QObject *object);
+
+ // Based on the same code in QMainWindow.
+ enum VersionMarkers {
+ VersionMarker = 0xff
+ };
+ Q_INVOKABLE QVariant saveState();
+ Q_INVOKABLE bool restoreState(const QVariant &state);
+
+Q_SIGNALS:
+ void orientationChanged();
+ void resizingChanged();
+ void handleChanged();
+
+protected:
+ QQuickSplitView(QQuickSplitViewPrivate &dd, QQuickItem *parent);
+
+ void componentComplete() override;
+ void hoverMoveEvent(QHoverEvent *event) override;
+ void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) override;
+
+ void itemAdded(int index, QQuickItem *item) override;
+ void itemMoved(int index, QQuickItem *item) override;
+ void itemRemoved(int index, QQuickItem *item) override;
+
+#if QT_CONFIG(accessibility)
+ QAccessible::Role accessibleRole() const override;
+#endif
+
+private:
+ Q_DISABLE_COPY(QQuickSplitView)
+ Q_DECLARE_PRIVATE(QQuickSplitView)
+};
+
+class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickSplitViewAttached : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QQuickSplitView *view READ view NOTIFY viewChanged FINAL)
+ Q_PROPERTY(qreal minimumWidth READ minimumWidth WRITE setMinimumWidth
+ RESET resetMinimumWidth NOTIFY minimumWidthChanged FINAL)
+ Q_PROPERTY(qreal minimumHeight READ minimumHeight WRITE setMinimumHeight
+ RESET resetMinimumHeight NOTIFY minimumHeightChanged FINAL)
+ Q_PROPERTY(qreal preferredWidth READ preferredWidth WRITE setPreferredWidth
+ RESET resetPreferredWidth NOTIFY preferredWidthChanged FINAL)
+ Q_PROPERTY(qreal preferredHeight READ preferredHeight WRITE setPreferredHeight
+ RESET resetPreferredHeight NOTIFY preferredHeightChanged FINAL)
+ Q_PROPERTY(qreal maximumWidth READ maximumWidth WRITE setMaximumWidth
+ RESET resetMaximumWidth NOTIFY maximumWidthChanged FINAL)
+ Q_PROPERTY(qreal maximumHeight READ maximumHeight WRITE setMaximumHeight
+ RESET resetMaximumHeight NOTIFY maximumHeightChanged FINAL)
+ Q_PROPERTY(bool fillHeight READ fillHeight WRITE setFillHeight NOTIFY fillHeightChanged FINAL)
+ Q_PROPERTY(bool fillWidth READ fillWidth WRITE setFillWidth NOTIFY fillWidthChanged FINAL)
+
+public:
+ explicit QQuickSplitViewAttached(QObject *parent = nullptr);
+
+ QQuickSplitView *view() const;
+
+ qreal minimumWidth() const;
+ void setMinimumWidth(qreal width);
+ void resetMinimumWidth();
+
+ qreal minimumHeight() const;
+ void setMinimumHeight(qreal height);
+ void resetMinimumHeight();
+
+ qreal preferredWidth() const;
+ void setPreferredWidth(qreal width);
+ void resetPreferredWidth();
+
+ qreal preferredHeight() const;
+ void setPreferredHeight(qreal height);
+ void resetPreferredHeight();
+
+ qreal maximumWidth() const;
+ void setMaximumWidth(qreal width);
+ void resetMaximumWidth();
+
+ qreal maximumHeight() const;
+ void setMaximumHeight(qreal height);
+ void resetMaximumHeight();
+
+ bool fillWidth() const;
+ void setFillWidth(bool fill);
+
+ bool fillHeight() const;
+ void setFillHeight(bool fill);
+
+Q_SIGNALS:
+ void viewChanged();
+ void minimumWidthChanged();
+ void minimumHeightChanged();
+ void preferredWidthChanged();
+ void preferredHeightChanged();
+ void maximumWidthChanged();
+ void maximumHeightChanged();
+ void fillWidthChanged();
+ void fillHeightChanged();
+
+private:
+ Q_DISABLE_COPY(QQuickSplitViewAttached)
+ Q_DECLARE_PRIVATE(QQuickSplitViewAttached)
+};
+
+class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickSplitHandleAttached : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(bool hovered READ isHovered NOTIFY hoveredChanged FINAL)
+ Q_PROPERTY(bool pressed READ isPressed NOTIFY pressedChanged FINAL)
+
+public:
+ explicit QQuickSplitHandleAttached(QObject *parent = nullptr);
+
+ bool isHovered() const;
+ bool isPressed() const;
+
+ static QQuickSplitHandleAttached *qmlAttachedProperties(QObject *object);
+
+Q_SIGNALS:
+ void hoveredChanged();
+ void pressedChanged();
+
+private:
+ Q_DISABLE_COPY(QQuickSplitHandleAttached)
+ Q_DECLARE_PRIVATE(QQuickSplitHandleAttached)
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QQuickSplitView)
+QML_DECLARE_TYPEINFO(QQuickSplitView, QML_HAS_ATTACHED_PROPERTIES)
+
+QML_DECLARE_TYPE(QQuickSplitHandleAttached)
+QML_DECLARE_TYPEINFO(QQuickSplitHandleAttached, QML_HAS_ATTACHED_PROPERTIES)
+
+#endif // QQUICKSPLITVIEW_P_H
diff --git a/src/quicktemplates2/qquicksplitview_p_p.h b/src/quicktemplates2/qquicksplitview_p_p.h
new file mode 100644
index 00000000..65fbd900
--- /dev/null
+++ b/src/quicktemplates2/qquicksplitview_p_p.h
@@ -0,0 +1,173 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 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 QQUICKSPLITVIEW_P_P_H
+#define QQUICKSPLITVIEW_P_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtQuickTemplates2/private/qquickcontainer_p_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQuickSplitView;
+class QQuickSplitViewAttached;
+class QQuickSplitHandleAttached;
+
+class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickSplitViewPrivate : public QQuickContainerPrivate
+{
+ Q_DECLARE_PUBLIC(QQuickSplitView)
+
+public:
+ void updateFillIndex();
+ void layoutResizeSplitItems(qreal &usedWidth, qreal &usedHeight, int &indexBeingResizedDueToDrag);
+ void layoutResizeFillItem(QQuickItem *fillItem, qreal &usedWidth, qreal &usedHeight, int indexBeingResizedDueToDrag);
+ void layoutPositionItems(const QQuickItem *fillItem);
+ void layout();
+ void createHandles();
+ void createHandleItem(int index);
+ void removeExcessHandles();
+ void destroyHandles();
+ void resizeHandle(QQuickItem *handleItem);
+ void resizeHandles();
+ void updateHandleVisibilities();
+ void setResizing(bool resizing);
+
+ bool isHorizontal() const;
+ qreal accumulatedSize(int firstIndex, int lastIndex) const;
+
+ struct EffectiveSizeData {
+ qreal effectiveMinimumWidth;
+ qreal effectiveMinimumHeight;
+ qreal effectivePreferredWidth;
+ qreal effectivePreferredHeight;
+ qreal effectiveMaximumWidth;
+ qreal effectiveMaximumHeight;
+ };
+
+ EffectiveSizeData effectiveSizeData(const QQuickItemPrivate *itemPrivate,
+ const QQuickSplitViewAttached *attached) const;
+
+ int handleIndexForSplitIndex(int splitIndex) const;
+
+ QQuickItem *getContentItem() override;
+ void handlePress(const QPointF &point) override;
+ void handleMove(const QPointF &point) override;
+ void handleRelease(const QPointF &point) override;
+
+ void itemVisibilityChanged(QQuickItem *item) override;
+
+ static QQuickSplitViewPrivate *get(QQuickSplitView *splitView);
+
+private:
+ Qt::Orientation m_orientation = Qt::Horizontal;
+ QQmlComponent *m_handle = nullptr;
+ QVector<QQuickItem*> m_handleItems;
+ int m_hoveredHandleIndex = -1;
+ int m_pressedHandleIndex = -1;
+ QPointF m_pressPos;
+ QPointF m_mousePos;
+ QPointF m_handlePosBeforePress;
+ qreal m_leftOrTopItemSizeBeforePress = 0.0;
+ qreal m_rightOrBottomItemSizeBeforePress = 0.0;
+ int m_fillIndex = -1;
+ bool m_layingOut = false;
+ bool m_resizing = false;
+};
+
+class QQuickSplitViewAttachedPrivate : public QObjectPrivate
+{
+ Q_DECLARE_PUBLIC(QQuickSplitViewAttached)
+
+public:
+ QQuickSplitViewAttachedPrivate();
+
+ void setView(QQuickSplitView *newView);
+ void layoutView();
+
+ static QQuickSplitViewAttachedPrivate *get(QQuickSplitViewAttached *attached);
+ static const QQuickSplitViewAttachedPrivate *get(const QQuickSplitViewAttached *attached);
+
+ QQuickItem *m_splitItem = nullptr;
+ QQuickSplitView *m_splitView = nullptr;
+
+ unsigned m_fillWidth : 1;
+ unsigned m_fillHeight : 1;
+ unsigned m_isFillWidthSet : 1;
+ unsigned m_isFillHeightSet : 1;
+ unsigned m_isMinimumWidthSet : 1;
+ unsigned m_isMinimumHeightSet : 1;
+ unsigned m_isPreferredWidthSet : 1;
+ unsigned m_isPreferredHeightSet : 1;
+ unsigned m_isMaximumWidthSet : 1;
+ unsigned m_isMaximumHeightSet : 1;
+ qreal m_minimumWidth;
+ qreal m_minimumHeight;
+ qreal m_preferredWidth;
+ qreal m_preferredHeight;
+ qreal m_maximumWidth;
+ qreal m_maximumHeight;
+};
+
+class QQuickSplitHandleAttachedPrivate : public QObjectPrivate
+{
+ Q_DECLARE_PUBLIC(QQuickSplitHandleAttached)
+
+public:
+ QQuickSplitHandleAttachedPrivate();
+
+ void setHovered(bool hovered);
+ void setPressed(bool pressed);
+
+ static QQuickSplitHandleAttachedPrivate *get(QQuickSplitHandleAttached *attached);
+ static const QQuickSplitHandleAttachedPrivate *get(const QQuickSplitHandleAttached *attached);
+
+ unsigned m_hovered : 1;
+ unsigned m_pressed : 1;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQUICKSPLITVIEW_P_P_H
diff --git a/src/quicktemplates2/quicktemplates2.pri b/src/quicktemplates2/quicktemplates2.pri
index 33bc47ca..c145c20f 100644
--- a/src/quicktemplates2/quicktemplates2.pri
+++ b/src/quicktemplates2/quicktemplates2.pri
@@ -73,6 +73,7 @@ HEADERS += \
$$PWD/qquickshortcutcontext_p_p.h \
$$PWD/qquickslider_p.h \
$$PWD/qquickspinbox_p.h \
+ $$PWD/qquicksplitview_p.h \
$$PWD/qquickstackelement_p_p.h \
$$PWD/qquickstacktransition_p_p.h \
$$PWD/qquickstackview_p.h \
@@ -149,6 +150,7 @@ SOURCES += \
$$PWD/qquickshortcutcontext.cpp \
$$PWD/qquickslider.cpp \
$$PWD/qquickspinbox.cpp \
+ $$PWD/qquicksplitview.cpp \
$$PWD/qquickstackelement.cpp \
$$PWD/qquickstacktransition.cpp \
$$PWD/qquickstackview.cpp \
diff --git a/tests/auto/controls/data/splitview/fillItemInMiddle.qml b/tests/auto/controls/data/splitview/fillItemInMiddle.qml
new file mode 100644
index 00000000..33aa5a8b
--- /dev/null
+++ b/tests/auto/controls/data/splitview/fillItemInMiddle.qml
@@ -0,0 +1,77 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, 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.12
+import QtQuick.Controls 2.5
+
+SplitView {
+ anchors.fill: parent
+
+ Rectangle {
+ objectName: "salmon"
+ color: objectName
+ implicitWidth: 25
+ implicitHeight: 25
+ }
+ Rectangle {
+ objectName: "navajowhite"
+ color: objectName
+ implicitWidth: 100
+ implicitHeight: 100
+
+ SplitView.fillWidth: true
+ }
+ Rectangle {
+ objectName: "steelblue"
+ color: objectName
+ implicitWidth: 200
+ implicitHeight: 200
+ }
+}
diff --git a/tests/auto/controls/data/splitview/fillItemOnLeft.qml b/tests/auto/controls/data/splitview/fillItemOnLeft.qml
new file mode 100644
index 00000000..ca20652b
--- /dev/null
+++ b/tests/auto/controls/data/splitview/fillItemOnLeft.qml
@@ -0,0 +1,77 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, 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.12
+import QtQuick.Controls 2.5
+
+SplitView {
+ anchors.fill: parent
+
+ Rectangle {
+ objectName: "salmon"
+ color: objectName
+ implicitWidth: 25
+ implicitHeight: 25
+
+ SplitView.fillWidth: true
+ }
+ Rectangle {
+ objectName: "navajowhite"
+ color: objectName
+ implicitWidth: 200
+ implicitHeight: 200
+ }
+ Rectangle {
+ objectName: "steelblue"
+ color: objectName
+ implicitWidth: 200
+ implicitHeight: 200
+ }
+}
diff --git a/tests/auto/controls/data/splitview/fillItemOnTop.qml b/tests/auto/controls/data/splitview/fillItemOnTop.qml
new file mode 100644
index 00000000..ea7aa2b6
--- /dev/null
+++ b/tests/auto/controls/data/splitview/fillItemOnTop.qml
@@ -0,0 +1,78 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, 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.12
+import QtQuick.Controls 2.5
+
+SplitView {
+ anchors.fill: parent
+ orientation: Qt.Vertical
+
+ Rectangle {
+ objectName: "salmon"
+ color: objectName
+ implicitWidth: 25
+ implicitHeight: 25
+
+ SplitView.fillHeight: true
+ }
+ Rectangle {
+ objectName: "navajowhite"
+ color: objectName
+ implicitWidth: 200
+ implicitHeight: 200
+ }
+ Rectangle {
+ objectName: "steelblue"
+ color: objectName
+ implicitWidth: 200
+ implicitHeight: 200
+ }
+}
diff --git a/tests/auto/controls/data/tst_splitview.qml b/tests/auto/controls/data/tst_splitview.qml
new file mode 100644
index 00000000..9fd5b855
--- /dev/null
+++ b/tests/auto/controls/data/tst_splitview.qml
@@ -0,0 +1,1749 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, 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.12
+import QtQuick.Controls 2.5
+import QtQuick.Window 2.5
+import QtTest 1.0
+import Qt.labs.settings 1.0
+
+TestCase {
+ id: testCase
+ width: 400
+ height: 400
+ visible: true
+ when: windowShown
+ name: "SplitView"
+
+ function initTestCase() {
+ // For the serialization tests.
+ Qt.application.name = "qtquickcontrols2-splitview-auto-test"
+ Qt.application.domain = "org.qt-project"
+ Qt.application.organization = "Qt Project"
+ }
+
+ function findHandles(splitView) {
+ var handles = []
+ for (var i = 0; i < splitView.children.length; ++i) {
+ var child = splitView.children[i]
+ if (child.objectName.toLowerCase().indexOf("handle") !== -1)
+ handles.push(child)
+ }
+ return handles
+ }
+
+ function compareSizes(control, expectedGeometries, context) {
+ if (context === undefined)
+ context = ""
+ else
+ context = " (" + context + ")"
+
+ compare(control.count, Math.floor(expectedGeometries.length / 2) + 1,
+ "Mismatch in actual vs expected number of split items" + context)
+
+ var handles = findHandles(control)
+ compare(handles.length, Math.floor(expectedGeometries.length / 2),
+ "Mismatch in actual vs expected number of handle items" + context)
+
+ for (var i = 0, splitItemIndex = 0, handleItemIndex = 0; i < expectedGeometries.length; ++i) {
+ var item = null
+ var itemType = ""
+ var typeSpecificIndex = -1
+ if (i % 2 == 0) {
+ item = control.itemAt(splitItemIndex)
+ itemType = "split item"
+ typeSpecificIndex = splitItemIndex
+ ++splitItemIndex
+ } else {
+ item = handles[handleItemIndex]
+ itemType = "handle item"
+ typeSpecificIndex = handleItemIndex
+ ++handleItemIndex
+ }
+
+ verify(item, itemType + " at index " + typeSpecificIndex + " should not be null" + context)
+
+ var expectedGeometry = expectedGeometries[i]
+ if (expectedGeometry.hasOwnProperty("hidden")) {
+ // It's geometry doesn't matter because it's hidden.
+ verify(!item.visible, itemType + " at index " + typeSpecificIndex + " should be hidden" + context)
+ continue
+ }
+
+ // Note that the indices mentioned here account for handles; they do not
+ // match the indices reported by QQuickSplitView's logging categories.
+ compare(item.x, expectedGeometry.x, "Mismatch in actual vs expected x value of "
+ + itemType + " at index " + typeSpecificIndex + context)
+ compare(item.y, expectedGeometry.y, "Mismatch in actual vs expected y value of "
+ + itemType + " at index " + typeSpecificIndex + context)
+ compare(item.width, expectedGeometry.width, "Mismatch in actual vs expected width value of "
+ + itemType + " at index " + typeSpecificIndex + context)
+ compare(item.height, expectedGeometry.height, "Mismatch in actual vs expected height value of "
+ + itemType + " at index " + typeSpecificIndex + context)
+ }
+ }
+
+ property real defaultHorizontalHandleWidth: 10
+ property real defaultVerticalHandleHeight: 10
+
+
+ Component {
+ id: signalSpyComponent
+ SignalSpy {}
+ }
+
+ Component {
+ id: handleComponent
+ Rectangle {
+ objectName: "handle"
+ implicitWidth: defaultHorizontalHandleWidth
+ implicitHeight: defaultVerticalHandleHeight
+ color: "#444"
+ }
+ }
+
+ SplitView {
+ id: dummyHorizontalSplitView
+ handle: handleComponent
+
+ Item { objectName: "dummyItem" }
+ Item { objectName: "dummyItem" }
+ }
+
+ SplitView {
+ id: dummyVerticalSplitView
+ orientation: Qt.Vertical
+ handle: handleComponent
+
+ Item { objectName: "dummyItem" }
+ Item { objectName: "dummyItem" }
+ }
+
+ Component {
+ id: splitViewComponent
+
+ SplitView {
+ anchors.fill: parent
+ handle: handleComponent
+ }
+ }
+
+ Component {
+ id: rectangleComponent
+
+ Rectangle {}
+ }
+
+ function test_addItemsAfterCompletion() {
+ var control = createTemporaryObject(splitViewComponent, testCase)
+ verify(control)
+
+ var item0 = rectangleComponent.createObject(control, { implicitWidth: 25, color: "salmon" })
+ verify(item0)
+ // The last item fills the width by default, and since there is only one item...
+ compare(item0.x, 0)
+ compare(item0.y, 0)
+ compare(item0.width, testCase.width)
+ compare(item0.height, testCase.height)
+
+ var item1 = rectangleComponent.createObject(control, { implicitWidth: 25, color: "steelblue" })
+ verify(item1)
+ // Now that a second item has been added, the first item goes back to its preferred (implicit) width.
+ compare(item0.x, 0)
+ compare(item0.y, 0)
+ compare(item0.width, item0.implicitWidth)
+ compare(item0.height, testCase.height)
+ var handles = findHandles(control)
+ var handle0 = handles[0]
+ compare(handle0.x, item0.implicitWidth)
+ compare(handle0.y, 0)
+ compare(handle0.width, defaultHorizontalHandleWidth)
+ compare(handle0.height, testCase.height)
+ compare(item1.x, item0.implicitWidth + defaultHorizontalHandleWidth)
+ compare(item1.y, 0)
+ compare(item1.width, testCase.width - item0.implicitWidth - defaultHorizontalHandleWidth)
+ compare(item1.height, testCase.height)
+ }
+
+ function test_addItemsWithNoSizeAfterCompletion() {
+ var control = createTemporaryObject(splitViewComponent, testCase)
+ verify(control)
+
+ var item0 = rectangleComponent.createObject(control, { color: "salmon" })
+ verify(item0)
+ compare(item0.x, 0)
+ compare(item0.y, 0)
+ compare(item0.width, testCase.width)
+ compare(item0.height, testCase.height)
+
+ var item1 = rectangleComponent.createObject(control, { color: "steelblue" })
+ verify(item1)
+ compare(item0.x, 0)
+ compare(item0.y, 0)
+ compare(item0.width, 0)
+ compare(item0.height, testCase.height)
+ var handles = findHandles(control)
+ var handle0 = handles[0]
+ compare(handle0.x, 0)
+ compare(handle0.y, 0)
+ compare(handle0.width, defaultHorizontalHandleWidth)
+ compare(handle0.height, testCase.height)
+ compare(item1.x, defaultHorizontalHandleWidth)
+ compare(item1.y, 0)
+ compare(item1.width, testCase.width - defaultHorizontalHandleWidth)
+ compare(item1.height, testCase.height)
+ }
+
+ Component {
+ id: threeZeroSizedItemsComponent
+
+ SplitView {
+ anchors.fill: parent
+ handle: handleComponent
+
+ Rectangle {
+ objectName: "salmon"
+ color: objectName
+ }
+ Rectangle {
+ objectName: "navajowhite"
+ color: objectName
+ }
+ Rectangle {
+ objectName: "steelblue"
+ color: objectName
+ }
+ }
+ }
+
+ function test_changeAttachedPropertiesAfterCompletion() {
+ var control = createTemporaryObject(threeZeroSizedItemsComponent, testCase)
+ verify(control)
+
+ var item0 = control.itemAt(0)
+ compare(item0.x, 0)
+ compare(item0.y, 0)
+ compare(item0.width, 0)
+ compare(item0.height, testCase.height)
+
+ var handles = findHandles(control)
+ var handle0 = handles[0]
+ compare(handle0.x, 0)
+ compare(handle0.y, 0)
+ compare(handle0.width, defaultHorizontalHandleWidth)
+ compare(handle0.height, testCase.height)
+
+ var item1 = control.itemAt(1)
+ compare(item1.x, defaultHorizontalHandleWidth)
+ compare(item1.y, 0)
+ compare(item1.width, 0)
+ compare(item1.height, testCase.height)
+
+ var handle1 = handles[1]
+ compare(handle1.x, defaultHorizontalHandleWidth)
+ compare(handle1.y, 0)
+ compare(handle1.width, defaultHorizontalHandleWidth)
+ compare(handle1.height, testCase.height)
+
+ var item2 = control.itemAt(2)
+ compare(item2.x, defaultHorizontalHandleWidth * 2)
+ compare(item2.y, 0)
+ compare(item2.width, testCase.width - item2.x)
+ compare(item2.height, testCase.height)
+
+ item0.SplitView.preferredWidth = 25
+ compare(item0.x, 0)
+ compare(item0.y, 0)
+ compare(item0.width, 25)
+ compare(item0.height, testCase.height)
+ compare(handle0.x, item0.width)
+ compare(handle0.y, 0)
+ compare(handle0.width, defaultHorizontalHandleWidth)
+ compare(handle0.height, testCase.height)
+ compare(item1.x, 25 + defaultHorizontalHandleWidth)
+ compare(item1.y, 0)
+ compare(item1.width, 0)
+ compare(item1.height, testCase.height)
+ compare(handle1.x, item1.x + item1.width)
+ compare(handle1.y, 0)
+ compare(handle1.width, defaultHorizontalHandleWidth)
+ compare(handle1.height, testCase.height)
+ compare(item2.x, item1.x + item1.width + defaultHorizontalHandleWidth)
+ compare(item2.y, 0)
+ compare(item2.width, testCase.width - item2.x)
+ compare(item2.height, testCase.height)
+
+ item0.SplitView.minimumWidth = 50
+ compare(item0.x, 0)
+ compare(item0.y, 0)
+ compare(item0.width, 50)
+ compare(item0.height, testCase.height)
+ compare(handle0.x, item0.width)
+ compare(handle0.y, 0)
+ compare(handle0.width, defaultHorizontalHandleWidth)
+ compare(handle0.height, testCase.height)
+ compare(item1.x, 50 + defaultHorizontalHandleWidth)
+ compare(item1.y, 0)
+ compare(item1.width, 0)
+ compare(item1.height, testCase.height)
+ compare(handle1.x, item1.x + item1.width)
+ compare(handle1.y, 0)
+ compare(handle1.width, defaultHorizontalHandleWidth)
+ compare(handle1.height, testCase.height)
+ compare(item2.x, item1.x + item1.width + defaultHorizontalHandleWidth)
+ compare(item2.y, 0)
+ compare(item2.width, testCase.width - item2.x)
+ compare(item2.height, testCase.height)
+
+ item0.SplitView.preferredWidth = 100
+ compare(item0.x, 0)
+ compare(item0.y, 0)
+ compare(item0.width, 100)
+ compare(item0.height, testCase.height)
+ compare(handle0.x, item0.width)
+ compare(handle0.y, 0)
+ compare(handle0.width, defaultHorizontalHandleWidth)
+ compare(handle0.height, testCase.height)
+ compare(item1.x, 100 + defaultHorizontalHandleWidth)
+ compare(item1.y, 0)
+ compare(item1.width, 0)
+ compare(item1.height, testCase.height)
+ compare(handle1.x, item1.x + item1.width)
+ compare(handle1.y, 0)
+ compare(handle1.width, defaultHorizontalHandleWidth)
+ compare(handle1.height, testCase.height)
+ compare(item2.x, item1.x + item1.width + defaultHorizontalHandleWidth)
+ compare(item2.y, 0)
+ compare(item2.width, testCase.width - item2.x)
+ compare(item2.height, testCase.height)
+
+ item0.SplitView.maximumWidth = 75
+ compare(item0.x, 0)
+ compare(item0.y, 0)
+ compare(item0.width, 75)
+ compare(item0.height, testCase.height)
+ compare(handle0.x, item0.width)
+ compare(handle0.y, 0)
+ compare(handle0.width, defaultHorizontalHandleWidth)
+ compare(handle0.height, testCase.height)
+ compare(item1.x, 75 + defaultHorizontalHandleWidth)
+ compare(item1.y, 0)
+ compare(item1.width, 0)
+ compare(item1.height, testCase.height)
+ compare(handle1.x, item1.x + item1.width)
+ compare(handle1.y, 0)
+ compare(handle1.width, defaultHorizontalHandleWidth)
+ compare(handle1.height, testCase.height)
+ compare(item2.x, item1.x + item1.width + defaultHorizontalHandleWidth)
+ compare(item2.y, 0)
+ compare(item2.width, testCase.width - item2.x)
+ compare(item2.height, testCase.height)
+
+ item1.SplitView.fillWidth = true
+ compare(item0.x, 0)
+ compare(item0.y, 0)
+ compare(item0.width, 75)
+ compare(item0.height, testCase.height)
+ compare(handle0.x, item0.width)
+ compare(handle0.y, 0)
+ compare(handle0.width, defaultHorizontalHandleWidth)
+ compare(handle0.height, testCase.height)
+ compare(item1.x, 75 + defaultHorizontalHandleWidth)
+ compare(item1.y, 0)
+ compare(item1.width, testCase.width - 75 - defaultHorizontalHandleWidth * 2)
+ compare(item1.height, testCase.height)
+ compare(handle1.x, item1.x + item1.width)
+ compare(handle1.y, 0)
+ compare(handle1.width, defaultHorizontalHandleWidth)
+ compare(handle1.height, testCase.height)
+ compare(item2.x, testCase.width)
+ compare(item2.y, 0)
+ compare(item2.width, 0)
+ compare(item2.height, testCase.height)
+ }
+
+ function test_useAttachedPropertiesIncorrectly() {
+ var control = createTemporaryObject(splitViewComponent, testCase)
+ verify(control)
+
+ var item = rectangleComponent.createObject(control, { implicitWidth: 25, color: "salmon" })
+ verify(item)
+
+ ignoreWarning(/.*SplitView: attached properties must be accessed through a direct child of SplitView/)
+ testCase.SplitView.fillWidth = true;
+ }
+
+ function test_sizes_data() {
+ var splitViewWidth = testCase.width
+ var splitViewHeight = testCase.height
+ var data = [
+ {
+ // When the combined size of items is too large, the non-fill items should just exceed
+ // the size of the SplitView, exactly as they would were they in a RowLayout, for example.
+ tag: "fillItemOnLeft",
+ expectedGeometries: [
+ // We're the fill item, but since the combined implicitWidths
+ // of the other two items take up all the space, we get none.
+ { x: 0, y: 0, width: 0, height: splitViewHeight },
+ // First handle.
+ { x: 0, y: 0, width: defaultHorizontalHandleWidth, height: splitViewHeight },
+ // The second item does not fill, so its width should be unchanged.
+ { x: defaultHorizontalHandleWidth, y: 0, width: 200, height: splitViewHeight },
+ // Second handle.
+ { x: 200 + defaultHorizontalHandleWidth, y: 0, width: defaultHorizontalHandleWidth,
+ height: splitViewHeight },
+ // The third item also gets its implicitWidth.
+ { x: 200 + defaultHorizontalHandleWidth * 2, y: 0, width: 200, height: splitViewHeight }
+ ]
+ },
+ {
+ // Same as above except vertical.
+ tag: "fillItemOnTop",
+ expectedGeometries: [
+ // We're the fill item, but since the combined implicitHeights
+ // of the other two items take up all the space, we get none.
+ { x: 0, y: 0, width: splitViewWidth, height: 0 },
+ // First handle.
+ { x: 0, y: 0, width: splitViewWidth, height: defaultVerticalHandleHeight },
+ // The second item does not fill, so its height should be unchanged.
+ { x: 0, y: defaultVerticalHandleHeight, width: splitViewWidth, height: 200 },
+ // Second handle.
+ { x: 0, y: 200 + defaultVerticalHandleHeight, width: splitViewWidth,
+ height: defaultVerticalHandleHeight },
+ // The third item also gets its implicitHeight.
+ { x: 0, y: 200 + defaultVerticalHandleHeight * 2, width: splitViewWidth, height: 200 }
+ ]
+ },
+ {
+ tag: "fillItemInMiddle",
+ expectedGeometries: [
+ // Our size is fixed.
+ { x: 0, y: 0, width: 25, height: splitViewHeight },
+ // First handle.
+ { x: 25, y: 0, width: defaultHorizontalHandleWidth, height: splitViewHeight },
+ // The second item fills.
+ { x: 25 + defaultHorizontalHandleWidth, y: 0,
+ width: splitViewWidth - 25 - 200 - defaultHorizontalHandleWidth * 2, height: splitViewHeight },
+ // Second handle.
+ { x: splitViewWidth - 200 - defaultHorizontalHandleWidth, y: 0,
+ width: defaultHorizontalHandleWidth, height: splitViewHeight },
+ // The third item's size is also fixed.
+ { x: splitViewWidth - 200, y: 0, width: 200, height: splitViewHeight }
+ ]
+ }
+ ]
+ return data
+ }
+
+ function test_sizes(data) {
+ var component = Qt.createComponent("splitview/" + data.tag + ".qml")
+ compare(component.status, Component.Ready, component.errorString());
+ var control = createTemporaryObject(component, testCase, { "handle": handleComponent })
+ verify(control)
+
+ compareSizes(control, data.expectedGeometries)
+ }
+
+ Component {
+ id: threeSizedItemsComponent
+
+ SplitView {
+ anchors.fill: parent
+ handle: handleComponent
+
+ Rectangle {
+ objectName: "salmon"
+ color: objectName
+ implicitWidth: 25
+ implicitHeight: 25
+ }
+ Rectangle {
+ objectName: "navajowhite"
+ color: objectName
+ implicitWidth: 100
+ implicitHeight: 100
+ }
+ Rectangle {
+ objectName: "steelblue"
+ color: objectName
+ implicitWidth: 200
+ implicitHeight: 200
+ }
+ }
+ }
+
+ function test_resetAttachedProperties_data() {
+ var splitViewWidth = testCase.width
+ var splitViewHeight = testCase.height
+ var data = [
+ {
+ tag: "resetMinimumWidth",
+ orientation: Qt.Horizontal,
+ // Set the minimumWidth to 50. It should be used instead of implicitWidth since it's greater than 25.
+ splitItemIndex: 0,
+ propertyName: "minimumWidth",
+ propertyValue: 50,
+ expectedGeometriesBefore: [
+ { x: 0, y: 0, width: 50, height: splitViewHeight },
+ { x: 50, y: 0, width: defaultHorizontalHandleWidth, height: splitViewHeight },
+ { x: 50 + defaultHorizontalHandleWidth, y: 0, width: 100, height: splitViewHeight },
+ { x: 50 + 100 + defaultHorizontalHandleWidth, y: 0, width: defaultHorizontalHandleWidth,
+ height: splitViewHeight },
+ { x: 50 + 100 + defaultHorizontalHandleWidth * 2, y: 0,
+ width: splitViewWidth - 50 - 100 - defaultHorizontalHandleWidth * 2, height: splitViewHeight }
+ ],
+ // minimumWidth is now undefined, so implicitWidth should be used instead.
+ expectedGeometriesAfter: [
+ { x: 0, y: 0, width: 25, height: splitViewHeight },
+ { x: 25, y: 0, width: defaultHorizontalHandleWidth, height: splitViewHeight },
+ { x: 25 + defaultHorizontalHandleWidth, y: 0, width: 100, height: splitViewHeight },
+ { x: 25 + 100 + defaultHorizontalHandleWidth, y: 0, width: defaultHorizontalHandleWidth,
+ height: splitViewHeight },
+ { x: 25 + 100 + defaultHorizontalHandleWidth * 2, y: 0,
+ width: splitViewWidth - 25 - 100 - defaultHorizontalHandleWidth * 2, height: splitViewHeight }
+ ]
+ },
+ {
+ tag: "resetMinimumHeight",
+ orientation: Qt.Vertical,
+ // Set the minimumHeight to 50. It should be used instead of implicitHeight since it's greater than 25.
+ splitItemIndex: 0,
+ propertyName: "minimumHeight",
+ propertyValue: 50,
+ expectedGeometriesBefore: [
+ { x: 0, y: 0, width: splitViewWidth, height: 50 },
+ { x: 0, y: 50, width: splitViewWidth, height: defaultVerticalHandleHeight },
+ { x: 0, y: 50 + defaultVerticalHandleHeight, width: splitViewWidth, height: 100 },
+ { x: 0, y: 50 + 100 + defaultVerticalHandleHeight, width: splitViewWidth,
+ height: defaultVerticalHandleHeight },
+ { x: 0, y: 50 + 100 + defaultVerticalHandleHeight * 2, width: splitViewWidth,
+ height: splitViewHeight - 50 - 100 - defaultVerticalHandleHeight * 2 }
+ ],
+ // preferredHeight is now undefined, so implicitHeight should be used instead.
+ expectedGeometriesAfter: [
+ { x: 0, y: 0, width: splitViewWidth, height: 25 },
+ { x: 0, y: 25, width: splitViewWidth, height: defaultVerticalHandleHeight },
+ { x: 0, y: 25 + defaultVerticalHandleHeight, width: splitViewWidth, height: 100 },
+ { x: 0, y: 25 + 100 + defaultVerticalHandleHeight, width: splitViewWidth,
+ height: defaultVerticalHandleHeight },
+ { x: 0, y: 25 + 100 + defaultVerticalHandleHeight * 2, width: splitViewWidth,
+ height: splitViewHeight - 25 - 100 - defaultVerticalHandleHeight * 2 }
+ ]
+ },
+ {
+ tag: "resetPreferredWidth",
+ orientation: Qt.Horizontal,
+ // Set the preferredWidth to 50; it should be used instead of implicitWidth.
+ splitItemIndex: 0,
+ propertyName: "preferredWidth",
+ propertyValue: 50,
+ expectedGeometriesBefore: [
+ { x: 0, y: 0, width: 50, height: splitViewHeight },
+ { x: 50, y: 0, width: defaultHorizontalHandleWidth, height: splitViewHeight },
+ { x: 50 + defaultHorizontalHandleWidth, y: 0, width: 100, height: splitViewHeight },
+ { x: 50 + 100 + defaultHorizontalHandleWidth, y: 0, width: defaultHorizontalHandleWidth,
+ height: splitViewHeight },
+ { x: 50 + 100 + defaultHorizontalHandleWidth * 2, y: 0,
+ width: splitViewWidth - 50 - 100 - defaultHorizontalHandleWidth * 2, height: splitViewHeight }
+ ],
+ // preferredWidth is now undefined, so implicitWidth should be used instead.
+ expectedGeometriesAfter: [
+ { x: 0, y: 0, width: 25, height: splitViewHeight },
+ { x: 25, y: 0, width: defaultHorizontalHandleWidth, height: splitViewHeight },
+ { x: 25 + defaultHorizontalHandleWidth, y: 0, width: 100, height: splitViewHeight },
+ { x: 25 + 100 + defaultHorizontalHandleWidth, y: 0, width: defaultHorizontalHandleWidth,
+ height: splitViewHeight },
+ { x: 25 + 100 + defaultHorizontalHandleWidth * 2, y: 0,
+ width: splitViewWidth - 25 - 100 - defaultHorizontalHandleWidth * 2, height: splitViewHeight }
+ ]
+ },
+ {
+ tag: "resetPreferredHeight",
+ orientation: Qt.Vertical,
+ // Set the preferredHeight to 50; it should be used instead of implicitHeight.
+ splitItemIndex: 0,
+ propertyName: "preferredHeight",
+ propertyValue: 50,
+ expectedGeometriesBefore: [
+ { x: 0, y: 0, width: splitViewWidth, height: 50 },
+ { x: 0, y: 50, width: splitViewWidth, height: defaultVerticalHandleHeight },
+ { x: 0, y: 50 + defaultVerticalHandleHeight, width: splitViewWidth, height: 100 },
+ { x: 0, y: 50 + 100 + defaultVerticalHandleHeight, width: splitViewWidth,
+ height: defaultVerticalHandleHeight },
+ { x: 0, y: 50 + 100 + defaultVerticalHandleHeight * 2, width: splitViewWidth,
+ height: splitViewHeight - 50 - 100 - defaultVerticalHandleHeight * 2 }
+ ],
+ // preferredHeight is now undefined, so implicitHeight should be used instead.
+ expectedGeometriesAfter: [
+ { x: 0, y: 0, width: splitViewWidth, height: 25 },
+ { x: 0, y: 25, width: splitViewWidth, height: defaultVerticalHandleHeight },
+ { x: 0, y: 25 + defaultVerticalHandleHeight, width: splitViewWidth, height: 100 },
+ { x: 0, y: 25 + 100 + defaultVerticalHandleHeight, width: splitViewWidth,
+ height: defaultVerticalHandleHeight },
+ { x: 0, y: 25 + 100 + defaultVerticalHandleHeight * 2, width: splitViewWidth,
+ height: splitViewHeight - 25 - 100 - defaultVerticalHandleHeight * 2 }
+ ]
+ },
+ {
+ tag: "resetMaximumWidth",
+ orientation: Qt.Horizontal,
+ // Set the maximumWidth to 15. It should be used instead of implicitWidth since it's less than 25.
+ splitItemIndex: 0,
+ propertyName: "maximumWidth",
+ propertyValue: 15,
+ expectedGeometriesBefore: [
+ { x: 0, y: 0, width: 15, height: splitViewHeight },
+ { x: 15, y: 0, width: defaultHorizontalHandleWidth, height: splitViewHeight },
+ { x: 15 + defaultHorizontalHandleWidth, y: 0, width: 100, height: splitViewHeight },
+ { x: 15 + 100 + defaultHorizontalHandleWidth, y: 0, width: defaultHorizontalHandleWidth,
+ height: splitViewHeight },
+ { x: 15 + 100 + defaultHorizontalHandleWidth * 2, y: 0,
+ width: splitViewWidth - 15 - 100 - defaultHorizontalHandleWidth * 2, height: splitViewHeight }
+ ],
+ // maximumWidth is now undefined, so implicitWidth should be used instead.
+ expectedGeometriesAfter: [
+ { x: 0, y: 0, width: 25, height: splitViewHeight },
+ { x: 25, y: 0, width: defaultHorizontalHandleWidth, height: splitViewHeight },
+ { x: 25 + defaultHorizontalHandleWidth, y: 0, width: 100, height: splitViewHeight },
+ { x: 25 + 100 + defaultHorizontalHandleWidth, y: 0, width: defaultHorizontalHandleWidth,
+ height: splitViewHeight },
+ { x: 25 + 100 + defaultHorizontalHandleWidth * 2, y: 0,
+ width: splitViewWidth - 25 - 100 - defaultHorizontalHandleWidth * 2, height: splitViewHeight }
+ ]
+ },
+ {
+ tag: "resetMaximumHeight",
+ orientation: Qt.Vertical,
+ // Set the preferredHeight to 15. It should be used instead of implicitHeight if it's not undefined.
+ splitItemIndex: 0,
+ propertyName: "maximumHeight",
+ propertyValue: 15,
+ expectedGeometriesBefore: [
+ { x: 0, y: 0, width: splitViewWidth, height: 15 },
+ { x: 0, y: 15, width: splitViewWidth, height: defaultVerticalHandleHeight },
+ { x: 0, y: 15 + defaultVerticalHandleHeight, width: splitViewWidth, height: 100 },
+ { x: 0, y: 15 + 100 + defaultVerticalHandleHeight, width: splitViewWidth,
+ height: defaultVerticalHandleHeight },
+ { x: 0, y: 15 + 100 + defaultVerticalHandleHeight * 2, width: splitViewWidth,
+ height: splitViewHeight - 15 - 100 - defaultVerticalHandleHeight * 2 }
+ ],
+ // preferredHeight is now undefined, so implicitHeight should be used instead.
+ expectedGeometriesAfter: [
+ { x: 0, y: 0, width: splitViewWidth, height: 25 },
+ { x: 0, y: 25, width: splitViewWidth, height: defaultVerticalHandleHeight },
+ { x: 0, y: 25 + defaultVerticalHandleHeight, width: splitViewWidth, height: 100 },
+ { x: 0, y: 25 + 100 + defaultVerticalHandleHeight, width: splitViewWidth,
+ height: defaultVerticalHandleHeight },
+ { x: 0, y: 25 + 100 + defaultVerticalHandleHeight * 2, width: splitViewWidth,
+ height: splitViewHeight - 25 - 100 - defaultVerticalHandleHeight * 2 }
+ ]
+ },
+ ]
+ return data;
+ }
+
+ function test_resetAttachedProperties(data) {
+ var control = createTemporaryObject(threeSizedItemsComponent, testCase,
+ { "orientation": data.orientation })
+ verify(control)
+
+ var splitItem = control.itemAt(data.splitItemIndex)
+ splitItem.SplitView[data.propertyName] = data.propertyValue
+ compareSizes(control, data.expectedGeometriesBefore, "after setting attached property")
+
+ splitItem.SplitView[data.propertyName] = undefined
+ compareSizes(control, data.expectedGeometriesAfter, "after resetting attached property")
+ }
+
+ function test_orientation() {
+ var control = createTemporaryObject(threeSizedItemsComponent, testCase)
+ verify(control)
+
+ var item0 = control.itemAt(0)
+ compare(item0.x, 0)
+ compare(item0.y, 0)
+ compare(item0.width, item0.implicitWidth)
+ compare(item0.height, testCase.height)
+
+ var handles = findHandles(control)
+ var handle0 = handles[0]
+ compare(handle0.x, item0.implicitWidth)
+ compare(handle0.y, 0)
+ compare(handle0.width, defaultHorizontalHandleWidth)
+ compare(handle0.height, testCase.height)
+
+ var item1 = control.itemAt(1)
+ compare(item1.x, item0.width + defaultHorizontalHandleWidth)
+ compare(item1.y, 0)
+ compare(item1.width, item1.implicitWidth)
+ compare(item1.height, testCase.height)
+
+ var handle1 = handles[1]
+ compare(handle1.x, item1.x + item1.width)
+ compare(handle1.y, 0)
+ compare(handle1.width, defaultHorizontalHandleWidth)
+ compare(handle1.height, testCase.height)
+
+ var item2 = control.itemAt(2)
+ compare(item2.x, item0.width + item1.width + defaultHorizontalHandleWidth * 2)
+ compare(item2.y, 0)
+ compare(item2.width, testCase.width - item2.x)
+ compare(item2.height, testCase.height)
+
+ control.orientation = Qt.Vertical
+ compare(item0.x, 0)
+ compare(item0.y, 0)
+ compare(item0.width, testCase.width)
+ compare(item0.height, item0.implicitHeight)
+ handles = findHandles(control)
+ handle0 = handles[0]
+ compare(handle0.x, 0)
+ compare(handle0.y, item0.implicitHeight)
+ compare(handle0.width, testCase.width)
+ compare(handle0.height, defaultVerticalHandleHeight)
+ compare(item1.x, 0)
+ compare(item1.y, item0.height + defaultVerticalHandleHeight)
+ compare(item1.width, testCase.width)
+ compare(item1.height, item1.implicitHeight)
+ handle1 = handles[1]
+ compare(handle1.x, 0)
+ compare(handle1.y, item1.y + item1.height)
+ compare(handle1.width, testCase.width)
+ compare(handle1.height, defaultVerticalHandleHeight)
+ compare(item2.x, 0)
+ compare(item2.y, item0.height + item1.height + defaultVerticalHandleHeight * 2)
+ compare(item2.width, testCase.width)
+ compare(item2.height, testCase.height - item2.y)
+ }
+
+ readonly property int splitViewMargins: 50
+
+ Component {
+ id: threeItemsMinSizeAndFillComponent
+
+ SplitView {
+ anchors.fill: parent
+ handle: handleComponent
+
+ Rectangle {
+ objectName: "salmon"
+ color: objectName
+ implicitWidth: 25
+ implicitHeight: 25
+ SplitView.minimumWidth: 25
+ SplitView.minimumHeight: 25
+ SplitView.fillWidth: true
+ SplitView.fillHeight: true
+ }
+ Rectangle {
+ objectName: "navajowhite"
+ color: objectName
+ implicitWidth: 100
+ implicitHeight: 100
+ }
+ Rectangle {
+ objectName: "steelblue"
+ color: objectName
+ implicitWidth: 200
+ implicitHeight: 200
+ }
+ }
+ }
+
+ function test_dragHandle_data() {
+ var splitViewWidth = testCase.width - splitViewMargins * 2
+ var splitViewHeight = testCase.height - splitViewMargins * 2
+ var data = [
+ {
+ tag: "fillThirdItemAndDragFirstHandlePastRightSide",
+ component: threeSizedItemsComponent,
+ orientation: Qt.Horizontal,
+ // The index of the item that will fill.
+ fillIndex: 2,
+ // The index of the handle to be dragged.
+ handleIndex: 0,
+ // The position where the center of the handle will be.
+ newHandlePos: Qt.point(testCase.width + 20, testCase.height / 2),
+ // The expected geometry of each item managed by the SplitView before dragging the handle.
+ expectedGeometriesBeforeDrag: [
+ // First item.
+ { x: 0, y: 0, width: 25, height: splitViewHeight },
+ // First handle.
+ { x: 25, y: 0, width: defaultHorizontalHandleWidth, height: splitViewHeight },
+ // Second item.
+ { x: 25 + defaultHorizontalHandleWidth, y: 0, width: 100, height: splitViewHeight },
+ // Second handle.
+ { x: 25 + 100 + defaultHorizontalHandleWidth, y: 0, width: defaultHorizontalHandleWidth, height: splitViewHeight },
+ // Third item (fills).
+ { x: 25 + 100 + defaultHorizontalHandleWidth * 2, y: 0,
+ width: splitViewWidth - 25 - 100 - defaultHorizontalHandleWidth * 2, height: splitViewHeight }
+ ],
+ // The expected geometry of each item managed by the SplitView after dragging the handle.
+ expectedGeometriesAfterDrag: [
+ // The fill item is to the right of the handle at index 0, so the handle belongs
+ // to the left item: us. We should consume all of the fill item's width.
+ { x: 0, y: 0, width: splitViewWidth - 100 - defaultHorizontalHandleWidth * 2,
+ height: splitViewHeight },
+ // First handle.
+ { x: splitViewWidth - defaultHorizontalHandleWidth * 2 - 100,
+ y: 0, width: defaultHorizontalHandleWidth, height: splitViewHeight },
+ // The second item does not fill, so its width should be unchanged.
+ { x: splitViewWidth - 100 - defaultHorizontalHandleWidth,
+ y: 0, width: 100, height: splitViewHeight },
+ // Second handle.
+ { x: splitViewWidth - defaultHorizontalHandleWidth,
+ y: 0, width: defaultHorizontalHandleWidth, height: splitViewHeight },
+ // The last item does fill, so it should lose all of its width.
+ { x: splitViewWidth, y: 0, width: 0, height: splitViewHeight }
+ ]
+ },
+ {
+ tag: "fillThirdItemAndDragFirstHandlePastBottomSide",
+ component: threeSizedItemsComponent,
+ orientation: Qt.Vertical,
+ fillIndex: 2,
+ handleIndex: 0,
+ newHandlePos: Qt.point(testCase.width / 2, testCase.height + 20),
+ expectedGeometriesBeforeDrag: [
+ { x: 0, y: 0, width: splitViewWidth, height: 25 },
+ { x: 0, y: 25, width: splitViewWidth, height: defaultVerticalHandleHeight },
+ { x: 0, y: 25 + defaultVerticalHandleHeight, width: splitViewWidth, height: 100 },
+ { x: 0, y: 25 + 100 + defaultVerticalHandleHeight, width: splitViewWidth, height: defaultVerticalHandleHeight },
+ { x: 0, y: 25 + 100 + defaultVerticalHandleHeight * 2,
+ width: splitViewWidth, height: splitViewHeight - 25 - 100 - defaultVerticalHandleHeight * 2 }
+ ],
+ // The expected geometry of each item managed by the SplitView after dragging the handle.
+ expectedGeometriesAfterDrag: [
+ // The fill item is to the bottom of the handle at index 0, so the handle belongs
+ // to the top item: us. We should consume all of the fill item's width.
+ { x: 0, y: 0, width: splitViewWidth,
+ height: splitViewHeight - 100 - defaultVerticalHandleHeight * 2 },
+ // First handle.
+ { x: 0, y: splitViewHeight - defaultVerticalHandleHeight * 2 - 100,
+ width: splitViewWidth, height: defaultVerticalHandleHeight },
+ // The second item does not fill, so its height should be unchanged.
+ { x: 0, y: splitViewWidth - 100 - defaultVerticalHandleHeight,
+ width: splitViewWidth, height: 100 },
+ // Second handle.
+ { x: 0, y: splitViewHeight - defaultVerticalHandleHeight,
+ width: splitViewWidth, height: defaultVerticalHandleHeight },
+ // The last item does fill, so it should lose all of its width.
+ { x: 0, y: splitViewHeight, width: splitViewWidth, height: 0 }
+ ]
+ },
+ {
+ tag: "fillThirdItemAndDragSecondHandlePastLeftSide",
+ component: threeSizedItemsComponent,
+ orientation: Qt.Horizontal,
+ fillIndex: 2,
+ handleIndex: 1,
+ newHandlePos: Qt.point(-20, testCase.height / 2),
+ expectedGeometriesBeforeDrag: [
+ { x: 0, y: 0, width: 25, height: splitViewHeight },
+ { x: 25, y: 0, width: defaultHorizontalHandleWidth, height: splitViewHeight },
+ { x: 25 + defaultHorizontalHandleWidth, y: 0, width: 100, height: splitViewHeight },
+ { x: 25 + 100 + defaultHorizontalHandleWidth, y: 0, width: defaultHorizontalHandleWidth, height: splitViewHeight },
+ { x: 25 + 100 + defaultHorizontalHandleWidth * 2, y: 0,
+ width: splitViewWidth - 25 - 100 - defaultHorizontalHandleWidth * 2, height: splitViewHeight }
+ ],
+ expectedGeometriesAfterDrag: [
+ // The fill item is to the right of the handle at index 1, so the handle belongs
+ // to the second item; our width should be unchanged.
+ { x: 0, y: 0, width: 25, height: splitViewHeight },
+ // First handle.
+ { x: 25, y: 0, width: defaultHorizontalHandleWidth, height: splitViewHeight },
+ // The second item is the one being resized, and since we're dragging its handle
+ // to the left, its width should decrease.
+ { x: 25 + defaultHorizontalHandleWidth, y: 0, width: 0, height: splitViewHeight },
+ // Second handle.
+ { x: 25 + defaultHorizontalHandleWidth, y: 0, width: defaultHorizontalHandleWidth,
+ height: splitViewHeight },
+ // The last item fills, so it should get the second item's lost width.
+ { x: 25 + defaultHorizontalHandleWidth * 2, y: 0,
+ width: splitViewWidth - 25 - defaultHorizontalHandleWidth * 2, height: splitViewHeight }
+ ]
+ },
+ {
+ tag: "fillThirdItemAndDragSecondHandlePastTopSide",
+ component: threeSizedItemsComponent,
+ orientation: Qt.Vertical,
+ fillIndex: 2,
+ handleIndex: 1,
+ newHandlePos: Qt.point(testCase.width / 2, -20),
+ expectedGeometriesBeforeDrag: [
+ { x: 0, y: 0, width: splitViewWidth, height: 25 },
+ { x: 0, y: 25, width: splitViewWidth, height: defaultVerticalHandleHeight },
+ { x: 0, y: 25 + defaultVerticalHandleHeight, width: splitViewWidth, height: 100 },
+ { x: 0, y: 25 + 100 + defaultVerticalHandleHeight, width: splitViewWidth, height: defaultVerticalHandleHeight },
+ { x: 0, y: 25 + 100 + defaultVerticalHandleHeight * 2,
+ width: splitViewWidth, height: splitViewHeight - 25 - 100 - defaultVerticalHandleHeight * 2 }
+ ],
+ expectedGeometriesAfterDrag: [
+ // The fill item is to the bottom of the handle at index 1, so the handle belongs
+ // to the second item; our height should be unchanged.
+ { x: 0, y: 0, width: splitViewWidth, height: 25 },
+ // First handle.
+ { x: 0, y: 25, width: splitViewWidth, height: defaultVerticalHandleHeight },
+ // The second item is the one being resized, and since we're dragging its handle
+ // to the top, its height should decrease.
+ { x: 0, y: 25 + defaultVerticalHandleHeight, width: splitViewWidth, height: 0 },
+ // Second handle.
+ { x: 0, y: 25 + defaultVerticalHandleHeight, width: splitViewWidth,
+ height: defaultVerticalHandleHeight },
+ // The last item fills, so it should get the second item's lost height.
+ { x: 0, y: 25 + defaultVerticalHandleHeight * 2,
+ width: splitViewWidth, height: splitViewHeight - 25 - defaultVerticalHandleHeight * 2 }
+ ]
+ },
+ {
+ // First item should start off empty and then eventually take up all of 3rd item's space
+ // as the handle is dragged past the right side.
+ tag: "fillFirstItemAndDragSecondHandlePastRightSide",
+ component: threeSizedItemsComponent,
+ orientation: Qt.Horizontal,
+ fillIndex: 0,
+ handleIndex: 1,
+ newHandlePos: Qt.point(testCase.width + 20, testCase.height / 2),
+ expectedGeometriesBeforeDrag: [
+ { x: 0, y: 0, width: 0, height: splitViewHeight },
+ { x: 0, y: 0, width: defaultHorizontalHandleWidth, height: splitViewHeight },
+ { x: defaultHorizontalHandleWidth, y: 0, width: 100, height: splitViewHeight },
+ { x: 100 + defaultHorizontalHandleWidth, y: 0, width: defaultHorizontalHandleWidth, height: splitViewHeight },
+ { x: 100 + defaultHorizontalHandleWidth * 2, y: 0, width: 200, height: splitViewHeight }
+ ],
+ expectedGeometriesAfterDrag: [
+ // The fill item is to the left of the handle at index 1, so the handle belongs
+ // to the third item. Since we're moving the handle to the right side of the
+ // SplitView, our width should grow as we consume the width of the third item.
+ { x: 0, y: 0, width: splitViewWidth - 100 - defaultHorizontalHandleWidth * 2, height: splitViewHeight },
+ // First handle.
+ { x: splitViewWidth - 100 - defaultHorizontalHandleWidth * 2, y: 0,
+ width: defaultHorizontalHandleWidth, height: splitViewHeight },
+ // The second item's width remains unchanged.
+ { x: splitViewWidth - 100 - defaultHorizontalHandleWidth, y: 0, width: 100, height: splitViewHeight },
+ // Second handle.
+ { x: splitViewWidth - defaultHorizontalHandleWidth, y: 0,
+ width: defaultHorizontalHandleWidth, height: splitViewHeight },
+ // The last item loses its width.
+ { x: splitViewWidth, y: 0, width: 0, height: splitViewHeight }
+ ]
+ },
+ {
+ // First item should start off empty and then eventually take up all of 3rd item's space
+ // as the handle is dragged past the bottom side.
+ tag: "fillFirstItemAndDragSecondHandlePastBottomSide",
+ component: threeSizedItemsComponent,
+ orientation: Qt.Vertical,
+ fillIndex: 0,
+ handleIndex: 1,
+ newHandlePos: Qt.point(testCase.width / 2, testCase.height + 20),
+ expectedGeometriesBeforeDrag: [
+ { x: 0, y: 0, width: splitViewWidth, height: 0 },
+ { x: 0, y: 0, width: splitViewWidth, height: defaultVerticalHandleHeight },
+ { x: 0, y: defaultVerticalHandleHeight, width: splitViewWidth, height: 100 },
+ { x: 0, y: 100 + defaultVerticalHandleHeight, width: splitViewWidth, height: defaultVerticalHandleHeight },
+ { x: 0, y: 100 + defaultVerticalHandleHeight * 2, width: splitViewWidth, height: 200 }
+ ],
+ expectedGeometriesAfterDrag: [
+ // The fill item is to the top of the handle at index 1, so the handle belongs
+ // to the third item. Since we're moving the handle to the bottom side of the
+ // SplitView, our height should grow as we consume the height of the third item.
+ { x: 0, y: 0, width: splitViewWidth, height: splitViewHeight - 100 - defaultVerticalHandleHeight * 2 },
+ // First handle.
+ { x: 0, y: splitViewHeight - 100 - defaultVerticalHandleHeight * 2,
+ width: splitViewWidth, height: defaultVerticalHandleHeight },
+ // The second item's width remains unchanged.
+ { x: 0, y: splitViewHeight - 100 - defaultVerticalHandleHeight, width: splitViewWidth, height: 100 },
+ // Second handle.
+ { x: 0, y: splitViewHeight - defaultVerticalHandleHeight,
+ width: splitViewWidth, height: defaultVerticalHandleHeight },
+ // The last item loses its width.
+ { x: 0, y: splitViewHeight, width: splitViewHeight, height: 0 }
+ ]
+ },
+ {
+ tag: "fillFirstItemAndDragFirstHandlePastLeftSide",
+ component: threeSizedItemsComponent,
+ orientation: Qt.Horizontal,
+ fillIndex: 0,
+ handleIndex: 0,
+ newHandlePos: Qt.point(-20, testCase.height / 2),
+ expectedGeometriesBeforeDrag: [
+ { x: 0, y: 0, width: 0, height: splitViewHeight },
+ { x: 0, y: 0, width: defaultHorizontalHandleWidth, height: splitViewHeight },
+ { x: defaultHorizontalHandleWidth, y: 0, width: 100, height: splitViewHeight },
+ { x: 100 + defaultHorizontalHandleWidth, y: 0, width: defaultHorizontalHandleWidth, height: splitViewHeight },
+ // The second item's implicitWidth is 100, and ours is 200. The available width is 300,
+ // so both items get their implicit widths.
+ { x: 100 + defaultHorizontalHandleWidth * 2, y: 0, width: splitViewWidth - 100, height: splitViewHeight }
+ ],
+ // Should be unchanged.
+ expectedGeometriesAfterDrag: [
+ { x: 0, y: 0, width: 0, height: splitViewHeight },
+ { x: 0, y: 0, width: defaultHorizontalHandleWidth, height: splitViewHeight },
+ { x: defaultHorizontalHandleWidth, y: 0, width: 100, height: splitViewHeight },
+ { x: 100 + defaultHorizontalHandleWidth, y: 0, width: defaultHorizontalHandleWidth, height: splitViewHeight },
+ { x: 100 + defaultHorizontalHandleWidth * 2, y: 0, width: splitViewWidth - 100, height: splitViewHeight }
+ ]
+ },
+ {
+ tag: "fillFirstItemWithMinWidthAndDragFirstHandlePastLeftSide",
+ component: threeItemsMinSizeAndFillComponent,
+ orientation: Qt.Horizontal,
+ fillIndex: 0,
+ handleIndex: 0,
+ newHandlePos: Qt.point(-20, testCase.height / 2),
+ expectedGeometriesBeforeDrag: [
+ { x: 0, y: 0, width: 25, height: splitViewHeight },
+ { x: 25, y: 0, width: defaultHorizontalHandleWidth, height: splitViewHeight },
+ { x: 25 + defaultHorizontalHandleWidth, y: 0, width: 100, height: splitViewHeight },
+ { x: 25 + 100 + defaultHorizontalHandleWidth, y: 0, width: defaultHorizontalHandleWidth, height: splitViewHeight },
+ { x: 25 + 100 + defaultHorizontalHandleWidth * 2, y: 0, width: splitViewWidth - 100, height: splitViewHeight }
+ ],
+ // Should be unchanged.
+ expectedGeometriesAfterDrag: [
+ { x: 0, y: 0, width: 25, height: splitViewHeight },
+ { x: 25, y: 0, width: defaultHorizontalHandleWidth, height: splitViewHeight },
+ { x: 25 + defaultHorizontalHandleWidth, y: 0, width: 100, height: splitViewHeight },
+ { x: 25 + 100 + defaultHorizontalHandleWidth, y: 0, width: defaultHorizontalHandleWidth, height: splitViewHeight },
+ { x: 25 + 100 + defaultHorizontalHandleWidth * 2, y: 0, width: splitViewWidth - 100, height: splitViewHeight }
+ ]
+ }
+ ]
+ return data
+ }
+
+ function test_dragHandle(data) {
+ var control = createTemporaryObject(data.component, testCase)
+ verify(control)
+
+ control.orientation = data.orientation
+
+ // Ensure that there is space to drag outside of the SplitView.
+ control.anchors.margins = splitViewMargins
+
+ var fillItem = control.itemAt(data.fillIndex)
+ if (control.orientation === Qt.Horizontal)
+ fillItem.SplitView.fillWidth = true
+ else
+ fillItem.SplitView.fillHeight = true
+
+ // Check the sizes of the items before the drag.
+ compareSizes(control, data.expectedGeometriesBeforeDrag, "before drag")
+
+ // Drag the handle.
+ var handles = findHandles(control)
+ var targetHandle = handles[data.handleIndex]
+ mousePress(targetHandle)
+ verify(control.resizing)
+ var localPos = testCase.mapToItem(targetHandle, data.newHandlePos.x, data.newHandlePos.y)
+ mouseMove(targetHandle, localPos.x - targetHandle.width / 2, localPos.y - targetHandle.height / 2)
+ verify(control.resizing)
+ compareSizes(control, data.expectedGeometriesAfterDrag, "after drag move")
+
+ // The geometries should remain unchanged after releasing.
+ mouseRelease(targetHandle, localPos.x - targetHandle.width / 2, localPos.y - targetHandle.height / 2, Qt.LeftButton)
+ verify(!control.resizing)
+ compareSizes(control, data.expectedGeometriesAfterDrag, "after drag release")
+ }
+
+ function test_splitViewGeometryChanges_data() {
+ var defaultSplitViewWidth = testCase.width
+ var defaultSplitViewHeight = testCase.height
+
+ var data = [
+ {
+ tag: "growWidth",
+ orientation: Qt.Horizontal,
+ splitViewWidth: 800,
+ expectedGeometries: [
+ { x: 0, y: 0, width: 25, height: defaultSplitViewHeight },
+ { x: 25, y: 0, width: defaultHorizontalHandleWidth, height: defaultSplitViewHeight },
+ { x: 25 + defaultHorizontalHandleWidth, y: 0, width: 100, height: defaultSplitViewHeight },
+ { x: 25 + 100 + defaultHorizontalHandleWidth, y: 0, width: defaultHorizontalHandleWidth,
+ height: defaultSplitViewHeight },
+ { x: 25 + 100 + defaultHorizontalHandleWidth * 2, y: 0,
+ width: 800 - 25 - 100 - defaultHorizontalHandleWidth * 2, height: defaultSplitViewHeight }
+ ]
+ },
+ {
+ // Same as above except vertical.
+ tag: "growHeight",
+ orientation: Qt.Vertical,
+ splitViewHeight: 800,
+ expectedGeometries: [
+ { x: 0, y: 0, width: defaultSplitViewWidth, height: 25 },
+ { x: 0, y: 25, width: defaultSplitViewWidth, height: defaultVerticalHandleHeight },
+ { x: 0, y: 25 + defaultVerticalHandleHeight, width: defaultSplitViewWidth, height: 100 },
+ { x: 0, y: 25 + 100 + defaultVerticalHandleHeight, width: defaultSplitViewWidth,
+ height: defaultVerticalHandleHeight },
+ { x: 0, y: 25 + 100 + defaultVerticalHandleHeight * 2, width: defaultSplitViewWidth,
+ height: 800 - 25 - 100 - defaultVerticalHandleHeight * 2 }
+ ]
+ },
+ {
+ tag: "shrinkWidth",
+ orientation: Qt.Horizontal,
+ splitViewWidth: 200,
+ expectedGeometries: [
+ { x: 0, y: 0, width: 25, height: defaultSplitViewHeight },
+ { x: 25, y: 0, width: defaultHorizontalHandleWidth, height: defaultSplitViewHeight },
+ { x: 25 + defaultHorizontalHandleWidth, y: 0, width: 100, height: defaultSplitViewHeight },
+ { x: 25 + 100 + defaultHorizontalHandleWidth, y: 0, width: defaultHorizontalHandleWidth,
+ height: defaultSplitViewHeight },
+ { x: 25 + 100 + defaultHorizontalHandleWidth * 2, y: 0,
+ width: 200 - 25 - 100 - defaultHorizontalHandleWidth * 2, height: defaultSplitViewHeight }
+ ]
+ },
+ {
+ // Same as above except vertical.
+ tag: "shrinkHeight",
+ orientation: Qt.Vertical,
+ splitViewHeight: 200,
+ expectedGeometries: [
+ { x: 0, y: 0, width: defaultSplitViewWidth, height: 25 },
+ { x: 0, y: 25, width: defaultSplitViewWidth, height: defaultVerticalHandleHeight },
+ { x: 0, y: 25 + defaultVerticalHandleHeight, width: defaultSplitViewWidth, height: 100 },
+ { x: 0, y: 25 + 100 + defaultVerticalHandleHeight, width: defaultSplitViewWidth,
+ height: defaultVerticalHandleHeight },
+ { x: 0, y: 25 + 100 + defaultVerticalHandleHeight * 2, width: defaultSplitViewWidth,
+ height: 200 - 25 - 100 - defaultVerticalHandleHeight * 2 }
+ ]
+ },
+ ]
+ return data
+ }
+
+ function test_splitViewGeometryChanges(data) {
+ var control = createTemporaryObject(threeSizedItemsComponent, testCase,
+ { "handle": handleComponent, "anchors.fill": undefined, "orientation": data.orientation })
+ verify(control)
+
+ if (data.hasOwnProperty("splitViewWidth"))
+ control.width = data.splitViewWidth
+ else
+ control.width = testCase.width
+
+ if (data.hasOwnProperty("splitViewHeight"))
+ control.height = data.splitViewHeight
+ else
+ control.height = testCase.height
+
+ compareSizes(control, data.expectedGeometries)
+ }
+
+ Component {
+ id: largerHandle
+ Rectangle {
+ objectName: "largerHandle"
+ implicitWidth: 20
+ implicitHeight: 20
+ color: "#444"
+ }
+ }
+
+ Component {
+ id: smallerHandle
+ Rectangle {
+ objectName: "smallerHandle"
+ implicitWidth: 5
+ implicitHeight: 5
+ color: "#444"
+ }
+ }
+
+ function test_handleChanges_data() {
+ var splitViewWidth = testCase.width
+ var splitViewHeight = testCase.height
+
+ var data = [
+ {
+ tag: "growHandleWidth",
+ orientation: Qt.Horizontal,
+ handleComponent: largerHandle,
+ expectedGeometries: [
+ { x: 0, y: 0, width: 25, height: splitViewHeight },
+ { x: 25, y: 0, width: 20, height: splitViewHeight },
+ { x: 25 + 20, y: 0, width: 100, height: splitViewHeight },
+ { x: 25 + 100 + 20, y: 0, width: 20, height: splitViewHeight },
+ { x: 25 + 100 + 20 * 2, y: 0, width: splitViewWidth - 25 - 100 - 20 * 2,
+ height: splitViewHeight }
+ ]
+ },
+ {
+ // Same as above except vertical.
+ tag: "growHandleHeight",
+ orientation: Qt.Vertical,
+ handleComponent: largerHandle,
+ expectedGeometries: [
+ { x: 0, y: 0, width: splitViewWidth, height: 25 },
+ { x: 0, y: 25, width: splitViewWidth, height: 20 },
+ { x: 0, y: 25 + 20, width: splitViewWidth, height: 100 },
+ { x: 0, y: 25 + 100 + 20, width: splitViewWidth, height: 20 },
+ { x: 0, y: 25 + 100 + 20 * 2, width: splitViewWidth,
+ height: splitViewHeight - 25 - 100 - 20 * 2 }
+ ]
+ },
+ {
+ tag: "shrinkHandleWidth",
+ orientation: Qt.Horizontal,
+ handleComponent: smallerHandle,
+ expectedGeometries: [
+ { x: 0, y: 0, width: 25, height: splitViewHeight },
+ { x: 25, y: 0, width: 5, height: splitViewHeight },
+ { x: 25 + 5, y: 0, width: 100, height: splitViewHeight },
+ { x: 25 + 100 + 5, y: 0, width: 5, height: splitViewHeight },
+ { x: 25 + 100 + 5 * 2, y: 0, width: splitViewWidth - 25 - 100 - 5 * 2,
+ height: splitViewHeight }
+ ]
+ },
+ {
+ // Same as above except vertical.
+ tag: "shrinkHandleHeight",
+ orientation: Qt.Vertical,
+ handleComponent: smallerHandle,
+ expectedGeometries: [
+ { x: 0, y: 0, width: splitViewWidth, height: 25 },
+ { x: 0, y: 25, width: splitViewWidth, height: 5 },
+ { x: 0, y: 25 + 5, width: splitViewWidth, height: 100 },
+ { x: 0, y: 25 + 100 + 5, width: splitViewWidth, height: 5 },
+ { x: 0, y: 25 + 100 + 5 * 2, width: splitViewWidth,
+ height: splitViewHeight - 25 - 100 - 5 * 2 }
+ ]
+ }
+ ]
+ return data
+ }
+
+ function test_handleChanges(data) {
+ var control = createTemporaryObject(threeSizedItemsComponent, testCase,
+ { "orientation": data.orientation })
+ verify(control)
+
+ control.handle = data.handleComponent
+
+ compareSizes(control, data.expectedGeometries)
+ }
+
+ function test_insertRemoveItems_data() {
+ var splitViewWidth = testCase.width
+ var splitViewHeight = testCase.height
+
+ var data = [
+ {
+ tag: "insertItemAtHorizontalEnd",
+ orientation: Qt.Horizontal,
+ insertItemAtIndex: 3,
+ expectedGeometries: [
+ { x: 0, y: 0, width: 25, height: splitViewHeight },
+ { x: 25, y: 0, width: defaultHorizontalHandleWidth, height: splitViewHeight },
+ { x: 25 + defaultHorizontalHandleWidth, y: 0, width: 100, height: splitViewHeight },
+ { x: 25 + 100 + defaultHorizontalHandleWidth, y: 0,
+ width: defaultHorizontalHandleWidth, height: splitViewHeight },
+ // This was the fill item originally, but since no fill item is explicitly
+ // specified, and we added an item to the right of it, it is no longer the fill item
+ // because it's no longer last.
+ { x: 25 + 100 + defaultHorizontalHandleWidth * 2, y: 0,
+ width: 200, height: splitViewHeight },
+ // Handle for newly added item.
+ { x: 25 + 100 + 200 + defaultHorizontalHandleWidth * 2, y: 0,
+ width: defaultHorizontalHandleWidth, height: splitViewHeight },
+ // Newly added item.
+ { x: 25 + 100 + 200 + defaultHorizontalHandleWidth * 3, y: 0,
+ width: splitViewWidth - 25 - 100 - 200 - defaultHorizontalHandleWidth * 3,
+ height: splitViewHeight },
+ ]
+ },
+ {
+ tag: "insertItemAtHorizontalBeginning",
+ orientation: Qt.Horizontal,
+ insertItemAtIndex: 0,
+ expectedGeometries: [
+ // Newly added item.
+ { x: 0, y: 0, width: 25, height: splitViewHeight },
+ { x: 25, y: 0, width: defaultHorizontalHandleWidth, height: splitViewHeight },
+ { x: 25 + defaultHorizontalHandleWidth, y: 0, width: 25, height: splitViewHeight },
+ { x: 25 * 2 + defaultHorizontalHandleWidth, y: 0,
+ width: defaultHorizontalHandleWidth, height: splitViewHeight },
+ { x: 25 * 2 + defaultHorizontalHandleWidth * 2, y: 0, width: 100, height: splitViewHeight },
+ { x: 25 * 2 + 100 + defaultHorizontalHandleWidth * 2, y: 0,
+ width: defaultHorizontalHandleWidth, height: splitViewHeight },
+ // Fill item doesn't change.
+ { x: 25 * 2 + 100 + defaultHorizontalHandleWidth * 3, y: 0,
+ width: splitViewWidth - 25 * 2 - 100 - defaultHorizontalHandleWidth * 3,
+ height: splitViewHeight },
+ ]
+ },
+ {
+ tag: "removeItemFromHorizontalEnd",
+ orientation: Qt.Horizontal,
+ removeItemAtIndex: 2,
+ expectedGeometries: [
+ { x: 0, y: 0, width: 25, height: splitViewHeight },
+ { x: 25, y: 0, width: defaultHorizontalHandleWidth, height: splitViewHeight },
+ { x: 25 + defaultHorizontalHandleWidth, y: 0,
+ width: splitViewWidth - 25 - defaultHorizontalHandleWidth, height: splitViewHeight },
+ ]
+ },
+ {
+ tag: "removeItemFromHorizontalBeginning",
+ orientation: Qt.Horizontal,
+ removeItemAtIndex: 0,
+ expectedGeometries: [
+ { x: 0, y: 0, width: 100, height: splitViewHeight },
+ { x: 100, y: 0, width: defaultHorizontalHandleWidth, height: splitViewHeight },
+ { x: 100 + defaultHorizontalHandleWidth, y: 0,
+ width: splitViewWidth - 100 - defaultHorizontalHandleWidth, height: splitViewHeight },
+ ]
+ }
+ ]
+ return data
+ }
+
+ Component {
+ id: smallRectComponent
+
+ Rectangle {
+ objectName: "darkseagreen"
+ color: objectName
+ implicitWidth: 25
+ implicitHeight: 25
+ }
+ }
+
+ function test_insertRemoveItems(data) {
+ var control = createTemporaryObject(threeSizedItemsComponent, testCase,
+ { "orientation": data.orientation })
+ verify(control)
+
+ if (data.hasOwnProperty("removeItemAtIndex")) {
+ var itemToRemove = control.itemAt(data.removeItemAtIndex)
+ verify(itemToRemove)
+
+ control.removeItem(itemToRemove)
+ } else if (data.hasOwnProperty("insertItemAtIndex")) {
+ var itemToAdd = smallRectComponent.createObject(control)
+ control.insertItem(data.insertItemAtIndex, itemToAdd)
+ }
+
+ compareSizes(control, data.expectedGeometries)
+ }
+
+ function test_removeAllItems() {
+ var control = createTemporaryObject(threeSizedItemsComponent, testCase)
+ verify(control)
+
+ while (control.count > 0)
+ var itemToRemove = control.removeItem(0)
+ // Shouldn't crash.
+ }
+
+ function test_hideItems_data() {
+ var splitViewWidth = testCase.width
+ var splitViewHeight = testCase.height
+
+ var data = [
+ {
+ tag: "hideItemAtHorizontalEnd",
+ orientation: Qt.Horizontal,
+ hideIndices: [2],
+ expectedGeometries: [
+ { x: 0, y: 0, width: 25, height: splitViewHeight },
+ { x: 25, y: 0, width: defaultHorizontalHandleWidth, height: splitViewHeight },
+ { x: 25 + defaultHorizontalHandleWidth, y: 0,
+ width: splitViewWidth - 25 - defaultHorizontalHandleWidth, height: splitViewHeight },
+ { hidden: true }, // Handle for second item should be hidden.
+ { hidden: true } // Last item should be hidden.
+ ]
+ },
+ {
+ tag: "hideItemAtHorizontalBeginning",
+ orientation: Qt.Horizontal,
+ hideIndices: [0],
+ expectedGeometries: [
+ { hidden: true }, // First item should be hidden.
+ { hidden: true }, // Handle for first item should be hidden.
+ { x: 0, y: 0, width: 100, height: splitViewHeight },
+ { x: 100, y: 0, width: defaultHorizontalHandleWidth, height: splitViewHeight },
+ { x: 100 + defaultHorizontalHandleWidth, y: 0,
+ width: splitViewWidth - 100 - defaultHorizontalHandleWidth, height: splitViewHeight }
+ ]
+ },
+ {
+ tag: "hideItemAtVerticalEnd",
+ orientation: Qt.Vertical,
+ hideIndices: [2],
+ expectedGeometries: [
+ { x: 0, y: 0, width: splitViewWidth, height: 25 },
+ { x: 0, y: 25, width: splitViewWidth, height: defaultVerticalHandleHeight },
+ { x: 0, y: 25 + defaultVerticalHandleHeight,
+ width: splitViewWidth, height: splitViewHeight - 25 - defaultVerticalHandleHeight },
+ { hidden: true }, // Handle for second item should be hidden.
+ { hidden: true } // Last item should be hidden.
+ ]
+ },
+ {
+ tag: "hideItemAtVerticalBeginning",
+ orientation: Qt.Vertical,
+ hideIndices: [0],
+ expectedGeometries: [
+ { hidden: true }, // First item should be hidden.
+ { hidden: true }, // Handle for first item should be hidden.
+ { x: 0, y: 0, width: splitViewWidth, height: 100 },
+ { x: 0, y: 100, width: splitViewWidth, height: defaultVerticalHandleHeight },
+ { x: 0, y: 100 + defaultVerticalHandleHeight,
+ width: splitViewWidth, height: splitViewHeight - 100 - defaultVerticalHandleHeight }
+ ]
+ },
+ {
+ // No handles should be visible when there's only one item.
+ tag: "hideLastTwoHorizontalItems",
+ orientation: Qt.Horizontal,
+ hideIndices: [1, 2],
+ expectedGeometries: [
+ { x: 0, y: 0, width: splitViewWidth, height: splitViewHeight },
+ { hidden: true }, // Handle for first item should be hidden.
+ { hidden: true }, // Second item should be hidden.
+ { hidden: true }, // Handle for second item should be hidden.
+ { hidden: true } // Third item should be hidden.
+ ]
+ }
+ ]
+ return data
+ }
+
+ function test_hideItems(data) {
+ var control = createTemporaryObject(threeSizedItemsComponent, testCase,
+ { "orientation": data.orientation })
+ verify(control)
+
+ for (var i = 0; i < data.hideIndices.length; ++i) {
+ var itemToHide = control.itemAt(data.hideIndices[i])
+ verify(itemToHide)
+ itemToHide.visible = false
+ }
+
+ compareSizes(control, data.expectedGeometries)
+ }
+
+ function test_hideAndShowItems_data() {
+ var splitViewWidth = testCase.width
+ var splitViewHeight = testCase.height
+
+ var data = [
+ {
+ tag: "hideLastTwoHorizontalItems",
+ orientation: Qt.Horizontal,
+ hideIndices: [1, 2],
+ expectedGeometriesAfterHiding: [
+ { x: 0, y: 0, width: splitViewWidth, height: splitViewHeight },
+ { hidden: true }, // Handle for first item should be hidden.
+ { hidden: true }, // Second item should be hidden.
+ { hidden: true }, // Handle for second item should be hidden.
+ { hidden: true } // Third item should be hidden.
+ ],
+ showIndices: [1],
+ expectedGeometriesAfterShowing: [
+ // First item should be visible with its implicit size.
+ { x: 0, y: 0, width: 25, height: splitViewHeight },
+ // Handle for first item should be visible.
+ { x: 25, y: 0, width: defaultHorizontalHandleWidth, height: splitViewHeight },
+ // Second item should be visible and fill.
+ { x: 25 + defaultHorizontalHandleWidth, y: 0,
+ width: splitViewWidth - 25 - defaultHorizontalHandleWidth, height: splitViewHeight },
+ { hidden: true }, // Handle for second item should be hidden.
+ { hidden: true } // Third item should be hidden.
+ ]
+ }
+ ]
+ return data
+ }
+
+ function test_hideAndShowItems(data) {
+ var control = createTemporaryObject(threeSizedItemsComponent, testCase,
+ { "orientation": data.orientation })
+ verify(control)
+
+ for (var i = 0; i < data.hideIndices.length; ++i) {
+ var itemToHide = control.itemAt(data.hideIndices[i])
+ verify(itemToHide)
+ itemToHide.visible = false
+ }
+ compareSizes(control, data.expectedGeometriesAfterHiding, "after hiding")
+
+ for (i = 0; i < data.showIndices.length; ++i) {
+ var itemToShow = control.itemAt(data.showIndices[i])
+ verify(itemToShow)
+ itemToShow.visible = true
+ }
+ compareSizes(control, data.expectedGeometriesAfterShowing, "after showing")
+ }
+
+ function test_moveHiddenItems_data() {
+ var splitViewWidth = testCase.width
+ var splitViewHeight = testCase.height
+
+ var data = [
+ {
+ tag: "hideSecondItemAndMoveItToFirst",
+ orientation: Qt.Horizontal,
+ hideIndices: [1],
+ moveFromIndex: 1,
+ moveToIndex: 0,
+ expectedGeometriesAfterMoving: [
+ { hidden: true }, // First item (was second) should be hidden.
+ { hidden: true }, // Handle for first item should be hidden.
+ // Second item (was first) should get its implicit size.
+ { x: 0, y: 0, width: 25, height: splitViewHeight },
+ { x: 25, y: 0, width: defaultHorizontalHandleWidth, height: splitViewHeight },
+ { x: 25 + defaultHorizontalHandleWidth, y: 0,
+ width: splitViewWidth - 25 - defaultHorizontalHandleWidth, height: splitViewHeight },
+ ],
+ showIndices: [0],
+ expectedGeometriesAfterShowing: [
+ // First item (was second) should be visible with its implicit size.
+ { x: 0, y: 0, width: 100, height: splitViewHeight },
+ // Handle for first item (was second) should be visible.
+ { x: 100, y: 0, width: defaultHorizontalHandleWidth, height: splitViewHeight },
+ // Second item (was first) should be visible with its implicit size.
+ { x: 100 + defaultHorizontalHandleWidth, y: 0,
+ width: 25, height: splitViewHeight },
+ { x: 100 + 25 + defaultHorizontalHandleWidth, y: 0,
+ width: defaultHorizontalHandleWidth, height: splitViewHeight },
+ { x: 100 + 25 + defaultHorizontalHandleWidth * 2, y: 0,
+ width: splitViewWidth - 100 - 25 - defaultHorizontalHandleWidth * 2, height: splitViewHeight }
+ ]
+ }
+ ]
+ return data
+ }
+
+ function test_moveHiddenItems(data) {
+ var control = createTemporaryObject(threeSizedItemsComponent, testCase,
+ { "orientation": data.orientation })
+ verify(control)
+
+ for (var i = 0; i < data.hideIndices.length; ++i) {
+ var itemToHide = control.itemAt(data.hideIndices[i])
+ verify(itemToHide)
+ itemToHide.visible = false
+ }
+
+ control.moveItem(data.moveFromIndex, data.moveToIndex)
+ compareSizes(control, data.expectedGeometriesAfterMoving, "after moving")
+
+ for (i = 0; i < data.showIndices.length; ++i) {
+ var itemToShow = control.itemAt(data.showIndices[i])
+ verify(itemToShow)
+ itemToShow.visible = true
+ }
+ compareSizes(control, data.expectedGeometriesAfterShowing, "after showing")
+ }
+
+ Component {
+ id: flickableComponent
+
+ Flickable {
+ anchors.fill: parent
+ anchors.margins: 100
+ }
+ }
+
+ function test_draggingHandleInFlickable() {
+ var flickable = createTemporaryObject(flickableComponent, testCase)
+ verify(flickable)
+
+ var control = threeSizedItemsComponent.createObject(flickable.contentItem,
+ { "orientation": data.orientation })
+ verify(control)
+
+ control.anchors.fill = undefined
+ control.width = 400
+ control.height = control.parent.height - 100
+ flickable.contentWidth = control.width
+ flickable.contentHeight = control.height
+
+ var contentXSpy = signalSpyComponent.createObject(flickable,
+ { target: flickable, signalName: "contentXChanged" })
+ verify(contentXSpy.valid)
+ var contentYSpy = signalSpyComponent.createObject(flickable,
+ { target: flickable, signalName: "contentYChanged" })
+ verify(contentYSpy.valid)
+
+ // Drag the first handle to the right;
+ // the flickable's contentX and contentY shouldn't change.
+ var firstItem = control.itemAt(0)
+ var firstItemOriginalWidth = firstItem.width
+ var handles = findHandles(control)
+ var firstHandle = handles[0]
+ // Add some vertical movement in there as well.
+ mouseDrag(firstHandle, firstHandle.width / 2, firstHandle.height / 2, 100, 50)
+ compare(contentXSpy.count, 0)
+ compare(contentYSpy.count, 0)
+ verify(firstItem.width > firstItemOriginalWidth)
+
+ // Now do the same except vertically.
+ control.orientation = Qt.Vertical
+ control.width = control.parent.width - 100
+ control.height = 400
+ var firstItemOriginalHeight = firstItem.height
+
+ // Add some horizontal movement in there as well.
+ mouseDrag(firstHandle, firstHandle.width / 2, firstHandle.height / 2, 50, 100)
+ compare(contentXSpy.count, 0)
+ compare(contentYSpy.count, 0)
+ verify(firstItem.height > firstItemOriginalHeight)
+ }
+
+ function test_hoveredPressed() {
+ if ((Qt.platform.pluginName === "offscreen") || (Qt.platform.pluginName === "minimal"))
+ skip("Mouse hovering not functional on offscreen/minimal platforms")
+
+ var control = createTemporaryObject(threeSizedItemsComponent, testCase)
+ verify(control)
+ control.anchors.margins = 50
+
+ var handles = findHandles(control)
+ var firstHandle = handles[0]
+
+ var handleCenter = control.mapFromItem(firstHandle, firstHandle.width / 2, firstHandle.height / 2)
+ // Test fails if we don't do two moves for some reason...
+ mouseMove(control, handleCenter.x, handleCenter.y)
+ mouseMove(control, handleCenter.x, handleCenter.y)
+ verify(firstHandle.SplitHandle.hovered)
+ verify(!firstHandle.SplitHandle.pressed)
+
+ mousePress(control, handleCenter.x, handleCenter.y)
+ verify(firstHandle.SplitHandle.hovered)
+ verify(firstHandle.SplitHandle.pressed)
+
+ mouseRelease(control, handleCenter.x, handleCenter.y)
+ verify(firstHandle.SplitHandle.hovered)
+ verify(!firstHandle.SplitHandle.pressed)
+
+ mouseMove(control, 0, 0)
+ verify(!firstHandle.SplitHandle.hovered)
+ verify(!firstHandle.SplitHandle.pressed)
+ }
+
+ // Tests removing/adding/moving an item while it's pressed.
+ function test_modifyWhileHoveredPressed() {
+ if ((Qt.platform.pluginName === "offscreen") || (Qt.platform.pluginName === "minimal"))
+ skip("Mouse hovering not functional on offscreen/minimal platforms")
+
+ var control = createTemporaryObject(threeSizedItemsComponent, testCase)
+ verify(control)
+ control.anchors.margins = 50
+
+ var handles = findHandles(control)
+ var firstHandle = handles[0]
+
+ // First, ensure that the handle is hovered + pressed.
+ var handleCenter = control.mapFromItem(firstHandle, firstHandle.width / 2, firstHandle.height / 2)
+ // Test fails if we don't do two moves for some reason...
+ mouseMove(control, handleCenter.x, handleCenter.y)
+ mouseMove(control, handleCenter.x, handleCenter.y)
+ verify(firstHandle.SplitHandle.hovered)
+ verify(!firstHandle.SplitHandle.pressed)
+
+ mousePress(control, handleCenter.x, handleCenter.y)
+ verify(firstHandle.SplitHandle.hovered)
+ verify(firstHandle.SplitHandle.pressed)
+
+ // Then, remove it by removing the first item.
+ control.removeItem(0)
+ handles = findHandles(control)
+ firstHandle = null
+ compare(handles.length, 1)
+
+ // No handles should be hovered/pressed.
+ for (var i = 0; i < handles.length; ++i) {
+ var handle = handles[i]
+ verify(!handle.SplitHandle.hovered, "handle at index " + i + " should not be hovered")
+ verify(!handle.SplitHandle.pressed, "handle at index " + i + " should not be hovered")
+ }
+
+ mouseRelease(control, handleCenter.x, handleCenter.y)
+ }
+
+ Component {
+ id: settingsComponent
+ Settings {
+ id: settings
+ }
+ }
+
+ function test_saveAndRestoreState() {
+ var control = createTemporaryObject(threeSizedItemsComponent, testCase)
+ verify(control)
+
+ var lastItem = control.itemAt(2)
+ verify(lastItem)
+ lastItem.SplitView.preferredWidth = 123
+
+ // Save the state.
+ var settings = createTemporaryObject(settingsComponent, testCase)
+ verify(settings)
+ settings.setValue("splitView", control.saveState())
+
+ // Recreate the item to restore it to its "default" values.
+ control = createTemporaryObject(threeSizedItemsComponent, testCase)
+ lastItem = control.itemAt(2)
+ verify(lastItem)
+ compare(lastItem.SplitView.preferredWidth, -1)
+
+ settings = createTemporaryObject(settingsComponent, testCase)
+ verify(settings)
+
+ // Restore the state.
+ control.restoreState(settings.value("splitView"))
+ compare(lastItem.SplitView.preferredWidth, 123)
+ }
+}
diff --git a/tests/manual/testbench/controls/SplitView.qml b/tests/manual/testbench/controls/SplitView.qml
new file mode 100644
index 00000000..7d534de4
--- /dev/null
+++ b/tests/manual/testbench/controls/SplitView.qml
@@ -0,0 +1,79 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, 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.12
+import QtQuick.Controls 2.5
+
+QtObject {
+ property var supportedStates: [
+ []
+ ]
+
+ property Component component: SplitView {
+ implicitWidth: 400
+ implicitHeight: 100
+
+ Rectangle {
+ color: "salmon"
+ implicitWidth: 25
+ implicitHeight: 25
+ }
+ Rectangle {
+ color: "navajowhite"
+ implicitWidth: 100
+ implicitHeight: 100
+ }
+ Rectangle {
+ color: "steelblue"
+ implicitWidth: 200
+ implicitHeight: 200
+ }
+ }
+}
diff --git a/tests/manual/testbench/qml.qrc b/tests/manual/testbench/qml.qrc
index a0927f35..743e6629 100644
--- a/tests/manual/testbench/qml.qrc
+++ b/tests/manual/testbench/qml.qrc
@@ -40,5 +40,6 @@
<file>controls/BusyIndicator.qml</file>
<file>testbench.qml</file>
<file>controls/MenuBar.qml</file>
+ <file>controls/SplitView.qml</file>
</qresource>
</RCC>