aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--examples/quick/layouts/doc/src/qtquicklayouts-examples.qdoc42
-rw-r--r--examples/quick/layouts/layouts.pro14
-rw-r--r--examples/quick/layouts/layouts.qml145
-rw-r--r--examples/quick/layouts/layouts.qmlproject16
-rw-r--r--examples/quick/layouts/layouts.qrc5
-rw-r--r--examples/quick/layouts/main.cpp62
-rw-r--r--examples/quick/quick.pro1
-rw-r--r--src/imports/imports.pro1
-rw-r--r--src/imports/layouts/layouts.pro22
-rw-r--r--src/imports/layouts/plugin.cpp73
-rw-r--r--src/imports/layouts/plugins.qmltypes102
-rw-r--r--src/imports/layouts/qmldir5
-rw-r--r--src/imports/layouts/qquickgridlayoutengine.cpp61
-rw-r--r--src/imports/layouts/qquickgridlayoutengine_p.h164
-rw-r--r--src/imports/layouts/qquicklayout.cpp1081
-rw-r--r--src/imports/layouts/qquicklayout_p.h326
-rw-r--r--src/imports/layouts/qquicklayoutstyleinfo.cpp80
-rw-r--r--src/imports/layouts/qquicklayoutstyleinfo_p.h60
-rw-r--r--src/imports/layouts/qquicklinearlayout.cpp908
-rw-r--r--src/imports/layouts/qquicklinearlayout_p.h250
-rw-r--r--src/imports/layouts/qquickstacklayout.cpp339
-rw-r--r--src/imports/layouts/qquickstacklayout_p.h108
-rw-r--r--src/quick/doc/images/columnlayout.pngbin0 -> 348 bytes
-rw-r--r--src/quick/doc/images/gridlayout.pngbin0 -> 2530 bytes
-rw-r--r--src/quick/doc/images/qtquicklayouts-example-layouts.pngbin0 -> 24795 bytes
-rw-r--r--src/quick/doc/images/rowlayout-minimum.pngbin0 -> 5481 bytes
-rw-r--r--src/quick/doc/images/rowlayout.pngbin0 -> 2627 bytes
-rw-r--r--src/quick/doc/qtquick.qdocconf2
-rw-r--r--src/quick/doc/snippets/qml/windowconstraints.qml76
-rw-r--r--src/quick/doc/src/concepts/layouts/qtquicklayouts-index.qdoc54
-rw-r--r--src/quick/doc/src/concepts/layouts/qtquicklayouts-overview.qdoc176
-rw-r--r--src/quick/doc/src/concepts/layouts/qtquicklayouts.qdoc46
-rw-r--r--src/quick/doc/src/qtquick.qdoc1
-rw-r--r--tests/auto/quick/examples/tst_examples.cpp20
-rw-r--r--tests/auto/quick/qquicklayouts/data/tst_gridlayout.qml1036
-rw-r--r--tests/auto/quick/qquicklayouts/data/tst_rowlayout.qml921
-rw-r--r--tests/auto/quick/qquicklayouts/qquicklayouts.pro13
-rw-r--r--tests/auto/quick/qquicklayouts/tst_qquicklayouts.cpp29
-rw-r--r--tests/auto/quick/quick.pro1
39 files changed, 6232 insertions, 8 deletions
diff --git a/examples/quick/layouts/doc/src/qtquicklayouts-examples.qdoc b/examples/quick/layouts/doc/src/qtquicklayouts-examples.qdoc
new file mode 100644
index 0000000000..23e36c0469
--- /dev/null
+++ b/examples/quick/layouts/doc/src/qtquicklayouts-examples.qdoc
@@ -0,0 +1,42 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Free Documentation License Usage
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of
+** this file. Please review the following information to ensure
+** the GNU Free Documentation License version 1.3 requirements
+** will be met: http://www.gnu.org/copyleft/fdl.html.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+/*!
+ \title Qt Quick Layouts - Basic Example
+ \example layouts
+ \brief Demonstrates how to use layout types to arrange a UI
+ \image qtquicklayouts-example-layouts.png
+ \ingroup qtquickexamples
+
+ This example shows how to easily arrange UI components into
+ \l{Qt Quick Layouts}{layouts} with \l{GridLayout}, \l{RowLayout}, and
+ \l{ColumnLayout}.
+
+ \include examples-run.qdocinc
+*/
+
+
+
diff --git a/examples/quick/layouts/layouts.pro b/examples/quick/layouts/layouts.pro
new file mode 100644
index 0000000000..4d676ef49b
--- /dev/null
+++ b/examples/quick/layouts/layouts.pro
@@ -0,0 +1,14 @@
+TEMPLATE = app
+
+QT += qml quick
+
+SOURCES += main.cpp
+
+RESOURCES += \
+ layouts.qrc
+EXAMPLE_FILES = \
+ layouts.qml
+
+target.path = $$[QT_INSTALL_EXAMPLES]/quick/layouts
+INSTALLS += target
+
diff --git a/examples/quick/layouts/layouts.qml b/examples/quick/layouts/layouts.qml
new file mode 100644
index 0000000000..48299f36aa
--- /dev/null
+++ b/examples/quick/layouts/layouts.qml
@@ -0,0 +1,145 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples 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.2
+import QtQuick.Controls 1.2
+import QtQuick.Layouts 1.3
+
+ApplicationWindow {
+ visible: true
+ title: "Basic layouts"
+ property int margin: 11
+ width: mainLayout.implicitWidth + 2 * margin
+ height: mainLayout.implicitHeight + 2 * margin
+ minimumWidth: mainLayout.Layout.minimumWidth + 2 * margin
+ minimumHeight: mainLayout.Layout.minimumHeight + 2 * margin
+
+ ColumnLayout {
+ id: mainLayout
+ anchors.fill: parent
+ anchors.margins: margin
+ GroupBox {
+ id: rowBox
+ title: "Row layout"
+ Layout.fillWidth: true
+
+ RowLayout {
+ id: rowLayout
+ anchors.fill: parent
+ TextField {
+ placeholderText: "This wants to grow horizontally"
+ Layout.fillWidth: true
+ }
+ Button {
+ text: "Button"
+ }
+ }
+ }
+
+ GroupBox {
+ id: gridBox
+ title: "Grid layout"
+ Layout.fillWidth: true
+
+ GridLayout {
+ id: gridLayout
+ rows: 3
+ flow: GridLayout.TopToBottom
+ anchors.fill: parent
+
+ Label { text: "Line 1" }
+ Label { text: "Line 2" }
+ Label { text: "Line 3" }
+
+ TextField { }
+ TextField { }
+ TextField { }
+
+ TextArea {
+ text: "This widget spans over three rows in the GridLayout.\n"
+ + "All items in the GridLayout are implicitly positioned from top to bottom."
+ Layout.rowSpan: 3
+ Layout.fillHeight: true
+ Layout.fillWidth: true
+ }
+ }
+ }
+ TextArea {
+ id: t3
+ text: "This fills the whole cell"
+ Layout.minimumHeight: 30
+ Layout.fillHeight: true
+ Layout.fillWidth: true
+ }
+ GroupBox {
+ id: stackBox
+ title: "Stack layout"
+ implicitWidth: 200
+ implicitHeight: 60
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ StackLayout {
+ id: stackLayout
+ anchors.fill: parent
+
+ function advance() { currentIndex = (currentIndex + 1) % count }
+
+ Repeater {
+ id: stackRepeater
+ model: 5
+ Rectangle {
+ color: Qt.hsla((0.5 + index)/stackRepeater.count, 0.3, 0.7, 1)
+ Button { anchors.centerIn: parent; text: "Page " + (index + 1); onClicked: { stackLayout.advance() } }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/examples/quick/layouts/layouts.qmlproject b/examples/quick/layouts/layouts.qmlproject
new file mode 100644
index 0000000000..e5a8bf02ca
--- /dev/null
+++ b/examples/quick/layouts/layouts.qmlproject
@@ -0,0 +1,16 @@
+import QmlProject 1.1
+
+Project {
+ mainFile: "main.qml"
+
+ /* Include .qml, .js, and image files from current directory and subdirectories */
+ QmlFiles {
+ directory: "."
+ }
+ JavaScriptFiles {
+ directory: "."
+ }
+ ImageFiles {
+ directory: "."
+ }
+}
diff --git a/examples/quick/layouts/layouts.qrc b/examples/quick/layouts/layouts.qrc
new file mode 100644
index 0000000000..1e01c43fe1
--- /dev/null
+++ b/examples/quick/layouts/layouts.qrc
@@ -0,0 +1,5 @@
+<!DOCTYPE RCC><RCC version="1.0">
+<qresource prefix="/layouts">
+ <file>layouts.qml</file>
+</qresource>
+</RCC>
diff --git a/examples/quick/layouts/main.cpp b/examples/quick/layouts/main.cpp
new file mode 100644
index 0000000000..ef3e27a799
--- /dev/null
+++ b/examples/quick/layouts/main.cpp
@@ -0,0 +1,62 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the examples 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$
+**
+****************************************************************************/
+
+#include <QGuiApplication>
+#include <QQmlApplicationEngine>
+
+int main(int argc, char* argv[])
+{
+ QGuiApplication app(argc, argv);
+
+ QQmlApplicationEngine engine;
+ engine.load(QUrl(QStringLiteral("qrc:///layouts/layouts.qml")));
+
+ return app.exec();
+}
diff --git a/examples/quick/quick.pro b/examples/quick/quick.pro
index c5ef46173c..e66e24882e 100644
--- a/examples/quick/quick.pro
+++ b/examples/quick/quick.pro
@@ -6,6 +6,7 @@ SUBDIRS = quick-accessibility \
canvas \
imageelements \
keyinteraction \
+ layouts \
localstorage \
models \
views \
diff --git a/src/imports/imports.pro b/src/imports/imports.pro
index f7002f9ed5..ff7b6e75af 100644
--- a/src/imports/imports.pro
+++ b/src/imports/imports.pro
@@ -10,6 +10,7 @@ SUBDIRS += \
qtHaveModule(quick) {
SUBDIRS += \
+ layouts \
qtquick2 \
particles \
window \
diff --git a/src/imports/layouts/layouts.pro b/src/imports/layouts/layouts.pro
new file mode 100644
index 0000000000..26574150de
--- /dev/null
+++ b/src/imports/layouts/layouts.pro
@@ -0,0 +1,22 @@
+CXX_MODULE = qml
+TARGET = qquicklayoutsplugin
+TARGETPATH = QtQuick/Layouts
+IMPORT_VERSION = 1.2
+
+QT *= qml-private quick-private gui-private core-private
+
+SOURCES += plugin.cpp \
+ qquicklayout.cpp \
+ qquicklinearlayout.cpp \
+ qquickstacklayout.cpp \
+ qquickgridlayoutengine.cpp \
+ qquicklayoutstyleinfo.cpp
+
+HEADERS += \
+ qquicklayout_p.h \
+ qquicklinearlayout_p.h \
+ qquickstacklayout_p.h \
+ qquickgridlayoutengine_p.h \
+ qquicklayoutstyleinfo_p.h
+
+load(qml_plugin)
diff --git a/src/imports/layouts/plugin.cpp b/src/imports/layouts/plugin.cpp
new file mode 100644
index 0000000000..4552b7219b
--- /dev/null
+++ b/src/imports/layouts/plugin.cpp
@@ -0,0 +1,73 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Quick Layouts module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtQml/qqmlextensionplugin.h>
+
+#include "qquicklinearlayout_p.h"
+#include "qquickstacklayout_p.h"
+
+QT_BEGIN_NAMESPACE
+
+//![class decl]
+class QtQuickLayoutsPlugin : public QQmlExtensionPlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface/1.0")
+public:
+ virtual void registerTypes(const char *uri)
+ {
+ Q_ASSERT(QLatin1String(uri) == QLatin1String("QtQuick.Layouts"));
+ Q_UNUSED(uri);
+
+ qmlRegisterType<QQuickRowLayout>(uri, 1, 0, "RowLayout");
+ qmlRegisterType<QQuickColumnLayout>(uri, 1, 0, "ColumnLayout");
+ qmlRegisterType<QQuickGridLayout>(uri, 1, 0, "GridLayout");
+ qmlRegisterType<QQuickStackLayout>(uri, 1, 3, "StackLayout");
+ qmlRegisterUncreatableType<QQuickLayout>(uri, 1, 0, "Layout",
+ QStringLiteral("Do not create objects of type Layout"));
+ qmlRegisterUncreatableType<QQuickLayout>(uri, 1, 2, "Layout",
+ QStringLiteral("Do not create objects of type Layout"));
+ qmlRegisterRevision<QQuickGridLayoutBase, 1>(uri, 1, 1);
+ }
+};
+//![class decl]
+
+QT_END_NAMESPACE
+
+#include "plugin.moc"
diff --git a/src/imports/layouts/plugins.qmltypes b/src/imports/layouts/plugins.qmltypes
new file mode 100644
index 0000000000..b130215b62
--- /dev/null
+++ b/src/imports/layouts/plugins.qmltypes
@@ -0,0 +1,102 @@
+import QtQuick.tooling 1.2
+
+// This file describes the plugin-supplied types contained in the library.
+// It is used for QML tooling purposes only.
+//
+// This file was auto-generated by:
+// 'qmlplugindump -nonrelocatable QtQuick.Layouts 1.2'
+
+Module {
+ dependencies: []
+ Component {
+ name: "QQuickColumnLayout"
+ defaultProperty: "data"
+ prototype: "QQuickLinearLayout"
+ exports: ["QtQuick.Layouts/ColumnLayout 1.0"]
+ exportMetaObjectRevisions: [0]
+ }
+ Component {
+ name: "QQuickGridLayout"
+ defaultProperty: "data"
+ prototype: "QQuickGridLayoutBase"
+ exports: ["QtQuick.Layouts/GridLayout 1.0"]
+ exportMetaObjectRevisions: [0]
+ Enum {
+ name: "Flow"
+ values: {
+ "LeftToRight": 0,
+ "TopToBottom": 1
+ }
+ }
+ Property { name: "columnSpacing"; type: "double" }
+ Property { name: "rowSpacing"; type: "double" }
+ Property { name: "columns"; type: "int" }
+ Property { name: "rows"; type: "int" }
+ Property { name: "flow"; type: "Flow" }
+ }
+ Component {
+ name: "QQuickGridLayoutBase"
+ defaultProperty: "data"
+ prototype: "QQuickLayout"
+ Property { name: "layoutDirection"; revision: 1; type: "Qt::LayoutDirection" }
+ Signal { name: "layoutDirectionChanged"; revision: 1 }
+ }
+ Component {
+ name: "QQuickLayout"
+ defaultProperty: "data"
+ prototype: "QQuickItem"
+ exports: ["QtQuick.Layouts/Layout 1.0", "QtQuick.Layouts/Layout 1.2"]
+ isCreatable: false
+ exportMetaObjectRevisions: [0, 0]
+ attachedType: "QQuickLayoutAttached"
+ }
+ Component {
+ name: "QQuickLayoutAttached"
+ prototype: "QObject"
+ Property { name: "minimumWidth"; type: "double" }
+ Property { name: "minimumHeight"; type: "double" }
+ Property { name: "preferredWidth"; type: "double" }
+ Property { name: "preferredHeight"; type: "double" }
+ Property { name: "maximumWidth"; type: "double" }
+ Property { name: "maximumHeight"; type: "double" }
+ Property { name: "fillHeight"; type: "bool" }
+ Property { name: "fillWidth"; type: "bool" }
+ Property { name: "row"; type: "int" }
+ Property { name: "column"; type: "int" }
+ Property { name: "rowSpan"; type: "int" }
+ Property { name: "columnSpan"; type: "int" }
+ Property { name: "alignment"; type: "Qt::Alignment" }
+ Property { name: "margins"; type: "double" }
+ Property { name: "leftMargin"; type: "double" }
+ Property { name: "topMargin"; type: "double" }
+ Property { name: "rightMargin"; type: "double" }
+ Property { name: "bottomMargin"; type: "double" }
+ }
+ Component {
+ name: "QQuickLinearLayout"
+ defaultProperty: "data"
+ prototype: "QQuickGridLayoutBase"
+ Property { name: "spacing"; type: "double" }
+ }
+ Component {
+ name: "QQuickRowLayout"
+ defaultProperty: "data"
+ prototype: "QQuickLinearLayout"
+ exports: ["QtQuick.Layouts/RowLayout 1.0"]
+ exportMetaObjectRevisions: [0]
+ }
+ Component {
+ name: "QQuickStackLayout"
+ defaultProperty: "data"
+ prototype: "QQuickLayout"
+ exports: ["QtQuick.Layouts/StackLayout 1.3"]
+ exportMetaObjectRevisions: [0]
+ Property { name: "count"; type: "int"; isReadonly: true }
+ Property { name: "currentIndex"; type: "int" }
+ Method {
+ name: "itemAt"
+ type: "QQuickItem*"
+ Parameter { name: "index"; type: "int" }
+ }
+ }
+}
diff --git a/src/imports/layouts/qmldir b/src/imports/layouts/qmldir
new file mode 100644
index 0000000000..00f85f7d64
--- /dev/null
+++ b/src/imports/layouts/qmldir
@@ -0,0 +1,5 @@
+module QtQuick.Layouts
+plugin qquicklayoutsplugin
+classname QtQuickLayoutsPlugin
+typeinfo plugins.qmltypes
+designersupported
diff --git a/src/imports/layouts/qquickgridlayoutengine.cpp b/src/imports/layouts/qquickgridlayoutengine.cpp
new file mode 100644
index 0000000000..fe716f0694
--- /dev/null
+++ b/src/imports/layouts/qquickgridlayoutengine.cpp
@@ -0,0 +1,61 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Quick Layouts module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickitem.h"
+#include "qquickgridlayoutengine_p.h"
+#include "qquicklayout_p.h"
+
+QT_BEGIN_NAMESPACE
+
+void QQuickGridLayoutEngine::setAlignment(QQuickItem *quickItem, Qt::Alignment alignment)
+{
+ if (QQuickGridLayoutItem *item = findLayoutItem(quickItem)) {
+ item->setAlignment(alignment);
+ invalidate();
+ }
+}
+
+Qt::Alignment QQuickGridLayoutEngine::alignment(QQuickItem *quickItem) const
+{
+ if (QGridLayoutItem *item = findLayoutItem(quickItem))
+ return item->alignment();
+ return 0;
+}
+
+QT_END_NAMESPACE
diff --git a/src/imports/layouts/qquickgridlayoutengine_p.h b/src/imports/layouts/qquickgridlayoutengine_p.h
new file mode 100644
index 0000000000..86f56a5af4
--- /dev/null
+++ b/src/imports/layouts/qquickgridlayoutengine_p.h
@@ -0,0 +1,164 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Quick Layouts module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQUICKGRIDLAYOUTENGINE_P_H
+#define QQUICKGRIDLAYOUTENGINE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of the graphics view layout classes. This header
+// file may change from version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtGui/private/qgridlayoutengine_p.h>
+#include <QtGui/private/qlayoutpolicy_p.h>
+#include <QtCore/qmath.h>
+
+#include "qquickitem.h"
+#include "qquicklayout_p.h"
+#include "qdebug.h"
+QT_BEGIN_NAMESPACE
+
+class QQuickGridLayoutItem : public QGridLayoutItem {
+public:
+ QQuickGridLayoutItem(QQuickItem *item, int row, int column,
+ int rowSpan = 1, int columnSpan = 1, Qt::Alignment alignment = 0)
+ : QGridLayoutItem(row, column, rowSpan, columnSpan, alignment), m_item(item), sizeHintCacheDirty(true), useFallbackToWidthOrHeight(true) {}
+
+
+ QSizeF sizeHint(Qt::SizeHint which, const QSizeF &constraint) const
+ {
+ Q_UNUSED(constraint); // Quick Layouts does not support constraint atm
+ return effectiveSizeHints()[which];
+ }
+
+ QSizeF *effectiveSizeHints() const
+ {
+ if (!sizeHintCacheDirty)
+ return cachedSizeHints;
+
+ QQuickLayout::effectiveSizeHints_helper(m_item, cachedSizeHints, 0, useFallbackToWidthOrHeight);
+ useFallbackToWidthOrHeight = false;
+
+ sizeHintCacheDirty = false;
+ return cachedSizeHints;
+ }
+
+ void setCachedSizeHints(QSizeF *sizeHints)
+ {
+ for (int i = 0; i < Qt::NSizeHints; ++i) {
+ cachedSizeHints[i] = sizeHints[i];
+ }
+ sizeHintCacheDirty = false;
+ }
+
+ void invalidate()
+ {
+ quickLayoutDebug() << "engine::invalidate()";
+ sizeHintCacheDirty = true;
+ }
+
+ QLayoutPolicy::Policy sizePolicy(Qt::Orientation orientation) const
+ {
+ return QQuickLayout::effectiveSizePolicy_helper(m_item, orientation, attachedLayoutObject(m_item, false));
+ }
+
+ void setGeometry(const QRectF &rect)
+ {
+ QQuickLayoutAttached *info = attachedLayoutObject(m_item, false);
+ const QRectF r = info ? rect.marginsRemoved(info->qMargins()) : rect;
+ const QSizeF oldSize(m_item->width(), m_item->height());
+ const QSizeF newSize = r.size();
+ m_item->setPosition(r.topLeft());
+ if (newSize == oldSize) {
+ if (QQuickLayout *lay = qobject_cast<QQuickLayout *>(m_item)) {
+ if (lay->arrangementIsDirty())
+ lay->rearrange(newSize);
+ }
+ } else {
+ m_item->setSize(newSize);
+ }
+ }
+
+ QQuickItem *layoutItem() const { return m_item; }
+
+ QQuickItem *m_item;
+private:
+ mutable QSizeF cachedSizeHints[Qt::NSizeHints];
+ mutable unsigned sizeHintCacheDirty : 1;
+ mutable unsigned useFallbackToWidthOrHeight : 1;
+};
+
+class QQuickGridLayoutEngine : public QGridLayoutEngine {
+public:
+ QQuickGridLayoutEngine() : QGridLayoutEngine(Qt::AlignVCenter, true /*snapToPixelGrid*/) { }
+
+ int indexOf(QQuickItem *item) const {
+ for (int i = 0; i < q_items.size(); ++i) {
+ if (item == static_cast<QQuickGridLayoutItem*>(q_items.at(i))->layoutItem())
+ return i;
+ }
+ return -1;
+ }
+
+ QQuickGridLayoutItem *findLayoutItem(QQuickItem *layoutItem) const
+ {
+ for (int i = q_items.count() - 1; i >= 0; --i) {
+ QQuickGridLayoutItem *item = static_cast<QQuickGridLayoutItem*>(q_items.at(i));
+ if (item->layoutItem() == layoutItem)
+ return item;
+ }
+ return 0;
+ }
+
+ void setAlignment(QQuickItem *quickItem, Qt::Alignment alignment);
+ Qt::Alignment alignment(QQuickItem *quickItem) const;
+
+};
+
+
+
+QT_END_NAMESPACE
+
+#endif // QQUICKGRIDLAYOUTENGINE_P_H
diff --git a/src/imports/layouts/qquicklayout.cpp b/src/imports/layouts/qquicklayout.cpp
new file mode 100644
index 0000000000..d4d4e1703d
--- /dev/null
+++ b/src/imports/layouts/qquicklayout.cpp
@@ -0,0 +1,1081 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Quick Layouts module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquicklayout_p.h"
+#include <QEvent>
+#include <QtCore/qcoreapplication.h>
+#include <QtCore/qnumeric.h>
+#include <QtCore/qmath.h>
+#include <limits>
+
+/*!
+ \qmltype Layout
+ \instantiates QQuickLayoutAttached
+ \inqmlmodule QtQuick.Layouts
+ \ingroup layouts
+ \brief Provides attached properties for items pushed onto a \l GridLayout,
+ \l RowLayout or \l ColumnLayout.
+
+ An object of type Layout is attached to children of the layout to provide layout specific
+ information about the item.
+ The properties of the attached object influence how the layout will arrange the items.
+
+ For instance, you can specify \l minimumWidth, \l preferredWidth, and
+ \l maximumWidth if the default values are not satisfactory.
+
+ When a layout is resized, items may grow or shrink. Due to this, items have a
+ \l{Layout::minimumWidth}{minimum size}, \l{Layout::preferredWidth}{preferred size} and a
+ \l{Layout::maximumWidth}{maximum size}.
+
+ If minimum size has not been explicitly specified on an item, the size is set to \c 0.
+ If maximum size has not been explicitly specified on an item, the size is set to
+ \c Number.POSITIVE_INFINITY.
+
+ For layouts, the implicit minimum and maximum sizes depend on the content of the layouts.
+
+ The \l fillWidth and \l fillHeight properties can either be \c true or \c false. If they are \c
+ false, the item's size will be fixed to its preferred size. Otherwise, it will grow or shrink
+ between its minimum and maximum size as the layout is resized.
+
+ \note It is not recommended to have bindings to the x, y, width, or height properties of items
+ in a layout, since this would conflict with the goals of Layout, and can also cause binding
+ loops.
+
+
+ \sa GridLayout
+ \sa RowLayout
+ \sa ColumnLayout
+*/
+
+QT_BEGIN_NAMESPACE
+
+QQuickLayoutAttached::QQuickLayoutAttached(QObject *parent)
+ : QObject(parent),
+ 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()),
+ m_defaultMargins(0),
+ m_row(-1),
+ m_column(-1),
+ m_rowSpan(1),
+ m_columnSpan(1),
+ m_fillWidth(false),
+ m_fillHeight(false),
+ m_isFillWidthSet(false),
+ m_isFillHeightSet(false),
+ m_isMinimumWidthSet(false),
+ m_isMinimumHeightSet(false),
+ m_isMaximumWidthSet(false),
+ m_isMaximumHeightSet(false),
+ m_changesNotificationEnabled(true),
+ m_isLeftMarginSet(false),
+ m_isTopMarginSet(false),
+ m_isRightMarginSet(false),
+ m_isBottomMarginSet(false),
+ m_alignment(0)
+{
+
+}
+
+/*!
+ \qmlattachedproperty real Layout::minimumWidth
+
+ This property holds the minimum width of an item in a layout.
+ The default value is the item's implicit minimum width.
+
+ If the item is a layout, the implicit minimum width will be the minimum width the layout can
+ have without any of its items shrinking below their minimum width.
+ The implicit minimum width for any other item is \c 0.
+
+ Setting this value to -1 will reset the width back to its implicit minimum width.
+
+
+ \sa preferredWidth
+ \sa maximumWidth
+*/
+void QQuickLayoutAttached::setMinimumWidth(qreal width)
+{
+ if (qIsNaN(width))
+ return;
+ m_isMinimumWidthSet = width >= 0;
+ if (m_minimumWidth == width)
+ return;
+
+ m_minimumWidth = width;
+ invalidateItem();
+ emit minimumWidthChanged();
+}
+
+/*!
+ \qmlattachedproperty real Layout::minimumHeight
+
+ This property holds the minimum height of an item in a layout.
+ The default value is the item's implicit minimum height.
+
+ If the item is a layout, the implicit minimum height will be the minimum height the layout can
+ have without any of its items shrinking below their minimum height.
+ The implicit minimum height for any other item is \c 0.
+
+ Setting this value to -1 will reset the height back to its implicit minimum height.
+
+ \sa preferredHeight
+ \sa maximumHeight
+*/
+void QQuickLayoutAttached::setMinimumHeight(qreal height)
+{
+ if (qIsNaN(height))
+ return;
+ m_isMinimumHeightSet = height >= 0;
+ if (m_minimumHeight == height)
+ return;
+
+ m_minimumHeight = height;
+ invalidateItem();
+ emit minimumHeightChanged();
+}
+
+/*!
+ \qmlattachedproperty real Layout::preferredWidth
+
+ This property holds the preferred width of an item in a layout.
+ If the preferred width is \c -1 it will be ignored, and the layout
+ will use \l{Item::implicitWidth}{implicitWidth} instead.
+ The default is \c -1.
+
+ \sa minimumWidth
+ \sa maximumWidth
+*/
+void QQuickLayoutAttached::setPreferredWidth(qreal width)
+{
+ if (qIsNaN(width) || m_preferredWidth == width)
+ return;
+
+ m_preferredWidth = width;
+ invalidateItem();
+ emit preferredWidthChanged();
+}
+
+/*!
+ \qmlattachedproperty real Layout::preferredHeight
+
+ This property holds the preferred height of an item in a layout.
+ If the preferred height is \c -1 it will be ignored, and the layout
+ will use \l{Item::implicitHeight}{implicitHeight} instead.
+ The default is \c -1.
+
+ \sa minimumHeight
+ \sa maximumHeight
+*/
+void QQuickLayoutAttached::setPreferredHeight(qreal height)
+{
+ if (qIsNaN(height) || m_preferredHeight == height)
+ return;
+
+ m_preferredHeight = height;
+ invalidateItem();
+ emit preferredHeightChanged();
+}
+
+/*!
+ \qmlattachedproperty real Layout::maximumWidth
+
+ This property holds the maximum width of an item in a layout.
+ The default value is the item's implicit maximum width.
+
+ If the item is a layout, the implicit maximum width will be the maximum width the layout can
+ have without any of its items growing beyond their maximum width.
+ The implicit maximum width for any other item is \c Number.POSITIVE_INFINITY.
+
+ Setting this value to \c -1 will reset the width back to its implicit maximum width.
+
+ \sa minimumWidth
+ \sa preferredWidth
+*/
+void QQuickLayoutAttached::setMaximumWidth(qreal width)
+{
+ if (qIsNaN(width))
+ return;
+ m_isMaximumWidthSet = width >= 0;
+ if (m_maximumWidth == width)
+ return;
+
+ m_maximumWidth = width;
+ invalidateItem();
+ emit maximumWidthChanged();
+}
+
+/*!
+ \qmlattachedproperty real Layout::maximumHeight
+
+ The default value is the item's implicit maximum height.
+
+ If the item is a layout, the implicit maximum height will be the maximum height the layout can
+ have without any of its items growing beyond their maximum height.
+ The implicit maximum height for any other item is \c Number.POSITIVE_INFINITY.
+
+ Setting this value to \c -1 will reset the height back to its implicit maximum height.
+
+ \sa minimumHeight
+ \sa preferredHeight
+*/
+void QQuickLayoutAttached::setMaximumHeight(qreal height)
+{
+ if (qIsNaN(height))
+ return;
+ m_isMaximumHeightSet = height >= 0;
+ if (m_maximumHeight == height)
+ return;
+
+ m_maximumHeight = height;
+ invalidateItem();
+ emit maximumHeightChanged();
+}
+
+void QQuickLayoutAttached::setMinimumImplicitSize(const QSizeF &sz)
+{
+ bool emitWidthChanged = false;
+ bool emitHeightChanged = false;
+ if (!m_isMinimumWidthSet && m_minimumWidth != sz.width()) {
+ m_minimumWidth = sz.width();
+ emitWidthChanged = true;
+ }
+ if (!m_isMinimumHeightSet && m_minimumHeight != sz.height()) {
+ m_minimumHeight = sz.height();
+ emitHeightChanged = true;
+ }
+ // Only invalidate the item once, and make sure we emit signal changed after the call to
+ // invalidateItem()
+ if (emitWidthChanged || emitHeightChanged) {
+ invalidateItem();
+ if (emitWidthChanged)
+ emit minimumWidthChanged();
+ if (emitHeightChanged)
+ emit minimumHeightChanged();
+ }
+}
+
+void QQuickLayoutAttached::setMaximumImplicitSize(const QSizeF &sz)
+{
+ bool emitWidthChanged = false;
+ bool emitHeightChanged = false;
+ if (!m_isMaximumWidthSet && m_maximumWidth != sz.width()) {
+ m_maximumWidth = sz.width();
+ emitWidthChanged = true;
+ }
+ if (!m_isMaximumHeightSet && m_maximumHeight != sz.height()) {
+ m_maximumHeight = sz.height();
+ emitHeightChanged = true;
+ }
+ // Only invalidate the item once, and make sure we emit changed signal after the call to
+ // invalidateItem()
+ if (emitWidthChanged || emitHeightChanged) {
+ invalidateItem();
+ if (emitWidthChanged)
+ emit maximumWidthChanged();
+ if (emitHeightChanged)
+ emit maximumHeightChanged();
+ }
+}
+
+/*!
+ \qmlattachedproperty bool Layout::fillWidth
+
+ If this property is \c true, the item will be as wide as possible while respecting
+ the given constraints. If the property is \c false, the item will have a fixed width
+ set to the preferred width.
+ The default is \c false, except for layouts themselves, which default to \c true.
+
+ \sa fillHeight
+*/
+void QQuickLayoutAttached::setFillWidth(bool fill)
+{
+ m_isFillWidthSet = true;
+ if (m_fillWidth != fill) {
+ m_fillWidth = fill;
+ invalidateItem();
+ emit fillWidthChanged();
+ }
+}
+
+/*!
+ \qmlattachedproperty bool Layout::fillHeight
+
+ If this property is \c true, the item will be as tall as possible while respecting
+ the given constraints. If the property is \c false, the item will have a fixed height
+ set to the preferred height.
+ The default is \c false, except for layouts themselves, which default to \c true.
+
+ \sa fillWidth
+*/
+void QQuickLayoutAttached::setFillHeight(bool fill)
+{
+ m_isFillHeightSet = true;
+ if (m_fillHeight != fill) {
+ m_fillHeight = fill;
+ invalidateItem();
+ emit fillHeightChanged();
+ }
+}
+
+/*!
+ \qmlattachedproperty int Layout::row
+
+ This property allows you to specify the row position of an item in a \l GridLayout.
+
+ If both \l column and this property are not set, it is up to the layout to assign a cell to the item.
+
+ The default value is \c 0.
+
+ \sa column
+ \sa rowSpan
+*/
+void QQuickLayoutAttached::setRow(int row)
+{
+ if (row >= 0 && row != m_row) {
+ m_row = row;
+ repopulateLayout();
+ emit rowChanged();
+ }
+}
+
+/*!
+ \qmlattachedproperty int Layout::column
+
+ This property allows you to specify the column position of an item in a \l GridLayout.
+
+ If both \l row and this property are not set, it is up to the layout to assign a cell to the item.
+
+ The default value is \c 0.
+
+ \sa row
+ \sa columnSpan
+*/
+void QQuickLayoutAttached::setColumn(int column)
+{
+ if (column >= 0 && column != m_column) {
+ m_column = column;
+ repopulateLayout();
+ emit columnChanged();
+ }
+}
+
+
+/*!
+ \qmlattachedproperty Qt.Alignment Layout::alignment
+
+ This property allows you to specify the alignment of an item within the cell(s) it occupies.
+
+ The default value is \c 0, which means it will be \c{Qt.AlignVCenter | Qt.AlignLeft}
+
+ A valid alignment is a combination of the following flags:
+ \list
+ \li Qt::AlignLeft
+ \li Qt::AlignHCenter
+ \li Qt::AlignRight
+ \li Qt::AlignTop
+ \li Qt::AlignVCenter
+ \li Qt::AlignBottom
+ \li Qt::AlignBaseline
+ \endlist
+
+*/
+void QQuickLayoutAttached::setAlignment(Qt::Alignment align)
+{
+ if (align != m_alignment) {
+ m_alignment = align;
+ if (QQuickLayout *layout = parentLayout()) {
+ layout->setAlignment(item(), align);
+ invalidateItem();
+ }
+ emit alignmentChanged();
+ }
+}
+
+/*!
+ \qmlattachedproperty real Layout::margins
+
+ Sets the margins outside of an item to all have the same value. The item
+ itself does not evaluate its own margins. It is the parent's responsibility
+ to decide if it wants to evaluate the margins.
+
+ Specifically, margins are only evaluated by ColumnLayout, RowLayout,
+ GridLayout, and other layout-like containers, such as SplitView, where the
+ effective cell size of an item will be increased as the margins are
+ increased.
+
+ Therefore, if an item with margins is a child of another \c Item, its
+ position, size and implicit size will remain unchanged.
+
+ Combining margins with alignment will align the item \e including its
+ margins. For instance, a vertically-centered Item with a top margin of \c 1
+ and a bottom margin of \c 9 will cause the Items effective alignment within
+ the cell to be 4 pixels above the center.
+
+ The default value is \c 0.
+
+ \sa leftMargin
+ \sa topMargin
+ \sa rightMargin
+ \sa bottomMargin
+
+ \since QtQuick.Layouts 1.2
+*/
+void QQuickLayoutAttached::setMargins(qreal m)
+{
+ if (m == m_defaultMargins)
+ return;
+
+ m_defaultMargins = m;
+ invalidateItem();
+ if (!m_isLeftMarginSet && m_margins.left() != m)
+ emit leftMarginChanged();
+ if (!m_isTopMarginSet && m_margins.top() != m)
+ emit topMarginChanged();
+ if (!m_isRightMarginSet && m_margins.right() != m)
+ emit rightMarginChanged();
+ if (!m_isBottomMarginSet && m_margins.bottom() != m)
+ emit bottomMarginChanged();
+ emit marginsChanged();
+}
+
+/*!
+ \qmlattachedproperty real Layout::leftMargin
+
+ Specifies the left margin outside of an item.
+ If the value is not set, it will use the value from \l margins.
+
+ \sa margins
+
+ \since QtQuick.Layouts 1.2
+*/
+void QQuickLayoutAttached::setLeftMargin(qreal m)
+{
+ const bool changed = leftMargin() != m;
+ m_margins.setLeft(m);
+ m_isLeftMarginSet = true;
+ if (changed) {
+ invalidateItem();
+ emit leftMarginChanged();
+ }
+}
+
+void QQuickLayoutAttached::resetLeftMargin()
+{
+ const bool changed = m_isLeftMarginSet && (m_defaultMargins != m_margins.left());
+ m_isLeftMarginSet = false;
+ if (changed) {
+ invalidateItem();
+ emit leftMarginChanged();
+ }
+}
+
+/*!
+ \qmlattachedproperty real Layout::topMargin
+
+ Specifies the top margin outside of an item.
+ If the value is not set, it will use the value from \l margins.
+
+ \sa margins
+
+ \since QtQuick.Layouts 1.2
+*/
+void QQuickLayoutAttached::setTopMargin(qreal m)
+{
+ const bool changed = topMargin() != m;
+ m_margins.setTop(m);
+ m_isTopMarginSet = true;
+ if (changed) {
+ invalidateItem();
+ emit topMarginChanged();
+ }
+}
+
+void QQuickLayoutAttached::resetTopMargin()
+{
+ const bool changed = m_isTopMarginSet && (m_defaultMargins != m_margins.top());
+ m_isTopMarginSet = false;
+ if (changed) {
+ invalidateItem();
+ emit topMarginChanged();
+ }
+}
+
+/*!
+ \qmlattachedproperty real Layout::rightMargin
+
+ Specifies the right margin outside of an item.
+ If the value is not set, it will use the value from \l margins.
+
+ \sa margins
+
+ \since QtQuick.Layouts 1.2
+*/
+void QQuickLayoutAttached::setRightMargin(qreal m)
+{
+ const bool changed = rightMargin() != m;
+ m_margins.setRight(m);
+ m_isRightMarginSet = true;
+ if (changed) {
+ invalidateItem();
+ emit rightMarginChanged();
+ }
+}
+
+void QQuickLayoutAttached::resetRightMargin()
+{
+ const bool changed = m_isRightMarginSet && (m_defaultMargins != m_margins.right());
+ m_isRightMarginSet = false;
+ if (changed) {
+ invalidateItem();
+ emit rightMarginChanged();
+ }
+}
+
+/*!
+ \qmlattachedproperty real Layout::bottomMargin
+
+ Specifies the bottom margin outside of an item.
+ If the value is not set, it will use the value from \l margins.
+
+ \sa margins
+
+ \since QtQuick.Layouts 1.2
+*/
+void QQuickLayoutAttached::setBottomMargin(qreal m)
+{
+ const bool changed = bottomMargin() != m;
+ m_margins.setBottom(m);
+ m_isBottomMarginSet = true;
+ if (changed) {
+ invalidateItem();
+ emit bottomMarginChanged();
+ }
+}
+
+void QQuickLayoutAttached::resetBottomMargin()
+{
+ const bool changed = m_isBottomMarginSet && (m_defaultMargins != m_margins.bottom());
+ m_isBottomMarginSet = false;
+ if (changed) {
+ invalidateItem();
+ emit bottomMarginChanged();
+ }
+}
+
+
+/*!
+ \qmlattachedproperty int Layout::rowSpan
+
+ This property allows you to specify the row span of an item in a \l GridLayout.
+
+ The default value is \c 1.
+
+ \sa columnSpan
+ \sa row
+*/
+void QQuickLayoutAttached::setRowSpan(int span)
+{
+ if (span != m_rowSpan) {
+ m_rowSpan = span;
+ repopulateLayout();
+ emit rowSpanChanged();
+ }
+}
+
+
+/*!
+ \qmlattachedproperty int Layout::columnSpan
+
+ This property allows you to specify the column span of an item in a \l GridLayout.
+
+ The default value is \c 1.
+
+ \sa rowSpan
+ \sa column
+*/
+void QQuickLayoutAttached::setColumnSpan(int span)
+{
+ if (span != m_columnSpan) {
+ m_columnSpan = span;
+ repopulateLayout();
+ emit columnSpanChanged();
+ }
+}
+
+
+qreal QQuickLayoutAttached::sizeHint(Qt::SizeHint which, Qt::Orientation orientation) const
+{
+ qreal result = 0;
+ if (QQuickLayout *layout = qobject_cast<QQuickLayout *>(item())) {
+ const QSizeF sz = layout->sizeHint(which);
+ result = (orientation == Qt::Horizontal ? sz.width() : sz.height());
+ } else {
+ if (which == Qt::MaximumSize)
+ result = std::numeric_limits<qreal>::infinity();
+ }
+ return result;
+}
+
+void QQuickLayoutAttached::invalidateItem()
+{
+ if (!m_changesNotificationEnabled)
+ return;
+ quickLayoutDebug() << "QQuickLayoutAttached::invalidateItem";
+ if (QQuickLayout *layout = parentLayout()) {
+ layout->invalidate(item());
+ }
+}
+
+void QQuickLayoutAttached::repopulateLayout()
+{
+ if (QQuickLayout *layout = parentLayout())
+ layout->updateLayoutItems();
+}
+
+QQuickLayout *QQuickLayoutAttached::parentLayout() const
+{
+ QQuickItem *parentItem = item();
+ if (parentItem) {
+ parentItem = parentItem->parentItem();
+ return qobject_cast<QQuickLayout *>(parentItem);
+ } else {
+ qWarning("Layout must be attached to Item elements");
+ }
+ return 0;
+}
+
+QQuickItem *QQuickLayoutAttached::item() const
+{
+ return qobject_cast<QQuickItem *>(parent());
+}
+
+
+QQuickLayout::QQuickLayout(QQuickLayoutPrivate &dd, QQuickItem *parent)
+ : QQuickItem(dd, parent),
+ m_dirty(false)
+{
+}
+
+QQuickLayout::~QQuickLayout()
+{
+ d_func()->m_isReady = false;
+}
+
+QQuickLayoutAttached *QQuickLayout::qmlAttachedProperties(QObject *object)
+{
+ return new QQuickLayoutAttached(object);
+}
+
+void QQuickLayout::updatePolish()
+{
+ rearrange(QSizeF(width(), height()));
+}
+
+void QQuickLayout::componentComplete()
+{
+ Q_D(QQuickLayout);
+ d->m_disableRearrange = true;
+ QQuickItem::componentComplete(); // will call our geometryChanged(), (where isComponentComplete() == true)
+ d->m_disableRearrange = false;
+ d->m_isReady = true;
+}
+
+void QQuickLayout::invalidate(QQuickItem * /*childItem*/)
+{
+ if (m_dirty)
+ return;
+
+ m_dirty = true;
+
+ if (!qobject_cast<QQuickLayout *>(parentItem())) {
+ quickLayoutDebug() << "QQuickLayout::invalidate(), polish()";
+ polish();
+ }
+}
+
+bool QQuickLayout::shouldIgnoreItem(QQuickItem *child, QQuickLayoutAttached *&info, QSizeF *sizeHints) const
+{
+ Q_D(const QQuickLayout);
+ bool ignoreItem = true;
+ QQuickItemPrivate *childPrivate = QQuickItemPrivate::get(child);
+ if (childPrivate->explicitVisible) {
+ effectiveSizeHints_helper(child, sizeHints, &info, true);
+ QSizeF effectiveMaxSize = sizeHints[Qt::MaximumSize];
+ if (!effectiveMaxSize.isNull()) {
+ QSizeF &prefS = sizeHints[Qt::PreferredSize];
+ if (effectiveSizePolicy_helper(child, Qt::Horizontal, info) == QLayoutPolicy::Fixed)
+ effectiveMaxSize.setWidth(prefS.width());
+ if (effectiveSizePolicy_helper(child, Qt::Vertical, info) == QLayoutPolicy::Fixed)
+ effectiveMaxSize.setHeight(prefS.height());
+ }
+ ignoreItem = effectiveMaxSize.isNull();
+ }
+
+ if (ignoreItem)
+ d->m_ignoredItems << child;
+ return ignoreItem;
+}
+struct QQuickItemPublic : public QQuickItem {
+ static bool isCompleted(QQuickItem *item) {
+ return static_cast<QQuickItemPublic*>(item)->isComponentComplete();
+ }
+};
+
+void QQuickLayout::itemChange(ItemChange change, const ItemChangeData &value)
+{
+ if (change == ItemChildAddedChange) {
+ QQuickItem *item = value.item;
+ QObject::connect(item, SIGNAL(implicitWidthChanged()), this, SLOT(invalidateSenderItem()));
+ QObject::connect(item, SIGNAL(implicitHeightChanged()), this, SLOT(invalidateSenderItem()));
+ QObject::connect(item, SIGNAL(baselineOffsetChanged(qreal)), this, SLOT(invalidateSenderItem()));
+ QQuickItemPrivate::get(item)->addItemChangeListener(this, QQuickItemPrivate::SiblingOrder);
+ if (isReady())
+ updateLayoutItems();
+ } else if (change == ItemChildRemovedChange) {
+ QQuickItem *item = value.item;
+ QObject::disconnect(item, SIGNAL(implicitWidthChanged()), this, SLOT(invalidateSenderItem()));
+ QObject::disconnect(item, SIGNAL(implicitHeightChanged()), this, SLOT(invalidateSenderItem()));
+ QObject::disconnect(item, SIGNAL(baselineOffsetChanged(qreal)), this, SLOT(invalidateSenderItem()));
+ QQuickItemPrivate::get(item)->removeItemChangeListener(this, QQuickItemPrivate::SiblingOrder);
+ if (isReady())
+ updateLayoutItems();
+ }
+ QQuickItem::itemChange(change, value);
+}
+
+void QQuickLayout::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
+{
+ Q_D(QQuickLayout);
+ QQuickItem::geometryChanged(newGeometry, oldGeometry);
+ if (d->m_disableRearrange || !isReady() || !newGeometry.isValid())
+ return;
+
+ quickLayoutDebug() << "QQuickStackLayout::geometryChanged" << newGeometry << oldGeometry;
+ rearrange(newGeometry.size());
+}
+
+void QQuickLayout::invalidateSenderItem()
+{
+ if (!isReady())
+ return;
+ QQuickItem *item = static_cast<QQuickItem *>(sender());
+ Q_ASSERT(item);
+ invalidate(item);
+}
+
+bool QQuickLayout::isReady() const
+{
+ return d_func()->m_isReady;
+}
+
+void QQuickLayout::itemSiblingOrderChanged(QQuickItem *item)
+{
+ Q_UNUSED(item);
+ updateLayoutItems();
+}
+
+void QQuickLayout::rearrange(const QSizeF &/*size*/)
+{
+ m_dirty = false;
+}
+
+
+/*
+ The layout engine assumes:
+ 1. minimum <= preferred <= maximum
+ 2. descent is within minimum and maximum bounds (### verify)
+
+ This function helps to ensure that by the following rules (in the following order):
+ 1. If minimum > maximum, set minimum = maximum
+ 2. Clamp preferred to be between the [minimum,maximum] range.
+ 3. If descent > minimum, set descent = minimum (### verify if this is correct, it might
+ need some refinements to multiline texts)
+
+ If any values are "not set" (i.e. negative), they will be left untouched, so that we
+ know which values needs to be fetched from the implicit hints (not user hints).
+ */
+static void normalizeHints(qreal &minimum, qreal &preferred, qreal &maximum, qreal &descent)
+{
+ if (minimum >= 0 && maximum >= 0 && minimum > maximum)
+ minimum = maximum;
+
+ if (preferred >= 0) {
+ if (minimum >= 0 && preferred < minimum) {
+ preferred = minimum;
+ } else if (maximum >= 0 && preferred > maximum) {
+ preferred = maximum;
+ }
+ }
+
+ if (minimum >= 0 && descent > minimum)
+ descent = minimum;
+}
+
+static void boundSize(QSizeF &result, const QSizeF &size)
+{
+ if (size.width() >= 0 && size.width() < result.width())
+ result.setWidth(size.width());
+ if (size.height() >= 0 && size.height() < result.height())
+ result.setHeight(size.height());
+}
+
+static void expandSize(QSizeF &result, const QSizeF &size)
+{
+ if (size.width() >= 0 && size.width() > result.width())
+ result.setWidth(size.width());
+ if (size.height() >= 0 && size.height() > result.height())
+ result.setHeight(size.height());
+}
+
+static inline void combineHints(qreal &current, qreal fallbackHint)
+{
+ if (current < 0)
+ current = fallbackHint;
+}
+
+static inline void combineSize(QSizeF &result, const QSizeF &fallbackSize)
+{
+ combineHints(result.rwidth(), fallbackSize.width());
+ combineHints(result.rheight(), fallbackSize.height());
+}
+
+static inline void combineImplicitHints(QQuickLayoutAttached *info, Qt::SizeHint which, QSizeF *size)
+{
+ if (!info) return;
+
+ Q_ASSERT(which == Qt::MinimumSize || which == Qt::MaximumSize);
+
+ const QSizeF constraint(which == Qt::MinimumSize
+ ? QSizeF(info->minimumWidth(), info->minimumHeight())
+ : QSizeF(info->maximumWidth(), info->maximumHeight()));
+
+ if (!info->isExtentExplicitlySet(Qt::Horizontal, which))
+ combineHints(size->rwidth(), constraint.width());
+ if (!info->isExtentExplicitlySet(Qt::Vertical, which))
+ combineHints(size->rheight(), constraint.height());
+}
+
+typedef qreal (QQuickLayoutAttached::*SizeGetter)() const;
+
+/*!
+ \internal
+ Note: Can potentially return the attached QQuickLayoutAttached object through \a attachedInfo.
+
+ It is like this is because it enables it to be reused.
+
+ The goal of this function is to return the effective minimum, preferred and maximum size hints
+ that the layout will use for this item.
+ This function takes care of gathering all explicitly set size hints, normalizes them so
+ that min < pref < max.
+ Further, the hints _not_explicitly_ set will then be initialized with the implicit size hints,
+ which is usually derived from the content of the layouts (or items).
+
+ The following table illustrates the preference of the properties used for measuring layout
+ items. If present, the USER properties will be preferred. If USER properties are not present,
+ the HINT properties will be preferred. Finally, the FALLBACK properties will be used as an
+ ultimate fallback.
+
+ Note that one can query if the value of Layout.minimumWidth or Layout.maximumWidth has been
+ explicitly or implicitly set with QQuickLayoutAttached::isExtentExplicitlySet(). This
+ determines if it should be used as a USER or as a HINT value.
+
+ Fractional size hints will be ceiled to the closest integer. This is in order to give some
+ slack when the items are snapped to the pixel grid.
+
+ | *Minimum* | *Preferred* | *Maximum* |
++----------------+----------------------+-----------------------+--------------------------+
+|USER (explicit) | Layout.minimumWidth | Layout.preferredWidth | Layout.maximumWidth |
+|HINT (implicit) | Layout.minimumWidth | implicitWidth | Layout.maximumWidth |
+|FALLBACK | 0 | width | Number.POSITIVE_INFINITY |
++----------------+----------------------+-----------------------+--------------------------+
+ */
+void QQuickLayout::effectiveSizeHints_helper(QQuickItem *item, QSizeF *cachedSizeHints, QQuickLayoutAttached **attachedInfo, bool useFallbackToWidthOrHeight)
+{
+ for (int i = 0; i < Qt::NSizeHints; ++i)
+ cachedSizeHints[i] = QSizeF();
+ QQuickLayoutAttached *info = attachedLayoutObject(item, false);
+ // First, retrieve the user-specified hints from the attached "Layout." properties
+ if (info) {
+ struct Getters {
+ SizeGetter call[NSizes];
+ };
+
+ static Getters horGetters = {
+ {&QQuickLayoutAttached::minimumWidth, &QQuickLayoutAttached::preferredWidth, &QQuickLayoutAttached::maximumWidth},
+ };
+
+ static Getters verGetters = {
+ {&QQuickLayoutAttached::minimumHeight, &QQuickLayoutAttached::preferredHeight, &QQuickLayoutAttached::maximumHeight}
+ };
+ for (int i = 0; i < NSizes; ++i) {
+ SizeGetter getter = horGetters.call[i];
+ Q_ASSERT(getter);
+
+ if (info->isExtentExplicitlySet(Qt::Horizontal, (Qt::SizeHint)i))
+ cachedSizeHints[i].setWidth((info->*getter)());
+
+ getter = verGetters.call[i];
+ Q_ASSERT(getter);
+ if (info->isExtentExplicitlySet(Qt::Vertical, (Qt::SizeHint)i))
+ cachedSizeHints[i].setHeight((info->*getter)());
+ }
+ }
+
+ QSizeF &minS = cachedSizeHints[Qt::MinimumSize];
+ QSizeF &prefS = cachedSizeHints[Qt::PreferredSize];
+ QSizeF &maxS = cachedSizeHints[Qt::MaximumSize];
+ QSizeF &descentS = cachedSizeHints[Qt::MinimumDescent];
+
+ // For instance, will normalize the following user-set hints
+ // from: [10, 5, 60]
+ // to: [10, 10, 60]
+ normalizeHints(minS.rwidth(), prefS.rwidth(), maxS.rwidth(), descentS.rwidth());
+ normalizeHints(minS.rheight(), prefS.rheight(), maxS.rheight(), descentS.rheight());
+
+ // All explicit values gathered, now continue to gather the implicit sizes
+
+ //--- GATHER MAXIMUM SIZE HINTS ---
+ combineImplicitHints(info, Qt::MaximumSize, &maxS);
+ combineSize(maxS, QSizeF(std::numeric_limits<qreal>::infinity(), std::numeric_limits<qreal>::infinity()));
+ // implicit max or min sizes should not limit an explicitly set preferred size
+ expandSize(maxS, prefS);
+ expandSize(maxS, minS);
+
+ //--- GATHER MINIMUM SIZE HINTS ---
+ combineImplicitHints(info, Qt::MinimumSize, &minS);
+ expandSize(minS, QSizeF(0,0));
+ boundSize(minS, prefS);
+ boundSize(minS, maxS);
+
+ //--- GATHER PREFERRED SIZE HINTS ---
+ // First, from implicitWidth/Height
+ qreal &prefWidth = prefS.rwidth();
+ qreal &prefHeight = prefS.rheight();
+ if (prefWidth < 0 && item->implicitWidth() > 0)
+ prefWidth = qCeil(item->implicitWidth());
+ if (prefHeight < 0 && item->implicitHeight() > 0)
+ prefHeight = qCeil(item->implicitHeight());
+
+ // If that fails, make an ultimate fallback to width/height
+
+ if (!info && (prefWidth < 0 || prefHeight < 0))
+ info = attachedLayoutObject(item);
+
+ if (useFallbackToWidthOrHeight && info) {
+ /* This block is a bit hacky, but if we want to support using width/height
+ as preferred size hints in layouts, (which we think most people expect),
+ we only want to use the initial width.
+ This is because the width will change due to layout rearrangement, and the preferred
+ width should return the same value, regardless of the current width.
+ We therefore store the width in the implicitWidth attached property.
+ Since the layout listens to changes of implicitWidth, (it will
+ basically cause an invalidation of the layout), we have to disable that
+ notification while we set the implicit width (and height).
+
+ Only use this fallback the first time the size hint is queried. Otherwise, we might
+ end up picking a width that is different than what was specified in the QML.
+ */
+ if (prefWidth < 0 || prefHeight < 0) {
+ item->blockSignals(true);
+ if (prefWidth < 0) {
+ prefWidth = item->width();
+ item->setImplicitWidth(prefWidth);
+ }
+ if (prefHeight < 0) {
+ prefHeight = item->height();
+ item->setImplicitHeight(prefHeight);
+ }
+ item->blockSignals(false);
+ }
+ }
+
+
+
+ // Normalize again after the implicit hints have been gathered
+ expandSize(prefS, minS);
+ boundSize(prefS, maxS);
+
+ //--- GATHER DESCENT
+ // Minimum descent is only applicable for the effective minimum height,
+ // so we gather the descent last.
+ const qreal minimumDescent = minS.height() - item->baselineOffset();
+ descentS.setHeight(minimumDescent);
+
+ if (info) {
+ QMarginsF margins = info->qMargins();
+ QSizeF extraMargins(margins.left() + margins.right(), margins.top() + margins.bottom());
+ minS += extraMargins;
+ prefS += extraMargins;
+ maxS += extraMargins;
+ descentS += extraMargins;
+ }
+ if (attachedInfo)
+ *attachedInfo = info;
+}
+
+/*!
+ \internal
+
+ Assumes \a info is set (if the object has an attached property)
+ */
+QLayoutPolicy::Policy QQuickLayout::effectiveSizePolicy_helper(QQuickItem *item, Qt::Orientation orientation, QQuickLayoutAttached *info)
+{
+ bool fillExtent = false;
+ bool isSet = false;
+ if (info) {
+ if (orientation == Qt::Horizontal) {
+ isSet = info->isFillWidthSet();
+ if (isSet) fillExtent = info->fillWidth();
+ } else {
+ isSet = info->isFillHeightSet();
+ if (isSet) fillExtent = info->fillHeight();
+ }
+ }
+ if (!isSet && qobject_cast<QQuickLayout*>(item))
+ fillExtent = true;
+ return fillExtent ? QLayoutPolicy::Preferred : QLayoutPolicy::Fixed;
+
+}
+
+
+
+QT_END_NAMESPACE
diff --git a/src/imports/layouts/qquicklayout_p.h b/src/imports/layouts/qquicklayout_p.h
new file mode 100644
index 0000000000..c7f04c1fed
--- /dev/null
+++ b/src/imports/layouts/qquicklayout_p.h
@@ -0,0 +1,326 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Quick Layouts module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQUICKLAYOUT_P_H
+#define QQUICKLAYOUT_P_H
+
+#include <QPointer>
+#include <QQuickItem>
+#include <private/qquickitem_p.h>
+#include <QtQuick/private/qquickitemchangelistener_p.h>
+#include <QtGui/private/qlayoutpolicy_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQuickLayoutAttached;
+
+#if 0 && !defined(QT_NO_DEBUG) && !defined(QT_NO_DEBUG_OUTPUT)
+# define quickLayoutDebug QMessageLogger(__FILE__, __LINE__, Q_FUNC_INFO).debug
+#else
+# define quickLayoutDebug QT_NO_QDEBUG_MACRO
+#endif
+
+class QQuickLayoutPrivate;
+class QQuickLayout : public QQuickItem, public QQuickItemChangeListener
+
+{
+ Q_OBJECT
+public:
+ enum SizeHint {
+ MinimumSize = 0,
+ PreferredSize,
+ MaximumSize,
+ NSizes
+ };
+
+ explicit QQuickLayout(QQuickLayoutPrivate &dd, QQuickItem *parent = 0);
+ ~QQuickLayout();
+
+ static QQuickLayoutAttached *qmlAttachedProperties(QObject *object);
+
+
+ void componentComplete() Q_DECL_OVERRIDE;
+ virtual QSizeF sizeHint(Qt::SizeHint whichSizeHint) const = 0;
+ virtual void setAlignment(QQuickItem *item, Qt::Alignment align) = 0;
+ virtual void invalidate(QQuickItem * childItem = 0);
+ virtual void updateLayoutItems() = 0;
+
+ // iterator
+ virtual QQuickItem *itemAt(int index) const = 0;
+ virtual int itemCount() const = 0;
+
+ virtual void rearrange(const QSizeF &);
+ bool arrangementIsDirty() const { return m_dirty; }
+
+ static void effectiveSizeHints_helper(QQuickItem *item, QSizeF *cachedSizeHints, QQuickLayoutAttached **info, bool useFallbackToWidthOrHeight);
+ static QLayoutPolicy::Policy effectiveSizePolicy_helper(QQuickItem *item, Qt::Orientation orientation, QQuickLayoutAttached *info);
+ bool shouldIgnoreItem(QQuickItem *child, QQuickLayoutAttached *&info, QSizeF *sizeHints) const;
+
+ void itemChange(ItemChange change, const ItemChangeData &value) Q_DECL_OVERRIDE;
+ void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) Q_DECL_OVERRIDE;
+ bool isReady() const;
+
+
+ /* QQuickItemChangeListener */
+ void itemSiblingOrderChanged(QQuickItem *item) Q_DECL_OVERRIDE;
+
+protected:
+ void updatePolish() Q_DECL_OVERRIDE;
+
+ enum Orientation {
+ Vertical = 0,
+ Horizontal,
+ NOrientations
+ };
+
+protected slots:
+ void invalidateSenderItem();
+
+private:
+ bool m_dirty;
+
+ Q_DECLARE_PRIVATE(QQuickLayout)
+
+ friend class QQuickLayoutAttached;
+};
+
+
+class QQuickLayoutPrivate : public QQuickItemPrivate
+{
+ Q_DECLARE_PUBLIC(QQuickLayout)
+public:
+ QQuickLayoutPrivate() : m_isReady(false), m_disableRearrange(true) {}
+
+protected:
+ unsigned m_isReady : 1;
+ unsigned m_disableRearrange : 1;
+ mutable QSet<QQuickItem *> m_ignoredItems;
+};
+
+
+class QQuickLayoutAttached : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(qreal minimumWidth READ minimumWidth WRITE setMinimumWidth NOTIFY minimumWidthChanged)
+ Q_PROPERTY(qreal minimumHeight READ minimumHeight WRITE setMinimumHeight NOTIFY minimumHeightChanged)
+ Q_PROPERTY(qreal preferredWidth READ preferredWidth WRITE setPreferredWidth NOTIFY preferredWidthChanged)
+ Q_PROPERTY(qreal preferredHeight READ preferredHeight WRITE setPreferredHeight NOTIFY preferredHeightChanged)
+ Q_PROPERTY(qreal maximumWidth READ maximumWidth WRITE setMaximumWidth NOTIFY maximumWidthChanged)
+ Q_PROPERTY(qreal maximumHeight READ maximumHeight WRITE setMaximumHeight NOTIFY maximumHeightChanged)
+ Q_PROPERTY(bool fillHeight READ fillHeight WRITE setFillHeight NOTIFY fillHeightChanged)
+ Q_PROPERTY(bool fillWidth READ fillWidth WRITE setFillWidth NOTIFY fillWidthChanged)
+ Q_PROPERTY(int row READ row WRITE setRow NOTIFY rowChanged)
+ Q_PROPERTY(int column READ column WRITE setColumn NOTIFY columnChanged)
+ Q_PROPERTY(int rowSpan READ rowSpan WRITE setRowSpan NOTIFY rowSpanChanged)
+ Q_PROPERTY(int columnSpan READ columnSpan WRITE setColumnSpan NOTIFY columnSpanChanged)
+ Q_PROPERTY(Qt::Alignment alignment READ alignment WRITE setAlignment NOTIFY alignmentChanged)
+
+ Q_PROPERTY(qreal margins READ margins WRITE setMargins NOTIFY marginsChanged)
+ Q_PROPERTY(qreal leftMargin READ leftMargin WRITE setLeftMargin RESET resetLeftMargin NOTIFY leftMarginChanged)
+ Q_PROPERTY(qreal topMargin READ topMargin WRITE setTopMargin RESET resetTopMargin NOTIFY topMarginChanged)
+ Q_PROPERTY(qreal rightMargin READ rightMargin WRITE setRightMargin RESET resetRightMargin NOTIFY rightMarginChanged)
+ Q_PROPERTY(qreal bottomMargin READ bottomMargin WRITE setBottomMargin RESET resetBottomMargin NOTIFY bottomMarginChanged)
+
+public:
+ QQuickLayoutAttached(QObject *object);
+
+ qreal minimumWidth() const { return !m_isMinimumWidthSet ? sizeHint(Qt::MinimumSize, Qt::Horizontal) : m_minimumWidth; }
+ void setMinimumWidth(qreal width);
+
+ qreal minimumHeight() const { return !m_isMinimumHeightSet ? sizeHint(Qt::MinimumSize, Qt::Vertical) : m_minimumHeight; }
+ void setMinimumHeight(qreal height);
+
+ qreal preferredWidth() const { return m_preferredWidth; }
+ void setPreferredWidth(qreal width);
+
+ qreal preferredHeight() const { return m_preferredHeight; }
+ void setPreferredHeight(qreal width);
+
+ qreal maximumWidth() const { return !m_isMaximumWidthSet ? sizeHint(Qt::MaximumSize, Qt::Horizontal) : m_maximumWidth; }
+ void setMaximumWidth(qreal width);
+
+ qreal maximumHeight() const { return !m_isMaximumHeightSet ? sizeHint(Qt::MaximumSize, Qt::Vertical) : m_maximumHeight; }
+ void setMaximumHeight(qreal height);
+
+ void setMinimumImplicitSize(const QSizeF &sz);
+ void setMaximumImplicitSize(const QSizeF &sz);
+
+ bool fillWidth() const { return m_fillWidth; }
+ void setFillWidth(bool fill);
+ bool isFillWidthSet() const { return m_isFillWidthSet; }
+
+ bool fillHeight() const { return m_fillHeight; }
+ void setFillHeight(bool fill);
+ bool isFillHeightSet() const { return m_isFillHeightSet; }
+
+ int row() const { return qMax(m_row, 0); }
+ void setRow(int row);
+ bool isRowSet() const { return m_row >= 0; }
+ int column() const { return qMax(m_column, 0); }
+ void setColumn(int column);
+ bool isColumnSet() const { return m_column >= 0; }
+
+ int rowSpan() const { return m_rowSpan; }
+ void setRowSpan(int span);
+ int columnSpan() const { return m_columnSpan; }
+ void setColumnSpan(int span);
+
+ Qt::Alignment alignment() const { return m_alignment; }
+ void setAlignment(Qt::Alignment align);
+
+ qreal margins() const { return m_defaultMargins; }
+ void setMargins(qreal m);
+
+ qreal leftMargin() const { return m_isLeftMarginSet ? m_margins.left() : m_defaultMargins; }
+ void setLeftMargin(qreal m);
+ void resetLeftMargin();
+
+ qreal topMargin() const { return m_isTopMarginSet ? m_margins.top() : m_defaultMargins; }
+ void setTopMargin(qreal m);
+ void resetTopMargin();
+
+ qreal rightMargin() const { return m_isRightMarginSet ? m_margins.right() : m_defaultMargins; }
+ void setRightMargin(qreal m);
+ void resetRightMargin();
+
+ qreal bottomMargin() const { return m_isBottomMarginSet ? m_margins.bottom() : m_defaultMargins; }
+ void setBottomMargin(qreal m);
+ void resetBottomMargin();
+
+ QMarginsF qMargins() const {
+ return QMarginsF(leftMargin(), topMargin(), rightMargin(), bottomMargin());
+ }
+
+ bool setChangesNotificationEnabled(bool enabled)
+ {
+ const bool old = m_changesNotificationEnabled;
+ m_changesNotificationEnabled = enabled;
+ return old;
+ }
+
+ qreal sizeHint(Qt::SizeHint which, Qt::Orientation orientation) const;
+
+ bool isExtentExplicitlySet(Qt::Orientation o, Qt::SizeHint whichSize) const
+ {
+ switch (whichSize) {
+ case Qt::MinimumSize:
+ return o == Qt::Horizontal ? m_isMinimumWidthSet : m_isMinimumHeightSet;
+ case Qt::MaximumSize:
+ return o == Qt::Horizontal ? m_isMaximumWidthSet : m_isMaximumHeightSet;
+ case Qt::PreferredSize:
+ return true; // Layout.preferredWidth is always explicitly set
+ case Qt::MinimumDescent: // Not supported
+ case Qt::NSizeHints:
+ return false;
+ }
+ return false;
+ }
+
+signals:
+ void minimumWidthChanged();
+ void minimumHeightChanged();
+ void preferredWidthChanged();
+ void preferredHeightChanged();
+ void maximumWidthChanged();
+ void maximumHeightChanged();
+ void fillWidthChanged();
+ void fillHeightChanged();
+ void leftMarginChanged();
+ void topMarginChanged();
+ void rightMarginChanged();
+ void bottomMarginChanged();
+ void marginsChanged();
+ void rowChanged();
+ void columnChanged();
+ void rowSpanChanged();
+ void columnSpanChanged();
+ void alignmentChanged();
+
+private:
+ void invalidateItem();
+ void repopulateLayout();
+ QQuickLayout *parentLayout() const;
+ QQuickItem *item() const;
+private:
+ qreal m_minimumWidth;
+ qreal m_minimumHeight;
+ qreal m_preferredWidth;
+ qreal m_preferredHeight;
+ qreal m_maximumWidth;
+ qreal m_maximumHeight;
+
+ qreal m_defaultMargins;
+ QMarginsF m_margins;
+
+ // GridLayout specific properties
+ int m_row;
+ int m_column;
+ int m_rowSpan;
+ int m_columnSpan;
+
+ unsigned m_fillWidth : 1;
+ unsigned m_fillHeight : 1;
+ unsigned m_isFillWidthSet : 1;
+ unsigned m_isFillHeightSet : 1;
+ unsigned m_isMinimumWidthSet : 1;
+ unsigned m_isMinimumHeightSet : 1;
+ // preferredWidth and preferredHeight are always explicit, since
+ // their implicit equivalent is implicitWidth and implicitHeight
+ unsigned m_isMaximumWidthSet : 1;
+ unsigned m_isMaximumHeightSet : 1;
+ unsigned m_changesNotificationEnabled : 1;
+ unsigned m_isLeftMarginSet : 1;
+ unsigned m_isTopMarginSet : 1;
+ unsigned m_isRightMarginSet : 1;
+ unsigned m_isBottomMarginSet : 1;
+ Qt::Alignment m_alignment;
+ friend class QQuickLayout;
+};
+
+inline QQuickLayoutAttached *attachedLayoutObject(QQuickItem *item, bool create = true)
+{
+ return static_cast<QQuickLayoutAttached *>(qmlAttachedPropertiesObject<QQuickLayout>(item, create));
+}
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QQuickLayout)
+QML_DECLARE_TYPEINFO(QQuickLayout, QML_HAS_ATTACHED_PROPERTIES)
+
+#endif // QQUICKLAYOUT_P_H
diff --git a/src/imports/layouts/qquicklayoutstyleinfo.cpp b/src/imports/layouts/qquicklayoutstyleinfo.cpp
new file mode 100644
index 0000000000..c33ceffb2d
--- /dev/null
+++ b/src/imports/layouts/qquicklayoutstyleinfo.cpp
@@ -0,0 +1,80 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Quick Layouts module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtGui/private/qfont_p.h>
+
+#include "qquicklayoutstyleinfo_p.h"
+
+
+QT_BEGIN_NAMESPACE
+
+QQuickLayoutStyleInfo::QQuickLayoutStyleInfo()
+{
+}
+
+qreal QQuickLayoutStyleInfo::spacing(Qt::Orientation /*orientation*/) const
+{
+#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) || defined(Q_OS_BLACKBERRY) || defined(Q_OS_QNX) || defined(Q_OS_WINRT)
+ // On Android and iOS the default spacing between each UI element is 8dp
+ qreal spacing = 8.0;
+#else
+ qreal spacing = 5.0;
+#endif
+
+#ifndef Q_OS_OSX
+ // On OS X the DPI is always 72 so we should not scale it
+ spacing = qRound(spacing * (qreal(qt_defaultDpiX()) / 96.0));
+#endif
+
+ return spacing;
+}
+
+qreal QQuickLayoutStyleInfo::windowMargin(Qt::Orientation /*orientation*/) const
+{
+ return 0;
+}
+
+bool QQuickLayoutStyleInfo::hasChangedCore() const
+{
+ // never changes
+ return false;
+}
+
+QT_END_NAMESPACE
+
diff --git a/src/imports/layouts/qquicklayoutstyleinfo_p.h b/src/imports/layouts/qquicklayoutstyleinfo_p.h
new file mode 100644
index 0000000000..ce86c2a37d
--- /dev/null
+++ b/src/imports/layouts/qquicklayoutstyleinfo_p.h
@@ -0,0 +1,60 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Quick Layouts module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQUICKLAYOUTSTYLEINFO_P_H
+#define QQUICKLAYOUTSTYLEINFO_P_H
+
+#include <QtGui/private/qabstractlayoutstyleinfo_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQuickLayoutStyleInfo : public QAbstractLayoutStyleInfo
+{
+public:
+ QQuickLayoutStyleInfo();
+
+ qreal spacing(Qt::Orientation orientation) const Q_DECL_OVERRIDE;
+ qreal windowMargin(Qt::Orientation orientation) const Q_DECL_OVERRIDE;
+ bool hasChangedCore() const Q_DECL_OVERRIDE;
+
+};
+
+QT_END_NAMESPACE
+
+#endif // QQUICKLAYOUTSTYLEINFO_P_H
diff --git a/src/imports/layouts/qquicklinearlayout.cpp b/src/imports/layouts/qquicklinearlayout.cpp
new file mode 100644
index 0000000000..2f8af4c58b
--- /dev/null
+++ b/src/imports/layouts/qquicklinearlayout.cpp
@@ -0,0 +1,908 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Quick Layouts module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquicklinearlayout_p.h"
+#include "qquickgridlayoutengine_p.h"
+#include "qquicklayoutstyleinfo_p.h"
+#include <QtCore/qnumeric.h>
+#include "qdebug.h"
+#include <limits>
+
+/*!
+ \qmltype RowLayout
+ \instantiates QQuickRowLayout
+ \inherits Item
+ \inqmlmodule QtQuick.Layouts
+ \ingroup layouts
+ \brief Identical to \l GridLayout, but having only one row.
+
+ It is available as a convenience for developers, as it offers a cleaner API.
+
+ Items in a RowLayout support these attached properties:
+ \list
+ \li \l{Layout::minimumWidth}{Layout.minimumWidth}
+ \li \l{Layout::minimumHeight}{Layout.minimumHeight}
+ \li \l{Layout::preferredWidth}{Layout.preferredWidth}
+ \li \l{Layout::preferredHeight}{Layout.preferredHeight}
+ \li \l{Layout::maximumWidth}{Layout.maximumWidth}
+ \li \l{Layout::maximumHeight}{Layout.maximumHeight}
+ \li \l{Layout::fillWidth}{Layout.fillWidth}
+ \li \l{Layout::fillHeight}{Layout.fillHeight}
+ \li \l{Layout::alignment}{Layout.alignment}
+ \endlist
+
+ \image rowlayout.png
+
+ \code
+ RowLayout {
+ id: layout
+ anchors.fill: parent
+ spacing: 6
+ Rectangle {
+ color: 'teal'
+ Layout.fillWidth: true
+ Layout.minimumWidth: 50
+ Layout.preferredWidth: 100
+ Layout.maximumWidth: 300
+ Layout.minimumHeight: 150
+ Text {
+ anchors.centerIn: parent
+ text: parent.width + 'x' + parent.height
+ }
+ }
+ Rectangle {
+ color: 'plum'
+ Layout.fillWidth: true
+ Layout.minimumWidth: 100
+ Layout.preferredWidth: 200
+ Layout.preferredHeight: 100
+ Text {
+ anchors.centerIn: parent
+ text: parent.width + 'x' + parent.height
+ }
+ }
+ }
+ \endcode
+
+ Read more about attached properties \l{QML Object Attributes}{here}.
+ \sa ColumnLayout
+ \sa GridLayout
+ \sa Row
+*/
+
+/*!
+ \qmltype ColumnLayout
+ \instantiates QQuickColumnLayout
+ \inherits Item
+ \inqmlmodule QtQuick.Layouts
+ \ingroup layouts
+ \brief Identical to \l GridLayout, but having only one column.
+
+ It is available as a convenience for developers, as it offers a cleaner API.
+
+ Items in a ColumnLayout support these attached properties:
+ \list
+ \li \l{Layout::minimumWidth}{Layout.minimumWidth}
+ \li \l{Layout::minimumHeight}{Layout.minimumHeight}
+ \li \l{Layout::preferredWidth}{Layout.preferredWidth}
+ \li \l{Layout::preferredHeight}{Layout.preferredHeight}
+ \li \l{Layout::maximumWidth}{Layout.maximumWidth}
+ \li \l{Layout::maximumHeight}{Layout.maximumHeight}
+ \li \l{Layout::fillWidth}{Layout.fillWidth}
+ \li \l{Layout::fillHeight}{Layout.fillHeight}
+ \li \l{Layout::alignment}{Layout.alignment}
+ \endlist
+
+ \image columnlayout.png
+
+ \code
+ ColumnLayout{
+ spacing: 2
+
+ Rectangle {
+ Layout.alignment: Qt.AlignCenter
+ color: "red"
+ Layout.preferredWidth: 40
+ Layout.preferredHeight: 40
+ }
+
+ Rectangle {
+ Layout.alignment: Qt.AlignRight
+ color: "green"
+ Layout.preferredWidth: 40
+ Layout.preferredHeight: 70
+ }
+
+ Rectangle {
+ Layout.alignment: Qt.AlignBottom
+ Layout.fillHeight: true
+ color: "blue"
+ Layout.preferredWidth: 70
+ Layout.preferredHeight: 40
+ }
+ }
+ \endcode
+
+ Read more about attached properties \l{QML Object Attributes}{here}.
+
+ \sa RowLayout
+ \sa GridLayout
+ \sa Column
+*/
+
+
+/*!
+ \qmltype GridLayout
+ \instantiates QQuickGridLayout
+ \inherits Item
+ \inqmlmodule QtQuick.Layouts
+ \ingroup layouts
+ \brief Provides a way of dynamically arranging items in a grid.
+
+
+
+ If the GridLayout is resized, all items in the layout will be rearranged. It is similar
+ to the widget-based QGridLayout. All visible children of the GridLayout element will belong to
+ the layout. If you want a layout with just one row or one column, you can use the
+ \l RowLayout or \l ColumnLayout. These offer a bit more convenient API, and improve
+ readability.
+
+ By default items will be arranged according to the \l flow property. The default value of
+ the \l flow property is \c GridLayout.LeftToRight.
+
+ If the \l columns property is specified, it will be treated as a maximum limit of how many
+ columns the layout can have, before the auto-positioning wraps back to the beginning of the
+ next row. The \l columns property is only used when \l flow is \c GridLayout.LeftToRight.
+
+ \image gridlayout.png
+
+ \code
+ GridLayout {
+ id: grid
+ columns: 3
+
+ Text { text: "Three"; font.bold: true; }
+ Text { text: "words"; color: "red" }
+ Text { text: "in"; font.underline: true }
+ Text { text: "a"; font.pixelSize: 20 }
+ Text { text: "row"; font.strikeout: true }
+ }
+ \endcode
+
+ The \l rows property works in a similar way, but items are auto-positioned vertically. The \l
+ rows property is only used when \l flow is \c GridLayout.TopToBottom.
+
+ You can specify which cell you want an item to occupy by setting the
+ \l{Layout::row}{Layout.row} and \l{Layout::column}{Layout.column} properties. You can also
+ specify the row span or column span by setting the \l{Layout::rowSpan}{Layout.rowSpan} or
+ \l{Layout::columnSpan}{Layout.columnSpan} properties.
+
+
+ Items in a GridLayout support these attached properties:
+ \list
+ \li \l{Layout::row}{Layout.row}
+ \li \l{Layout::column}{Layout.column}
+ \li \l{Layout::rowSpan}{Layout.rowSpan}
+ \li \l{Layout::columnSpan}{Layout.columnSpan}
+ \li \l{Layout::minimumWidth}{Layout.minimumWidth}
+ \li \l{Layout::minimumHeight}{Layout.minimumHeight}
+ \li \l{Layout::preferredWidth}{Layout.preferredWidth}
+ \li \l{Layout::preferredHeight}{Layout.preferredHeight}
+ \li \l{Layout::maximumWidth}{Layout.maximumWidth}
+ \li \l{Layout::maximumHeight}{Layout.maximumHeight}
+ \li \l{Layout::fillWidth}{Layout.fillWidth}
+ \li \l{Layout::fillHeight}{Layout.fillHeight}
+ \li \l{Layout::alignment}{Layout.alignment}
+ \endlist
+
+ Read more about attached properties \l{QML Object Attributes}{here}.
+
+ \sa RowLayout
+ \sa ColumnLayout
+ \sa Grid
+*/
+
+
+
+QT_BEGIN_NAMESPACE
+
+QQuickGridLayoutBase::QQuickGridLayoutBase()
+ : QQuickLayout(*new QQuickGridLayoutBasePrivate)
+{
+
+}
+
+QQuickGridLayoutBase::QQuickGridLayoutBase(QQuickGridLayoutBasePrivate &dd,
+ Qt::Orientation orientation,
+ QQuickItem *parent /*= 0*/)
+ : QQuickLayout(dd, parent)
+{
+ Q_D(QQuickGridLayoutBase);
+ d->orientation = orientation;
+ d->styleInfo = new QQuickLayoutStyleInfo;
+}
+
+Qt::Orientation QQuickGridLayoutBase::orientation() const
+{
+ Q_D(const QQuickGridLayoutBase);
+ return d->orientation;
+}
+
+void QQuickGridLayoutBase::setOrientation(Qt::Orientation orientation)
+{
+ Q_D(QQuickGridLayoutBase);
+ if (d->orientation == orientation)
+ return;
+
+ d->orientation = orientation;
+ invalidate();
+}
+
+QSizeF QQuickGridLayoutBase::sizeHint(Qt::SizeHint whichSizeHint) const
+{
+ Q_D(const QQuickGridLayoutBase);
+ return d->engine.sizeHint(whichSizeHint, QSizeF(), d->styleInfo);
+}
+
+/*!
+ \qmlproperty enumeration GridLayout::layoutDirection
+ \since QtQuick.Layouts 1.1
+
+ This property holds the layout direction of the grid layout - it controls whether items are
+ laid out from left to right or right to left. If \c Qt.RightToLeft is specified,
+ left-aligned items will be right-aligned and right-aligned items will be left-aligned.
+
+ Possible values:
+
+ \list
+ \li Qt.LeftToRight (default) - Items are laid out from left to right.
+ \li Qt.RightToLeft - Items are laid out from right to left.
+ \endlist
+
+ \sa RowLayout::layoutDirection, ColumnLayout::layoutDirection
+*/
+Qt::LayoutDirection QQuickGridLayoutBase::layoutDirection() const
+{
+ Q_D(const QQuickGridLayoutBase);
+ return d->m_layoutDirection;
+}
+
+void QQuickGridLayoutBase::setLayoutDirection(Qt::LayoutDirection dir)
+{
+ Q_D(QQuickGridLayoutBase);
+ d->m_layoutDirection = dir;
+ invalidate();
+}
+
+Qt::LayoutDirection QQuickGridLayoutBase::effectiveLayoutDirection() const
+{
+ Q_D(const QQuickGridLayoutBase);
+ return !d->effectiveLayoutMirror == (layoutDirection() == Qt::LeftToRight)
+ ? Qt::LeftToRight : Qt::RightToLeft;
+}
+
+void QQuickGridLayoutBase::setAlignment(QQuickItem *item, Qt::Alignment alignment)
+{
+ Q_D(QQuickGridLayoutBase);
+ d->engine.setAlignment(item, alignment);
+}
+
+QQuickGridLayoutBase::~QQuickGridLayoutBase()
+{
+ Q_D(QQuickGridLayoutBase);
+
+ /* Avoid messy deconstruction, should give:
+ * Faster deconstruction
+ * Less risk of signals reaching already deleted objects
+ */
+ for (int i = 0; i < itemCount(); ++i) {
+ QQuickItem *item = itemAt(i);
+ QObject::disconnect(item, SIGNAL(destroyed()), this, SLOT(onItemDestroyed()));
+ QObject::disconnect(item, SIGNAL(visibleChanged()), this, SLOT(onItemVisibleChanged()));
+ QObject::disconnect(item, SIGNAL(implicitWidthChanged()), this, SLOT(invalidateSenderItem()));
+ QObject::disconnect(item, SIGNAL(implicitHeightChanged()), this, SLOT(invalidateSenderItem()));
+ }
+ delete d->styleInfo;
+}
+
+void QQuickGridLayoutBase::componentComplete()
+{
+ quickLayoutDebug() << objectName() << "QQuickGridLayoutBase::componentComplete()" << parent();
+ QQuickLayout::componentComplete();
+ updateLayoutItems();
+
+ QQuickItem *par = parentItem();
+ if (qobject_cast<QQuickLayout*>(par))
+ return;
+ rearrange(QSizeF(width(), height()));
+}
+
+/*
+ Invalidation happens like this as a reaction to that a size hint changes on an item "a":
+
+ Suppose we have the following Qml document:
+ RowLayout {
+ id: l1
+ RowLayout {
+ id: l2
+ Item {
+ id: a
+ }
+ Item {
+ id: b
+ }
+ }
+ }
+
+ 1. l2->invalidateChildItem(a) is called on l2, where item refers to "a".
+ (this will dirty the cached size hints of item "a")
+ 2. l2->invalidate() is called
+ this will :
+ i) invalidate the layout engine
+ ii) dirty the cached size hints of item "l2" (by calling parentLayout()->invalidateChildItem
+
+ */
+/*!
+ \internal
+
+ Invalidates \a childItem and this layout.
+ After a call to invalidate, the next call to retrieve e.g. sizeHint will be up-to date.
+ This function will also call QQuickLayout::invalidate(0), to ensure that the parent layout
+ is invalidated.
+ */
+void QQuickGridLayoutBase::invalidate(QQuickItem *childItem)
+{
+ Q_D(QQuickGridLayoutBase);
+ if (!isReady())
+ return;
+ if (d->m_rearranging) {
+ d->m_invalidateAfterRearrange << childItem;
+ return;
+ }
+
+ quickLayoutDebug() << "QQuickGridLayoutBase::invalidate()";
+
+ if (childItem) {
+ if (QQuickGridLayoutItem *layoutItem = d->engine.findLayoutItem(childItem))
+ layoutItem->invalidate();
+ if (d->m_ignoredItems.contains(childItem)) {
+ updateLayoutItems();
+ return;
+ }
+ }
+ // invalidate engine
+ d->engine.invalidate();
+
+ QQuickLayout::invalidate(this);
+
+ QQuickLayoutAttached *info = attachedLayoutObject(this);
+
+ const QSizeF min = sizeHint(Qt::MinimumSize);
+ const QSizeF pref = sizeHint(Qt::PreferredSize);
+ const QSizeF max = sizeHint(Qt::MaximumSize);
+
+ const bool old = info->setChangesNotificationEnabled(false);
+ info->setMinimumImplicitSize(min);
+ info->setMaximumImplicitSize(max);
+ info->setChangesNotificationEnabled(old);
+ if (pref.width() == implicitWidth() && pref.height() == implicitHeight()) {
+ // In case setImplicitSize does not emit implicit{Width|Height}Changed
+ if (QQuickLayout *parentLayout = qobject_cast<QQuickLayout *>(parentItem()))
+ parentLayout->invalidate(this);
+ } else {
+ setImplicitSize(pref.width(), pref.height());
+ }
+}
+
+void QQuickGridLayoutBase::updateLayoutItems()
+{
+ Q_D(QQuickGridLayoutBase);
+ if (!isReady())
+ return;
+ if (d->m_rearranging) {
+ d->m_updateAfterRearrange = true;
+ return;
+ }
+
+ quickLayoutDebug() << "QQuickGridLayoutBase::updateLayoutItems";
+ d->engine.deleteItems();
+ insertLayoutItems();
+
+ invalidate();
+ quickLayoutDebug() << "QQuickGridLayoutBase::updateLayoutItems LEAVING";
+}
+
+QQuickItem *QQuickGridLayoutBase::itemAt(int index) const
+{
+ Q_D(const QQuickGridLayoutBase);
+ return static_cast<QQuickGridLayoutItem*>(d->engine.itemAt(index))->layoutItem();
+}
+
+int QQuickGridLayoutBase::itemCount() const
+{
+ Q_D(const QQuickGridLayoutBase);
+ return d->engine.itemCount();
+}
+
+void QQuickGridLayoutBase::itemChange(ItemChange change, const ItemChangeData &value)
+{
+ if (change == ItemChildAddedChange) {
+ quickLayoutDebug() << "ItemChildAddedChange";
+ QQuickItem *item = value.item;
+ QObject::connect(item, SIGNAL(destroyed()), this, SLOT(onItemDestroyed()));
+ QObject::connect(item, SIGNAL(visibleChanged()), this, SLOT(onItemVisibleChanged()));
+ } else if (change == ItemChildRemovedChange) {
+ quickLayoutDebug() << "ItemChildRemovedChange";
+ QQuickItem *item = value.item;
+ QObject::disconnect(item, SIGNAL(destroyed()), this, SLOT(onItemDestroyed()));
+ QObject::disconnect(item, SIGNAL(visibleChanged()), this, SLOT(onItemVisibleChanged()));
+ }
+
+ QQuickLayout::itemChange(change, value);
+}
+
+void QQuickGridLayoutBase::removeGridItem(QGridLayoutItem *gridItem)
+{
+ Q_D(QQuickGridLayoutBase);
+ const int index = gridItem->firstRow(d->orientation);
+ d->engine.removeItem(gridItem);
+ d->engine.removeRows(index, 1, d->orientation);
+}
+
+void QQuickGridLayoutBase::onItemVisibleChanged()
+{
+ if (!isReady())
+ return;
+ quickLayoutDebug() << "QQuickGridLayoutBase::onItemVisibleChanged";
+ updateLayoutItems();
+}
+
+void QQuickGridLayoutBase::onItemDestroyed()
+{
+ if (!isReady())
+ return;
+ Q_D(QQuickGridLayoutBase);
+ quickLayoutDebug() << "QQuickGridLayoutBase::onItemDestroyed";
+ QQuickItem *inDestruction = static_cast<QQuickItem *>(sender());
+ if (QQuickGridLayoutItem *gridItem = d->engine.findLayoutItem(inDestruction)) {
+ removeGridItem(gridItem);
+ delete gridItem;
+ invalidate();
+ }
+}
+
+void QQuickGridLayoutBase::rearrange(const QSizeF &size)
+{
+ Q_D(QQuickGridLayoutBase);
+ if (!isReady())
+ return;
+
+ d->m_rearranging = true;
+ quickLayoutDebug() << objectName() << "QQuickGridLayoutBase::rearrange()" << size;
+ Qt::LayoutDirection visualDir = effectiveLayoutDirection();
+ d->engine.setVisualDirection(visualDir);
+
+ /*
+ qreal left, top, right, bottom;
+ left = top = right = bottom = 0; // ### support for margins?
+ if (visualDir == Qt::RightToLeft)
+ qSwap(left, right);
+ */
+
+ // Set m_dirty to false in case size hint changes during arrangement.
+ // This could happen if there is a binding like implicitWidth: height
+ QQuickLayout::rearrange(size);
+ d->engine.setGeometries(QRectF(QPointF(0,0), size), d->styleInfo);
+ d->m_rearranging = false;
+
+ for (QQuickItem *invalid : qAsConst(d->m_invalidateAfterRearrange))
+ invalidate(invalid);
+ d->m_invalidateAfterRearrange.clear();
+
+ if (d->m_updateAfterRearrange) {
+ updateLayoutItems();
+ d->m_updateAfterRearrange = false;
+ }
+}
+
+/**********************************
+ **
+ ** QQuickGridLayout
+ **
+ **/
+QQuickGridLayout::QQuickGridLayout(QQuickItem *parent /* = 0*/)
+ : QQuickGridLayoutBase(*new QQuickGridLayoutPrivate, Qt::Horizontal, parent)
+{
+}
+
+/*!
+ \qmlproperty real GridLayout::columnSpacing
+
+ This property holds the spacing between each column.
+ The default value is \c 5.
+*/
+qreal QQuickGridLayout::columnSpacing() const
+{
+ Q_D(const QQuickGridLayout);
+ return d->engine.spacing(Qt::Horizontal, d->styleInfo);
+}
+
+void QQuickGridLayout::setColumnSpacing(qreal spacing)
+{
+ Q_D(QQuickGridLayout);
+ if (qIsNaN(spacing) || columnSpacing() == spacing)
+ return;
+
+ d->engine.setSpacing(spacing, Qt::Horizontal);
+ invalidate();
+}
+
+/*!
+ \qmlproperty real GridLayout::rowSpacing
+
+ This property holds the spacing between each row.
+ The default value is \c 5.
+*/
+qreal QQuickGridLayout::rowSpacing() const
+{
+ Q_D(const QQuickGridLayout);
+ return d->engine.spacing(Qt::Vertical, d->styleInfo);
+}
+
+void QQuickGridLayout::setRowSpacing(qreal spacing)
+{
+ Q_D(QQuickGridLayout);
+ if (qIsNaN(spacing) || rowSpacing() == spacing)
+ return;
+
+ d->engine.setSpacing(spacing, Qt::Vertical);
+ invalidate();
+}
+
+/*!
+ \qmlproperty int GridLayout::columns
+
+ This property holds the column limit for items positioned if \l flow is
+ \c GridLayout.LeftToRight.
+ The default value is that there is no limit.
+*/
+int QQuickGridLayout::columns() const
+{
+ Q_D(const QQuickGridLayout);
+ return d->columns;
+}
+
+void QQuickGridLayout::setColumns(int columns)
+{
+ Q_D(QQuickGridLayout);
+ if (d->columns == columns)
+ return;
+ d->columns = columns;
+ updateLayoutItems();
+ emit columnsChanged();
+}
+
+
+/*!
+ \qmlproperty int GridLayout::rows
+
+ This property holds the row limit for items positioned if \l flow is \c GridLayout.TopToBottom.
+ The default value is that there is no limit.
+*/
+int QQuickGridLayout::rows() const
+{
+ Q_D(const QQuickGridLayout);
+ return d->rows;
+}
+
+void QQuickGridLayout::setRows(int rows)
+{
+ Q_D(QQuickGridLayout);
+ if (d->rows == rows)
+ return;
+ d->rows = rows;
+ updateLayoutItems();
+ emit rowsChanged();
+}
+
+
+/*!
+ \qmlproperty enumeration GridLayout::flow
+
+ This property holds the flow direction of items that does not have an explicit cell
+ position set.
+ It is used together with the \l columns or \l rows property, where
+ they specify when flow is reset to the next row or column respectively.
+
+ Possible values are:
+
+ \list
+ \li GridLayout.LeftToRight (default) - Items are positioned next to
+ each other, then wrapped to the next line.
+ \li GridLayout.TopToBottom - Items are positioned next to each
+ other from top to bottom, then wrapped to the next column.
+ \endlist
+
+ \sa rows
+ \sa columns
+*/
+QQuickGridLayout::Flow QQuickGridLayout::flow() const
+{
+ Q_D(const QQuickGridLayout);
+ return d->flow;
+}
+
+void QQuickGridLayout::setFlow(QQuickGridLayout::Flow flow)
+{
+ Q_D(QQuickGridLayout);
+ if (d->flow == flow)
+ return;
+ d->flow = flow;
+ // If flow is changed, the layout needs to be repopulated
+ updateLayoutItems();
+ emit flowChanged();
+}
+
+void QQuickGridLayout::insertLayoutItems()
+{
+ Q_D(QQuickGridLayout);
+
+ int nextCellPos[2] = {0,0};
+ int &nextColumn = nextCellPos[0];
+ int &nextRow = nextCellPos[1];
+
+ const int flowOrientation = flow();
+ int &flowColumn = nextCellPos[flowOrientation];
+ int &flowRow = nextCellPos[1 - flowOrientation];
+ int flowBound = (flowOrientation == QQuickGridLayout::LeftToRight) ? columns() : rows();
+
+ if (flowBound < 0)
+ flowBound = std::numeric_limits<int>::max();
+
+ d->m_ignoredItems.clear();
+ QSizeF sizeHints[Qt::NSizeHints];
+ const auto items = childItems();
+ for (QQuickItem *child : items) {
+ QQuickLayoutAttached *info = 0;
+
+ // Will skip all items with effective maximum width/height == 0
+ if (shouldIgnoreItem(child, info, sizeHints))
+ continue;
+
+ Qt::Alignment alignment = 0;
+ int row = -1;
+ int column = -1;
+ int span[2] = {1,1};
+ int &columnSpan = span[0];
+ int &rowSpan = span[1];
+
+ bool invalidRowColumn = false;
+ if (info) {
+ if (info->isRowSet() || info->isColumnSet()) {
+ // If row is specified and column is not specified (or vice versa),
+ // the unspecified component of the cell position should default to 0
+ row = column = 0;
+ if (info->isRowSet()) {
+ row = info->row();
+ invalidRowColumn = row < 0;
+ }
+ if (info->isColumnSet()) {
+ column = info->column();
+ invalidRowColumn = column < 0;
+ }
+ }
+ if (invalidRowColumn) {
+ qWarning("QQuickGridLayoutBase::insertLayoutItems: invalid row/column: %d",
+ row < 0 ? row : column);
+ return;
+ }
+ rowSpan = info->rowSpan();
+ columnSpan = info->columnSpan();
+ if (columnSpan < 1 || rowSpan < 1) {
+ qWarning("QQuickGridLayoutBase::addItem: invalid row span/column span: %d",
+ rowSpan < 1 ? rowSpan : columnSpan);
+ return;
+ }
+
+ alignment = info->alignment();
+ }
+
+ Q_ASSERT(columnSpan >= 1);
+ Q_ASSERT(rowSpan >= 1);
+ const int sp = span[flowOrientation];
+ if (sp > flowBound)
+ return;
+
+ if (row >= 0)
+ nextRow = row;
+ if (column >= 0)
+ nextColumn = column;
+
+ if (row < 0 || column < 0) {
+ /* if row or column is not specified, find next position by
+ advancing in the flow direction until there is a cell that
+ can accept the item.
+
+ The acceptance rules are pretty simple, but complexity arises
+ when an item requires several cells (due to spans):
+ 1. Check if the cells that the item will require
+ does not extend beyond columns (for LeftToRight) or
+ rows (for TopToBottom).
+ 2. Check if the cells that the item will require is not already
+ taken by another item.
+ */
+ bool cellAcceptsItem;
+ while (true) {
+ // Check if the item does not span beyond the layout bound
+ cellAcceptsItem = (flowColumn + sp) <= flowBound;
+
+ // Check if all the required cells are not taken
+ for (int rs = 0; cellAcceptsItem && rs < rowSpan; ++rs) {
+ for (int cs = 0; cellAcceptsItem && cs < columnSpan; ++cs) {
+ if (d->engine.itemAt(nextRow + rs, nextColumn + cs)) {
+ cellAcceptsItem = false;
+ }
+ }
+ }
+ if (cellAcceptsItem)
+ break;
+ ++flowColumn;
+ if (flowColumn == flowBound) {
+ flowColumn = 0;
+ ++flowRow;
+ }
+ }
+ }
+ column = nextColumn;
+ row = nextRow;
+ QQuickGridLayoutItem *layoutItem = new QQuickGridLayoutItem(child, row, column, rowSpan, columnSpan, alignment);
+ layoutItem->setCachedSizeHints(sizeHints);
+
+ d->engine.insertItem(layoutItem, -1);
+ }
+}
+
+/**********************************
+ **
+ ** QQuickLinearLayout
+ **
+ **/
+QQuickLinearLayout::QQuickLinearLayout(Qt::Orientation orientation,
+ QQuickItem *parent /*= 0*/)
+ : QQuickGridLayoutBase(*new QQuickLinearLayoutPrivate, orientation, parent)
+{
+}
+
+/*!
+ \qmlproperty enumeration RowLayout::layoutDirection
+ \since QtQuick.Layouts 1.1
+
+ This property holds the layout direction of the row layout - it controls whether items are laid
+ out from left ro right or right to left. If \c Qt.RightToLeft is specified,
+ left-aligned items will be right-aligned and right-aligned items will be left-aligned.
+
+ Possible values:
+
+ \list
+ \li Qt.LeftToRight (default) - Items are laid out from left to right.
+ \li Qt.RightToLeft - Items are laid out from right to left
+ \endlist
+
+ \sa GridLayout::layoutDirection, ColumnLayout::layoutDirection
+*/
+/*!
+ \qmlproperty enumeration ColumnLayout::layoutDirection
+ \since QtQuick.Layouts 1.1
+
+ This property holds the layout direction of the column layout - it controls whether items are laid
+ out from left ro right or right to left. If \c Qt.RightToLeft is specified,
+ left-aligned items will be right-aligned and right-aligned items will be left-aligned.
+
+ Possible values:
+
+ \list
+ \li Qt.LeftToRight (default) - Items are laid out from left to right.
+ \li Qt.RightToLeft - Items are laid out from right to left
+ \endlist
+
+ \sa GridLayout::layoutDirection, RowLayout::layoutDirection
+*/
+
+
+/*!
+ \qmlproperty real RowLayout::spacing
+
+ This property holds the spacing between each cell.
+ The default value is \c 5.
+*/
+/*!
+ \qmlproperty real ColumnLayout::spacing
+
+ This property holds the spacing between each cell.
+ The default value is \c 5.
+*/
+
+qreal QQuickLinearLayout::spacing() const
+{
+ Q_D(const QQuickLinearLayout);
+ return d->engine.spacing(d->orientation, d->styleInfo);
+}
+
+void QQuickLinearLayout::setSpacing(qreal space)
+{
+ Q_D(QQuickLinearLayout);
+ if (qIsNaN(space) || spacing() == space)
+ return;
+
+ d->engine.setSpacing(space, Qt::Horizontal | Qt::Vertical);
+ invalidate();
+}
+
+void QQuickLinearLayout::insertLayoutItems()
+{
+ Q_D(QQuickLinearLayout);
+ d->m_ignoredItems.clear();
+ QSizeF sizeHints[Qt::NSizeHints];
+ const auto items = childItems();
+ for (QQuickItem *child : items) {
+ Q_ASSERT(child);
+ QQuickLayoutAttached *info = 0;
+
+ // Will skip all items with effective maximum width/height == 0
+ if (shouldIgnoreItem(child, info, sizeHints))
+ continue;
+
+ Qt::Alignment alignment = 0;
+ if (info)
+ alignment = info->alignment();
+
+ const int index = d->engine.rowCount(d->orientation);
+ d->engine.insertRow(index, d->orientation);
+
+ int gridRow = 0;
+ int gridColumn = index;
+ if (d->orientation == Qt::Vertical)
+ qSwap(gridRow, gridColumn);
+ QQuickGridLayoutItem *layoutItem = new QQuickGridLayoutItem(child, gridRow, gridColumn, 1, 1, alignment);
+ layoutItem->setCachedSizeHints(sizeHints);
+ d->engine.insertItem(layoutItem, index);
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/imports/layouts/qquicklinearlayout_p.h b/src/imports/layouts/qquicklinearlayout_p.h
new file mode 100644
index 0000000000..86404f8d79
--- /dev/null
+++ b/src/imports/layouts/qquicklinearlayout_p.h
@@ -0,0 +1,250 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Quick Layouts module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQUICKLINEARLAYOUT_P_H
+#define QQUICKLINEARLAYOUT_P_H
+
+#include "qquicklayout_p.h"
+#include "qquickgridlayoutengine_p.h"
+
+QT_BEGIN_NAMESPACE
+
+/**********************************
+ **
+ ** QQuickGridLayoutBase
+ **
+ **/
+class QQuickGridLayoutBasePrivate;
+
+class QQuickGridLayoutBase : public QQuickLayout
+{
+ Q_OBJECT
+
+ Q_PROPERTY(Qt::LayoutDirection layoutDirection READ layoutDirection WRITE setLayoutDirection NOTIFY layoutDirectionChanged REVISION 1)
+
+public:
+
+ QQuickGridLayoutBase();
+
+ explicit QQuickGridLayoutBase(QQuickGridLayoutBasePrivate &dd,
+ Qt::Orientation orientation,
+ QQuickItem *parent = 0);
+
+ ~QQuickGridLayoutBase();
+ void componentComplete() Q_DECL_OVERRIDE;
+ void invalidate(QQuickItem *childItem = 0) Q_DECL_OVERRIDE;
+ Qt::Orientation orientation() const;
+ void setOrientation(Qt::Orientation orientation);
+ QSizeF sizeHint(Qt::SizeHint whichSizeHint) const Q_DECL_OVERRIDE;
+ Qt::LayoutDirection layoutDirection() const;
+ void setLayoutDirection(Qt::LayoutDirection dir);
+ Qt::LayoutDirection effectiveLayoutDirection() const;
+ void setAlignment(QQuickItem *item, Qt::Alignment align) Q_DECL_OVERRIDE;
+
+protected:
+ void updateLayoutItems() Q_DECL_OVERRIDE;
+ QQuickItem *itemAt(int index) const Q_DECL_OVERRIDE;
+ int itemCount() const Q_DECL_OVERRIDE;
+
+ void rearrange(const QSizeF &size) Q_DECL_OVERRIDE;
+ virtual void insertLayoutItems() {}
+ void itemChange(ItemChange change, const ItemChangeData &data) Q_DECL_OVERRIDE;
+
+signals:
+ Q_REVISION(1) void layoutDirectionChanged();
+
+protected slots:
+ void onItemVisibleChanged();
+ void onItemDestroyed();
+
+private:
+ void removeGridItem(QGridLayoutItem *gridItem);
+ Q_DECLARE_PRIVATE(QQuickGridLayoutBase)
+};
+
+class QQuickLayoutStyleInfo;
+
+class QQuickGridLayoutBasePrivate : public QQuickLayoutPrivate
+{
+ Q_DECLARE_PUBLIC(QQuickGridLayoutBase)
+
+public:
+ QQuickGridLayoutBasePrivate() : m_rearranging(false)
+ , m_updateAfterRearrange(false)
+ , m_layoutDirection(Qt::LeftToRight)
+ {}
+
+ void mirrorChange() Q_DECL_OVERRIDE
+ {
+ Q_Q(QQuickGridLayoutBase);
+ q->invalidate();
+ }
+
+ QQuickGridLayoutEngine engine;
+ Qt::Orientation orientation;
+ unsigned m_rearranging : 1;
+ unsigned m_updateAfterRearrange : 1;
+ QVector<QQuickItem *> m_invalidateAfterRearrange;
+ Qt::LayoutDirection m_layoutDirection : 2;
+
+ QQuickLayoutStyleInfo *styleInfo;
+};
+
+/**********************************
+ **
+ ** QQuickGridLayout
+ **
+ **/
+class QQuickGridLayoutPrivate;
+class QQuickGridLayout : public QQuickGridLayoutBase
+{
+ Q_OBJECT
+
+ Q_PROPERTY(qreal columnSpacing READ columnSpacing WRITE setColumnSpacing NOTIFY columnSpacingChanged)
+ Q_PROPERTY(qreal rowSpacing READ rowSpacing WRITE setRowSpacing NOTIFY rowSpacingChanged)
+ Q_PROPERTY(int columns READ columns WRITE setColumns NOTIFY columnsChanged)
+ Q_PROPERTY(int rows READ rows WRITE setRows NOTIFY rowsChanged)
+ Q_PROPERTY(Flow flow READ flow WRITE setFlow NOTIFY flowChanged)
+public:
+ explicit QQuickGridLayout(QQuickItem *parent = 0);
+ qreal columnSpacing() const;
+ void setColumnSpacing(qreal spacing);
+ qreal rowSpacing() const;
+ void setRowSpacing(qreal spacing);
+
+ int columns() const;
+ void setColumns(int columns);
+ int rows() const;
+ void setRows(int rows);
+
+ Q_ENUMS(Flow)
+ enum Flow { LeftToRight, TopToBottom };
+ Flow flow() const;
+ void setFlow(Flow flow);
+
+ void insertLayoutItems();
+
+signals:
+ void columnSpacingChanged();
+ void rowSpacingChanged();
+
+ void columnsChanged();
+ void rowsChanged();
+
+ void flowChanged();
+private:
+ Q_DECLARE_PRIVATE(QQuickGridLayout)
+};
+
+class QQuickGridLayoutPrivate : public QQuickGridLayoutBasePrivate
+{
+ Q_DECLARE_PUBLIC(QQuickGridLayout)
+public:
+ QQuickGridLayoutPrivate(): columns(-1), rows(-1), flow(QQuickGridLayout::LeftToRight) {}
+ int columns;
+ int rows;
+ QQuickGridLayout::Flow flow;
+};
+
+
+/**********************************
+ **
+ ** QQuickLinearLayout
+ **
+ **/
+class QQuickLinearLayoutPrivate;
+class QQuickLinearLayout : public QQuickGridLayoutBase
+{
+ Q_OBJECT
+ Q_PROPERTY(qreal spacing READ spacing WRITE setSpacing NOTIFY spacingChanged)
+public:
+ explicit QQuickLinearLayout(Qt::Orientation orientation,
+ QQuickItem *parent = 0);
+ void insertLayoutItem(QQuickItem *item);
+ qreal spacing() const;
+ void setSpacing(qreal spacing);
+
+ void insertLayoutItems();
+
+signals:
+ void spacingChanged();
+private:
+ Q_DECLARE_PRIVATE(QQuickLinearLayout)
+};
+
+class QQuickLinearLayoutPrivate : public QQuickGridLayoutBasePrivate
+{
+ Q_DECLARE_PUBLIC(QQuickLinearLayout)
+public:
+ QQuickLinearLayoutPrivate() {}
+};
+
+
+/**********************************
+ **
+ ** QQuickRowLayout
+ **
+ **/
+class QQuickRowLayout : public QQuickLinearLayout
+{
+ Q_OBJECT
+
+public:
+ explicit QQuickRowLayout(QQuickItem *parent = 0)
+ : QQuickLinearLayout(Qt::Horizontal, parent) {}
+};
+
+
+/**********************************
+ **
+ ** QQuickColumnLayout
+ **
+ **/
+class QQuickColumnLayout : public QQuickLinearLayout
+{
+ Q_OBJECT
+
+public:
+ explicit QQuickColumnLayout(QQuickItem *parent = 0)
+ : QQuickLinearLayout(Qt::Vertical, parent) {}
+};
+
+QT_END_NAMESPACE
+
+#endif // QQUICKLINEARLAYOUT_P_H
diff --git a/src/imports/layouts/qquickstacklayout.cpp b/src/imports/layouts/qquickstacklayout.cpp
new file mode 100644
index 0000000000..a223dd0374
--- /dev/null
+++ b/src/imports/layouts/qquickstacklayout.cpp
@@ -0,0 +1,339 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Quick Layouts module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickstacklayout_p.h"
+#include <limits>
+
+/*!
+ \qmltype StackLayout
+ \instantiates QQuickStackLayout
+ \inherits Item
+ \inqmlmodule QtQuick.Layouts
+ \ingroup layouts
+ \brief The StackLayout class provides a stack of items where
+ only one item is visible at a time.
+
+ The current visible item can be modified by setting the \l currentIndex property.
+ The index corresponds to the order of the StackLayout's children.
+
+ In contrast to most other layouts, child Items' \l{Layout::fillWidth}{Layout.fillWidth} and \l{Layout::fillHeight}{Layout.fillHeight} properties
+ default to \c true. As a consequence, child items are by default filled to match the size of the StackLayout as long as their
+ \l{Layout::maximumWidth}{Layout.maximumWidth} or \l{Layout::maximumHeight}{Layout.maximumHeight} does not prevent it.
+
+ Items are added to the layout by reparenting the item to the layout. Similarly, removal is done by reparenting the item from the layout.
+ Both of these operations will affect the layout's \l count property.
+
+ The following code will create a StackLayout where only the 'plum' rectangle is visible.
+ \code
+ StackLayout {
+ id: layout
+ anchors.fill: parent
+ currentIndex: 1
+ Rectangle {
+ color: 'teal'
+ implicitWidth: 200
+ implicitHeight: 200
+ }
+ Rectangle {
+ color: 'plum'
+ implicitWidth: 300
+ implicitHeight: 200
+ }
+ }
+ \endcode
+
+ Items in a StackLayout support these attached properties:
+ \list
+ \li \l{Layout::minimumWidth}{Layout.minimumWidth}
+ \li \l{Layout::minimumHeight}{Layout.minimumHeight}
+ \li \l{Layout::preferredWidth}{Layout.preferredWidth}
+ \li \l{Layout::preferredHeight}{Layout.preferredHeight}
+ \li \l{Layout::maximumWidth}{Layout.maximumWidth}
+ \li \l{Layout::maximumHeight}{Layout.maximumHeight}
+ \li \l{Layout::fillWidth}{Layout.fillWidth}
+ \li \l{Layout::fillHeight}{Layout.fillHeight}
+ \endlist
+
+ Read more about attached properties \l{QML Object Attributes}{here}.
+ \sa ColumnLayout
+ \sa GridLayout
+ \sa RowLayout
+ \sa StackView
+*/
+
+QQuickStackLayout::QQuickStackLayout(QQuickItem *parent) :
+ QQuickLayout(*new QQuickStackLayoutPrivate, parent)
+{
+}
+
+/*!
+ \qmlproperty int StackLayout::count
+
+ This property holds the number of items that belong to the layout.
+
+ Only items that are children of the StackLayout will be candidates for layouting.
+*/
+int QQuickStackLayout::count() const
+{
+ Q_D(const QQuickStackLayout);
+ return d->count;
+}
+
+/*!
+ \qmlproperty int StackLayout::currentIndex
+
+ This property holds the index of the child item that is currently visible in the StackLayout.
+ By default it will be \c -1 for an empty layout, otherwise the default is \c 0 (referring to the first item).
+*/
+int QQuickStackLayout::currentIndex() const
+{
+ Q_D(const QQuickStackLayout);
+ return d->currentIndex;
+}
+
+void QQuickStackLayout::setCurrentIndex(int index)
+{
+ Q_D(QQuickStackLayout);
+ if (index != d->currentIndex) {
+ QQuickItem *prev = itemAt(d->currentIndex);
+ QQuickItem *next = itemAt(index);
+ d->currentIndex = index;
+ d->explicitCurrentIndex = true;
+ if (prev)
+ prev->setVisible(false);
+ if (next)
+ next->setVisible(true);
+
+ if (isComponentComplete()) {
+ rearrange(QSizeF(width(), height()));
+ emit currentIndexChanged();
+ }
+ }
+}
+
+void QQuickStackLayout::componentComplete()
+{
+ QQuickLayout::componentComplete(); // will call our geometryChange(), (where isComponentComplete() == true)
+
+ updateLayoutItems();
+
+ QQuickItem *par = parentItem();
+ if (qobject_cast<QQuickLayout*>(par))
+ return;
+
+ rearrange(QSizeF(width(), height()));
+}
+
+QSizeF QQuickStackLayout::sizeHint(Qt::SizeHint whichSizeHint) const
+{
+ QSizeF &askingFor = m_cachedSizeHints[whichSizeHint];
+ if (!askingFor.isValid()) {
+ QSizeF &minS = m_cachedSizeHints[Qt::MinimumSize];
+ QSizeF &prefS = m_cachedSizeHints[Qt::PreferredSize];
+ QSizeF &maxS = m_cachedSizeHints[Qt::MaximumSize];
+
+ minS = QSizeF(0,0);
+ prefS = QSizeF(0,0);
+ maxS = QSizeF(std::numeric_limits<qreal>::infinity(), std::numeric_limits<qreal>::infinity());
+
+ const int count = itemCount();
+ m_cachedItemSizeHints.resize(count);
+ for (int i = 0; i < count; ++i) {
+ SizeHints &hints = m_cachedItemSizeHints[i];
+ QQuickStackLayout::collectItemSizeHints(itemAt(i), hints.array);
+ minS = minS.expandedTo(hints.min());
+ prefS = prefS.expandedTo(hints.pref());
+ //maxS = maxS.boundedTo(hints.max()); // Can be resized to be larger than any of its items.
+ // This is the same as QStackLayout does it.
+ // Not sure how descent makes sense here...
+ }
+ }
+ return askingFor;
+}
+
+int QQuickStackLayout::indexOf(QQuickItem *childItem) const
+{
+ if (childItem) {
+ int indexOfItem = 0;
+ const auto items = childItems();
+ for (QQuickItem *item : items) {
+ if (shouldIgnoreItem(item))
+ continue;
+ if (childItem == item)
+ return indexOfItem;
+ ++indexOfItem;
+ }
+ }
+ return -1;
+}
+
+QQuickItem *QQuickStackLayout::itemAt(int index) const
+{
+ const auto items = childItems();
+ for (QQuickItem *item : items) {
+ if (shouldIgnoreItem(item))
+ continue;
+ if (index == 0)
+ return item;
+ --index;
+ }
+ return 0;
+}
+
+int QQuickStackLayout::itemCount() const
+{
+ int count = 0;
+ const auto items = childItems();
+ for (QQuickItem *item : items) {
+ if (shouldIgnoreItem(item))
+ continue;
+ ++count;
+ }
+ return count;
+}
+
+void QQuickStackLayout::setAlignment(QQuickItem * /*item*/, Qt::Alignment /*align*/)
+{
+ // ### Do we have to respect alignment?
+}
+
+void QQuickStackLayout::invalidate(QQuickItem *childItem)
+{
+ Q_D(QQuickStackLayout);
+ if (d->m_ignoredItems.contains(childItem)) {
+ // If an invalid item gets a valid size, it should be included, as it was added to the layout
+ updateLayoutItems();
+ return;
+ }
+
+ const int indexOfChild = indexOf(childItem);
+ if (indexOfChild >= 0 && indexOfChild < m_cachedItemSizeHints.count()) {
+ m_cachedItemSizeHints[indexOfChild].min() = QSizeF();
+ m_cachedItemSizeHints[indexOfChild].pref() = QSizeF();
+ m_cachedItemSizeHints[indexOfChild].max() = QSizeF();
+ }
+
+ for (int i = 0; i < Qt::NSizeHints; ++i)
+ m_cachedSizeHints[i] = QSizeF();
+ QQuickLayout::invalidate(this);
+
+ QQuickLayoutAttached *info = attachedLayoutObject(this);
+
+ const QSizeF min = sizeHint(Qt::MinimumSize);
+ const QSizeF pref = sizeHint(Qt::PreferredSize);
+ const QSizeF max = sizeHint(Qt::MaximumSize);
+
+ const bool old = info->setChangesNotificationEnabled(false);
+ info->setMinimumImplicitSize(min);
+ info->setMaximumImplicitSize(max);
+ info->setChangesNotificationEnabled(old);
+ if (pref.width() == implicitWidth() && pref.height() == implicitHeight()) {
+ // In case setImplicitSize does not emit implicit{Width|Height}Changed
+ if (QQuickLayout *parentLayout = qobject_cast<QQuickLayout *>(parentItem()))
+ parentLayout->invalidate(this);
+ } else {
+ setImplicitSize(pref.width(), pref.height());
+ }
+}
+
+void QQuickStackLayout::updateLayoutItems()
+{
+ Q_D(QQuickStackLayout);
+ d->m_ignoredItems.clear();
+ const int count = itemCount();
+ int oldIndex = d->currentIndex;
+ if (!d->explicitCurrentIndex)
+ d->currentIndex = (count > 0 ? 0 : -1);
+
+ if (d->currentIndex != oldIndex)
+ emit currentIndexChanged();
+
+ if (count != d->count) {
+ d->count = count;
+ emit countChanged();
+ }
+ for (int i = 0; i < count; ++i)
+ itemAt(i)->setVisible(d->currentIndex == i);
+
+ invalidate();
+}
+
+void QQuickStackLayout::rearrange(const QSizeF &newSize)
+{
+ Q_D(QQuickStackLayout);
+ if (newSize.isNull() || !newSize.isValid())
+ return;
+ (void)sizeHint(Qt::PreferredSize); // Make sure m_cachedItemSizeHints are valid
+
+ if (d->currentIndex == -1 || d->currentIndex >= m_cachedItemSizeHints.count())
+ return;
+ QQuickStackLayout::SizeHints &hints = m_cachedItemSizeHints[d->currentIndex];
+ QQuickItem *item = itemAt(d->currentIndex);
+ Q_ASSERT(item);
+ item->setPosition(QPointF(0,0)); // ### respect alignment?
+ item->setSize(newSize.expandedTo(hints.min()).boundedTo(hints.max()));
+ QQuickLayout::rearrange(newSize);
+}
+
+void QQuickStackLayout::collectItemSizeHints(QQuickItem *item, QSizeF *sizeHints)
+{
+ QQuickLayoutAttached *info = 0;
+ QQuickLayout::effectiveSizeHints_helper(item, sizeHints, &info, true);
+ if (!info)
+ return;
+ if (info->isFillWidthSet() && !info->fillWidth()) {
+ const qreal pref = sizeHints[Qt::PreferredSize].width();
+ sizeHints[Qt::MinimumSize].setWidth(pref);
+ sizeHints[Qt::MaximumSize].setWidth(pref);
+ }
+
+ if (info->isFillHeightSet() && !info->fillHeight()) {
+ const qreal pref = sizeHints[Qt::PreferredSize].height();
+ sizeHints[Qt::MinimumSize].setHeight(pref);
+ sizeHints[Qt::MaximumSize].setHeight(pref);
+ }
+}
+
+bool QQuickStackLayout::shouldIgnoreItem(QQuickItem *item) const
+{
+ const bool ignored = QQuickItemPrivate::get(item)->isTransparentForPositioner();
+ if (ignored)
+ d_func()->m_ignoredItems << item;
+ return ignored;
+}
diff --git a/src/imports/layouts/qquickstacklayout_p.h b/src/imports/layouts/qquickstacklayout_p.h
new file mode 100644
index 0000000000..7b6400c3a3
--- /dev/null
+++ b/src/imports/layouts/qquickstacklayout_p.h
@@ -0,0 +1,108 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Quick Layouts module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQUICKSTACKLAYOUT_H
+#define QQUICKSTACKLAYOUT_H
+
+#include <qquicklayout_p.h>
+
+class QQuickStackLayoutPrivate;
+
+class QQuickStackLayout : public QQuickLayout
+{
+ Q_OBJECT
+ Q_PROPERTY(int count READ count NOTIFY countChanged)
+ Q_PROPERTY(int currentIndex READ currentIndex WRITE setCurrentIndex NOTIFY currentIndexChanged)
+
+public:
+ explicit QQuickStackLayout(QQuickItem *parent = 0);
+ int count() const;
+ int currentIndex() const;
+ void setCurrentIndex(int index);
+
+ void componentComplete() Q_DECL_OVERRIDE;
+ QSizeF sizeHint(Qt::SizeHint whichSizeHint) const Q_DECL_OVERRIDE;
+ void setAlignment(QQuickItem *item, Qt::Alignment align) Q_DECL_OVERRIDE;
+ void invalidate(QQuickItem *childItem = 0) Q_DECL_OVERRIDE;
+ void updateLayoutItems() Q_DECL_OVERRIDE;
+ void rearrange(const QSizeF &) Q_DECL_OVERRIDE;
+
+ // iterator
+ Q_INVOKABLE QQuickItem *itemAt(int index) const Q_DECL_OVERRIDE;
+ int itemCount() const Q_DECL_OVERRIDE;
+ int indexOf(QQuickItem *item) const;
+
+
+
+signals:
+ void currentIndexChanged();
+ void countChanged();
+
+public slots:
+
+private:
+ static void collectItemSizeHints(QQuickItem *item, QSizeF *sizeHints);
+ bool shouldIgnoreItem(QQuickItem *item) const;
+ Q_DECLARE_PRIVATE(QQuickStackLayout)
+
+ QList<QQuickItem*> m_items;
+
+ typedef struct {
+ inline QSizeF &min() { return array[Qt::MinimumSize]; }
+ inline QSizeF &pref() { return array[Qt::PreferredSize]; }
+ inline QSizeF &max() { return array[Qt::MaximumSize]; }
+ QSizeF array[Qt::NSizeHints];
+ } SizeHints;
+
+ mutable QVector<SizeHints> m_cachedItemSizeHints;
+ mutable QSizeF m_cachedSizeHints[Qt::NSizeHints];
+};
+
+class QQuickStackLayoutPrivate : public QQuickLayoutPrivate
+{
+ Q_DECLARE_PUBLIC(QQuickStackLayout)
+public:
+ QQuickStackLayoutPrivate() : count(0), currentIndex(-1), explicitCurrentIndex(false) {}
+private:
+ int count;
+ int currentIndex;
+ bool explicitCurrentIndex;
+};
+
+#endif // QQUICKSTACKLAYOUT_H
diff --git a/src/quick/doc/images/columnlayout.png b/src/quick/doc/images/columnlayout.png
new file mode 100644
index 0000000000..f03eb7b996
--- /dev/null
+++ b/src/quick/doc/images/columnlayout.png
Binary files differ
diff --git a/src/quick/doc/images/gridlayout.png b/src/quick/doc/images/gridlayout.png
new file mode 100644
index 0000000000..493813c481
--- /dev/null
+++ b/src/quick/doc/images/gridlayout.png
Binary files differ
diff --git a/src/quick/doc/images/qtquicklayouts-example-layouts.png b/src/quick/doc/images/qtquicklayouts-example-layouts.png
new file mode 100644
index 0000000000..94619bae3f
--- /dev/null
+++ b/src/quick/doc/images/qtquicklayouts-example-layouts.png
Binary files differ
diff --git a/src/quick/doc/images/rowlayout-minimum.png b/src/quick/doc/images/rowlayout-minimum.png
new file mode 100644
index 0000000000..5875325c54
--- /dev/null
+++ b/src/quick/doc/images/rowlayout-minimum.png
Binary files differ
diff --git a/src/quick/doc/images/rowlayout.png b/src/quick/doc/images/rowlayout.png
new file mode 100644
index 0000000000..519a62fddd
--- /dev/null
+++ b/src/quick/doc/images/rowlayout.png
Binary files differ
diff --git a/src/quick/doc/qtquick.qdocconf b/src/quick/doc/qtquick.qdocconf
index 044c1696ff..4f141a733a 100644
--- a/src/quick/doc/qtquick.qdocconf
+++ b/src/quick/doc/qtquick.qdocconf
@@ -33,7 +33,7 @@ qhp.QtQuick.subprojects.examples.selectors = fake:example
tagfile = ../../../doc/qtquick/qtquick.tags
-depends += qtcore qtxmlpatterns qtqml qtgui qtlinguist qtquickcontrols qtquicklayouts qtdoc qtquickdialogs qtsensors qtwidgets qmake qtmultimedia qtgraphicaleffects
+depends += qtcore qtxmlpatterns qtqml qtgui qtlinguist qtquickcontrols qtdoc qtquickdialogs qtsensors qtwidgets qmake qtmultimedia qtgraphicaleffects
headerdirs += ..\
../../quickwidgets
diff --git a/src/quick/doc/snippets/qml/windowconstraints.qml b/src/quick/doc/snippets/qml/windowconstraints.qml
new file mode 100644
index 0000000000..3f3534f494
--- /dev/null
+++ b/src/quick/doc/snippets/qml/windowconstraints.qml
@@ -0,0 +1,76 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 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.2
+import QtQuick.Layouts 1.2
+import QtQuick.Window 2.2
+
+Window {
+ //! [binddefaultsize]
+ width: layout.implicitWidth
+ height: layout.implicitHeight
+ //! [binddefaultsize]
+ //! [bindconstraints]
+ minimumWidth: layout.Layout.minimumWidth
+ minimumHeight: layout.Layout.minimumHeight
+ maximumWidth: 1000
+ maximumHeight: layout.Layout.maximumHeight
+ //! [bindconstraints]
+
+ //! [rowlayout]
+ //! [anchoring]
+ RowLayout {
+ id: layout
+ anchors.fill: parent
+ //! [anchoring]
+ spacing: 6
+ Rectangle {
+ color: 'azure'
+ Layout.fillWidth: true
+ Layout.minimumWidth: 50
+ Layout.preferredWidth: 100
+ Layout.maximumWidth: 300
+ Layout.minimumHeight: 150
+ Text {
+ anchors.centerIn: parent
+ text: parent.width + 'x' + parent.height
+ }
+ }
+ Rectangle {
+ color: 'plum'
+ Layout.fillWidth: true
+ Layout.minimumWidth: 100
+ Layout.preferredWidth: 200
+ Layout.preferredHeight: 100
+ Text {
+ anchors.centerIn: parent
+ text: parent.width + 'x' + parent.height
+ }
+ }
+ }
+ //! [rowlayout]
+}
diff --git a/src/quick/doc/src/concepts/layouts/qtquicklayouts-index.qdoc b/src/quick/doc/src/concepts/layouts/qtquicklayouts-index.qdoc
new file mode 100644
index 0000000000..0be66fad2c
--- /dev/null
+++ b/src/quick/doc/src/concepts/layouts/qtquicklayouts-index.qdoc
@@ -0,0 +1,54 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 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$
+**
+****************************************************************************/
+
+/*!
+ \page qtquicklayouts-index.html
+ \title Qt Quick Layouts
+ \brief A module with a set of QML elements that arrange QML items in a user interface.
+
+ Qt Quick Layouts are a set of QML types used to arrange items in a user interface. In contrast
+ to \l{Item Positioners}{positioners}, Qt Quick Layouts can also resize their items. This makes
+ them well suited for resizable user interfaces. Since layouts are items they can consequently
+ be nested.
+
+ The module is new in Qt 5.1 and requires \l{Qt Quick} 2.1.
+
+ Visit the \l{Qt Quick Layouts Overview} page to get started.
+
+ \section1 Layouts
+
+ \annotatedlist layouts
+
+ \section1 Related information
+
+ \list
+ \li \l{Qt Quick}
+ \li \l{Qt Quick Layouts Overview}
+ \li \l{Qt Quick Layouts - Basic Example}
+ \li \l{Qt Quick Layouts QML Types}{Qt Quick Layouts QML Types}
+ \endlist
+*/
diff --git a/src/quick/doc/src/concepts/layouts/qtquicklayouts-overview.qdoc b/src/quick/doc/src/concepts/layouts/qtquicklayouts-overview.qdoc
new file mode 100644
index 0000000000..1b6e7dc539
--- /dev/null
+++ b/src/quick/doc/src/concepts/layouts/qtquicklayouts-overview.qdoc
@@ -0,0 +1,176 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 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$
+**
+****************************************************************************/
+
+/*!
+ \page qtquicklayouts-overview.html
+ \title Qt Quick Layouts Overview
+ \brief A set of APIs for arranging QML items in a user interface.
+
+ Qt Quick Layouts are items that are used to arrange items in a user interface. Since Qt Quick
+ Layouts also resize their items, they are well suited for resizable user interfaces.
+
+ \section1 Getting started
+
+ The QML types can be imported into your application using the following import statement in your \c {.qml} file.
+
+ \code
+ import QtQuick.Layouts 1.2
+ \endcode
+
+ \section1 Key Features
+
+
+ Some of the key features are:
+
+ \list
+ \li \l{Layout::alignment}{Alignment} of items can be specified with the
+ \l{Layout::alignment}{Layout.alignment} property
+ \li \l{Layout::fillWidth}{Resizable items} can be specified with the
+ \l{Layout::fillWidth}{Layout.fillWidth} and \l{Layout::fillHeight}{Layout.fillHeight}
+ properties.
+ \li \l{Size constraints} can be specified with
+ \l{Layout::minimumWidth}{Layout.minimumWidth},
+ \l{Layout::preferredWidth}{Layout.preferredWidth}, and
+ \l{Layout::maximumWidth}{Layout.maximumWidth} properties ("Width" can be replaced
+ with "Height" for specifying similar constraints to the height).
+ \li \l{RowLayout::spacing}{Spacings} can be specified with \l{RowLayout::spacing}{spacing},
+ \l{GridLayout::rowSpacing}{rowSpacing} or \l{GridLayout::columnSpacing}{columnSpacing}
+ \endlist
+
+ In addition to the above features, GridLayout adds these features:
+ \list
+ \li \l{Layout::row}{Grid coordinates} can be specified with the \l{Layout::row}{Layout.row} and
+ \l{Layout::column}{Layout.column}.
+ \li \l{GridLayout::flow}{Automatic grid coordinates} used together with the
+ \l{GridLayout::flow}{flow}, \l{GridLayout::rows}{rows}, and
+ \l{GridLayout::columns}{columns} properties.
+ \li \l{Layout::columnSpan}{Spans} across rows or columns can be specified with the
+ \l{Layout::rowSpan}{Layout.rowSpan} and \l{Layout::columnSpan}{Layout.columnSpan}
+ properties.
+ \endlist
+
+
+
+ \section1 Size Constraints
+ Since an item can be resized by its layout, the layout needs to know the
+ \l{Layout::minimumWidth}{minimum}, \l{Layout::preferredWidth}{preferred},
+ and \l{Layout::maximumWidth}{maximum} sizes of all items where \l{Layout::fillWidth}{Layout.fillWidth} or
+ \l{Layout::fillHeight}{Layout.fillHeight} is set to \c true.
+ For instance, the following will produce a layout with two rectangles lying side-by-side that
+ stretches horizontally. The azure rectangle can be resized from 50x150 to 300x150, and the plum
+ rectangle can be resized from 100x100 to ∞x100.
+
+ \snippet windowconstraints.qml rowlayout
+
+ \image rowlayout-minimum.png "RowLayout at its minimum"
+
+ Combining each item's constraints will give these implicit constraints to the layout element:
+
+ \table
+ \header
+ \li
+ \li minimum
+ \li preferred
+ \li maximum
+ \row
+ \li implicit constraints (width)
+ \li 156
+ \li 306
+ \li ∞ (\c Number.POSITIVE_INFINITY)
+ \row
+ \li implicit constraints (heights)
+ \li 150
+ \li 150
+ \li 150
+ \endtable
+
+ Thus, the layout cannot be narrower than 156 or be taller or shorter than 150 without breaking
+ any of the constraints of its child items.
+
+ \section2 Specifying Preferred Size
+ For each item, the effective preferred size may come from one of several candidate properties.
+ For determining the effective preferred size, it will query these candidate properties in the
+ following order, and use the first candidate with a valid width or height.
+
+ \table
+ \header
+ \li Candidate properties
+ \li Description
+ \row
+ \li \l{Layout::preferredWidth}{Layout.preferredWidth} or
+ \l{Layout::preferredHeight}{Layout.preferredHeight}
+ \li These properties are supposed to be modified by the application if the default implicit
+ size does not give the optimal arrangement.
+ \row
+ \li \l{Item::implicitWidth}{implicitWidth} or \l{Item::implicitHeight}{implicitHeight}
+ \li These properties are supposed to be supplied by each item to give a meaningful ideal size,
+ for example the size needed to display all the contents of a \l Text type.
+ An implicit width or height of \c 0 is interpreted as invalid.
+ \row
+ \li \l{Item::width}{width} and \l{Item::height}{height}
+ \li If none of the above properties are valid, the layout will resort to the
+ \l{Item::width}{width} and \l{Item::height}{height} properties.
+ \endtable
+
+ An item can specify \l{Layout::preferredWidth}{Layout.preferredWidth} without having to specify
+ \l{Layout::preferredHeight}{Layout.preferredHeight}. In this case, the effective preferred
+ height will be determined from the \l{Item::implicitHeight}{implicitHeight} (or ultimately
+ \l{Item::height}{height}).
+
+ \note Resorting to \l{Item::width}{width} or \l{Item::height}{height} properties is only
+ provided as a final fallback. If you want to override the preferred size, it is recommended to
+ use \l{Layout::preferredWidth}{Layout.preferredWidth} or
+ \l{Layout::preferredHeight}{Layout.preferredHeight}. Relying on the \l{Item::width}{width} or
+ \l{Item::height}{height} properties for specifying the preferred size might give some
+ unexpected behavior. For instance, changing the \l{Item::width}{width} or
+ \l{Item::height}{height} properties won't trigger a layout rearrangement. Also, when the layout
+ is forced to do a full rebuild it might use the actual width and height, and not the width and
+ height specified in the QML file.
+
+
+ \section1 Connecting windows and layouts
+ You can just use normal anchoring concepts to ensure that the layout will follow the window
+ resizing.
+
+ \snippet qml/windowconstraints.qml anchoring
+
+ The size constraints of layouts can be used to ensure that the window cannot be resized beyond
+ the layout constraints. You can take the size constraints from the layout and set these
+ constraints on the minimumWidth, minimumHeight, maximumWidth, and maximumHeight of the Window
+ element. The following code ensures that the window cannot be resized beyond the constraints of
+ the layout:
+
+ \snippet qml/windowconstraints.qml bindconstraints
+
+ \note Since layout.Layout.maximumWidth is infinite in this case, we cannot bind that to the
+ maximumWidth property of Window, since that is an integer number. We therefore set a fixed
+ maximum width to 1000.
+
+ Finally, you usually want the initial size of the window to be the layout's implicit size:
+
+ \snippet qml/windowconstraints.qml binddefaultsize
+*/
diff --git a/src/quick/doc/src/concepts/layouts/qtquicklayouts.qdoc b/src/quick/doc/src/concepts/layouts/qtquicklayouts.qdoc
new file mode 100644
index 0000000000..8f390c83db
--- /dev/null
+++ b/src/quick/doc/src/concepts/layouts/qtquicklayouts.qdoc
@@ -0,0 +1,46 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 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$
+**
+****************************************************************************/
+
+/*!
+ \qmlmodule QtQuick.Layouts 1.3
+ \title Qt Quick Layouts QML Types
+ \ingroup qmlmodules
+ \brief Provides QML types for arranging QML items in a user interface.
+
+ The \l{Qt Quick Layouts} module provides QML types for arranging
+ QML items in a user interface.
+ These QML types work in conjunction with \l{Qt Quick} and
+ \l{Qt Quick Controls}.
+
+ The QML types can be imported into your application using the
+ following import statement in your .qml file.
+
+ \code
+ import QtQuick.Layouts 1.3
+ \endcode
+
+*/
diff --git a/src/quick/doc/src/qtquick.qdoc b/src/quick/doc/src/qtquick.qdoc
index 98a77a48d8..4bdd02241d 100644
--- a/src/quick/doc/src/qtquick.qdoc
+++ b/src/quick/doc/src/qtquick.qdoc
@@ -101,6 +101,7 @@ Additional Qt Quick information:
containing a JavaScript interface for an SQLite database
\li \l{Qt Quick Particles QML Types}{Particles} - provides a particle
system for Qt Quick
+ \li \l{Qt Quick Layouts}{Layouts} - provides layouts for arranging Qt Quick items
\li \l{Qt Quick Window QML Types}{Window} - contains types for creating
top-level windows and accessing screen information
\li \l{Qt Quick Dialogs}{Dialogs} - contains types for creating and
diff --git a/tests/auto/quick/examples/tst_examples.cpp b/tests/auto/quick/examples/tst_examples.cpp
index 240e8a791e..548b20a80a 100644
--- a/tests/auto/quick/examples/tst_examples.cpp
+++ b/tests/auto/quick/examples/tst_examples.cpp
@@ -291,7 +291,6 @@ void tst_examples::sgsnippets_data()
void tst_examples::sgsnippets()
{
- QQuickWindow window;
QFETCH(QString, file);
@@ -301,19 +300,26 @@ void tst_examples::sgsnippets()
QCOMPARE(component.status(), QQmlComponent::Ready);
QScopedPointer<QObject> object(component.beginCreate(engine.rootContext()));
+ QQuickWindow *window = qobject_cast<QQuickWindow*>(object.data());
QQuickItem *root = qobject_cast<QQuickItem *>(object.data());
- if (!root)
+ if (!root && !window) {
component.completeCreate();
- QVERIFY(root);
+ QVERIFY(false);
+ }
+ if (!window)
+ window = new QQuickWindow;
- window.resize(240, 320);
- window.show();
- QVERIFY(QTest::qWaitForWindowExposed(&window));
+ window->resize(240, 320);
+ window->show();
+ QVERIFY(QTest::qWaitForWindowExposed(window));
- root->setParentItem(window.contentItem());
+ if (root)
+ root->setParentItem(window->contentItem());
component.completeCreate();
qApp->processEvents();
+ if (root)
+ delete window;
}
QTEST_MAIN(tst_examples)
diff --git a/tests/auto/quick/qquicklayouts/data/tst_gridlayout.qml b/tests/auto/quick/qquicklayouts/data/tst_gridlayout.qml
new file mode 100644
index 0000000000..6a1c0632ad
--- /dev/null
+++ b/tests/auto/quick/qquicklayouts/data/tst_gridlayout.qml
@@ -0,0 +1,1036 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 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.2
+import QtTest 1.0
+import QtQuick.Layouts 1.1
+
+Item {
+ id: container
+ width: 200
+ height: 200
+ TestCase {
+ id: testCase
+ name: "Tests_GridLayout"
+ when: windowShown
+ width: 200
+ height: 200
+
+ Component {
+ id: layout_flow_Component
+ GridLayout {
+ columns: 4
+ columnSpacing: 0
+ rowSpacing: 0
+ Repeater {
+ model: 6
+ Rectangle {
+ property var itemRect: [x, y, width, height]
+ color: "red"
+ Layout.preferredWidth: 10
+ Layout.preferredHeight: 10
+ Text { text: index }
+ }
+ }
+ }
+ }
+
+ function test_flow()
+ {
+ var layout = layout_flow_Component.createObject(container);
+ tryCompare(layout.children[0], "itemRect", [ 0, 0, 10, 10])
+ tryCompare(layout.children[1], "itemRect", [10, 0, 10, 10])
+ tryCompare(layout.children[2], "itemRect", [20, 0, 10, 10])
+ tryCompare(layout.children[3], "itemRect", [30, 0, 10, 10])
+
+ tryCompare(layout.children[4], "itemRect", [ 0, 10, 10, 10])
+ tryCompare(layout.children[5], "itemRect", [10, 10, 10, 10])
+
+ layout.rows = 4
+ layout.flow = GridLayout.TopToBottom
+ tryCompare(layout.children[0], "itemRect", [ 0, 0, 10, 10])
+ tryCompare(layout.children[1], "itemRect", [ 0, 10, 10, 10])
+ tryCompare(layout.children[2], "itemRect", [ 0, 20, 10, 10])
+ tryCompare(layout.children[3], "itemRect", [ 0, 30, 10, 10])
+
+ tryCompare(layout.children[4], "itemRect", [10, 0, 10, 10])
+ tryCompare(layout.children[5], "itemRect", [10, 10, 10, 10])
+
+ layout.destroy()
+ }
+
+ Component {
+ id: layout_flowLeftToRight_Component
+ GridLayout {
+ columns: 4
+ columnSpacing: 0
+ rowSpacing: 0
+ // red rectangles are auto-positioned
+ // black rectangles are explicitly positioned with row,column
+ Rectangle {
+ // First one should auto position itself at (0,0)
+ id: r1
+ color: "red"
+ width: 20
+ height: 20
+ }
+ Rectangle {
+ // (1,1)
+ id: r2
+ color: "black"
+ width: 20
+ height: 20
+ Layout.row: 1
+ Layout.column: 1
+ Layout.rowSpan: 2
+ Layout.columnSpan: 2
+ Layout.fillHeight: true
+ Layout.fillWidth: true
+ }
+ Rectangle {
+ // (0,1)
+ id: r3
+ color: "black"
+ width: 20
+ height: 20
+ Layout.row: 0
+ Layout.column: 1
+ }
+ Rectangle {
+ // This one won't fit on the left and right sides of the big black box
+ // inserted at (3,0)
+ id: r4
+ color: "red"
+ width: 20
+ height: 20
+ Layout.columnSpan: 2
+ Layout.rowSpan: 2
+ Layout.fillHeight: true
+ Layout.fillWidth: true
+ }
+ Rectangle {
+ // continue flow from (0,2)
+ id: r5
+ color: "black"
+ width: 20
+ height: 20
+ Layout.row: 0
+ Layout.column: 2
+ }
+ Repeater {
+ // ...and let the rest of the items automatically fill in the empty cells
+ model: 8
+ Rectangle {
+ color: "red"
+ width: 20
+ height: 20
+ Text { text: index }
+ }
+ }
+ }
+ }
+
+ function test_flowLeftToRight() {
+ var layout = layout_flowLeftToRight_Component.createObject(container);
+ compare(layout.implicitWidth, 80);
+ compare(layout.children[0].x, 0);
+ compare(layout.children[0].y, 0);
+ compare(layout.children[1].x, 20);
+ compare(layout.children[1].y, 20);
+ compare(layout.children[2].x, 20);
+ compare(layout.children[2].y, 0);
+ compare(layout.children[3].x, 0);
+ compare(layout.children[3].y, 60);
+ compare(layout.children[4].x, 40);
+ compare(layout.children[4].y, 0);
+
+ // assumes that the repeater is the last item among the items it creates
+ compare(layout.children[5].x, 60);
+ compare(layout.children[5].y, 00);
+ compare(layout.children[6].x, 00);
+ compare(layout.children[6].y, 20);
+ compare(layout.children[7].x, 60);
+ compare(layout.children[7].y, 20);
+ compare(layout.children[8].x, 00);
+ compare(layout.children[8].y, 40);
+ compare(layout.children[9].x, 60);
+ compare(layout.children[9].y, 40);
+ compare(layout.children[10].x, 40);
+ compare(layout.children[10].y, 60);
+ compare(layout.children[11].x, 60);
+ compare(layout.children[11].y, 60);
+ compare(layout.children[12].x, 40);
+ compare(layout.children[12].y, 80);
+
+ layout.destroy();
+ }
+
+
+ Component {
+ id: layout_flowLeftToRightDefaultPositions_Component
+ GridLayout {
+ columns: 2
+ columnSpacing: 0
+ rowSpacing: 0
+ // red rectangles are auto-positioned
+ // black rectangles are explicitly positioned with row,column
+ // gray rectangles are items with just one row or just one column specified
+ Rectangle {
+ // First one should auto position itself at (0,0)
+ id: r1
+ color: "red"
+ width: 20
+ height: 20
+ }
+ Rectangle {
+ // (1,0)
+ id: r2
+ color: "gray"
+ width: 20
+ height: 20
+ Layout.row: 1
+ }
+ Rectangle {
+ // (1,1)
+ id: r3
+ color: "black"
+ width: 20
+ height: 20
+ Layout.row: 1
+ Layout.column: 1
+ }
+ Rectangle {
+ // (1,0), warning emitted
+ id: r4
+ color: "gray"
+ width: 20
+ height: 20
+ Layout.row: 1
+ }
+ }
+ }
+
+ function test_flowLeftToRightDefaultPositions() {
+ ignoreWarning("QGridLayoutEngine::addItem: Cell (1, 0) already taken");
+ var layout = layout_flowLeftToRightDefaultPositions_Component.createObject(container);
+ compare(layout.implicitWidth, 40);
+ compare(layout.children[0].x, 0);
+ compare(layout.children[0].y, 0);
+ compare(layout.children[1].x, 0);
+ compare(layout.children[1].y, 20);
+ compare(layout.children[2].x, 20);
+ compare(layout.children[2].y, 20);
+ layout.destroy();
+ }
+
+
+ Component {
+ id: layout_flowTopToBottom_Component
+ GridLayout {
+ rows: 4
+ columnSpacing: 0
+ rowSpacing: 0
+ flow: GridLayout.TopToBottom
+ // red rectangles are auto-positioned
+ // black rectangles are explicitly positioned with row,column
+ Rectangle {
+ // First one should auto position itself at (0,0)
+ id: r1
+ color: "red"
+ width: 20
+ height: 20
+ }
+ Rectangle {
+ // (1,1)
+ id: r2
+ color: "black"
+ width: 20
+ height: 20
+ Layout.row: 1
+ Layout.column: 1
+ Layout.rowSpan: 2
+ Layout.columnSpan: 2
+ Layout.fillHeight: true
+ Layout.fillWidth: true
+ }
+ Rectangle {
+ // (2,0)
+ id: r3
+ color: "black"
+ width: 20
+ height: 20
+ Layout.row: 2
+ Layout.column: 0
+ }
+ Rectangle {
+ // This one won't fit on the left and right sides of the big black box
+ // inserted at (0,3)
+ id: r4
+ color: "red"
+ width: 20
+ height: 20
+ Layout.rowSpan: 2
+ Layout.fillHeight: true
+ }
+ Rectangle {
+ // continue flow from (1,0)
+ id: r5
+ color: "black"
+ width: 20
+ height: 20
+ Layout.row: 1
+ Layout.column: 0
+ }
+ Repeater {
+ // ...and let the rest of the items automatically fill in the empty cells
+ model: 8
+ Rectangle {
+ color: "red"
+ width: 20
+ height: 20
+ Text { text: index }
+ }
+ }
+ }
+ }
+
+ function test_flowTopToBottom() {
+ var layout = layout_flowTopToBottom_Component.createObject(container);
+ compare(layout.children[0].x, 0);
+ compare(layout.children[0].y, 0);
+ compare(layout.children[1].x, 20);
+ compare(layout.children[1].y, 20);
+ compare(layout.children[2].x, 0);
+ compare(layout.children[2].y, 40);
+ compare(layout.children[3].x, 60);
+ compare(layout.children[3].y, 0);
+ compare(layout.children[4].x, 0);
+ compare(layout.children[4].y, 20);
+
+ // The repeated items
+ compare(layout.children[5].x, 0);
+ compare(layout.children[5].y, 60);
+ compare(layout.children[6].x, 20);
+ compare(layout.children[6].y, 0);
+ compare(layout.children[7].x, 20);
+ compare(layout.children[7].y, 60);
+ compare(layout.children[8].x, 40);
+ compare(layout.children[8].y, 0);
+ compare(layout.children[9].x, 40);
+ compare(layout.children[9].y, 60);
+ compare(layout.children[10].x, 60);
+ compare(layout.children[10].y, 40);
+ compare(layout.children[11].x, 60);
+ compare(layout.children[11].y, 60);
+ compare(layout.children[12].x, 80);
+ compare(layout.children[12].y, 0);
+
+ layout.destroy();
+ }
+
+ Component {
+ id: layout_spanAcrossEmptyRows_Component
+ /* This test has a large number of empty rows and columns, but there is one item
+ that spans across some of these empty rows/columns.
+ Do not modify (especially do not add items unless you understand what this is
+ testing)
+ */
+
+ GridLayout {
+ columnSpacing: 0
+ rowSpacing: 0
+ // black rectangles are explicitly positioned with row,column
+ Rectangle {
+ // (0,0)
+ id: r0
+ color: "black"
+ Layout.row: 0
+ Layout.column: 0
+ Layout.preferredWidth: 20
+ Layout.preferredHeight: 20
+ Layout.maximumWidth: 40
+ Layout.maximumHeight: 40
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ }
+ Rectangle {
+ // (0,1)
+ id: r1
+ color: "black"
+ Layout.row: 0
+ Layout.column: 1
+ Layout.columnSpan: 2
+ Layout.rowSpan: 2
+ Layout.preferredWidth: 20
+ Layout.preferredHeight: 20
+ Layout.maximumWidth: 40
+ Layout.maximumHeight: 40
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ }
+ Rectangle {
+ // (0,99)
+ id: r2
+ color: "black"
+ Layout.row: 0
+ Layout.column: 99
+ Layout.preferredWidth: 20
+ Layout.preferredHeight: 20
+ Layout.maximumWidth: 40
+ Layout.maximumHeight: 40
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ }
+ }
+ }
+
+ function test_spanAcrossEmptyRows() {
+ var layout = layout_spanAcrossEmptyRows_Component.createObject(container);
+ compare(layout.children[0].x, 0);
+ compare(layout.children[0].y, 0);
+ compare(layout.children[1].x, 20);
+ compare(layout.children[1].y, 0);
+ compare(layout.children[2].x, 40);
+ compare(layout.children[2].y, 0);
+
+ compare(layout.implicitWidth, 60);
+ compare(layout.Layout.maximumWidth, 120);
+
+ layout.destroy();
+ }
+
+ Component {
+ id: layout_spanIsMoreThanColumns_Component
+
+ GridLayout {
+ columnSpacing: 1
+ rowSpacing: 1
+ columns: 2
+
+ Rectangle {
+ implicitWidth: 10
+ implicitHeight: 10
+ Layout.columnSpan: 3
+ }
+ }
+ }
+
+ function test_spanIsMoreThanColumns() {
+ var layout = layout_spanIsMoreThanColumns_Component.createObject(container);
+ // item was not added, therefore implicit width is 0
+ compare(layout.implicitWidth, 0);
+ layout.destroy();
+ }
+
+ function test_sizeHints() {
+ var layout = layout_spanAcrossEmptyRows_Component.createObject(container);
+ compare(layout.visible, true)
+
+ var minWidth = layout.Layout.minimumWidth
+ var minHeight = layout.Layout.minimumHeight
+
+ var prefWidth = layout.implicitWidth
+ var prefHeight = layout.implicitHeight
+
+ var maxWidth = layout.Layout.maximumWidth
+ var maxHeight = layout.Layout.maximumHeight
+
+ layout.visible = false
+ compare(minWidth, layout.Layout.minimumWidth)
+ compare(minHeight, layout.Layout.minimumHeight)
+ compare(prefWidth, layout.implicitWidth)
+ compare(prefHeight, layout.implicitHeight)
+ compare(maxWidth, layout.Layout.maximumWidth)
+ compare(maxHeight, layout.Layout.maximumHeight)
+
+ layout.destroy();
+ }
+
+ Component {
+ id: layout_alignment_Component
+ GridLayout {
+ columns: 2
+ columnSpacing: 0
+ rowSpacing: 0
+ Rectangle {
+ // First one should auto position itself at (0,0)
+ property var itemRect: [x, y, width, height]
+ color: "red"
+ Layout.preferredWidth: 20
+ Layout.preferredHeight: 20
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ }
+ Rectangle {
+ // (0,1)
+ property var itemRect: [x, y, width, height]
+ color: "red"
+ Layout.preferredWidth: 20
+ Layout.preferredHeight: 20
+ Layout.alignment: Qt.AlignBottom
+ }
+ Rectangle {
+ // (1,0)
+ property var itemRect: [x, y, width, height]
+ color: "red"
+ Layout.preferredWidth: 20
+ Layout.preferredHeight: 20
+ Layout.alignment: Qt.AlignRight
+ }
+ Rectangle {
+ // (1,1)
+ property var itemRect: [x, y, width, height]
+ color: "red"
+ Layout.preferredWidth: 10
+ Layout.preferredHeight: 10
+ Layout.alignment: Qt.AlignHCenter | Qt.AlignTop
+ }
+ Rectangle {
+ // (2,0)
+ property var itemRect: [x, y, width, height]
+ color: "red"
+ Layout.preferredWidth: 30
+ Layout.preferredHeight: 30
+ Layout.alignment: Qt.AlignRight
+ Layout.columnSpan: 2
+ }
+ Rectangle {
+ // (3,0)
+ property var itemRect: [x, y, width, height]
+ baselineOffset: 7
+ color: "red"
+ Layout.row: 3
+ Layout.column: 0
+ Layout.preferredWidth: 10
+ Layout.preferredHeight: 10
+ Layout.alignment: Qt.AlignLeft | Qt.AlignBaseline
+ }
+ Rectangle {
+ // (3,1)
+ property var itemRect: [x, y, width, height]
+ baselineOffset: 7
+ color: "red"
+ Layout.preferredWidth: 10
+ Layout.preferredHeight: 10
+ Layout.alignment: Qt.AlignRight | Qt.AlignBaseline
+ }
+
+ }
+ }
+
+ function test_alignment()
+ {
+ var layout = layout_alignment_Component.createObject(container);
+ layout.width = 60;
+ layout.height = 100;
+
+
+ tryCompare(layout.children[0], "itemRect", [ 0, 0, 40, 40]);
+ tryCompare(layout.children[1], "itemRect", [40, 20, 20, 20]);
+ tryCompare(layout.children[2], "itemRect", [20, 40, 20, 20]);
+ tryCompare(layout.children[3], "itemRect", [45, 40, 10, 10]);
+ tryCompare(layout.children[4], "itemRect", [30, 60, 30, 30]);
+ tryCompare(layout.children[5], "itemRect", [ 0, 90, 10, 10]);
+ tryCompare(layout.children[6], "itemRect", [50, 90, 10, 10]);
+
+
+ layout.children[1].Layout.alignment = Qt.AlignTop
+ tryCompare(layout.children[1], "x", 40);
+ tryCompare(layout.children[1], "y", 0);
+
+ layout.children[2].Layout.alignment = Qt.AlignLeft
+ tryCompare(layout.children[2], "x", 0);
+ tryCompare(layout.children[2], "y", 40);
+
+ layout.children[3].Layout.alignment = Qt.AlignLeft|Qt.AlignVCenter
+ tryCompare(layout.children[3], "x", 40);
+ tryCompare(layout.children[3], "y", 45);
+
+ layout.children[4].Layout.alignment = Qt.AlignLeft
+ tryCompare(layout.children[4], "x", 0);
+ tryCompare(layout.children[4], "y", 60);
+
+ layout.destroy();
+ }
+
+
+ Component {
+ id: layout_rightToLeft_Component
+ GridLayout {
+ layoutDirection: Qt.RightToLeft
+ columnSpacing: 0
+ rowSpacing: 0
+ columns: 3
+ Rectangle {
+ property var itemRect: [x, y, width, height]
+ color: "#cbffc4"
+ Layout.preferredWidth: 50
+ Layout.preferredHeight: 50
+ Layout.alignment: Qt.AlignCenter
+ }
+ Rectangle {
+ property var itemRect: [x, y, width, height]
+ color: "#c4d1ff"
+ Layout.preferredWidth: 50
+ Layout.preferredHeight: 50
+ Layout.alignment: Qt.AlignRight
+ }
+ Rectangle {
+ property var itemRect: [x, y, width, height]
+ color: "#ffd5c4"
+ Layout.preferredWidth: 50
+ Layout.preferredHeight: 50
+ Layout.alignment: Qt.AlignLeft
+ }
+ }
+ }
+
+ function verifyIsRightToLeft(layout)
+ {
+ tryCompare(layout.children[0], "itemRect", [125, 0, 50, 50]);
+ tryCompare(layout.children[1], "itemRect", [60, 0, 50, 50]);
+ tryCompare(layout.children[2], "itemRect", [10, 0, 50, 50]);
+ }
+
+ function verifyIsLeftToRight(layout)
+ {
+ tryCompare(layout.children[0], "itemRect", [5, 0, 50, 50]);
+ tryCompare(layout.children[1], "itemRect", [70, 0, 50, 50]);
+ tryCompare(layout.children[2], "itemRect", [120, 0, 50, 50]);
+ }
+
+ function test_rightToLeft()
+ {
+ var layout = layout_rightToLeft_Component.createObject(container);
+ layout.width = 180;
+ layout.height = 50;
+
+ // Right To Left
+ verifyIsRightToLeft(layout)
+ layout.LayoutMirroring.enabled = true
+ layout.layoutDirection = Qt.LeftToRight
+ verifyIsRightToLeft(layout)
+
+ // Left To Right
+ layout.LayoutMirroring.enabled = false
+ layout.layoutDirection = Qt.LeftToRight
+ verifyIsLeftToRight(layout);
+ layout.LayoutMirroring.enabled = true
+ layout.layoutDirection = Qt.RightToLeft
+ verifyIsLeftToRight(layout);
+
+ layout.LayoutMirroring.enabled = false
+ verifyIsRightToLeft(layout)
+
+ layout.layoutDirection = Qt.LeftToRight
+ verifyIsLeftToRight(layout);
+
+ layout.LayoutMirroring.enabled = true
+ verifyIsRightToLeft(layout)
+
+ layout.destroy();
+ }
+
+ Component {
+ id: layout_columnsOrRowsChanged_Component
+ GridLayout {
+ id: layout
+ rowSpacing: 0
+ columnSpacing: 0
+ Repeater {
+ model: 4
+ Rectangle {
+ property var itemRect: [x, y, width, height]
+ width: 10
+ height: 10
+ color: "#ff0000"
+ }
+ }
+ }
+ }
+
+ function test_columnsChanged()
+ {
+ var layout = layout_columnsOrRowsChanged_Component.createObject(container);
+ layout.width = 40;
+ layout.height = 20;
+ tryCompare(layout.children[0], "itemRect", [ 0, 5, 10, 10])
+ tryCompare(layout.children[1], "itemRect", [10, 5, 10, 10])
+ tryCompare(layout.children[2], "itemRect", [20, 5, 10, 10])
+ tryCompare(layout.children[3], "itemRect", [30, 5, 10, 10])
+
+ layout.columns = 2
+ tryCompare(layout.children[0], "itemRect", [ 0, 0, 10, 10])
+ tryCompare(layout.children[1], "itemRect", [20, 0, 10, 10])
+ tryCompare(layout.children[2], "itemRect", [ 0, 10, 10, 10])
+ tryCompare(layout.children[3], "itemRect", [20, 10, 10, 10])
+
+ layout.destroy()
+ }
+
+ function test_rowsChanged()
+ {
+ var layout = layout_columnsOrRowsChanged_Component.createObject(container);
+ layout.flow = GridLayout.TopToBottom
+ layout.width = 20;
+ layout.height = 40;
+ tryCompare(layout.children[0], "itemRect", [ 0, 0, 10, 10])
+ tryCompare(layout.children[1], "itemRect", [ 0, 10, 10, 10])
+ tryCompare(layout.children[2], "itemRect", [ 0, 20, 10, 10])
+ tryCompare(layout.children[3], "itemRect", [ 0, 30, 10, 10])
+
+ layout.rows = 2
+ tryCompare(layout.children[0], "itemRect", [ 0, 5, 10, 10])
+ tryCompare(layout.children[1], "itemRect", [ 0, 25, 10, 10])
+ tryCompare(layout.children[2], "itemRect", [10, 5, 10, 10])
+ tryCompare(layout.children[3], "itemRect", [10, 25, 10, 10])
+
+ layout.destroy()
+ }
+
+ Component {
+ id: layout_columnOrRowChanged_Component
+ GridLayout {
+ id: layout
+ rowSpacing: 0
+ columnSpacing: 0
+ Rectangle {
+ property var itemRect: [x, y, width, height]
+ width: 10
+ height: 10
+ Layout.column: 0
+ color: "#ff0000"
+ }
+ Rectangle {
+ property var itemRect: [x, y, width, height]
+ Layout.column: 1
+ width: 10
+ height: 10
+ color: "#ff0000"
+ }
+ Rectangle {
+ property var itemRect: [x, y, width, height]
+ //Layout.column: 2
+ width: 10
+ height: 10
+ color: "#ff0000"
+ }
+ }
+ }
+
+ function test_columnOrRowChanged()
+ {
+ var layout = layout_columnOrRowChanged_Component.createObject(container);
+ layout.width = layout.implicitWidth
+ layout.height = layout.implicitHeight
+ // c0-c1-c2
+ tryCompare(layout.children[0], "itemRect", [ 0, 0, 10, 10])
+ tryCompare(layout.children[1], "itemRect", [10, 0, 10, 10])
+ tryCompare(layout.children[2], "itemRect", [20, 0, 10, 10])
+
+ layout.children[0].Layout.column = 3
+ //c1-c2-c0
+ tryCompare(layout.children[0], "itemRect", [20, 0, 10, 10])
+ tryCompare(layout.children[1], "itemRect", [ 0, 0, 10, 10])
+ tryCompare(layout.children[2], "itemRect", [10, 0, 10, 10])
+
+ layout.children[2].Layout.column = 4
+ //c1-c0-c2
+ tryCompare(layout.children[0], "itemRect", [10, 0, 10, 10])
+ tryCompare(layout.children[1], "itemRect", [ 0, 0, 10, 10])
+ tryCompare(layout.children[2], "itemRect", [20, 0, 10, 10])
+
+ layout.children[0].Layout.row = 1
+ // two rows, so we adjust it to its new implicitHeight
+ layout.height = layout.implicitHeight
+ //c1 c2
+ // c0
+ tryCompare(layout.children[0], "itemRect", [10, 10, 10, 10])
+ tryCompare(layout.children[1], "itemRect", [ 0, 0, 10, 10])
+ tryCompare(layout.children[2], "itemRect", [20, 0, 10, 10])
+
+ layout.destroy()
+ }
+
+ Component {
+ id: layout_baselines_Component
+ GridLayout {
+ id: layout
+ columnSpacing: 0
+ Rectangle {
+ property var itemRect: [x, y, width, height]
+ implicitWidth: 10
+ implicitHeight: 10
+ baselineOffset: 10
+ }
+ Rectangle {
+ property var itemRect: [x, y, width, height]
+ implicitWidth: 10
+ implicitHeight: 10
+ }
+ }
+ }
+ function test_baselines()
+ {
+ var layout = layout_baselines_Component.createObject(container);
+ tryCompare(layout.children[0], "itemRect", [ 0, 0, 10, 10])
+ tryCompare(layout.children[1], "itemRect", [10, 0, 10, 10])
+ compare(layout.implicitWidth, 20)
+ compare(layout.implicitHeight, 10)
+
+ layout.children[0].Layout.alignment = Qt.AlignBaseline
+ layout.children[1].Layout.alignment = Qt.AlignBaseline
+
+ tryCompare(layout.children[0], "itemRect", [ 0, 0, 10, 10])
+ tryCompare(layout.children[1], "itemRect", [10, 10, 10, 10])
+ compare(layout.implicitWidth, 20)
+ compare(layout.implicitHeight, 20)
+
+ layout.destroy();
+ }
+
+ Component {
+ id: layout_spacings_Component
+ GridLayout {
+ id: layout
+ Repeater {
+ model: 2
+ Rectangle {
+ property var itemRect: [x, y, width, height]
+ implicitWidth: 10
+ implicitHeight: 10
+ }
+ }
+ }
+ }
+
+ function test_spacings()
+ {
+ var layout = layout_spacings_Component.createObject(container);
+
+ // breaks down below -19. This is acceptable, since it means that the implicit size of the layout is negative
+ var testSpacings = [Number.NaN, 0, 10, -5, -19]
+ layout.rowSpacing = 0
+ for (var i = 0; i < testSpacings.length; ++i) {
+ var sp = testSpacings[i]
+ if (isNaN(sp)) {
+ sp = 5 // Test defaults
+ } else {
+ layout.columnSpacing = sp
+ }
+ tryCompare(layout.children[0], "itemRect", [ 0, 0, 10, 10])
+ tryCompare(layout.children[1], "itemRect", [10 + sp, 0, 10, 10])
+ compare(layout.implicitWidth, 20 + sp)
+ }
+
+ // do not crash
+ layout.columnSpacing = -100
+ waitForRendering(layout)
+ verify(isFinite(layout.implicitWidth))
+ layout.destroy();
+ }
+
+ Component {
+ id: layout_alignToPixelGrid_Component
+ GridLayout {
+ columns: 3
+ rowSpacing: 0
+ columnSpacing: 2
+ Repeater {
+ model: 3*3
+ Rectangle {
+ color: "red"
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ }
+ }
+ }
+ }
+
+ function test_alignToPixelGrid()
+ {
+ var layout = layout_alignToPixelGrid_Component.createObject(container)
+ layout.width = 30
+ layout.height = 28
+
+ var rectWidth = (layout.width - 2 * layout.columnSpacing)/3
+ var rectHeight = layout.height/3
+
+ waitForRendering(layout);
+
+ var sp = layout.columnSpacing
+ var idealGeom = [0,0,rectWidth,rectHeight]
+ for (var r = 0; r < 3; ++r) {
+ idealGeom[0] = 0
+ idealGeom[2] = rectWidth
+ for (var c = 0; c < 3; ++c) {
+ var child = layout.children[3*r + c]
+ var visualGeom = [child.x, child.y, child.x + child.width, child.y + child.height]
+
+ // verify that visualGeom is an integer number
+ for (var i = 0; i < 2; ++i)
+ compare(visualGeom[i] % 1, 0)
+
+ // verify that x,y is no more than one pixel from idealGeom
+ fuzzyCompare(visualGeom[0], idealGeom[0], 1)
+ fuzzyCompare(visualGeom[1], idealGeom[1], 1)
+
+ // verify that the visual size is no more than 1 pixel taller/wider than the ideal size.
+ verify(visualGeom[2] <= idealGeom[2] + 1)
+ verify(visualGeom[3] <= idealGeom[3] + 1)
+ idealGeom[0] = idealGeom[2] + sp
+ idealGeom[2] = idealGeom[0] + rectWidth
+ }
+ idealGeom[1] = idealGeom[3]
+ idealGeom[3] = idealGeom[1] + rectHeight
+ }
+
+ layout.destroy()
+ }
+
+ Component {
+
+ id: layout_Margins_Component
+ GridLayout {
+ columns: 2
+ rowSpacing: 0
+ columnSpacing: 0
+ Rectangle {
+ color: "red"
+ Layout.preferredWidth: 20
+ Layout.preferredHeight: 20
+ Layout.margins: 10
+ Layout.leftMargin: 2
+ Layout.topMargin: 3
+ Layout.rightMargin: 4
+ Layout.bottomMargin: 4
+ }
+ Rectangle {
+ color: "red"
+ Layout.preferredWidth: 20
+ Layout.preferredHeight: 20
+ Layout.leftMargin: 4
+ Layout.topMargin: 5
+ Layout.rightMargin: 6
+ Layout.bottomMargin: 6
+ }
+ Rectangle {
+ color: "red"
+ Layout.preferredWidth: 20
+ Layout.preferredHeight: 20
+ Layout.leftMargin: 3
+ Layout.topMargin: 4
+ Layout.rightMargin: 5
+ Layout.bottomMargin: 5
+ }
+ }
+ }
+
+ function test_Margins()
+ {
+ var layout = layout_Margins_Component.createObject(container)
+
+ compare(layout.implicitWidth, 3 + 20 + 5 + 4 + 20 + 6)
+ compare(layout.implicitHeight, 5 + 20 + 6 + 4 + 20 + 5)
+ layout.width = layout.implicitWidth
+ layout.height = layout.implicitHeight
+
+ waitForRendering(layout)
+
+ var c0 = layout.children[0]
+ var c1 = layout.children[1]
+ var c2 = layout.children[2]
+
+ compare(c0.x, 2)
+ compare(c0.y, 5)
+ compare(c1.x, 3 + 20 + 5 + 4)
+ compare(c1.y, 5)
+ compare(c2.x, 3)
+ compare(c2.y, 5 + 20 + 6 + 4)
+
+ // reset left|rightMargin. It should then use the generic "margins" property
+ c0.Layout.leftMargin = undefined
+ compare(layout.implicitWidth, 10 + 20 + 4 + 4 + 20 + 6)
+ c0.Layout.bottomMargin = undefined
+ compare(layout.implicitHeight, 3 + 20 + 10 + 4 + 20 + 5)
+ }
+
+ Component {
+ id: layout_invalidateWhileRearranging_Component
+
+ GridLayout {
+ columns: 1
+ Rectangle {
+ height: 50
+ Layout.fillWidth: true
+ color: 'blue'
+ }
+
+ Rectangle {
+ height: 50
+ Layout.fillWidth: true
+ color: 'red'
+ onYChanged: {
+ visible = false;
+ }
+ }
+ }
+ }
+
+ function test_invalidateWhileRearranging_QTBUG_44139()
+ {
+ var layout = layout_invalidateWhileRearranging_Component.createObject(container)
+
+ waitForRendering(layout);
+ verify(layout.children[1].visible == false);
+ layout.destroy()
+ }
+ }
+}
diff --git a/tests/auto/quick/qquicklayouts/data/tst_rowlayout.qml b/tests/auto/quick/qquicklayouts/data/tst_rowlayout.qml
new file mode 100644
index 0000000000..4b47b396a3
--- /dev/null
+++ b/tests/auto/quick/qquicklayouts/data/tst_rowlayout.qml
@@ -0,0 +1,921 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 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.2
+import QtTest 1.0
+import QtQuick.Layouts 1.0
+
+Item {
+ id: container
+ width: 200
+ height: 200
+ TestCase {
+ id: testCase
+ name: "Tests_RowLayout"
+ when: windowShown
+ width: 200
+ height: 200
+
+ function itemRect(item)
+ {
+ return [item.x, item.y, item.width, item.height];
+ }
+
+ function test_fixedAndExpanding() {
+ var test_layoutStr =
+ 'import QtQuick 2.2; \
+ import QtQuick.Layouts 1.0; \
+ RowLayout { \
+ id: row; \
+ width: 15; \
+ spacing: 0; \
+ property alias r1: _r1; \
+ Rectangle { \
+ id: _r1; \
+ width: 5; \
+ height: 10; \
+ color: "#8080ff"; \
+ Layout.fillWidth: false \
+ } \
+ property alias r2: _r2; \
+ Rectangle { \
+ id: _r2; \
+ width: 10; \
+ height: 20; \
+ color: "#c0c0ff"; \
+ Layout.fillWidth: true \
+ } \
+ } '
+
+ var lay = Qt.createQmlObject(test_layoutStr, container, '');
+ tryCompare(lay, 'implicitWidth', 15);
+ compare(lay.implicitHeight, 20);
+ compare(lay.height, 20);
+ lay.width = 30
+ compare(lay.r1.x, 0);
+ compare(lay.r1.width, 5);
+ compare(lay.r2.x, 5);
+ compare(lay.r2.width, 25);
+ lay.destroy()
+ }
+
+ function test_allExpanding() {
+ var test_layoutStr =
+ 'import QtQuick 2.2; \
+ import QtQuick.Layouts 1.0; \
+ RowLayout { \
+ id: row; \
+ width: 15; \
+ spacing: 0; \
+ property alias r1: _r1; \
+ Rectangle { \
+ id: _r1; \
+ width: 5; \
+ height: 10; \
+ color: "#8080ff"; \
+ Layout.fillWidth: true \
+ } \
+ property alias r2: _r2; \
+ Rectangle { \
+ id: _r2; \
+ width: 10; \
+ height: 20; \
+ color: "#c0c0ff"; \
+ Layout.fillWidth: true \
+ } \
+ } '
+
+ var tmp = Qt.createQmlObject(test_layoutStr, container, '');
+ tryCompare(tmp, 'implicitWidth', 15);
+ compare(tmp.implicitHeight, 20);
+ compare(tmp.height, 20);
+ tmp.width = 30
+ compare(tmp.r1.width, 10);
+ compare(tmp.r2.width, 20);
+ compare(tmp.Layout.minimumWidth, 0)
+ compare(tmp.Layout.maximumWidth, Number.POSITIVE_INFINITY)
+ tmp.destroy()
+ }
+
+ function test_initialNestedLayouts() {
+ var test_layoutStr =
+ 'import QtQuick 2.2; \
+ import QtQuick.Layouts 1.0; \
+ ColumnLayout { \
+ id : col; \
+ property alias row: _row; \
+ objectName: "col"; \
+ anchors.fill: parent; \
+ RowLayout { \
+ id : _row; \
+ property alias r1: _r1; \
+ property alias r2: _r2; \
+ objectName: "row"; \
+ spacing: 0; \
+ Rectangle { \
+ id: _r1; \
+ color: "red"; \
+ implicitWidth: 50; \
+ implicitHeight: 20; \
+ } \
+ Rectangle { \
+ id: _r2; \
+ color: "green"; \
+ implicitWidth: 50; \
+ implicitHeight: 20; \
+ Layout.fillWidth: true; \
+ } \
+ } \
+ } '
+ var col = Qt.createQmlObject(test_layoutStr, container, '');
+ tryCompare(col, 'width', 200);
+ tryCompare(col.row, 'width', 200);
+ tryCompare(col.row.r1, 'width', 50);
+ tryCompare(col.row.r2, 'width', 150);
+ col.destroy()
+ }
+
+ function test_implicitSize() {
+ var test_layoutStr =
+ 'import QtQuick 2.2; \
+ import QtQuick.Layouts 1.0; \
+ RowLayout { \
+ id: row; \
+ objectName: "row"; \
+ spacing: 0; \
+ height: 30; \
+ anchors.left: parent.left; \
+ anchors.right: parent.right; \
+ Rectangle { \
+ color: "red"; \
+ height: 2; \
+ Layout.minimumWidth: 50; \
+ } \
+ Rectangle { \
+ color: "green"; \
+ width: 10; \
+ Layout.minimumHeight: 4; \
+ } \
+ Rectangle { \
+ implicitWidth: 1000; \
+ Layout.maximumWidth: 40; \
+ implicitHeight: 6 \
+ } \
+ } '
+ var row = Qt.createQmlObject(test_layoutStr, container, '');
+ compare(row.implicitWidth, 50 + 10 + 40);
+ compare(row.implicitHeight, 6);
+ row.destroy()
+ }
+
+ function test_countGeometryChanges() {
+ var test_layoutStr =
+ 'import QtQuick 2.2; \
+ import QtQuick.Layouts 1.0; \
+ ColumnLayout { \
+ id : col; \
+ property alias row: _row; \
+ objectName: "col"; \
+ anchors.fill: parent; \
+ RowLayout { \
+ id : _row; \
+ property alias r1: _r1; \
+ property alias r2: _r2; \
+ objectName: "row"; \
+ spacing: 0; \
+ property int counter : 0; \
+ onWidthChanged: { ++counter; } \
+ Rectangle { \
+ id: _r1; \
+ color: "red"; \
+ implicitWidth: 50; \
+ implicitHeight: 20; \
+ property int counter : 0; \
+ onWidthChanged: { ++counter; } \
+ Layout.fillWidth: true; \
+ } \
+ Rectangle { \
+ id: _r2; \
+ color: "green"; \
+ implicitWidth: 50; \
+ implicitHeight: 20; \
+ property int counter : 0; \
+ onWidthChanged: { ++counter; } \
+ Layout.fillWidth: true; \
+ } \
+ } \
+ } '
+ var col = Qt.createQmlObject(test_layoutStr, container, '');
+ compare(col.width, 200);
+ compare(col.row.width, 200);
+ compare(col.row.r1.width, 100);
+ compare(col.row.r2.width, 100);
+ compare(col.row.r1.counter, 1);
+ compare(col.row.r2.counter, 1);
+ verify(col.row.counter <= 2);
+ col.destroy()
+ }
+
+ Component {
+ id: layoutItem_Component
+ Rectangle {
+ implicitWidth: 20
+ implicitHeight: 20
+ }
+ }
+
+ Component {
+ id: columnLayoutItem_Component
+ ColumnLayout {
+ spacing: 0
+ }
+ }
+
+ Component {
+ id: layout_addAndRemoveItems_Component
+ RowLayout {
+ spacing: 0
+ }
+ }
+
+ function test_addAndRemoveItems()
+ {
+ var layout = layout_addAndRemoveItems_Component.createObject(container)
+ compare(layout.implicitWidth, 0)
+ compare(layout.implicitHeight, 0)
+
+ var rect0 = layoutItem_Component.createObject(layout)
+ compare(layout.implicitWidth, 20)
+ compare(layout.implicitHeight, 20)
+
+ var rect1 = layoutItem_Component.createObject(layout)
+ rect1.Layout.preferredWidth = 30;
+ rect1.Layout.preferredHeight = 30;
+ compare(layout.implicitWidth, 50)
+ compare(layout.implicitHeight, 30)
+
+ var col = columnLayoutItem_Component.createObject(layout)
+ var rect2 = layoutItem_Component.createObject(col)
+ rect2.Layout.fillHeight = true
+ var rect3 = layoutItem_Component.createObject(col)
+ rect3.Layout.fillHeight = true
+
+ compare(layout.implicitWidth, 70)
+ compare(col.implicitHeight, 40)
+ compare(layout.implicitHeight, 40)
+
+ rect3.destroy()
+ wait(0) // this will hopefully effectuate the destruction of the object
+
+ col.destroy()
+ wait(0)
+ compare(layout.implicitWidth, 50)
+ compare(layout.implicitHeight, 30)
+
+ rect0.destroy()
+ wait(0)
+ compare(layout.implicitWidth, 30)
+ compare(layout.implicitHeight, 30)
+
+ rect1.destroy()
+ wait(0)
+ compare(layout.implicitWidth, 0)
+ compare(layout.implicitHeight, 0)
+
+ layout.destroy()
+ }
+
+ Component {
+ id: layout_alignment_Component
+ RowLayout {
+ spacing: 0
+ Rectangle {
+ color: "red"
+ Layout.preferredWidth: 20
+ Layout.preferredHeight: 20
+ Layout.fillHeight: true
+ }
+ Rectangle {
+ color: "red"
+ Layout.preferredWidth: 20
+ Layout.preferredHeight: 20
+ // use default alignment
+ }
+ Rectangle {
+ color: "red"
+ Layout.preferredWidth: 20
+ Layout.preferredHeight: 20
+ Layout.alignment: Qt.AlignTop
+ }
+ Rectangle {
+ color: "red"
+ Layout.preferredWidth: 20
+ Layout.preferredHeight: 20
+ Layout.alignment: Qt.AlignVCenter
+ }
+ Rectangle {
+ color: "red"
+ Layout.preferredWidth: 20
+ Layout.preferredHeight: 20
+ Layout.alignment: Qt.AlignBottom
+ }
+ }
+ }
+
+ function test_alignment()
+ {
+ var layout = layout_alignment_Component.createObject(container);
+ layout.width = 100;
+ layout.height = 40;
+
+ compare(itemRect(layout.children[0]), [ 0, 0, 20, 40]);
+ compare(itemRect(layout.children[1]), [20, 10, 20, 20]);
+ compare(itemRect(layout.children[2]), [40, 0, 20, 20]);
+ compare(itemRect(layout.children[3]), [60, 10, 20, 20]);
+ compare(itemRect(layout.children[4]), [80, 20, 20, 20]);
+ layout.destroy();
+ }
+
+ Component {
+ id: layout_sizeHintNormalization_Component
+ GridLayout {
+ columnSpacing: 0
+ rowSpacing: 0
+ Rectangle {
+ id: r1
+ color: "red"
+ Layout.minimumWidth: 1
+ Layout.preferredWidth: 2
+ Layout.maximumWidth: 3
+
+ Layout.minimumHeight: 20
+ Layout.preferredHeight: 20
+ Layout.maximumHeight: 20
+ Layout.fillWidth: true
+ }
+ }
+ }
+
+ function test_sizeHintNormalization_data() {
+ return [
+ { tag: "fallbackValues", widthHints: [-1, -1, -1], implicitWidth: 42, expected:[0,42,Number.POSITIVE_INFINITY]},
+ { tag: "acceptZeroWidths", widthHints: [0, 0, 0], implicitWidth: 42, expected:[0,0,0]},
+ { tag: "123", widthHints: [1,2,3], expected:[1,2,3]},
+ { tag: "132", widthHints: [1,3,2], expected:[1,2,2]},
+ { tag: "213", widthHints: [2,1,3], expected:[2,2,3]},
+ { tag: "231", widthHints: [2,3,1], expected:[1,1,1]},
+ { tag: "321", widthHints: [3,2,1], expected:[1,1,1]},
+ { tag: "312", widthHints: [3,1,2], expected:[2,2,2]},
+
+ { tag: "1i3", widthHints: [1,-1,3], implicitWidth: 2, expected:[1,2,3]},
+ { tag: "1i2", widthHints: [1,-1,2], implicitWidth: 3, expected:[1,2,2]},
+ { tag: "2i3", widthHints: [2,-1,3], implicitWidth: 1, expected:[2,2,3]},
+ { tag: "2i1", widthHints: [2,-1,1], implicitWidth: 3, expected:[1,1,1]},
+ { tag: "3i1", widthHints: [3,-1,1], implicitWidth: 2, expected:[1,1,1]},
+ { tag: "3i2", widthHints: [3,-1,2], implicitWidth: 1, expected:[2,2,2]},
+ ];
+ }
+
+ function test_sizeHintNormalization(data) {
+ var layout = layout_sizeHintNormalization_Component.createObject(container);
+ if (data.implicitWidth !== undefined) {
+ layout.children[0].implicitWidth = data.implicitWidth
+ }
+ layout.children[0].Layout.minimumWidth = data.widthHints[0];
+ layout.children[0].Layout.preferredWidth = data.widthHints[1];
+ layout.children[0].Layout.maximumWidth = data.widthHints[2];
+ wait(0); // Trigger processEvents() (allow LayoutRequest to be processed)
+ var normalizedResult = [layout.Layout.minimumWidth, layout.implicitWidth, layout.Layout.maximumWidth]
+ compare(normalizedResult, data.expected);
+ layout.destroy();
+ }
+
+ Component {
+ id: layout_sizeHint_Component
+ RowLayout {
+ property int implicitWidthChangedCount : 0
+ onImplicitWidthChanged: { ++implicitWidthChangedCount }
+ GridLayout {
+ columnSpacing: 0
+ rowSpacing: 0
+ Rectangle {
+ id: r1
+ color: "red"
+ Layout.minimumWidth: 1
+ Layout.preferredWidth: 2
+ Layout.maximumWidth: 3
+
+ Layout.minimumHeight: 20
+ Layout.preferredHeight: 20
+ Layout.maximumHeight: 20
+ Layout.fillWidth: true
+ }
+ }
+ }
+ }
+
+ function test_sizeHint_data() {
+ return [
+ { tag: "propagateNone", layoutHints: [10, 20, 30], childHints: [11, 21, 31], expected:[10, 20, 30]},
+ { tag: "propagateMinimumWidth", layoutHints: [-1, 20, 30], childHints: [10, 21, 31], expected:[10, 20, 30]},
+ { tag: "propagatePreferredWidth", layoutHints: [10, -1, 30], childHints: [11, 20, 31], expected:[10, 20, 30]},
+ { tag: "propagateMaximumWidth", layoutHints: [10, 20, -1], childHints: [11, 21, 30], expected:[10, 20, 30]},
+ { tag: "propagateAll", layoutHints: [-1, -1, -1], childHints: [10, 20, 30], expected:[10, 20, 30]},
+ { tag: "propagateCrazy", layoutHints: [-1, -1, -1], childHints: [40, 21, 30], expected:[30, 30, 30]},
+ { tag: "expandMinToExplicitPref", layoutHints: [-1, 1, -1], childHints: [11, 21, 31], expected:[ 1, 1, 31]},
+ { tag: "expandMaxToExplicitPref", layoutHints: [-1, 99, -1], childHints: [11, 21, 31], expected:[11, 99, 99]},
+ { tag: "expandAllToExplicitMin", layoutHints: [99, -1, -1], childHints: [11, 21, 31], expected:[99, 99, 99]},
+ { tag: "expandPrefToExplicitMin", layoutHints: [24, -1, -1], childHints: [11, 21, 31], expected:[24, 24, 31]},
+ { tag: "boundPrefToExplicitMax", layoutHints: [-1, -1, 19], childHints: [11, 21, 31], expected:[11, 19, 19]},
+ { tag: "boundAllToExplicitMax", layoutHints: [-1, -1, 9], childHints: [11, 21, 31], expected:[ 9, 9, 9]},
+ ];
+ }
+
+ function itemSizeHints(item) {
+ return [item.Layout.minimumWidth, item.implicitWidth, item.Layout.maximumWidth]
+ }
+
+ function test_sizeHint(data) {
+ var layout = layout_sizeHint_Component.createObject(container)
+
+ var grid = layout.children[0]
+ grid.Layout.minimumWidth = data.layoutHints[0]
+ grid.Layout.preferredWidth = data.layoutHints[1]
+ grid.Layout.maximumWidth = data.layoutHints[2]
+
+ var child = grid.children[0]
+ if (data.implicitWidth !== undefined) {
+ child.implicitWidth = data.implicitWidth
+ }
+ child.Layout.minimumWidth = data.childHints[0]
+ child.Layout.preferredWidth = data.childHints[1]
+ child.Layout.maximumWidth = data.childHints[2]
+
+ var effectiveSizeHintResult = [layout.Layout.minimumWidth, layout.implicitWidth, layout.Layout.maximumWidth]
+ compare(effectiveSizeHintResult, data.expected)
+ layout.destroy()
+ }
+
+ function test_sizeHintPropagationCount() {
+ var layout = layout_sizeHint_Component.createObject(container)
+ var child = layout.children[0].children[0]
+
+ child.Layout.minimumWidth = -1
+ compare(itemSizeHints(layout), [0, 2, 3])
+ child.Layout.preferredWidth = -1
+ compare(itemSizeHints(layout), [0, 0, 3])
+ child.Layout.maximumWidth = -1
+ compare(itemSizeHints(layout), [0, 0, Number.POSITIVE_INFINITY])
+ layout.Layout.maximumWidth = 1000
+ compare(itemSizeHints(layout), [0, 0, 1000])
+ layout.Layout.maximumWidth = -1
+ compare(itemSizeHints(layout), [0, 0, Number.POSITIVE_INFINITY])
+
+ layout.implicitWidthChangedCount = 0
+ child.Layout.minimumWidth = 10
+ compare(itemSizeHints(layout), [10, 10, Number.POSITIVE_INFINITY])
+ compare(layout.implicitWidthChangedCount, 1)
+
+ child.Layout.preferredWidth = 20
+ compare(itemSizeHints(layout), [10, 20, Number.POSITIVE_INFINITY])
+ compare(layout.implicitWidthChangedCount, 2)
+
+ child.Layout.maximumWidth = 30
+ compare(itemSizeHints(layout), [10, 20, 30])
+ compare(layout.implicitWidthChangedCount, 2)
+
+ child.Layout.maximumWidth = 15
+ compare(itemSizeHints(layout), [10, 15, 15])
+ compare(layout.implicitWidthChangedCount, 3)
+
+ child.Layout.maximumWidth = 30
+ compare(itemSizeHints(layout), [10, 20, 30])
+ compare(layout.implicitWidthChangedCount, 4)
+
+ layout.Layout.maximumWidth = 29
+ compare(layout.Layout.maximumWidth, 29)
+ layout.Layout.maximumWidth = -1
+ compare(layout.Layout.maximumWidth, 30)
+
+ layout.destroy()
+ }
+
+ Component {
+ id: layout_change_implicitWidth_during_rearrange
+ ColumnLayout {
+ width: 100
+ height: 20
+ RowLayout {
+ spacing: 0
+ Rectangle {
+ Layout.fillHeight: true
+ Layout.fillWidth: false
+ implicitWidth: height
+ color: "red"
+ }
+ Rectangle {
+ Layout.fillHeight: true
+ Layout.fillWidth: true
+ color: "blue"
+ }
+ }
+ }
+ }
+
+ function test_change_implicitWidth_during_rearrange() {
+ var layout = layout_change_implicitWidth_during_rearrange.createObject(container)
+ var red = layout.children[0].children[0]
+ var blue = layout.children[0].children[1]
+ waitForRendering(layout);
+ tryCompare(red, 'width', 20)
+ tryCompare(blue, 'width', 80)
+ layout.height = 40
+ tryCompare(red, 'width', 40)
+ tryCompare(blue, 'width', 60)
+ layout.destroy()
+ }
+
+ Component {
+ id: layout_addIgnoredItem_Component
+ RowLayout {
+ spacing: 0
+ Rectangle {
+ id: r
+ }
+ }
+ }
+
+ function test_addIgnoredItem()
+ {
+ var layout = layout_addIgnoredItem_Component.createObject(container)
+ compare(layout.implicitWidth, 0)
+ compare(layout.implicitHeight, 0)
+ var r = layout.children[0]
+ r.Layout.preferredWidth = 20
+ r.Layout.preferredHeight = 30
+ compare(layout.implicitWidth, 20)
+ compare(layout.implicitHeight, 30)
+
+ layout.destroy();
+ }
+
+
+ Component {
+ id: layout_rowLayout_Component
+ RowLayout {
+ }
+ }
+
+ function test_stretchItem_data()
+ {
+ return [
+ { expectedWidth: 0},
+ { preferredWidth: 20, expectedWidth: 20},
+ { preferredWidth: 0, expectedWidth: 0},
+ { preferredWidth: 20, fillWidth: true, expectedWidth: 100},
+ { width: 20, fillWidth: true, expectedWidth: 100},
+ { width: 0, fillWidth: true, expectedWidth: 100},
+ { preferredWidth: 0, fillWidth: true, expectedWidth: 100},
+ { preferredWidth: 1, maximumWidth: 0, fillWidth: true, expectedWidth: 0},
+ { preferredWidth: 0, minimumWidth: 1, expectedWidth: 1},
+ ];
+ }
+
+ function test_stretchItem(data)
+ {
+ var layout = layout_rowLayout_Component.createObject(container)
+ var r = layoutItem_Component.createObject(layout)
+ // Reset previously relevant properties
+ r.width = 0
+ r.implicitWidth = 0
+ compare(layout.implicitWidth, 0)
+
+ if (data.preferredWidth !== undefined)
+ r.Layout.preferredWidth = data.preferredWidth
+ if (data.fillWidth !== undefined)
+ r.Layout.fillWidth = data.fillWidth
+ if (data.width !== undefined)
+ r.width = data.width
+ if (data.minimumWidth !== undefined)
+ r.Layout.minimumWidth = data.minimumWidth
+ if (data.maximumWidth !== undefined)
+ r.Layout.maximumWidth = data.maximumWidth
+
+ layout.width = 100
+
+ compare(r.width, data.expectedWidth)
+
+ layout.destroy();
+ }
+
+ Component {
+ id: layout_alignToPixelGrid_Component
+ RowLayout {
+ spacing: 2
+ Rectangle {
+ implicitWidth: 10
+ implicitHeight: 10
+ Layout.alignment: Qt.AlignVCenter
+ }
+ Rectangle {
+ implicitWidth: 10
+ implicitHeight: 10
+ Layout.alignment: Qt.AlignVCenter
+ }
+ }
+ }
+ function test_alignToPixelGrid()
+ {
+ var layout = layout_alignToPixelGrid_Component.createObject(container)
+ layout.width = 21
+ layout.height = 21
+ var r0 = layout.children[0]
+ compare(r0.x, 0) // 0.0
+ compare(r0.y, 6) // 5.5
+ var r1 = layout.children[1]
+ compare(r1.x, 12) // 11.5
+ compare(r1.y, 6) // 5.5
+ layout.destroy();
+ }
+
+ Component {
+ id: test_distributeToPixelGrid_Component
+ RowLayout {
+ spacing: 0
+ Rectangle {
+ color: 'red'
+ Layout.minimumWidth: 10
+ Layout.preferredWidth: 50
+ Layout.maximumWidth: 90
+ Layout.fillWidth: true
+ implicitHeight: 10
+ }
+ Rectangle {
+ color: 'red'
+ Layout.minimumWidth: 10
+ Layout.preferredWidth: 20
+ Layout.maximumWidth: 90
+ Layout.fillWidth: true
+ implicitHeight: 10
+ }
+ Rectangle {
+ color: 'red'
+ Layout.minimumWidth: 10
+ Layout.preferredWidth: 70
+ Layout.maximumWidth: 90
+ Layout.fillWidth: true
+ implicitHeight: 10
+ }
+ }
+ }
+
+ function test_distributeToPixelGrid_data() {
+ return [
+ { tag: "narrow", spacing: 0, width: 60 },
+ { tag: "belowPreferred", spacing: 0, width: 130 },
+ { tag: "belowPreferredWithSpacing", spacing: 10, width: 130 },
+ { tag: "abovePreferred", spacing: 0, width: 150 },
+ { tag: "stretchSomethingToMaximum", spacing: 0, width: 240,
+ expected: [90, 60, 90] },
+ { tag: "minSizeHasFractions", spacing: 2, width: 31 + 4, hints: [{min: 10+1/3}, {min: 10+1/3}, {min: 10+1/3}],
+ /*expected: [11, 11, 11]*/ }, /* verify that nothing gets allocated a size smaller than its minimum */
+ { tag: "maxSizeHasFractions", spacing: 2, width: 271 + 4, hints: [{max: 90+1/3}, {max: 90+1/3}, {max: 90+1/3}],
+ /*expected: [90, 90, 90]*/ }, /* verify that nothing gets allocated a size larger than its maximum */
+ { tag: "fixedSizeHasFractions", spacing: 2, width: 31 + 4, hints: [{min: 10+1/3, max: 10+1/3}, {min: 10+1/3, max: 10+1/3}, {min: 10+1/3, max: 10+1/3}],
+ /*expected: [11, 11, 11]*/ }, /* verify that nothing gets allocated a size smaller than its minimum */
+ ];
+ }
+
+ function test_distributeToPixelGrid(data)
+ {
+ // CONFIGURATION
+ var layout = test_distributeToPixelGrid_Component.createObject(container)
+ layout.spacing = data.spacing
+ layout.width = data.width
+ layout.height = 10
+ var kids = layout.children
+
+ if (data.hasOwnProperty('hints')) {
+ var hints = data.hints
+ for (var i = 0; i < hints.length; ++i) {
+ var h = hints[i]
+ if (h.hasOwnProperty('min'))
+ kids[i].Layout.minimumWidth = h.min
+ if (h.hasOwnProperty('pref'))
+ kids[i].Layout.preferredWidth = h.pref
+ if (h.hasOwnProperty('max'))
+ kids[i].Layout.maximumWidth = h.max
+ }
+ }
+ waitForRendering(layout)
+
+ var sum = 2 * layout.spacing
+ // TEST
+ for (var i = 0; i < kids.length; ++i) {
+ compare(kids[i].x % 1, 0) // checks if position is a whole integer
+ // verify if the items are within the size constraints as specified
+ verify(kids[i].width >= kids[i].Layout.minimumWidth)
+ verify(kids[i].width <= kids[i].Layout.maximumWidth)
+ if (data.hasOwnProperty('expected'))
+ compare(kids[i].width, data.expected[i])
+ sum += kids[i].width
+ }
+ fuzzyCompare(sum, layout.width, 1)
+
+ layout.destroy();
+ }
+
+
+
+ Component {
+ id: layout_deleteLayout
+ ColumnLayout {
+ property int dummyproperty: 0 // yes really - its needed
+ RowLayout {
+ Text { text: "label1" } // yes, both are needed
+ Text { text: "label2" }
+ }
+ }
+ }
+
+ function test_destroyLayout()
+ {
+ var layout = layout_deleteLayout.createObject(container)
+ layout.children[0].children[0].visible = true
+ layout.visible = false
+ layout.destroy() // Do not crash
+ }
+
+ function test_sizeHintWithHiddenChildren(data) {
+ var layout = layout_sizeHint_Component.createObject(container)
+ var grid = layout.children[0]
+ var child = grid.children[0]
+
+ // Implicit sizes are not affected by the visibility of the parent layout.
+ // This is in order for the layout to know the preferred size it should show itself at.
+ compare(grid.visible, true) // LAYOUT SHOWN
+ compare(grid.implicitWidth, 2);
+ child.visible = false
+ compare(grid.implicitWidth, 0);
+ child.visible = true
+ compare(grid.implicitWidth, 2);
+
+ grid.visible = false // LAYOUT HIDDEN
+ compare(grid.implicitWidth, 2);
+ child.visible = false
+ expectFail('', 'If GridLayout is hidden, GridLayout is not notified when child is explicitly hidden')
+ compare(grid.implicitWidth, 0);
+ child.visible = true
+ compare(grid.implicitWidth, 2);
+
+ layout.destroy();
+ }
+
+ Component {
+ id: row_sizeHint_Component
+ Row {
+ Rectangle {
+ id: r1
+ color: "red"
+ width: 2
+ height: 20
+ }
+ }
+ }
+
+ function test_sizeHintWithHiddenChildrenForRow(data) {
+ var row = row_sizeHint_Component.createObject(container)
+ var child = row.children[0]
+ compare(row.visible, true) // POSITIONER SHOWN
+ compare(row.implicitWidth, 2);
+ child.visible = false
+ tryCompare(row, 'implicitWidth', 0);
+ child.visible = true
+ tryCompare(row, 'implicitWidth', 2);
+
+ row.visible = false // POSITIONER HIDDEN
+ compare(row.implicitWidth, 2);
+ child.visible = false
+ expectFail('', 'If Row is hidden, Row is not notified when child is explicitly hidden')
+ compare(row.implicitWidth, 0);
+ child.visible = true
+ compare(row.implicitWidth, 2);
+ }
+
+ Component {
+ id: rearrangeNestedLayouts_Component
+ RowLayout {
+ id: layout
+ anchors.fill: parent
+ width: 200
+ height: 20
+ RowLayout {
+ id: row
+ spacing: 0
+
+ Rectangle {
+ id: fixed
+ color: 'red'
+ implicitWidth: 20
+ implicitHeight: 20
+ }
+ Rectangle {
+ id: filler
+ color: 'grey'
+ Layout.fillWidth: true
+ implicitHeight: 20
+ }
+ }
+ }
+ }
+
+ function test_rearrangeNestedLayouts()
+ {
+ var layout = rearrangeNestedLayouts_Component.createObject(container)
+ var fixed = layout.children[0].children[0]
+ var filler = layout.children[0].children[1]
+
+ compare(itemRect(fixed), [0,0,20,20])
+ compare(itemRect(filler), [20,0,180,20])
+
+ fixed.implicitWidth = 100
+ waitForRendering(layout)
+ compare(itemRect(fixed), [0,0,100,20])
+ compare(itemRect(filler), [100,0,100,20])
+ }
+
+ Component {
+ id: changeChildrenOfHiddenLayout_Component
+ RowLayout {
+ property int childCount: 1
+ Repeater {
+ model: parent.childCount
+ Text {
+ text: 'Just foo it'
+ }
+ }
+ }
+ }
+ function test_changeChildrenOfHiddenLayout()
+ {
+ var layout = changeChildrenOfHiddenLayout_Component.createObject(container)
+ var child = layout.children[0]
+ waitForRendering(layout)
+ layout.visible = false
+ waitForRendering(layout)
+ // Remove and add children to the hidden layout..
+ layout.childCount = 0
+ waitForRendering(layout)
+ layout.childCount = 1
+ waitForRendering(layout)
+ layout.destroy()
+ }
+ }
+}
diff --git a/tests/auto/quick/qquicklayouts/qquicklayouts.pro b/tests/auto/quick/qquicklayouts/qquicklayouts.pro
new file mode 100644
index 0000000000..9ed3e076be
--- /dev/null
+++ b/tests/auto/quick/qquicklayouts/qquicklayouts.pro
@@ -0,0 +1,13 @@
+QT += core-private gui-private qml-private
+TEMPLATE=app
+TARGET=tst_qquicklayouts
+
+CONFIG += qmltestcase
+SOURCES += tst_qquicklayouts.cpp
+
+TESTDATA = data/*
+
+OTHER_FILES += \
+ data/tst_rowlayout.qml \
+ data/tst_gridlayout.qml
+
diff --git a/tests/auto/quick/qquicklayouts/tst_qquicklayouts.cpp b/tests/auto/quick/qquicklayouts/tst_qquicklayouts.cpp
new file mode 100644
index 0000000000..373019091f
--- /dev/null
+++ b/tests/auto/quick/qquicklayouts/tst_qquicklayouts.cpp
@@ -0,0 +1,29 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 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:GPL-EXCEPT$
+** 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 General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include <QtQuickTest/quicktest.h>
+QUICK_TEST_MAIN(qquicklayouts)
diff --git a/tests/auto/quick/quick.pro b/tests/auto/quick/quick.pro
index f25a28d45b..13bd6d78e2 100644
--- a/tests/auto/quick/quick.pro
+++ b/tests/auto/quick/quick.pro
@@ -21,6 +21,7 @@ PRIVATETESTS += \
qquickfontloader_static \
qquickfontmetrics \
qquickimageprovider \
+ qquicklayouts \
qquickpath \
qquicksmoothedanimation \
qquickspringanimation \