blob: 85fe54eca618582c29c0b69dd0266389db0f8b72 [file] [log] [blame]
/****************************************************************************
**
** Copyright (C) 2020 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];
}
Component {
id: rectangle_Component
Rectangle {
width: 100
height: 50
}
}
Component {
id: itemsWithAnchorsLayout_Component
RowLayout {
spacing: 2
Item {
anchors.fill: parent
implicitWidth: 10
implicitHeight: 10
}
Item {
anchors.centerIn: parent
implicitWidth: 10
implicitHeight: 10
}
Item {
anchors.left: parent.left
implicitWidth: 10
implicitHeight: 10
}
Item {
anchors.right: parent.right
implicitWidth: 10
implicitHeight: 10
}
Item {
anchors.top: parent.top
implicitWidth: 10
implicitHeight: 10
}
Item {
anchors.bottom: parent.bottom
implicitWidth: 10
implicitHeight: 10
}
Item {
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
implicitWidth: 10
implicitHeight: 10
}
Item {
anchors.margins: 42 // although silly, it should not cause a warning from the Layouts POV
implicitWidth: 10
implicitHeight: 10
}
}
}
function test_warnAboutLayoutItemsWithAnchors()
{
var regex = new RegExp("QML Item: Detected anchors on an item that is managed by a layout. "
+ "This is undefined behavior; use Layout.alignment instead.")
for (var i = 0; i < 7; ++i) {
ignoreWarning(regex)
}
var layout = itemsWithAnchorsLayout_Component.createObject(container)
waitForRendering(layout)
layout.destroy()
}
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, '');
waitForRendering(tmp)
compare(tmp.implicitWidth, 15);
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()
}
Component {
id: propagateImplicitWidthToParent_Component
Item {
width: 200
height: 20
// These might trigger a updateLayoutItems() before its component is completed...
implicitWidth: row.implicitWidth
implicitHeight: row.implicitHeight
RowLayout {
id : row
anchors.fill: parent
property alias r1: _r1
property alias r2: _r2
spacing: 0
Rectangle {
id: _r1
color: "red"
implicitWidth: 50
implicitHeight: 20
}
Rectangle {
id: _r2
color: "green"
implicitWidth: 50
implicitHeight: 20
Layout.fillWidth: true
}
}
}
}
function test_propagateImplicitWidthToParent() {
var item = createTemporaryObject(propagateImplicitWidthToParent_Component, container)
var row = item.children[0]
compare(row.width, 200)
compare(itemRect(row.r1), [0, 0, 50, 20])
compare(itemRect(row.r2), [50, 0, 150, 20])
}
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);
var r2 = row.children[2]
r2.implicitWidth = 20
verify(waitForRendering(row))
compare(row.implicitWidth, 50 + 10 + 20)
var r3 = rectangle_Component.createObject(container)
r3.implicitWidth = 30
r3.parent = row
compare(row.implicitWidth, 50 + 10 + 20 + 30)
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()
}
function test_dynamicSizeAdaptationsForInitiallyInvisibleItemsInLayout() {
var test_layoutStr =
'import QtQuick 2.2; \
import QtQuick.Layouts 1.0; \
RowLayout { \
id: row; \
width: 10; \
spacing: 0; \
property alias r1: _r1; \
Rectangle { \
id: _r1; \
visible: false; \
height: 10; \
Layout.fillWidth: true; \
color: "#8080ff"; \
} \
property alias r2: _r2; \
Rectangle { \
id: _r2; \
height: 10; \
Layout.fillWidth: true; \
color: "#c0c0ff"; \
} \
} '
var lay = Qt.createQmlObject(test_layoutStr, container, '');
compare(lay.r1.width, 0)
compare(lay.r2.width, 10)
lay.r1.visible = true;
waitForRendering(lay)
compare(lay.r1.width, 5)
compare(lay.r2.width, 5)
lay.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 = createTemporaryObject(layout_addAndRemoveItems_Component, 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)
}
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"
implicitWidth: 1
implicitHeight: 1
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]},
/**
* Test how fractional size hint values are rounded. Some hints are ceiled towards the closest integer.
* Note some of these tests are not authorative, but are here to demonstrate current behavior.
* To summarize, it seems to be:
* - min: always ceiled
* - pref: Ceils only implicit (!) hints. Might also be ceiled if explicit
preferred size is less than implicit minimum size, but that's just a
side-effect of that preferred should never be less than minimum.
(tag "ceilShrinkMinToPref" below)
* - max: never ceiled
*/
{ tag: "ceilImplicitMin", layoutHints: [ -1, -1, -1], childHints: [ .1, 1.1, 9.1], expected:[ 1, 2, 9.1]},
{ tag: "ceilExplicitMin", layoutHints: [1.1, -1, -1], childHints: [ .1, 2.1, 9.1], expected:[ 2, 3, 9.1]},
{ tag: "ceilImplicitMin2", layoutHints: [ -1, 4.1, -1], childHints: [ .1, 1.1, 9.1], expected:[ 1, 4.1, 9.1]},
{ tag: "ceilShrinkMinToPref", layoutHints: [ -1, 2.1, -1], childHints: [ 5, 6.1, 8.1], expected:[ 3, 3, 8.1]},
{ tag: "ceilExpandMaxToPref", layoutHints: [ -1, 6.1, -1], childHints: [1.1, 3.1, 3.1], expected:[ 2, 6.1, 6.1]},
];
}
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, 1, 3])
child.Layout.maximumWidth = -1
compare(itemSizeHints(layout), [0, 1, Number.POSITIVE_INFINITY])
layout.Layout.maximumWidth = 1000
compare(itemSizeHints(layout), [0, 1, 1000])
layout.Layout.maximumWidth = -1
compare(itemSizeHints(layout), [0, 1, 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
}
}
function test_distributeToPixelGrid_data() {
return [
{ tag: "narrow", spacing: 0, width: 60, hints: [{pref: 50}, {pref: 20}, {pref: 70}] },
{ tag: "belowPreferred", spacing: 0, width: 130, hints: [{pref: 50}, {pref: 20}, {pref: 70}]},
{ tag: "belowPreferredWithSpacing", spacing: 10, width: 130, hints: [{pref: 50}, {pref: 20}, {pref: 70}]},
{ tag: "abovePreferred", spacing: 0, width: 150, hints: [{pref: 50}, {pref: 20}, {pref: 70}]},
{ tag: "stretchSomethingToMaximum", spacing: 0, width: 240, hints: [{pref: 50}, {pref: 20}, {pref: 70}],
expected: [90, 60, 90] },
{ tag: "minSizeHasFractions", spacing: 2, width: 33 + 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 */
{ tag: "481", spacing: 0, width: 481,
hints: [{min:0, pref:0, max:999}, {min:0, pref:0, max: 999}, {min: 0, pref: 0, max:0}],
expected: [241, 240, 0] },
{ tag: "theend", spacing: 1, width: 18,
hints: [{min: 10, pref: 10, max:10}, {min:3, pref:3.33}, {min:2, pref:2.33}],
expected: [10, 4, 2] },
{ tag: "theend2", spacing: 1, width: 18,
hints: [{min: 10, pref: 10, max:10}, {min:3, pref:3.33}, {min:2.33, pref:2.33}],
expected: [10, 3, 3] },
{ tag: "43", spacing: 0, width: 43,
hints: [{min: 10, pref: 10, max:10}, {min:10, pref:30.33}, {min:2.33, pref:2.33}],
expected: [10, 30, 3] },
{ tag: "40", spacing: 0, width: 40,
hints: [{min: 10, pref: 10, max:10}, {min:10, pref:30.33}, {min:2.33, pref:2.33}],
expected: [10, 27, 3] },
{ tag: "roundingAccumulates1", spacing: 0, width: 50,
hints: [{pref: 10, max:30.3},
{min:2.3, pref:2.3}, {min:2.3, pref:2.3}, {min:2.3, pref:2.3}, {min:2.3, pref:2.3}, {min:2.3, pref:2.3},
{min:2.3, pref:2.3}, {min:2.3, pref:2.3}, {min:2.3, pref:2.3}, {min:2.3, pref:2.3}, {min:2.3, pref:2.3},
{pref: 10, max:30.3}],
expected: [10, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 10] },
{ tag: "roundingAccumulates2", spacing: 0, width: 60,
hints: [{pref: 20, max:30.3},
{min:2.3, pref:2.3}, {min:2.3, pref:2.3}, {min:2.3, pref:2.3}, {min:2.3, pref:2.3}, {min:2.3, pref:2.3},
{min:2.3, pref:2.3}, {min:2.3, pref:2.3}, {min:2.3, pref:2.3}, {min:2.3, pref:2.3}, {min:2.3, pref:2.3},
{pref: 20, max:30.3}],
expected: [15, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 15] },
];
}
function test_distributeToPixelGrid(data)
{
// CONFIGURATION
var layout = test_distributeToPixelGrid_Component.createObject(container)
layout.spacing = data.spacing
layout.width = data.width
layout.height = 10
var hints = data.hints
var i;
var n = hints.length
for (i = 0; i < n; ++i) {
var rect = layoutItem_Component.createObject(layout)
rect.Layout.fillWidth = true
var h = hints[i]
rect.Layout.minimumWidth = h.hasOwnProperty('min') ? h.min : 10
if (h.hasOwnProperty('pref'))
rect.Layout.preferredWidth = h.pref
rect.Layout.maximumWidth = h.hasOwnProperty('max') ? h.max : 90
}
var kids = layout.children
waitForRendering(layout)
var sum = (n - 1) * layout.spacing
// TEST
for (i = 0; i < n; ++i) {
compare(kids[i].x % 1, 0) // checks if position is a whole integer
// check if width is a whole integer (unless there are constraints preventing it from stretching)
verify(kids[i].width % 1 == 0
|| Math.floor(kids[i].Layout.maximumWidth) < kids[i].width
|| layout.width < layout.Layout.maximumWidth + 1)
// 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_destroyImplicitInvisibleLayout()
{
var root = rectangle_Component.createObject(container)
root.visible = false
var layout = layout_deleteLayout.createObject(root)
layout.visible = true
// at this point the layout is still invisible because root is invisible
layout.destroy()
// Do not crash when destructing the layout
waitForRendering(container) // should ideally call gc(), but does not work
root.destroy()
}
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)
wait(0); // Trigger processEvents() (allow LayoutRequest to be processed)
compare(itemRect(fixed), [0,0,100,20])
compare(itemRect(filler), [100,0,100,20])
}
Component {
id: rearrangeFixedSizeLayout_Component
RowLayout {
id: layout
width: 200
height: 20
spacing: 0
RowLayout {
id: row
spacing: 0
Rectangle {
id: r0
color: 'red'
implicitWidth: 20
implicitHeight: 20
}
Rectangle {
id: r1
color: 'grey'
implicitWidth: 80
implicitHeight: 20
}
}
ColumnLayout {
id: row2
spacing: 0
Rectangle {
id: r2_0
color: 'blue'
Layout.fillWidth: true
implicitWidth: 100
implicitHeight: 20
}
}
}
}
function test_rearrangeFixedSizeLayout()
{
var layout = createTemporaryObject(rearrangeFixedSizeLayout_Component, testCase)
var row = layout.children[0]
var r0 = row.children[0]
var r1 = row.children[1]
waitForRendering(layout)
compare(itemRect(r0), [0,0,20,20])
compare(itemRect(r1), [20,0,80,20])
// just swap their widths. The layout should keep the same size
r0.implicitWidth = 80
r1.implicitWidth = 20
waitForRendering(layout)
// even if the layout did not change size, it should rearrange its children
compare(itemRect(row), [0,0, 100, 20])
compare(itemRect(r0), [0,0,80,20])
compare(itemRect(r1), [80,0,20,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()
}
function test_defaultPropertyAliasCrash() {
var containerUserComponent = Qt.createComponent("rowlayout/ContainerUser.qml");
compare(containerUserComponent.status, Component.Ready);
var containerUser = containerUserComponent.createObject(testCase);
verify(containerUser);
// Shouldn't crash.
containerUser.destroy();
}
function test_defaultPropertyAliasCrashAgain() {
var containerUserComponent = Qt.createComponent("rowlayout/ContainerUser2.qml");
compare(containerUserComponent.status, Component.Ready);
var containerUser = createTemporaryObject(containerUserComponent, testCase);
verify(containerUser);
// Shouldn't crash upon destroying containerUser.
}
/*
Tests that a layout-managed item that sets layer.enabled to true
still renders something. This is a simpler test case that only
reproduces the issue when the layout that manages it is made visible
after component completion, but QTBUG-63269 has a more complex example
where this (setting visible to true afterwards) isn't necessary.
*/
function test_layerEnabled() {
var component = Qt.createComponent("rowlayout/LayerEnabled.qml");
compare(component.status, Component.Ready);
var rootRect = createTemporaryObject(component, container);
verify(rootRect);
rootRect.layout.visible = true;
waitForRendering(rootRect.layout)
compare(rootRect.item1.width, 100)
}
//---------------------------
Component {
id: rowlayoutWithTextItems_Component
RowLayout {
Text {
Layout.fillWidth: true
text: "OneWord"
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
}
Text {
Layout.fillWidth: true
text: "OneWord"
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
}
}
}
// QTBUG-73683
function test_rowlayoutWithTextItems() {
var layout = createTemporaryObject(rowlayoutWithTextItems_Component, container)
waitForRendering(layout)
layout.width = layout.width - 2 // set the size to be smaller than its "minimum size"
waitForRendering(layout) // do not exit before all warnings have been received
// DO NOT CRASH due to stack overflow (or loop endlessly due to updatePolish()/polish() loop)
}
}
}