/**************************************************************************** ** ** 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$ ** ****************************************************************************/ import QtQuick 2.2 import QtTest 1.0 import QtQuick.Layouts 1.3 Item { id: container width: 200 height: 200 TestCase { id: testCase name: "Tests_StackLayout" when: windowShown width: 200 height: 200 function geometry(item) { return [item.x, item.y, item.width, item.height] } Component { id: countGeometryChanges_Component StackLayout { id: stack property alias col: _col property alias row: _row width: 100 ColumnLayout { id: _col property alias r1: _r1 property alias r2: _r2 property alias r3: _r3 spacing: 0 property int counter : 0 onWidthChanged: { ++counter; } Rectangle { id: _r1 implicitWidth: 20 implicitHeight: 20 Layout.fillWidth: true property int counter : 0 onWidthChanged: { ++counter; } } Rectangle { id: _r2 implicitWidth: 50 implicitHeight: 50 Layout.fillWidth: true property int counter : 0 onWidthChanged: { ++counter; } } Rectangle { id: _r3 implicitWidth: 40 implicitHeight: 40 Layout.fillWidth: true property int counter : 0 onWidthChanged: { ++counter; } } } RowLayout { id: _row property alias r5: _r5 spacing: 0 property int counter : 0 onWidthChanged: { ++counter; } Rectangle { id: _r5 implicitWidth: 100 implicitHeight: 100 Layout.fillWidth: true property int counter : 0 onWidthChanged: { ++counter; } } } } } function test_countGeometryChanges() { var stack = countGeometryChanges_Component.createObject(container) compare(stack.currentIndex, 0) compare(stack.col.width, 100) compare(stack.col.height, 110) compare(stack.row.width, 100) compare(stack.row.height, 100) verify(stack.col.r1.counter <= 2) compare(stack.col.r2.counter, 1) verify(stack.col.r3.counter <= 2) verify(stack.col.counter <= 2) compare(stack.row.counter, 1) // not visible, will only receive the initial geometry change compare(stack.row.r5.counter, 0) stack.destroy() } Component { id: layoutItem_Component Rectangle { implicitWidth: 20 implicitHeight: 20 } } Component { id: emtpy_StackLayout_Component StackLayout { property int num_onCountChanged: 0 property int num_onCurrentIndexChanged: 0 onCountChanged: { ++num_onCountChanged; } onCurrentIndexChanged: { ++num_onCurrentIndexChanged; } } } function test_addAndRemoveItems() { var stack = emtpy_StackLayout_Component.createObject(container) stack.currentIndex = 2 compare(stack.implicitWidth, 0) compare(stack.implicitHeight, 0) var rect0 = layoutItem_Component.createObject(stack) compare(stack.implicitWidth, 20) compare(stack.implicitHeight, 20) compare(rect0.visible, false) var rect1 = layoutItem_Component.createObject(stack) rect1.Layout.preferredWidth = 30 rect1.Layout.preferredHeight = 10 compare(stack.implicitWidth, 30) compare(stack.implicitHeight, 20) compare(rect0.visible, false) compare(rect1.visible, false) var rect2 = layoutItem_Component.createObject(stack) rect2.x = 42 // ### items in a stacklayout will have their x and y positions discarded. rect2.y = 42 rect2.Layout.preferredWidth = 80 rect2.Layout.preferredHeight = 30 rect2.Layout.fillWidth = true compare(stack.implicitWidth, 80) compare(stack.implicitHeight, 30) compare(rect0.visible, false) compare(rect1.visible, false) compare(rect2.visible, true) compare(geometry(rect2), geometry(stack)) rect2.destroy() wait(0) // this will hopefully effectuate the destruction of the object compare(stack.implicitWidth, 30) compare(stack.implicitHeight, 20) rect0.destroy() wait(0) compare(stack.implicitWidth, 30) compare(stack.implicitHeight, 10) rect1.destroy() wait(0) compare(stack.implicitWidth, 0) compare(stack.implicitHeight, 0) stack.destroy() } function test_sizeHint_data() { return [ { tag: "propagateNone", layoutHints: [10, 20, 30], childHints: [11, 21, 31], expected:[10, 20, Number.POSITIVE_INFINITY]}, { tag: "propagateMinimumWidth", layoutHints: [-1, 20, 30], childHints: [10, 21, 31], expected:[10, 20, Number.POSITIVE_INFINITY]}, { tag: "propagatePreferredWidth", layoutHints: [10, -1, 30], childHints: [11, 20, 31], expected:[10, 20, Number.POSITIVE_INFINITY]}, { tag: "propagateMaximumWidth", layoutHints: [10, 20, -1], childHints: [11, 21, 30], expected:[10, 20, Number.POSITIVE_INFINITY]}, { tag: "propagateAll", layoutHints: [-1, -1, -1], childHints: [10, 20, 30], expected:[10, 20, Number.POSITIVE_INFINITY]}, { tag: "propagateCrazy", layoutHints: [-1, -1, -1], childHints: [40, 21, 30], expected:[30, 30, Number.POSITIVE_INFINITY]}, { tag: "expandMinToExplicitPref", layoutHints: [-1, 1, -1], childHints: [11, 21, 31], expected:[ 1, 1, Number.POSITIVE_INFINITY]}, { tag: "expandMaxToExplicitPref", layoutHints: [-1, 99, -1], childHints: [11, 21, 31], expected:[11, 99, Number.POSITIVE_INFINITY]}, { tag: "expandAllToExplicitMin", layoutHints: [99, -1, -1], childHints: [11, 21, 31], expected:[99, 99, Number.POSITIVE_INFINITY]}, { tag: "expandPrefToExplicitMin", layoutHints: [24, -1, -1], childHints: [11, 21, 31], expected:[24, 24, Number.POSITIVE_INFINITY]}, { tag: "boundPrefToExplicitMax", layoutHints: [-1, -1, 19], childHints: [11, 21, 31], expected:[11, 19, Number.POSITIVE_INFINITY]}, { tag: "boundAllToExplicitMax", layoutHints: [-1, -1, 9], childHints: [11, 21, 31], expected:[ 9, 9, Number.POSITIVE_INFINITY]}, ]; } function itemSizeHints(item) { return [item.Layout.minimumWidth, item.implicitWidth, item.Layout.maximumWidth] } Component { id: stacklayout_sizeHint_Component StackLayout { property int implicitWidthChangedCount : 0 onImplicitWidthChanged: { ++implicitWidthChangedCount } ColumnLayout { 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) { var layout = stacklayout_sizeHint_Component.createObject(container) var col = layout.children[0] col.Layout.minimumWidth = data.layoutHints[0] col.Layout.preferredWidth = data.layoutHints[1] col.Layout.maximumWidth = data.layoutHints[2] var child = col.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() } Component { id: stacklayout_addIgnoredItem_Component StackLayout { Repeater { id: rep model: 1 Rectangle { id: r } } } } // Items with no size information is ignored. function test_addIgnoredItem() { var stack = stacklayout_addIgnoredItem_Component.createObject(container) compare(stack.count, 1) compare(stack.implicitWidth, 0) compare(stack.implicitHeight, 0) var r = stack.children[0] r.Layout.preferredWidth = 20 r.Layout.preferredHeight = 30 compare(stack.count, 1) compare(stack.implicitWidth, 20) compare(stack.implicitHeight, 30) stack.destroy(); } function test_dontCrashWhenAnchoredToAWindow() { var test_layoutStr = 'import QtQuick 2.2; \ import QtQuick.Window 2.1; \ import QtQuick.Layouts 1.3; \ Window { \ visible: true; \ width: stack.implicitWidth; \ height: stack.implicitHeight; \ StackLayout { \ id: stack; \ currentIndex: 0; \ anchors.fill: parent; \ Rectangle { \ color: "red"; \ implicitWidth: 300; \ implicitHeight: 200; \ } \ } \ } ' var lay = Qt.createQmlObject(test_layoutStr, container, ''); tryCompare(lay, 'width', 300); lay.destroy() } Component { id: test_dontCrashWhenChildIsResizedToNull_Component StackLayout { property alias rect : _rect Rectangle { id: _rect; color: "red" implicitWidth: 200 implicitHeight: 200 } } } function test_dontCrashWhenChildIsResizedToNull() { var layout = test_dontCrashWhenChildIsResizedToNull_Component.createObject(container) layout.rect.width = 0 layout.width = 222 // trigger a rearrange with a valid size layout.height = 222 } Component { id: test_currentIndex_Component StackLayout { currentIndex: 1 Text { text: "0" } Text { text: "1" } } } function test_currentIndex() { var layout = test_currentIndex_Component.createObject(container) var c0 = layout.children[0] var c1 = layout.children[1] compare(layout.currentIndex, 1) tryCompare(layout, 'visible', true) compare(c0.visible, false) compare(c1.visible, true) layout.currentIndex = 0 compare(c0.visible, true) compare(c1.visible, false) var c2 = layoutItem_Component.createObject(layout) compare(c2.visible, false) /* * destroy the current item and check if visibility advances to next */ c0.destroy() tryCompare(c1, 'visible', true) compare(c2.visible, false) c1.destroy() tryCompare(c2, 'visible', true) c2.destroy() tryCompare(layout, 'currentIndex', 0) layout.destroy() /* * Test the default/implicit value of currentIndex, either -1 (if empty) or 0: */ layout = emtpy_StackLayout_Component.createObject(container) tryCompare(layout, 'visible', true) compare(layout.currentIndex, -1) compare(layout.num_onCurrentIndexChanged, 0) // make it non-empty c0 = layoutItem_Component.createObject(layout) compare(layout.currentIndex, 0) compare(layout.num_onCurrentIndexChanged, 1) compare(c0.visible, true) // make it empty again c0.destroy() wait(0) compare(layout.currentIndex, -1) //tryCompare(layout, 'currentIndex', -1) compare(layout.num_onCurrentIndexChanged, 2) /* * Check that explicit value doesn't change, * and that no items are visible if the index is invalid/out of range */ layout.currentIndex = 2 compare(layout.currentIndex, 2) compare(layout.num_onCurrentIndexChanged, 3) c0 = layoutItem_Component.createObject(layout) compare(layout.currentIndex, 2) compare(c0.visible, false) c1 = layoutItem_Component.createObject(layout) compare(layout.currentIndex, 2) compare(c0.visible, false) compare(c1.visible, false) c2 = layoutItem_Component.createObject(layout) compare(layout.currentIndex, 2) compare(c0.visible, false) compare(c1.visible, false) compare(c2.visible, true) c2.destroy() wait(0) compare(layout.currentIndex, 2) compare(c0.visible, false) compare(c1.visible, false) c1.destroy() wait(0) compare(layout.currentIndex, 2) compare(c0.visible, false) c0.destroy() wait(0) compare(layout.currentIndex, 2) compare(layout.num_onCurrentIndexChanged, 3) } function test_count() { var layout = emtpy_StackLayout_Component.createObject(container) tryCompare(layout, 'visible', true) compare(layout.count, 0) compare(layout.currentIndex, -1) compare(layout.num_onCountChanged, 0) compare(layout.num_onCurrentIndexChanged, 0) var c0 = layoutItem_Component.createObject(layout) compare(layout.count, 1) compare(layout.currentIndex, 0) compare(layout.num_onCurrentIndexChanged, 1) compare(layout.num_onCountChanged, 1) } } }