diff options
Diffstat (limited to 'tests/auto/quick/qquicklayouts/data')
10 files changed, 1469 insertions, 15 deletions
diff --git a/tests/auto/quick/qquicklayouts/data/LayoutHelperLibrary.js b/tests/auto/quick/qquicklayouts/data/LayoutHelperLibrary.js new file mode 100644 index 0000000000..7dbbc1ad64 --- /dev/null +++ b/tests/auto/quick/qquicklayouts/data/LayoutHelperLibrary.js @@ -0,0 +1,42 @@ +.pragma library + + +function buildLayout(layoutData, parentItem) { + let layout = null + switch (layoutData.type) { + case "GridLayout": + case "RowLayout": + case "ColumnLayout": + layout = Qt.createQmlObject("import QtQuick.Layouts\n" + + layoutData.type + " {}", parentItem) + break + default: + console.log("data.layout.type not recognized(" + layoutdata.type + ")") + } + if (layout) { + for (let name in layoutData) { + let val = layoutData[name] + switch (name) { + case "items": + let arrLayoutData = layoutData.items + for (let i = 0; i < arrLayoutData.length; i++) { + let layoutItemDesc = arrLayoutData[i] + let strProps = "" + for (let keyName in layoutItemDesc) { + strProps += "Layout." + keyName + ": " + layoutItemDesc[keyName] + ";" + } + // For some reason we cannot assign the "Layout." attached properties from + // here, so for now we have to serialize them as strings. + let rect = Qt.createQmlObject("import QtQuick\nimport QtQuick.Layouts\n\nRectangle { implicitWidth: 20; implicitHeight: 20; " + strProps + "}", layout) + } + break; + case "type": + break; + default: + layout[name] = val + break; + } + } + } + return layout +} diff --git a/tests/auto/quick/qquicklayouts/data/rowlayout/Container.qml b/tests/auto/quick/qquicklayouts/data/rowlayout/Container.qml index 962bc78580..9a5ae0cc7a 100644 --- a/tests/auto/quick/qquicklayouts/data/rowlayout/Container.qml +++ b/tests/auto/quick/qquicklayouts/data/rowlayout/Container.qml @@ -1,5 +1,5 @@ // Copyright (C) 2017 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.7 import QtQuick.Layouts 1.3 diff --git a/tests/auto/quick/qquicklayouts/data/rowlayout/Container2.qml b/tests/auto/quick/qquicklayouts/data/rowlayout/Container2.qml index a2aa7b93b2..30880e2d91 100644 --- a/tests/auto/quick/qquicklayouts/data/rowlayout/Container2.qml +++ b/tests/auto/quick/qquicklayouts/data/rowlayout/Container2.qml @@ -1,5 +1,5 @@ // Copyright (C) 2017 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.9 import QtQuick.Layouts 1.3 diff --git a/tests/auto/quick/qquicklayouts/data/rowlayout/ContainerUser.qml b/tests/auto/quick/qquicklayouts/data/rowlayout/ContainerUser.qml index c324131ef6..22cf2353da 100644 --- a/tests/auto/quick/qquicklayouts/data/rowlayout/ContainerUser.qml +++ b/tests/auto/quick/qquicklayouts/data/rowlayout/ContainerUser.qml @@ -1,5 +1,5 @@ // Copyright (C) 2017 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.6 import QtQuick.Window 2.2 diff --git a/tests/auto/quick/qquicklayouts/data/rowlayout/ContainerUser2.qml b/tests/auto/quick/qquicklayouts/data/rowlayout/ContainerUser2.qml index 7a5389e81e..46fbae02a0 100644 --- a/tests/auto/quick/qquicklayouts/data/rowlayout/ContainerUser2.qml +++ b/tests/auto/quick/qquicklayouts/data/rowlayout/ContainerUser2.qml @@ -1,5 +1,5 @@ // Copyright (C) 2017 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.6 diff --git a/tests/auto/quick/qquicklayouts/data/rowlayout/LayerEnabled.qml b/tests/auto/quick/qquicklayouts/data/rowlayout/LayerEnabled.qml index d7ad38ffaf..ac12868627 100644 --- a/tests/auto/quick/qquicklayouts/data/rowlayout/LayerEnabled.qml +++ b/tests/auto/quick/qquicklayouts/data/rowlayout/LayerEnabled.qml @@ -1,5 +1,5 @@ // Copyright (C) 2018 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.7 import QtQuick.Layouts 1.3 diff --git a/tests/auto/quick/qquicklayouts/data/tst_gridlayout.qml b/tests/auto/quick/qquicklayouts/data/tst_gridlayout.qml index 261b8a9a8e..f311cc34d6 100644 --- a/tests/auto/quick/qquicklayouts/data/tst_gridlayout.qml +++ b/tests/auto/quick/qquicklayouts/data/tst_gridlayout.qml @@ -1,9 +1,10 @@ // Copyright (C) 2020 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.6 import QtTest 1.0 import QtQuick.Layouts 1.1 +import "LayoutHelperLibrary.js" as LayoutHelpers Item { id: container @@ -207,7 +208,7 @@ Item { } function test_flowLeftToRightDefaultPositions() { - ignoreWarning("QGridLayoutEngine::addItem: Cell (1, 0) already taken"); + ignoreWarning(/QGridLayoutEngine::addItem: Can't add .* at cell \(1, 0\) because it's already taken by .*/); var layout = createTemporaryObject(layout_flowLeftToRightDefaultPositions_Component, container); compare(layout.implicitWidth, 40); compare(layout.children[0].x, 0); @@ -1255,5 +1256,126 @@ Item { } verify(layout.implicitHeight > initialImplicitHeight) } + + function test_uniformCellSizes_data() + { + return [ + { + tag: "hor 9/3", + layout: { + type: "GridLayout", + columns: 3, + items: [ + {minimumWidth: 1, preferredWidth: 10, maximumWidth: 20, fillWidth: true}, + {minimumWidth: 1, preferredWidth: 4, maximumWidth: 10, fillWidth: true}, + {minimumWidth: 1, preferredWidth: 50, maximumWidth: 99, fillWidth: true} + ] + }, + layoutWidth: 9, + expectedWidths: [3, 3, 3], + expectedPositions: [0, 3, 6] + }, + { + tag: "hor 30/3", + layout: { + type: "GridLayout", + columns: 3, + items: [ + {minimumWidth: 1, preferredWidth: 10, maximumWidth: 20, fillWidth: true}, + {minimumWidth: 1, preferredWidth: 4, maximumWidth: 10, fillWidth: true}, + {minimumWidth: 1, preferredWidth: 50, maximumWidth: 99, fillWidth: true} + ] + }, + layoutWidth: 30, + expectedWidths: [10, 10, 10] + }, + { + tag: "hor 60/3", + layout: { + type: "GridLayout", + columns: 3, + items: [ + {minimumWidth: 1, preferredWidth: 10, maximumWidth: 20, fillWidth: true}, + {minimumWidth: 1, preferredWidth: 4, maximumWidth: 10, fillWidth: true}, + {minimumWidth: 1, preferredWidth: 50, maximumWidth: 99, fillWidth: true} + ] + }, + layoutWidth: 60, + expectedWidths: [20, 10, 20], // We are beyond the maximumWidth. of the middle item, + expectedPositions: [0, 20, 40] // check that *cellSize* is still uniform + // (middle item will be left-aligned in the cell by default) + }, + { + tag: "hor 66/3", + layout: { + type: "GridLayout", + columns: 3, + items: [ + {minimumWidth: 1, preferredWidth: 10, maximumWidth: 20, fillWidth: true}, + {minimumWidth: 1, preferredWidth: 4, maximumWidth: 10, fillWidth: true}, + {minimumWidth: 1, preferredWidth: 50, maximumWidth: 99, fillWidth: true} + ] + }, + layoutWidth: 66, + expectedWidths: [20, 10, 22], + expectedPositions: [0, 22, 44] + }, + { + tag: "ver 66/3", + layout: { + type: "GridLayout", + columns: 1, + items: [ + {minimumHeight: 1, preferredHeight: 10, maximumHeight: 20, fillHeight: true}, + {minimumHeight: 1, preferredHeight: 4, maximumHeight: 10, fillHeight: true}, + {minimumHeight: 1, preferredHeight: 50, maximumHeight: 99, fillHeight: true} + ] + }, + layoutHeight: 66, + expectedHeights: [20, 10, 22], + // If items are too small to fit the cell, they have a default alignment of + // Qt::AlignLeft | Qt::AlignVCenter + expectedPositions: [1, 22+6, 44] + } + ]; + } + + function test_uniformCellSizes(data) + { + let layout = LayoutHelpers.buildLayout(data.layout, testCase) + let isHorizontal = data.hasOwnProperty("expectedWidths") + layout.rowSpacing = 0 + layout.columnSpacing = 0 + layout.uniformCellWidths = true + layout.uniformCellHeights = true + waitForPolish(layout) + if (data.hasOwnProperty('layoutWidth')) { + layout.width = data.layoutWidth + } + if (data.hasOwnProperty('layoutHeight')) { + layout.height = data.layoutHeight + } + + let expectedSizes = isHorizontal ? data.expectedWidths : data.expectedHeights + let actualSizes = [] + let i = 0 + for (i = 0; i < layout.children.length; i++) { + let item = layout.children[i] + actualSizes.push(isHorizontal ? item.width : item.height) + } + compare(actualSizes, expectedSizes) + + if (data.hasOwnProperty('expectedPositions')) { + let actualPositions = [] + // If items are too small to fit the cell, they have a default alignment of + // Qt::AlignLeft | Qt::AlignVCenter + for (i = 0; i < layout.children.length; i++) { + let item = layout.children[i] + actualPositions.push(isHorizontal ? item.x : item.y) + } + compare(actualPositions, data.expectedPositions) + } + } + } } diff --git a/tests/auto/quick/qquicklayouts/data/tst_layoutproxy.qml b/tests/auto/quick/qquicklayouts/data/tst_layoutproxy.qml new file mode 100644 index 0000000000..28b0f9a61c --- /dev/null +++ b/tests/auto/quick/qquicklayouts/data/tst_layoutproxy.qml @@ -0,0 +1,687 @@ +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +import QtQuick 2.6 +import QtTest 1.0 +import QtQuick.Layouts + +Item { + id: container + width: 200 + height: 200 + TestCase { + id: testCase + name: "Tests_LayoutProxy" + when: windowShown + width: parent.width + height: parent.height + + Component { + id: layout_proxy_Component + Item { + anchors.fill: container + + property var rect1: Rectangle { + id: redRectanlge + parent: container + Layout.fillWidth: true + Layout.fillHeight: true + Layout.margins: 0 + property var itemRect: [mapToItem(container, Qt.point(0, 0)).x, + mapToItem(container, Qt.point(0, 0)).y, + width, height] + color: "red" + } + + + property var rect2: Rectangle { + id: blueRectanlge + parent: container + Layout.fillWidth: true + Layout.fillHeight: true + Layout.margins: 0 + property var itemRect: [mapToItem(container, Qt.point(0, 0)).x, + mapToItem(container, Qt.point(0, 0)).y, + width, height] + color: "blue" + } + + property var layout1: RowLayout { + parent: container + anchors.fill: parent + spacing: 0 + LayoutItemProxy { target: redRectanlge } + LayoutItemProxy { target: blueRectanlge } + } + + property var layout2: ColumnLayout { + parent: container + anchors.fill: parent + spacing: 0 + LayoutItemProxy { target: redRectanlge } + LayoutItemProxy { target: blueRectanlge } + } + } + } + + function test_Proxy_simple() + { + var item = createTemporaryObject(layout_proxy_Component, container); + + item.layout2.visible = false + item.layout1.visible = true + + tryCompare(item.rect1, "itemRect", [ 0, 0, 100, 200]) + tryCompare(item.rect2, "itemRect", [ 100, 0, 100, 200]) + + item.layout1.visible = false + item.layout2.visible = true + + tryCompare(item.rect1, "itemRect", [ 0, 0, 200, 100]) + tryCompare(item.rect2, "itemRect", [ 0, 100, 200, 100]) + + item.layout1.visible = true + item.layout2.visible = false + + tryCompare(item.rect1, "itemRect", [ 0, 0, 100, 200]) + tryCompare(item.rect2, "itemRect", [ 100, 0, 100, 200]) + + } + + function test_Proxy_layout_destruction1() + { + var item = createTemporaryObject(layout_proxy_Component, container); + + item.layout1.visible = true + item.layout2.visible = false + + tryCompare(item.rect1, "itemRect", [ 0, 0, 100, 200]) + tryCompare(item.rect2, "itemRect", [ 100, 0, 100, 200]) + + item.layout1.visible = false + item.layout2.visible = true + + tryCompare(item.rect1, "itemRect", [ 0, 0, 200, 100]) + tryCompare(item.rect2, "itemRect", [ 0, 100, 200, 100]) + + item.layout2.destroy() //destroy the layout that has control + wait(0) // process the scheduled delete and actually invoke the dtor + compare(item.layout2, null) + item.layout1.visible = true //check that the other one still works + + tryCompare(item.rect1, "itemRect", [ 0, 0, 100, 200]) + tryCompare(item.rect2, "itemRect", [ 100, 0, 100, 200]) + } + + function test_Proxy_layout_destruction2() + { + var item = createTemporaryObject(layout_proxy_Component, container); + + item.layout1.visible = false + item.layout2.visible = false + + //destroy both layouts while none has control + item.layout1.destroy() + item.layout2.destroy() + wait(0) // process the scheduled delete and actually invoke the dtor + //all layouts should be gone now + compare(item.layout1, null) + compare(item.layout2, null) + //but the rectangles should still be here + verify(item.rect1 !== null) + verify(item.rect2 !== null) + } + + function test_Proxy_layout_destruction3() + { + var item = createTemporaryObject(layout_proxy_Component, container); + + item.layout1.visible = true + item.layout2.visible = true + + //destroy both layouts while both have control + item.layout1.destroy() + item.layout2.destroy() + wait(0) // process the scheduled delete and actually invoke the dtor + //all layouts should be gone now + compare(item.layout1, null) + compare(item.layout2, null) + //but the rectangles should still be here + verify(item.rect1 !== null) + verify(item.rect2 !== null) + } + + function test_Proxy_layout_destruction_of_targets() + { + var item = createTemporaryObject(layout_proxy_Component, container); + + item.layout1.visible = true + item.layout2.visible = false + + //destroy a rectangle just to see if the proxy crashes + item.rect1.destroy() + wait(0) // process the scheduled delete and actually invoke the dtor + compare(item.rect1, null) + + //the proxy still has the size of the item and is still there + tryCompare(item.layout1.children[0], "x", 0) + tryCompare(item.layout1.children[0], "y", 0) + tryCompare(item.layout1.children[0], "width", 100) + tryCompare(item.layout1.children[0], "height", 200) + //the second item is still here + tryCompare(item.rect2, "itemRect", [ 100, 0, 100, 200]) + //the most important thing is that it does not crash + + } + + Component { + id: layout_proxy_Component_Three + Item { + anchors.fill: container + property var rect1: Rectangle { + id: redRectanlge + parent: container + Layout.fillWidth: true + Layout.fillHeight: true + Layout.margins: 0 + property var itemRect: [mapToItem(container, Qt.point(0, 0)).x, + mapToItem(container, Qt.point(0, 0)).y, + width, height] + color: "red" + } + + property var rect2: Rectangle { + id: blueRectanlge + parent: container + Layout.fillWidth: true + Layout.fillHeight: true + Layout.margins: 0 + property var itemRect: [mapToItem(container, Qt.point(0, 0)).x, + mapToItem(container, Qt.point(0, 0)).y, + width, height] + color: "blue" + } + + property var layout1: RowLayout { + parent: container + anchors.fill: parent + spacing: 0 + LayoutItemProxy { target: redRectanlge } + LayoutItemProxy { target: blueRectanlge } + } + + property var layout2: ColumnLayout { + parent: container + anchors.fill: parent + spacing: 0 + LayoutItemProxy { target: redRectanlge } + LayoutItemProxy { target: blueRectanlge } + } + + property var layout3: ColumnLayout { + parent: container + anchors.fill: parent + spacing: 0 + LayoutItemProxy { target: redRectanlge } + LayoutItemProxy { target: blueRectanlge } + Rectangle { + Layout.fillWidth: true + Layout.fillHeight: true + color: "green" + } + } + } + } + + function test_Proxy_native_item() + { + var item = createTemporaryObject(layout_proxy_Component_Three, container); + + item.layout1.visible = true + item.layout2.visible = false + item.layout3.visible = false + + tryCompare(item.rect1, "itemRect", [ 0, 0, 100, 200]) + tryCompare(item.rect2, "itemRect", [ 100, 0, 100, 200]) + + item.layout1.visible = false + item.layout2.visible = true + item.layout3.visible = false + + tryCompare(item.rect1, "itemRect", [ 0, 0, 200, 100]) + tryCompare(item.rect2, "itemRect", [ 0, 100, 200, 100]) + + item.layout1.visible = false + item.layout2.visible = false + item.layout3.visible = true + + tryCompare(item.rect1, "itemRect", [ 0, 0, 200, 67]) //66.6 = 67 + tryCompare(item.rect2, "itemRect", [ 0, 67, 200, 66]) + } + + + Component { + id: layout_proxy_Component_overwrite + Item { + anchors.fill: container + property var rect1: Rectangle { + id: redRectanlge + parent: container + Layout.fillWidth: true + Layout.fillHeight: true + Layout.preferredWidth: 180 + Layout.preferredHeight: 180 + Layout.margins: 0 + property var itemRect: [mapToItem(container, Qt.point(0, 0)).x, + mapToItem(container, Qt.point(0, 0)).y, + width, height] + color: "red" + } + + property var rect2: Rectangle { + id: blueRectanlge + parent: container + Layout.fillWidth: true + Layout.fillHeight: true + Layout.preferredWidth: 20 + Layout.preferredHeight: 20 + Layout.margins: 0 + property var itemRect: [mapToItem(container, Qt.point(0, 0)).x, + mapToItem(container, Qt.point(0, 0)).y, + width, height] + color: "blue" + } + + property var layout1: RowLayout { + parent: container + anchors.fill: parent + spacing: 0 + LayoutItemProxy { target: redRectanlge } + LayoutItemProxy { target: blueRectanlge } + } + + property var layout2: ColumnLayout { + parent: container + anchors.fill: parent + spacing: 0 + LayoutItemProxy { target: redRectanlge } + LayoutItemProxy { target: blueRectanlge } + } + } + } + + function test_Proxy_overwrite_layout_properties() + { + var item = createTemporaryObject(layout_proxy_Component_overwrite, container); + + item.layout1.visible = true + item.layout2.visible = false + + tryCompare(item.rect1, "itemRect", [ 0, 0, 180, 200]) + tryCompare(item.rect2, "itemRect", [ 180, 0, 20, 200]) + + item.layout1.visible = false + item.layout2.visible = true + + tryCompare(item.rect1, "itemRect", [ 0, 0, 200, 180]) + tryCompare(item.rect2, "itemRect", [ 0, 180, 200, 20]) + + //should overwrite the rectangles preferences + item.layout1.children[0].Layout.preferredWidth = 100 + item.layout1.children[0].Layout.preferredHeight = 100 + item.layout1.children[1].Layout.preferredWidth = 100 + item.layout1.children[1].Layout.preferredHeight = 100 + + item.layout1.visible = true + item.layout2.visible = false + + tryCompare(item.rect1, "itemRect", [ 0, 0, 100, 200]) + tryCompare(item.rect2, "itemRect", [ 100, 0, 100, 200]) + } + + Component { + id: layout_proxy_Component_overwrite_declarative + Item { + anchors.fill: container + property var rect1: Rectangle { + id: redRectanlge + parent: container + Layout.fillWidth: true + Layout.fillHeight: true + Layout.minimumWidth: 1 + Layout.minimumHeight: 1 + Layout.maximumWidth: 3 + Layout.maximumHeight: 3 + Layout.preferredWidth: 2 + Layout.preferredHeight: 2 + Layout.margins: 1 + Layout.leftMargin: 2 + Layout.topMargin: 3 + Layout.rightMargin: 4 + Layout.bottomMargin: 5 + Layout.alignment: Qt.AlignBottom + property var itemRect: [mapToItem(container, Qt.point(0, 0)).x, + mapToItem(container, Qt.point(0, 0)).y, + width, height] + color: "red" + } + + + property var layout1: RowLayout { + parent: container + anchors.fill: parent + spacing: 0 + LayoutItemProxy { target: redRectanlge + Layout.fillWidth: false + Layout.fillHeight: false + Layout.minimumWidth: 100 + Layout.minimumHeight: 100 + Layout.maximumWidth: 300 + Layout.maximumHeight: 300 + Layout.preferredWidth: 200 + Layout.preferredHeight: 200 + Layout.margins: 100 + Layout.leftMargin: 200 + Layout.topMargin: 300 + Layout.rightMargin: 400 + Layout.bottomMargin: 500 + Layout.alignment: Qt.AlignTop + } + } + + property var layout2: RowLayout { + parent: container + anchors.fill: parent + spacing: 0 + LayoutItemProxy { target: redRectanlge } + } + } + } + + function test_Proxy_overwrite_layout_properties_declarative() + { + var item = createTemporaryObject(layout_proxy_Component_overwrite_declarative, container); + + compare(item.layout2.children[0].Layout.fillWidth, item.rect1.Layout.fillWidth) + compare(item.layout2.children[0].Layout.fillHeight, item.rect1.Layout.fillHeight) + compare(item.layout2.children[0].Layout.minimumWidth, item.rect1.Layout.minimumWidth) + compare(item.layout2.children[0].Layout.minimumHeight, item.rect1.Layout.minimumHeight) + compare(item.layout2.children[0].Layout.maximumWidth, item.rect1.Layout.maximumWidth) + compare(item.layout2.children[0].Layout.maximumHeight, item.rect1.Layout.maximumHeight) + compare(item.layout2.children[0].Layout.preferredWidth, item.rect1.Layout.preferredWidth) + compare(item.layout2.children[0].Layout.preferredHeight, item.rect1.Layout.preferredHeight) + compare(item.layout2.children[0].Layout.margins, item.rect1.Layout.margins) + compare(item.layout2.children[0].Layout.leftMargin, item.rect1.Layout.leftMargin) + compare(item.layout2.children[0].Layout.topMargin, item.rect1.Layout.topMargin) + compare(item.layout2.children[0].Layout.rightMargin, item.rect1.Layout.rightMargin) + compare(item.layout2.children[0].Layout.bottomMargin, item.rect1.Layout.bottomMargin) + compare(item.layout2.children[0].Layout.alignment, item.rect1.Layout.alignment) + + verify(item.layout1.children[0].Layout.fillWidth != item.rect1.Layout.fillWidth) + verify(item.layout1.children[0].Layout.fillHeight != item.rect1.Layout.fillHeight) + verify(item.layout1.children[0].Layout.minimumWidth != item.rect1.Layout.minimumWidth) + verify(item.layout1.children[0].Layout.minimumHeight != item.rect1.Layout.minimumHeight) + verify(item.layout1.children[0].Layout.maximumWidth != item.rect1.Layout.maximumWidth) + verify(item.layout1.children[0].Layout.maximumHeight != item.rect1.Layout.maximumHeight) + verify(item.layout1.children[0].Layout.preferredWidth != item.rect1.Layout.preferredWidth) + verify(item.layout1.children[0].Layout.preferredHeight != item.rect1.Layout.preferredHeight) + verify(item.layout1.children[0].Layout.margins != item.rect1.Layout.margins) + verify(item.layout1.children[0].Layout.leftMargin != item.rect1.Layout.leftMargin) + verify(item.layout1.children[0].Layout.topMargin != item.rect1.Layout.topMargin) + verify(item.layout1.children[0].Layout.rightMargin != item.rect1.Layout.rightMargin) + verify(item.layout1.children[0].Layout.bottomMargin != item.rect1.Layout.bottomMargin) + verify(item.layout1.children[0].Layout.alignment != item.rect1.alignment) + + compare(item.layout1.children[0].Layout.fillWidth, false) + compare(item.layout1.children[0].Layout.fillHeight, false) + compare(item.layout1.children[0].Layout.minimumWidth, item.rect1.Layout.minimumWidth * 100) + compare(item.layout1.children[0].Layout.minimumHeight, item.rect1.Layout.minimumHeight * 100) + compare(item.layout1.children[0].Layout.maximumWidth, item.rect1.Layout.maximumWidth * 100) + compare(item.layout1.children[0].Layout.maximumHeight, item.rect1.Layout.maximumHeight * 100) + compare(item.layout1.children[0].Layout.preferredWidth, item.rect1.Layout.preferredWidth * 100) + compare(item.layout1.children[0].Layout.preferredHeight, item.rect1.Layout.preferredHeight * 100) + compare(item.layout1.children[0].Layout.margins, item.rect1.Layout.margins * 100) + compare(item.layout1.children[0].Layout.leftMargin, item.rect1.Layout.leftMargin * 100) + compare(item.layout1.children[0].Layout.topMargin, item.rect1.Layout.topMargin * 100) + compare(item.layout1.children[0].Layout.rightMargin, item.rect1.Layout.rightMargin * 100) + compare(item.layout1.children[0].Layout.bottomMargin, item.rect1.Layout.bottomMargin * 100) + compare(item.layout1.children[0].Layout.alignment, Qt.AlignTop) + } + + Component { + id: layout_proxy_Component_nesting + Item { + anchors.fill: container + + property var rect1: Rectangle { + id: redRectanlge + parent: container + Layout.fillWidth: true + Layout.fillHeight: true + Layout.margins: 0 + property var itemRect: [parent ? parent.mapToItem(container, Qt.point(x, y)).x : 0, + parent ? parent.mapToItem(container, Qt.point(x, y)).y : 0, + width, height] + color: "red" + } + + + property var rect2: Rectangle { + id: blueRectanlge + parent: container + Layout.fillWidth: true + Layout.fillHeight: true + Layout.margins: 0 + property var itemRect: [parent ? parent.mapToItem(container, Qt.point(x, y)).x : 0, + parent ? parent.mapToItem(container, Qt.point(x, y)).y : 0, + width, height] + color: "blue" + } + + property var rect3: Rectangle { + id: greenRectanlge + parent: container + Layout.fillWidth: true + Layout.fillHeight: true + Layout.margins: 0 + property var itemRect: [parent ? parent.mapToItem(container, Qt.point(x, y)).x : 0, + parent ? parent.mapToItem(container, Qt.point(x, y)).y : 0, + width, height] + color: "green" + } + + property var layout1: RowLayout { + parent: container + anchors.fill: parent + spacing: 0 + LayoutItemProxy { target: redRectanlge } + ColumnLayout { + spacing: 0 + LayoutItemProxy { target: blueRectanlge } + LayoutItemProxy { target: greenRectanlge } + } + } + + property var layout2: ColumnLayout { + parent: container + anchors.fill: parent + spacing: 0 + LayoutItemProxy { target: redRectanlge } + RowLayout { + spacing: 0 + LayoutItemProxy { target: blueRectanlge } + LayoutItemProxy { target: greenRectanlge } + } + } + } + } + + function test_Proxy_nesting() + { + var item = createTemporaryObject(layout_proxy_Component_nesting, container); + + item.layout2.visible = false + item.layout1.visible = true + + tryCompare(item.rect1, "itemRect", [ 0, 0, 100, 200]) + tryCompare(item.rect2, "itemRect", [ 100, 0, 100, 100]) + tryCompare(item.rect3, "itemRect", [ 100, 100, 100, 100]) + + item.layout1.visible = false + item.layout2.visible = true + + tryCompare(item.rect1, "itemRect", [ 0, 0, 200, 100]) + tryCompare(item.rect2, "itemRect", [ 0, 100, 100, 100]) + tryCompare(item.rect3, "itemRect", [ 100, 100, 100, 100]) + + item.layout1.visible = true + item.layout2.visible = false + + tryCompare(item.rect1, "itemRect", [ 0, 0, 100, 200]) + tryCompare(item.rect2, "itemRect", [ 100, 0, 100, 100]) + tryCompare(item.rect3, "itemRect", [ 100, 100, 100, 100]) + } + + + + Component { + id: layout_proxy_Component_nesting_item + Item { + anchors.fill: container + + property var rect1: Rectangle { + id: redRectanlge + parent: container + Layout.fillWidth: true + Layout.fillHeight: true + Layout.margins: 0 + property var itemRect: [parent ? parent.mapToItem(container, Qt.point(x, y)).x : 0, + parent ? parent.mapToItem(container, Qt.point(x, y)).y : 0, + width, height] + color: "red" + } + + property var rect2: Rectangle { + id: blueRectanlge + parent: container + Layout.fillWidth: true + Layout.fillHeight: true + Layout.margins: 0 + property var itemRect: [parent ? parent.mapToItem(container, Qt.point(x, y)).x : 0, + parent ? parent.mapToItem(container, Qt.point(x, y)).y : 0, + width, height] + color: "blue" + } + + property var rect3: Rectangle { + id: greenRectanlge + parent: container + Layout.fillWidth: true + Layout.fillHeight: true + Layout.margins: 0 + property var itemRect: [parent ? parent.mapToItem(container, Qt.point(x, y)).x : 0, + parent ? parent.mapToItem(container, Qt.point(x, y)).y : 0, + width, height] + color: "green" + } + + property var rect4: Rectangle { + id: yellowRectangle + parent: container + Layout.fillWidth: true + Layout.fillHeight: true + Layout.margins: 0 + property var itemRect: [parent ? parent.mapToItem(container, Qt.point(x, y)).x : 0, + parent ? parent.mapToItem(container, Qt.point(x, y)).y : 0, + width, height] + color: "yellow" + } + + property var rect5: Rectangle { + id: brownRectangle + parent: container + Layout.fillWidth: true + Layout.fillHeight: true + Layout.margins: 0 + property var itemRect: [parent ? parent.mapToItem(container, Qt.point(x, y)).x : 0, + parent ? parent.mapToItem(container, Qt.point(x, y)).y : 0, + width, height] + color: "brown" + } + + property var layout1: RowLayout { + parent: container + anchors.fill: parent + spacing: 0 + LayoutItemProxy { target: redRectanlge } + ColumnLayout { + spacing: 0 + LayoutItemProxy { target: blueRectanlge } + LayoutItemProxy { target: greenRectanlge } + Item { + implicitWidth: rll.implicitWidth + implicitHeight: rll.implicitHeight + Layout.fillWidth: true + Layout.fillHeight: true + RowLayout { + id: rll + anchors.fill: parent + spacing: 0 + LayoutItemProxy { target: yellowRectangle } + LayoutItemProxy { target: brownRectangle } + } + } + } + } + + property var layout2: ColumnLayout { + parent: container + anchors.fill: parent + spacing: 0 + LayoutItemProxy { target: redRectanlge } + RowLayout { + spacing: 0 + LayoutItemProxy { target: blueRectanlge } + LayoutItemProxy { target: greenRectanlge } + Item { + implicitWidth: cll.implicitWidth + implicitHeight: cll.implicitHeight + Layout.fillWidth: true + Layout.fillHeight: true + ColumnLayout { + id: cll + anchors.fill: parent + spacing: 0 + LayoutItemProxy { target: yellowRectangle } + LayoutItemProxy { target: brownRectangle } + } + } + } + } + } + } + + function test_Proxy_nesting_item() + { + var item = createTemporaryObject(layout_proxy_Component_nesting_item, container); + + item.layout1.visible = true + item.layout2.visible = false + + tryCompare(item.rect1, "itemRect", [ 0, 0, 100, 200]) + tryCompare(item.rect2, "itemRect", [ 100, 0, 100, 67]) + tryCompare(item.rect3, "itemRect", [ 100, 67, 100, 66]) + tryCompare(item.rect4, "itemRect", [ 100, 133, 50, 67]) + tryCompare(item.rect5, "itemRect", [ 150, 133, 50, 67]) + + item.layout2.visible = true + item.layout1.visible = false + + tryCompare(item.rect1, "itemRect", [ 0, 0, 200, 100]) + tryCompare(item.rect2, "itemRect", [ 0, 100, 67, 100]) + tryCompare(item.rect3, "itemRect", [ 67, 100, 66, 100]) + tryCompare(item.rect4, "itemRect", [ 133, 100, 67, 50]) + tryCompare(item.rect5, "itemRect", [ 133, 150, 67, 50]) + } + } +} diff --git a/tests/auto/quick/qquicklayouts/data/tst_rowlayout.qml b/tests/auto/quick/qquicklayouts/data/tst_rowlayout.qml index 506893b631..56198f0d2e 100644 --- a/tests/auto/quick/qquicklayouts/data/tst_rowlayout.qml +++ b/tests/auto/quick/qquicklayouts/data/tst_rowlayout.qml @@ -1,9 +1,11 @@ // Copyright (C) 2020 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only -import QtQuick 2.2 -import QtTest 1.0 -import QtQuick.Layouts 1.0 +import QtQuick +import QtTest +import QtQuick.Controls +import QtQuick.Layouts +import "LayoutHelperLibrary.js" as LayoutHelpers import org.qtproject.Test @@ -23,6 +25,13 @@ Item { return [item.x, item.y, item.width, item.height]; } + function cleanup() { + if (LayoutSetup.useDefaultSizePolicy) { + LayoutSetup.useDefaultSizePolicy = false + compare(LayoutSetup.useDefaultSizePolicy, false) + } + } + Component { id: rectangle_Component Rectangle { @@ -926,6 +935,28 @@ Item { }, layoutWidth: 28, expectedWidths: [22, 6] + },{ + tag: "resize_to_0_width", + layout: { + type: "RowLayout", + items: [ + {preferredWidth: 10, fillWidth: true}, + ] + }, + layoutWidth: 0, + expectedWidths: [0] + },{ + tag: "preferred_infinity", // Do not crash/assert when the preferred size is infinity + layout: { + type: "RowLayout", + items: [ + {minimumWidth: 10, preferredWidth: Number.POSITIVE_INFINITY, fillWidth: true}, + {minimumWidth: 20, preferredWidth: Number.POSITIVE_INFINITY, fillWidth: true}, + ] + }, + layoutWidth: 31, // Important that this is between minimum and preferred width of the layout. + expectedWidths: [10, 21] // The result here does not have to be exact. (This + // test is mostly concerned about not crashing). } ]; } @@ -946,6 +977,148 @@ Item { layout.destroy(); } + function test_uniformCellSizes_data() + { + return [ + { + tag: "hor 9/3", + layout: { + type: "RowLayout", + items: [ + {minimumWidth: 1, preferredWidth: 10, maximumWidth: 20, fillWidth: true}, + {minimumWidth: 1, preferredWidth: 4, maximumWidth: 10, fillWidth: true}, + {minimumWidth: 1, preferredWidth: 50, maximumWidth: 99, fillWidth: true} + ] + }, + layoutWidth: 9, + expectedWidths: [3, 3, 3], + expectedPositions: [0, 3, 6] + }, + { + tag: "hor 30/3", + layout: { + type: "RowLayout", + items: [ + {minimumWidth: 1, preferredWidth: 10, maximumWidth: 20, fillWidth: true}, + {minimumWidth: 1, preferredWidth: 4, maximumWidth: 10, fillWidth: true}, + {minimumWidth: 1, preferredWidth: 50, maximumWidth: 99, fillWidth: true} + ] + }, + layoutWidth: 30, + expectedWidths: [10, 10, 10] + }, + { + tag: "hor 60/3", + layout: { + type: "RowLayout", + items: [ + {minimumWidth: 1, preferredWidth: 10, maximumWidth: 20, fillWidth: true}, + {minimumWidth: 1, preferredWidth: 4, maximumWidth: 10, fillWidth: true}, + {minimumWidth: 1, preferredWidth: 50, maximumWidth: 99, fillWidth: true} + ] + }, + layoutWidth: 60, + expectedWidths: [20, 10, 20], // We are beyond the maximumWidth. of the middle item, + expectedPositions: [0, 20, 40] // check that *cellSize* is still uniform + // (middle item will be left-aligned in the cell by default) + }, + { + tag: "hor 66/3", + layout: { + type: "RowLayout", + items: [ + {minimumWidth: 1, preferredWidth: 10, maximumWidth: 20, fillWidth: true}, + {minimumWidth: 1, preferredWidth: 4, maximumWidth: 10, fillWidth: true}, + {minimumWidth: 1, preferredWidth: 50, maximumWidth: 99, fillWidth: true} + ] + }, + layoutWidth: 66, + expectedWidths: [20, 10, 22], + expectedPositions: [0, 22, 44] + }, + { + tag: "ver 66/3", + layout: { + type: "ColumnLayout", + items: [ + {minimumHeight: 1, preferredHeight: 10, maximumHeight: 20, fillHeight: true}, + {minimumHeight: 1, preferredHeight: 4, maximumHeight: 10, fillHeight: true}, + {minimumHeight: 1, preferredHeight: 50, maximumHeight: 99, fillHeight: true} + ] + }, + layoutHeight: 66, + expectedHeights: [20, 10, 22], + // If items are too small to fit the cell, they have a default alignment of + // Qt::AlignLeft | Qt::AlignVCenter + expectedPositions: [1, 22+6, 44] + } + ]; + } + + function test_uniformCellSizes(data) + { + let layout = LayoutHelpers.buildLayout(data.layout, testCase) + let isHorizontal = data.hasOwnProperty("expectedWidths") + layout.spacing = 0 + layout.uniformCellSizes = true + waitForPolish(layout) + if (data.hasOwnProperty('layoutWidth')) { + layout.width = data.layoutWidth + } + if (data.hasOwnProperty('layoutHeight')) { + layout.height = data.layoutHeight + } + + let expectedSizes = (isHorizontal ? data.expectedWidths : data.expectedHeights) + let actualSizes = [] + let i = 0 + for (i = 0; i < layout.children.length; i++) { + let item = layout.children[i] + actualSizes.push(isHorizontal ? item.width : item.height) + } + compare(actualSizes, expectedSizes) + + if (data.hasOwnProperty('expectedPositions')) { + let actualPositions = [] + for (i = 0; i < layout.children.length; i++) { + let item = layout.children[i] + actualPositions.push(isHorizontal ? item.x : item.y) + } + compare(actualPositions, data.expectedPositions) + } + } + + Component { + id: uniformCellSizes_QML_Component + RowLayout { + spacing: 0 + uniformCellSizes: true + Rectangle { + implicitWidth: 1 + Layout.fillHeight: true + Layout.fillWidth: true + color: "red" + } + Rectangle { + implicitWidth: 2 + Layout.fillHeight: true + Layout.fillWidth: true + color: "blue" + } + } + } + + function test_uniformCellSizes_QML(data) + { + var layout = createTemporaryObject(uniformCellSizes_QML_Component, testCase) + layout.width = 40 + layout.height = 20 + let expectedWidths = [20, 20] + let actualWidths = [layout.children[0].width, layout.children[1].width] + compare(actualWidths, expectedWidths) + } + + Component { id: layout_alignToPixelGrid_Component RowLayout { @@ -1168,6 +1341,78 @@ Item { } Component { + id: sizeHintBindingLoopComp + Item { + id: root + anchors.fill: parent + property var customWidth: 100 + RowLayout { + id: col + Item { + id: item + implicitHeight: 80 + implicitWidth: Math.max(col2.implicitWidth, root.customWidth + 20) + ColumnLayout { + id: col2 + width: parent.width + Item { + id: rect + implicitWidth: root.customWidth + implicitHeight: 80 + } + } + } + } + } + } + + function test_sizeHintBindingLoopIssue() { + var item = createTemporaryObject(sizeHintBindingLoopComp, container) + waitForRendering(item) + item.customWidth += 10 + waitForRendering(item) + verify(!LayoutSetup.bindingLoopDetected, "Detected binding loop") + LayoutSetup.resetBindingLoopDetectedFlag() + } + + Component { + id: polishLayoutItemComp + Item { + anchors.fill: parent + implicitHeight: contentLayout.implicitHeight + implicitWidth: contentLayout.implicitWidth + property alias textLayout: contentLayout + RowLayout { + width: parent.width + height: parent.height + ColumnLayout { + id: contentLayout + Layout.alignment: Qt.AlignHCenter | Qt.AlignTop + Layout.maximumWidth: 200 + Repeater { + model: 2 + Text { + Layout.fillWidth: true + text: "This is a long text causing line breaks to show the bug." + wrapMode: Text.Wrap + } + } + Item { + Layout.fillHeight: true + } + } + } + } + } + + function test_polishLayoutItemIssue() { + var rootItem = createTemporaryObject(polishLayoutItemComp, container) + waitForRendering(rootItem) + var textItem = rootItem.textLayout.children[1] + verify(textItem.y >= rootItem.textLayout.children[0].height) + } + + Component { id: rearrangeNestedLayouts_Component RowLayout { id: layout @@ -1268,6 +1513,44 @@ Item { } Component { + id: rearrangeInvalidatedChildInNestedLayout + ColumnLayout { + anchors.fill: parent + RowLayout { + spacing: 0 + Text { + Layout.preferredWidth: 50 + text: "Text Text Text" + wrapMode: Text.WordWrap + } + Rectangle { + color: "red"; + Layout.preferredWidth: 50 + Layout.preferredHeight: 20 + } + Rectangle { + color: "green" + Layout.preferredHeight: 20 + Layout.fillWidth: true + } + } + } + } + + function test_rearrangeInvalidatedChildInNestedLayout() { + let layout = rearrangeInvalidatedChildInNestedLayout.createObject(container) + waitForRendering(layout) + + let item1 = layout.children[0].children[0] + let item2 = layout.children[0].children[1] + let item3 = layout.children[0].children[2] + + compare(item1.width, 50) + compare(item2.width, 50) + compare(item3.width, 100) + } + + Component { id: changeChildrenOfHiddenLayout_Component RowLayout { property int childCount: 1 @@ -1334,6 +1617,49 @@ Item { compare(rootRect.item1.width, 100) } + //--------------------------- + // Layout with negative size + Component { + id: negativeSize_Component + Item { + id: rootItem + width: 0 + height: 0 + // default width x height: (0 x 0) + RowLayout { + spacing: 0 + anchors.fill: parent + anchors.leftMargin: 1 // since parent size == (0 x 0), it causes layout size + anchors.bottomMargin: 1 // to become (-1, -1) + Item { + Layout.fillWidth: true + Layout.fillHeight: true + } + } + } + } + + function test_negativeSize() { + let rootItem = createTemporaryObject(negativeSize_Component, container) + let rowLayout = rootItem.children[0] + let item = rowLayout.children[0] + + const arr = [7, 1, 7, 0] + arr.forEach((n) => { + rootItem.width = n + rootItem.height = n + + // n === 0 is special: It will cause the layout to have a + // negative size. In this case it will simply not rearrange its + // child (and leave it at its previous size, 6) + const expectedItemExtent = n === 0 ? 6 : n - 1 + + compare(item.width, expectedItemExtent) + compare(item.height, expectedItemExtent) + }); + } + + //--------------------------- Component { id: rowlayoutWithTextItems_Component @@ -1467,9 +1793,146 @@ Item { compare(rootItem.maxWidth, 66) // Should not trigger a binding loop - verify(!BindingLoopDetector.bindingLoopDetected, "Detected binding loop") - BindingLoopDetector.reset() + verify(!LayoutSetup.bindingLoopDetected, "Detected binding loop") + LayoutSetup.resetBindingLoopDetectedFlag() } - } + + //--------------------------- + // QTBUG-111792 + Component { + id: rowlayoutCrashes_Component + RowLayout { + spacing: 5 + Rectangle { + color: "red" + implicitWidth: 10 + implicitHeight: 10 + } + Rectangle { + color: "green" + implicitWidth: 10 + implicitHeight: 10 + } + } + } + + function test_dontCrashAfterDestroyingChildren_data() { + return [ + { tag: "setWidth", func: function (layout) { layout.width = 42 } }, + { tag: "setHeight", func: function (layout) { layout.height = 42 } }, + { tag: "getImplicitWidth", func: function (layout) { let x = layout.implicitWidth } }, + { tag: "getImplicitHeight", func: function (layout) { let x = layout.implicitHeight } }, + ] + } + + function test_dontCrashAfterDestroyingChildren(data) { + var layout = createTemporaryObject(rowlayoutCrashes_Component, container) + waitForRendering(layout) + compare(layout.implicitWidth, 25) + layout.children[0].destroy() // deleteLater() + wait(0) // process the scheduled delete and actually invoke the dtor + data.func(layout) // call a function that might ultimately access the deleted item (but shouldn't) + } + + //--------------------------- + // Default layout size policy + Component { + id: defaultLayoutComp + Item { + id: rootItem + width: 110 + height: 100 + // Check default layout size policy + RowLayout { + spacing: 0 + anchors.fill: parent + // Rectangle item - SizePolicy { Horizontal: Fixed; Vertical: Fixed } + Rectangle {} + // Button item - SizePolicy { Horizontal: Preferred; Vertical: Fixed } + Button {} + // Frame item - SizePolicy { Horizontal: Preferred; Vertical: Preferred } + Frame {} + } + } + } + + function test_defaultLayoutSize() { + let rootItem = createTemporaryObject(defaultLayoutComp, container) + waitForRendering(rootItem) + + let rowLayout = rootItem.children[0] + + // Test default size policy disabled by default + compare(LayoutSetup.useDefaultSizePolicy, false) + + let defaultButtonWidth = rowLayout.children[1].width + let defaultFrameWidth = rowLayout.children[2].width + + // Enable attached properties for items and check its size + { + rowLayout.children[0].Layout.fillWidth = true + rowLayout.children[0].Layout.fillHeight = true + rowLayout.children[1].Layout.fillWidth = true + rowLayout.children[1].Layout.fillHeight = true + rowLayout.children[2].Layout.fillWidth = true + rowLayout.children[2].Layout.fillHeight = true + waitForRendering(rowLayout) + let isAbovePreferred = rowLayout.width >= rowLayout.implicitWidth + // Removing rectangle as width & implicitWidth be zero always, which makes validation of no use + for (let i = 1; i < rowLayout.children.length; i++) { + compare(rowLayout.children[i].width >= rowLayout.children[i].implicitWidth, isAbovePreferred) + compare(rowLayout.children[i].height, rowLayout.height) + } + } + + // Destroy existing object + rootItem.destroy() + + // Enable default size policy + LayoutSetup.useDefaultSizePolicy = true + compare(LayoutSetup.useDefaultSizePolicy, true) + rootItem = createTemporaryObject(defaultLayoutComp, container) + waitForRendering(rootItem) + rowLayout = rootItem.children[0] + + // The default size policy would stretch button and frame accordingly + { + verify(Math.abs(rowLayout.width - (rowLayout.children[0].width + rowLayout.children[1].width + rowLayout.children[2].width)) <= 1) + compare(rowLayout.children[1].width < rowLayout.children[2].width, rowLayout.children[1].implicitWidth < rowLayout.children[2].implicitWidth) + compare(rowLayout.children[1].height, rowLayout.children[1].implicitHeight) + compare(rowLayout.children[2].height, rowLayout.height) + } + + // Change the width and height of the root item to see layout size change + // Since default size policy for button and frame are Preferred, these items should + // stretch + { + let szDefaultButtonWidth = rowLayout.children[1].width + let szDefaultFrameWidth = rowLayout.children[2].width + + rootItem.width = 210 + rootItem.height = 200 + waitForRendering(rootItem) + verify(rowLayout.children[1].width > szDefaultButtonWidth) + compare(rowLayout.children[1].height, rowLayout.children[1].implicitHeight) + verify(rowLayout.children[2].width > szDefaultFrameWidth) + compare(rowLayout.children[2].height, rowLayout.height) + } + + // Disable size policies through attached properties and check item size + { + rowLayout.children[1].Layout.fillWidth = false + rowLayout.children[2].Layout.fillWidth = false + rowLayout.children[2].Layout.fillHeight = false + waitForRendering(rowLayout) + compare(rowLayout.children[1].width, defaultButtonWidth) + compare(rowLayout.children[2].width, defaultFrameWidth) + for (let index = 1; index < rowLayout.children.length; index++) + compare(rowLayout.children[index].height, rowLayout.children[index].implicitHeight) + } + + rootItem.destroy() + } + } } diff --git a/tests/auto/quick/qquicklayouts/data/tst_stacklayout.qml b/tests/auto/quick/qquicklayouts/data/tst_stacklayout.qml index e8d311183e..f627a90dfd 100644 --- a/tests/auto/quick/qquicklayouts/data/tst_stacklayout.qml +++ b/tests/auto/quick/qquicklayouts/data/tst_stacklayout.qml @@ -1,5 +1,5 @@ // Copyright (C) 2020 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick 2.15 import QtTest 1.15 @@ -724,6 +724,146 @@ Item { compare(layout.num_onCountChanged, 1) } + // QTBUG-111902 + Component { + id: stackComponent + Loader { + id: loader + asynchronous: true + sourceComponent: StackLayout { + id: stackLayout + Repeater { + model: 3 + Item { + required property int index + } + } + } + } + } + + function test_loadStackLayoutAsynchronously() { + var loaderObj = stackComponent.createObject(container) + // Check for loader status to be ready + tryCompare(loaderObj, 'status', 1) + // Get stack layout object + var stackLayoutObj = loaderObj.item + // Check repeater index of child object + compare(stackLayoutObj.children[0].index, 0) + compare(stackLayoutObj.children[1].index, 1) + compare(stackLayoutObj.children[2].index, 2) + // Check stack layout attached property index + compare(stackLayoutObj.children[0].StackLayout.index, 0) + compare(stackLayoutObj.children[1].StackLayout.index, 1) + compare(stackLayoutObj.children[2].StackLayout.index, 2) + } + + Component { + id: test_repeater_Component + + Item { + property alias stackLayout : stackLayout + property var model : ListModel { + /* + * We cannot programmatically reorder siblings (QQuickItem::stackBefore() + * and QQuickItem::stackAfter() are not not available to QML, and we cannot + * alter the Item::children property to reorder siblings) + * Therefore, we have to go through the hoops with a ListModel and Repeater in + * order to trigger sibling reordering, just as reported in QTBUG-112691. + * Adding an item to a specific index (with model.insert()), will be done in + * two steps: + * 1. Append an Item to be the last of the siblings + * 2. Reorder that Rectangle to be at the correct child index that corresponds + * to the index given to model.insert() + * + * Adding an item to a specific index will therefore test sibling reordering + */ + id: listModel + } + StackLayout { + id: stackLayout + anchors.fill: parent + Layout.fillHeight: true + Layout.fillWidth: true + Repeater { + id: repeater + model:listModel + delegate: Rectangle { + implicitWidth: 100 + implicitHeight: 100 + objectName: model.color + color: model.color + } + } + } + } + } + + function test_repeater() { + let item = createTemporaryObject(test_repeater_Component, container) + let layout = item.stackLayout + let model = item.model + function verifyVisibilityOfItems() { + for (let i = 0; i < layout.count; ++i) { + compare(layout.children[i].visible, layout.currentIndex === i) + } + } + + compare(layout.currentIndex, -1) + compare(layout.count, 0) + model.append({ "color": "red" }) + compare(layout.currentIndex, 0) + compare(layout.count, 1) + verifyVisibilityOfItems() + + model.append({ "color": "green" }) + compare(layout.currentIndex, 0) + compare(layout.count, 2) + verifyVisibilityOfItems() + + model.append({ "color": "blue" }) + compare(layout.currentIndex, 0) + compare(layout.count, 3) + verifyVisibilityOfItems() + + model.insert(0, { "color": "black" }) + compare(layout.currentIndex, 1) + compare(layout.count, 4) + verifyVisibilityOfItems() + + // An implicit currentIndex will reset back to -1 if + // the StackLayout is empty + model.clear() + compare(layout.currentIndex, -1) + compare(layout.count, 0) + + // set explicit index to out of bounds + layout.currentIndex = 1 + compare(layout.currentIndex, 1) + compare(layout.count, 0) + verifyVisibilityOfItems() + + model.append({ "color": "red" }) + compare(layout.currentIndex, 1) + compare(layout.count, 1) + verifyVisibilityOfItems() + + model.append({ "color": "green" }) + compare(layout.currentIndex, 1) + compare(layout.count, 2) + verifyVisibilityOfItems() + + model.insert(1, { "color": "brown" }) + compare(layout.currentIndex, 2) + compare(layout.count, 3) + verifyVisibilityOfItems() + + // remove red, currentIndex should decrease + model.remove(0, 1) + compare(layout.currentIndex, 1) + compare(layout.count, 2) + verifyVisibilityOfItems() + } } } |