qtdeclarative/tests/auto/quick/qquicklayouts/data/tst_rowlayout.qml

1635 lines
67 KiB
QML

// Copyright (C) 2020 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick 2.2
import QtTest 1.0
import QtQuick.Layouts 1.0
import "LayoutHelperLibrary.js" as LayoutHelpers
import org.qtproject.Test
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: layout_rowLayout_Component
RowLayout {
}
}
Component {
id: layout_columnLayout_Component
ColumnLayout {
}
}
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(".*: 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
waitForItemPolished(row)
compare(row.implicitWidth, 50 + 10 + 20)
var r3 = rectangle_Component.createObject(container)
r3.implicitWidth = 30
r3.parent = row
waitForItemPolished(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)
waitForItemPolished(layout)
compare(layout.implicitWidth, 20)
compare(layout.implicitHeight, 20)
var rect1 = layoutItem_Component.createObject(layout)
rect1.Layout.preferredWidth = 30;
rect1.Layout.preferredHeight = 30;
waitForItemPolished(layout)
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
waitForItemPolished(layout)
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
waitForItemPolished(layout)
col.destroy()
wait(0)
waitForItemPolished(layout)
compare(layout.implicitWidth, 50)
compare(layout.implicitHeight, 30)
rect0.destroy()
wait(0)
waitForItemPolished(layout)
compare(layout.implicitWidth, 30)
compare(layout.implicitHeight, 30)
rect1.destroy()
wait(0)
waitForItemPolished(layout)
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;
waitForItemPolished(layout)
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();
}
function buildLayout(layout, arrLayoutData) {
for (let i = 0; i < arrLayoutData.length; i++) {
let layoutItemDesc = arrLayoutData[i]
let rect = layoutItem_Component.createObject(layout)
for (let keyName in layoutItemDesc) {
rect.Layout[keyName] = layoutItemDesc[keyName]
}
}
}
function test_dynamicAlignment_data()
{
return [
{
tag: "simple",
layout: {
type: "RowLayout",
items: [
{preferredWidth: 30, preferredHeight: 20, fillHeight: true},
{preferredWidth: 30, preferredHeight: 20},
]
},
expectedGeometries: [
[ 0, 0, 30, 60],
[30, 20, 30, 20]
]
},{
tag: "valign",
layout: {
type: "RowLayout",
items: [
{preferredWidth: 12, preferredHeight: 20, fillHeight: true},
{preferredWidth: 12, preferredHeight: 20},
{preferredWidth: 12, preferredHeight: 20, alignment: Qt.AlignTop},
{preferredWidth: 12, preferredHeight: 20, alignment: Qt.AlignVCenter},
{preferredWidth: 12, preferredHeight: 20, alignment: Qt.AlignBottom}
]
},
expectedGeometries: [
[ 0, 0, 12, 60],
[12, 20, 12, 20],
[24, 0, 12, 20],
[36, 20, 12, 20],
[48, 40, 12, 20]
]
},{
tag: "halign",
layout: {
type: "ColumnLayout",
items: [
{preferredWidth: 20, preferredHeight: 12, fillWidth: true},
{preferredWidth: 20, preferredHeight: 12},
{preferredWidth: 20, preferredHeight: 12, alignment: Qt.AlignLeft},
{preferredWidth: 20, preferredHeight: 12, alignment: Qt.AlignHCenter},
{preferredWidth: 20, preferredHeight: 12, alignment: Qt.AlignRight}
]
},
expectedGeometries: [
[ 0, 0, 60, 12],
[ 0, 12, 20, 12],
[ 0, 24, 20, 12],
[20, 36, 20, 12],
[40, 48, 20, 12]
]
}
]
}
function test_dynamicAlignment(data)
{
let layout
switch (data.layout.type) {
case "RowLayout":
layout = createTemporaryObject(layout_rowLayout_Component, container)
break
case "ColumnLayout":
layout = createTemporaryObject(layout_columnLayout_Component, container)
break
default:
console.log("data.layout.type not recognized(" + data.layout.type + ")")
}
layout.spacing = 0
buildLayout(layout, data.layout.items)
layout.width = 60
layout.height = 60 // divides in 1/2/3/4/5/6
waitForItemPolished(layout)
for (let i = 0; i < layout.children.length; ++i) {
let itm = layout.children[i]
compare(itemRect(itm), data.expectedGeometries[i])
}
}
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];
waitForItemPolished(layout)
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]
waitForItemPolished(layout)
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
waitForItemPolished(layout)
compare(itemSizeHints(layout), [0, 2, 3])
child.Layout.preferredWidth = -1
waitForItemPolished(layout)
compare(itemSizeHints(layout), [0, 1, 3])
child.Layout.maximumWidth = -1
waitForItemPolished(layout)
compare(itemSizeHints(layout), [0, 1, Number.POSITIVE_INFINITY])
layout.Layout.maximumWidth = 1000
waitForItemPolished(layout)
compare(itemSizeHints(layout), [0, 1, 1000])
layout.Layout.maximumWidth = -1
waitForItemPolished(layout)
compare(itemSizeHints(layout), [0, 1, Number.POSITIVE_INFINITY])
layout.implicitWidthChangedCount = 0
child.Layout.minimumWidth = 10
waitForItemPolished(layout)
compare(itemSizeHints(layout), [10, 10, Number.POSITIVE_INFINITY])
compare(layout.implicitWidthChangedCount, 1)
child.Layout.preferredWidth = 20
waitForItemPolished(layout)
compare(itemSizeHints(layout), [10, 20, Number.POSITIVE_INFINITY])
compare(layout.implicitWidthChangedCount, 2)
child.Layout.maximumWidth = 30
waitForItemPolished(layout)
compare(itemSizeHints(layout), [10, 20, 30])
compare(layout.implicitWidthChangedCount, 2)
child.Layout.maximumWidth = 15
waitForItemPolished(layout)
compare(itemSizeHints(layout), [10, 15, 15])
compare(layout.implicitWidthChangedCount, 3)
child.Layout.maximumWidth = 30
waitForItemPolished(layout)
compare(itemSizeHints(layout), [10, 20, 30])
compare(layout.implicitWidthChangedCount, 4)
layout.Layout.maximumWidth = 29
waitForItemPolished(layout)
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
waitForItemPolished(layout)
compare(layout.implicitWidth, 20)
compare(layout.implicitHeight, 30)
layout.destroy();
}
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
waitForItemPolished(layout)
layout.width = 100
compare(r.width, data.expectedWidth)
layout.destroy();
}
function test_distribution_data()
{
return [
{
tag: "one",
layout: {
type: "RowLayout",
items: [
{minimumWidth: 1, preferredWidth: 10, maximumWidth: 20, fillWidth: true},
{minimumWidth: 1, preferredWidth: 4, maximumWidth: 10, fillWidth: true},
]
},
layoutWidth: 28,
expectedWidths: [20, 8]
},{
tag: "two",
layout: {
type: "RowLayout",
items: [
{minimumWidth: 1, preferredWidth: 10, horizontalStretchFactor: 4, fillWidth: true},
{minimumWidth: 1, preferredWidth: 4, horizontalStretchFactor: 1, fillWidth: true},
]
},
layoutWidth: 28,
expectedWidths: [22, 6]
},{
tag: "resize_to_0_width",
layout: {
type: "RowLayout",
items: [
{preferredWidth: 10, fillWidth: true},
]
},
layoutWidth: 0,
expectedWidths: [0]
}
];
}
function test_distribution(data)
{
var layout = layout_rowLayout_Component.createObject(container)
layout.spacing = 0
buildLayout(layout, data.layout.items)
waitForPolish(layout)
layout.width = data.layoutWidth
let actualWidths = []
for (let i = 0; i < layout.children.length; i++) {
actualWidths.push(layout.children[i].width)
}
compare(actualWidths, data.expectedWidths)
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: 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
waitForItemPolished(layout)
compare(grid.implicitWidth, 0);
child.visible = true
waitForItemPolished(layout)
compare(grid.implicitWidth, 2);
grid.visible = false // LAYOUT HIDDEN
waitForItemPolished(layout)
compare(grid.implicitWidth, 2);
child.visible = false
expectFail('', 'If GridLayout is hidden, GridLayout is not notified when child is explicitly hidden')
waitForItemPolished(grid)
compare(grid.implicitWidth, 0);
child.visible = true
waitForItemPolished(grid)
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)
}
Component {
id: layout_dependentWidth_QTBUG_87253_Component
RowLayout {
anchors.fill: parent;
RowLayout {
spacing: 10
Text {
id: btnOPE
text: qsTr("Ok")
Layout.fillWidth: true
Layout.preferredWidth: (parent.width - 20) / 2
}
Text {
id: btnSeeChanged
text: qsTr("Not Ok");
Layout.fillWidth: true
Layout.preferredWidth: (parent.width - 20) / 2
}
}
}
}
function test_dependentWidth_QTBUG_87253()
{
var layout = createTemporaryObject(layout_dependentWidth_QTBUG_87253_Component, container)
// Do not crash
waitForRendering(layout)
}
//---------------------------
Component {
id: rowlayoutWithRectangle_Component
RowLayout {
property alias spy : signalSpy
Rectangle {
color: "red"
implicitWidth: 10
implicitHeight: 10
}
SignalSpy {
id: signalSpy
target: parent
signalName: "implicitWidthChanged"
}
}
}
// QTBUG-93988
function test_ensurePolished() {
var layout = createTemporaryObject(rowlayoutWithRectangle_Component, container)
compare(layout.spy.count, 1)
waitForRendering(layout)
compare(layout.implicitWidth, 10)
var r0 = layout.children[0]
r0.implicitWidth = 42
compare(layout.spy.count, 1) // Not yet updated, awaiting PolishEvent...
layout.ensurePolished()
compare(layout.spy.count, 2)
compare(layout.implicitWidth, 42)
}
//---------------------------
Component {
id: rowlayoutCausesBindingLoop_Component
Item {
id: root
width: 100
height: 100
property real maxWidth : Math.max(header.implicitWidth, content.implicitWidth)
RowLayout {
id: header
y: 0
Rectangle {
color: "red"
implicitWidth: 10
implicitHeight: 10
}
}
Rectangle {
id: content
y: 10
implicitWidth: 42
implicitHeight: 10
color: Qt.rgba(root.maxWidth/66, 0, 1, 1)
}
}
}
function test_bindingLoop() {
var rootItem = createTemporaryObject(rowlayoutCausesBindingLoop_Component, container)
waitForRendering(rootItem)
var header = rootItem.children[0]
var content = rootItem.children[1]
var rect = header.children[0]
rect.implicitWidth = 20
content.implicitWidth = 66
waitForItemPolished(header)
compare(rootItem.maxWidth, 66)
// Should not trigger a binding loop
verify(!BindingLoopDetector.bindingLoopDetected, "Detected binding loop")
BindingLoopDetector.reset()
}
//---------------------------
// 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)
}
}
}