diff options
author | Jan Arve Saether <jan-arve.saether@theqtcompany.com> | 2016-02-18 13:28:59 +0100 |
---|---|---|
committer | J-P Nurmi <jpnurmi@theqtcompany.com> | 2016-02-26 13:18:27 +0000 |
commit | 974643829f677247a8b06aba563a28714b48f1a9 (patch) | |
tree | 2e040aacedff10bb0f579081bbae21693fea01be /tests/auto/quick/qquicklayouts | |
parent | fb2710a7f182ffb910f6b121e8bc125a4f61dcdf (diff) |
Move QtQuick.Layouts to qtdeclarative from qtquickcontrols
This is in order for it to be available without having to install
Qt Quick Controls
Change-Id: I3f0d0dc108829947cd189b7861944e556e00cef3
Reviewed-by: J-P Nurmi <jpnurmi@theqtcompany.com>
Diffstat (limited to 'tests/auto/quick/qquicklayouts')
-rw-r--r-- | tests/auto/quick/qquicklayouts/data/tst_gridlayout.qml | 1036 | ||||
-rw-r--r-- | tests/auto/quick/qquicklayouts/data/tst_rowlayout.qml | 921 | ||||
-rw-r--r-- | tests/auto/quick/qquicklayouts/qquicklayouts.pro | 13 | ||||
-rw-r--r-- | tests/auto/quick/qquicklayouts/tst_qquicklayouts.cpp | 29 |
4 files changed, 1999 insertions, 0 deletions
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) |