2022-05-13 13:12:05 +00:00
|
|
|
// Copyright (C) 2017 The Qt Company Ltd.
|
|
|
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
2015-01-30 19:57:40 +00:00
|
|
|
|
2020-03-26 16:01:51 +00:00
|
|
|
import QtQuick
|
|
|
|
import QtTest
|
|
|
|
import QtQuick.Controls
|
2022-08-30 08:02:40 +00:00
|
|
|
import Qt.test.controls
|
2015-01-30 19:57:40 +00:00
|
|
|
|
|
|
|
TestCase {
|
|
|
|
id: testCase
|
|
|
|
width: 200
|
|
|
|
height: 200
|
|
|
|
visible: true
|
|
|
|
when: windowShown
|
|
|
|
name: "StackView"
|
|
|
|
|
2015-03-28 20:51:10 +00:00
|
|
|
Item { id: item }
|
2018-02-12 21:37:35 +00:00
|
|
|
Component { id: textField; TextField { } }
|
2015-01-30 19:57:40 +00:00
|
|
|
Component { id: component; Item { } }
|
|
|
|
|
|
|
|
Component {
|
|
|
|
id: stackView
|
|
|
|
StackView { }
|
|
|
|
}
|
|
|
|
|
2016-05-27 07:51:35 +00:00
|
|
|
Component {
|
|
|
|
id: signalSpy
|
|
|
|
SignalSpy { }
|
|
|
|
}
|
|
|
|
|
2020-05-18 09:11:07 +00:00
|
|
|
Component { id: withRequired; Item { required property int i }}
|
|
|
|
|
2022-11-25 08:11:57 +00:00
|
|
|
function test_defaults() {
|
|
|
|
failOnWarning(/.?/)
|
|
|
|
|
|
|
|
let control = createTemporaryObject(stackView, testCase)
|
|
|
|
verify(control)
|
|
|
|
}
|
|
|
|
|
2015-01-30 19:57:40 +00:00
|
|
|
function test_initialItem() {
|
2016-12-20 09:49:23 +00:00
|
|
|
var control1 = createTemporaryObject(stackView, testCase)
|
2015-06-29 17:14:55 +00:00
|
|
|
verify(control1)
|
|
|
|
compare(control1.currentItem, null)
|
2015-01-30 19:57:40 +00:00
|
|
|
control1.destroy()
|
|
|
|
|
2016-12-20 09:49:23 +00:00
|
|
|
var control2 = createTemporaryObject(stackView, testCase, {initialItem: item})
|
2015-06-29 17:14:55 +00:00
|
|
|
verify(control2)
|
|
|
|
compare(control2.currentItem, item)
|
2015-01-30 19:57:40 +00:00
|
|
|
control2.destroy()
|
2015-06-29 17:14:55 +00:00
|
|
|
|
2016-12-20 09:49:23 +00:00
|
|
|
var control3 = createTemporaryObject(stackView, testCase, {initialItem: component})
|
2015-06-29 17:14:55 +00:00
|
|
|
verify(control3)
|
|
|
|
verify(control3.currentItem)
|
|
|
|
control3.destroy()
|
2015-01-30 19:57:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function test_currentItem() {
|
2016-12-20 09:49:23 +00:00
|
|
|
var control = createTemporaryObject(stackView, testCase, {initialItem: item})
|
2015-08-28 12:13:02 +00:00
|
|
|
verify(control)
|
2015-01-30 19:57:40 +00:00
|
|
|
compare(control.currentItem, item)
|
|
|
|
control.push(component)
|
|
|
|
verify(control.currentItem !== item)
|
2015-09-14 14:27:24 +00:00
|
|
|
control.pop(StackView.Immediate)
|
2015-01-30 19:57:40 +00:00
|
|
|
compare(control.currentItem, item)
|
|
|
|
}
|
|
|
|
|
|
|
|
function test_busy() {
|
2016-12-20 09:49:23 +00:00
|
|
|
var control = createTemporaryObject(stackView, testCase)
|
2015-08-28 12:13:02 +00:00
|
|
|
verify(control)
|
2015-01-30 19:57:40 +00:00
|
|
|
compare(control.busy, false)
|
2015-10-29 14:50:39 +00:00
|
|
|
|
|
|
|
var busyCount = 0
|
2016-10-07 12:51:51 +00:00
|
|
|
var busySpy = signalSpy.createObject(control, {target: control, signalName: "busyChanged"})
|
2015-10-29 14:50:39 +00:00
|
|
|
verify(busySpy.valid)
|
|
|
|
|
2015-01-30 19:57:40 +00:00
|
|
|
control.push(component)
|
|
|
|
compare(control.busy, false)
|
2015-10-29 14:50:39 +00:00
|
|
|
compare(busySpy.count, busyCount)
|
|
|
|
|
2015-01-30 19:57:40 +00:00
|
|
|
control.push(component)
|
|
|
|
compare(control.busy, true)
|
2015-10-29 14:50:39 +00:00
|
|
|
compare(busySpy.count, ++busyCount)
|
|
|
|
tryCompare(control, "busy", false)
|
|
|
|
compare(busySpy.count, ++busyCount)
|
|
|
|
|
|
|
|
control.replace(component)
|
|
|
|
compare(control.busy, true)
|
|
|
|
compare(busySpy.count, ++busyCount)
|
2015-01-30 19:57:40 +00:00
|
|
|
tryCompare(control, "busy", false)
|
2015-10-29 14:50:39 +00:00
|
|
|
compare(busySpy.count, ++busyCount)
|
|
|
|
|
2015-01-30 19:57:40 +00:00
|
|
|
control.pop()
|
|
|
|
compare(control.busy, true)
|
2015-10-29 14:50:39 +00:00
|
|
|
compare(busySpy.count, ++busyCount)
|
2015-01-30 19:57:40 +00:00
|
|
|
tryCompare(control, "busy", false)
|
2015-10-29 14:50:39 +00:00
|
|
|
compare(busySpy.count, ++busyCount)
|
|
|
|
|
|
|
|
control.pushEnter = null
|
|
|
|
control.pushExit = null
|
|
|
|
|
|
|
|
control.push(component)
|
|
|
|
compare(control.busy, false)
|
|
|
|
compare(busySpy.count, busyCount)
|
|
|
|
|
|
|
|
control.replaceEnter = null
|
|
|
|
control.replaceExit = null
|
|
|
|
|
|
|
|
control.replace(component)
|
|
|
|
compare(control.busy, false)
|
|
|
|
compare(busySpy.count, busyCount)
|
|
|
|
|
|
|
|
control.popEnter = null
|
|
|
|
control.popExit = null
|
|
|
|
|
|
|
|
control.pop()
|
|
|
|
compare(control.busy, false)
|
|
|
|
compare(busySpy.count, busyCount)
|
2015-01-30 19:57:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function test_status() {
|
2016-12-20 09:49:23 +00:00
|
|
|
var control = createTemporaryObject(stackView, testCase)
|
2015-08-28 12:13:02 +00:00
|
|
|
verify(control)
|
2015-01-30 19:57:40 +00:00
|
|
|
|
|
|
|
var item1 = component.createObject(control)
|
2015-09-14 21:53:12 +00:00
|
|
|
compare(item1.StackView.status, StackView.Inactive)
|
2015-01-30 19:57:40 +00:00
|
|
|
control.push(item1)
|
2015-09-14 21:53:12 +00:00
|
|
|
compare(item1.StackView.status, StackView.Active)
|
2015-01-30 19:57:40 +00:00
|
|
|
|
|
|
|
var item2 = component.createObject(control)
|
2015-09-14 21:53:12 +00:00
|
|
|
compare(item2.StackView.status, StackView.Inactive)
|
2015-01-30 19:57:40 +00:00
|
|
|
control.push(item2)
|
2015-09-14 21:53:12 +00:00
|
|
|
compare(item2.StackView.status, StackView.Activating)
|
|
|
|
compare(item1.StackView.status, StackView.Deactivating)
|
|
|
|
tryCompare(item2.StackView, "status", StackView.Active)
|
|
|
|
tryCompare(item1.StackView, "status", StackView.Inactive)
|
2015-01-30 19:57:40 +00:00
|
|
|
|
|
|
|
control.pop()
|
2015-09-14 21:53:12 +00:00
|
|
|
compare(item2.StackView.status, StackView.Deactivating)
|
|
|
|
compare(item1.StackView.status, StackView.Activating)
|
|
|
|
tryCompare(item2.StackView, "status", StackView.Inactive)
|
|
|
|
tryCompare(item1.StackView, "status", StackView.Active)
|
2015-03-28 20:51:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function test_index() {
|
2016-12-20 09:49:23 +00:00
|
|
|
var control = createTemporaryObject(stackView, testCase)
|
2015-08-28 12:13:02 +00:00
|
|
|
verify(control)
|
2015-03-28 20:51:10 +00:00
|
|
|
|
|
|
|
var item1 = component.createObject(control)
|
2015-09-14 21:53:12 +00:00
|
|
|
compare(item1.StackView.index, -1)
|
2015-09-14 14:27:24 +00:00
|
|
|
control.push(item1, StackView.Immediate)
|
2015-09-14 21:53:12 +00:00
|
|
|
compare(item1.StackView.index, 0)
|
2015-03-28 20:51:10 +00:00
|
|
|
|
|
|
|
var item2 = component.createObject(control)
|
2015-09-14 21:53:12 +00:00
|
|
|
compare(item2.StackView.index, -1)
|
2015-09-14 14:27:24 +00:00
|
|
|
control.push(item2, StackView.Immediate)
|
2015-09-14 21:53:12 +00:00
|
|
|
compare(item2.StackView.index, 1)
|
|
|
|
compare(item1.StackView.index, 0)
|
2015-03-28 20:51:10 +00:00
|
|
|
|
2015-09-14 14:27:24 +00:00
|
|
|
control.pop(StackView.Immediate)
|
2015-09-14 21:53:12 +00:00
|
|
|
compare(item2.StackView.index, -1)
|
|
|
|
compare(item1.StackView.index, 0)
|
2015-01-30 19:57:40 +00:00
|
|
|
}
|
|
|
|
|
2015-06-30 11:39:43 +00:00
|
|
|
function test_view() {
|
2016-12-20 09:49:23 +00:00
|
|
|
var control = createTemporaryObject(stackView, testCase)
|
2015-08-28 12:13:02 +00:00
|
|
|
verify(control)
|
2015-06-30 11:39:43 +00:00
|
|
|
|
|
|
|
var item1 = component.createObject(control)
|
2015-09-14 21:53:12 +00:00
|
|
|
compare(item1.StackView.view, null)
|
2015-09-14 14:27:24 +00:00
|
|
|
control.push(item1, StackView.Immediate)
|
2015-09-14 21:53:12 +00:00
|
|
|
compare(item1.StackView.view, control)
|
2015-06-30 11:39:43 +00:00
|
|
|
|
|
|
|
var item2 = component.createObject(control)
|
2015-09-14 21:53:12 +00:00
|
|
|
compare(item2.StackView.view, null)
|
2015-09-14 14:27:24 +00:00
|
|
|
control.push(item2, StackView.Immediate)
|
2015-09-14 21:53:12 +00:00
|
|
|
compare(item2.StackView.view, control)
|
|
|
|
compare(item1.StackView.view, control)
|
2015-06-30 11:39:43 +00:00
|
|
|
|
2015-09-14 14:27:24 +00:00
|
|
|
control.pop(StackView.Immediate)
|
2015-09-14 21:53:12 +00:00
|
|
|
compare(item2.StackView.view, null)
|
|
|
|
compare(item1.StackView.view, control)
|
2015-06-30 11:39:43 +00:00
|
|
|
}
|
|
|
|
|
2015-01-30 19:57:40 +00:00
|
|
|
function test_depth() {
|
2016-12-20 09:49:23 +00:00
|
|
|
var control = createTemporaryObject(stackView, testCase)
|
2015-08-28 12:13:02 +00:00
|
|
|
verify(control)
|
2017-05-23 08:26:06 +00:00
|
|
|
|
|
|
|
var depthChanges = 0
|
|
|
|
var emptyChanges = 0
|
2017-05-20 16:03:06 +00:00
|
|
|
var depthSpy = signalSpy.createObject(control, {target: control, signalName: "depthChanged"})
|
2017-05-23 08:26:06 +00:00
|
|
|
var emptySpy = signalSpy.createObject(control, {target: control, signalName: "emptyChanged"})
|
2017-05-20 16:03:06 +00:00
|
|
|
verify(depthSpy.valid)
|
2017-05-23 08:26:06 +00:00
|
|
|
verify(emptySpy.valid)
|
2015-01-30 19:57:40 +00:00
|
|
|
compare(control.depth, 0)
|
2017-05-20 17:27:24 +00:00
|
|
|
compare(control.empty, true)
|
2017-05-23 08:26:06 +00:00
|
|
|
|
2015-09-14 14:27:24 +00:00
|
|
|
control.push(item, StackView.Immediate)
|
2015-01-30 19:57:40 +00:00
|
|
|
compare(control.depth, 1)
|
2017-05-23 08:26:06 +00:00
|
|
|
compare(depthSpy.count, ++depthChanges)
|
2017-05-20 17:27:24 +00:00
|
|
|
compare(control.empty, false)
|
2017-05-23 08:26:06 +00:00
|
|
|
compare(emptySpy.count, ++emptyChanges)
|
|
|
|
|
2016-12-06 08:31:22 +00:00
|
|
|
control.clear()
|
|
|
|
compare(control.depth, 0)
|
2017-05-23 08:26:06 +00:00
|
|
|
compare(depthSpy.count, ++depthChanges)
|
2017-05-20 17:27:24 +00:00
|
|
|
compare(control.empty, true)
|
2017-05-23 08:26:06 +00:00
|
|
|
compare(emptySpy.count, ++emptyChanges)
|
|
|
|
|
2016-12-06 08:31:22 +00:00
|
|
|
control.push(component, StackView.Immediate)
|
2015-01-30 19:57:40 +00:00
|
|
|
compare(control.depth, 1)
|
2017-05-23 08:26:06 +00:00
|
|
|
compare(depthSpy.count, ++depthChanges)
|
2017-05-20 17:27:24 +00:00
|
|
|
compare(control.empty, false)
|
2017-05-23 08:26:06 +00:00
|
|
|
compare(emptySpy.count, ++emptyChanges)
|
|
|
|
|
2015-09-14 14:27:24 +00:00
|
|
|
control.push(component, StackView.Immediate)
|
2015-01-30 19:57:40 +00:00
|
|
|
compare(control.depth, 2)
|
2017-05-23 08:26:06 +00:00
|
|
|
compare(depthSpy.count, ++depthChanges)
|
2017-05-20 17:27:24 +00:00
|
|
|
compare(control.empty, false)
|
2017-05-23 08:26:06 +00:00
|
|
|
compare(emptySpy.count, emptyChanges)
|
|
|
|
|
|
|
|
control.replace(component, StackView.Immediate)
|
|
|
|
compare(control.depth, 2)
|
|
|
|
compare(depthSpy.count, depthChanges)
|
|
|
|
compare(control.empty, false)
|
|
|
|
compare(emptySpy.count, emptyChanges)
|
|
|
|
|
|
|
|
control.replace([component, component], StackView.Immediate)
|
|
|
|
compare(control.depth, 3)
|
|
|
|
compare(depthSpy.count, ++depthChanges)
|
|
|
|
compare(control.empty, false)
|
|
|
|
compare(emptySpy.count, emptyChanges)
|
|
|
|
|
|
|
|
control.pop(null, StackView.Immediate)
|
2015-01-30 19:57:40 +00:00
|
|
|
compare(control.depth, 1)
|
2017-05-23 08:26:06 +00:00
|
|
|
compare(depthSpy.count, ++depthChanges)
|
2017-05-20 17:27:24 +00:00
|
|
|
compare(control.empty, false)
|
2017-05-23 08:26:06 +00:00
|
|
|
compare(emptySpy.count, emptyChanges)
|
|
|
|
|
2015-09-14 14:27:24 +00:00
|
|
|
control.pop(StackView.Immediate) // ignored
|
2015-01-30 19:57:40 +00:00
|
|
|
compare(control.depth, 1)
|
2017-05-23 08:26:06 +00:00
|
|
|
compare(depthSpy.count, depthChanges)
|
2017-05-20 17:27:24 +00:00
|
|
|
compare(control.empty, false)
|
2017-05-23 08:26:06 +00:00
|
|
|
compare(emptySpy.count, emptyChanges)
|
|
|
|
|
2015-01-30 19:57:40 +00:00
|
|
|
control.clear()
|
|
|
|
compare(control.depth, 0)
|
2017-05-23 08:26:06 +00:00
|
|
|
compare(depthSpy.count, ++depthChanges)
|
2017-05-23 07:58:37 +00:00
|
|
|
compare(control.empty, true)
|
2017-05-23 08:26:06 +00:00
|
|
|
compare(emptySpy.count, ++emptyChanges)
|
|
|
|
|
2017-05-20 16:03:06 +00:00
|
|
|
control.clear()
|
|
|
|
compare(control.depth, 0)
|
2017-05-23 08:26:06 +00:00
|
|
|
compare(depthSpy.count, depthChanges)
|
2017-05-20 17:27:24 +00:00
|
|
|
compare(control.empty, true)
|
2017-05-23 08:26:06 +00:00
|
|
|
compare(emptySpy.count, emptyChanges)
|
2015-01-30 19:57:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function test_size() {
|
2016-12-20 09:49:23 +00:00
|
|
|
var container = createTemporaryObject(component, testCase, {width: 200, height: 200})
|
2015-08-28 12:13:02 +00:00
|
|
|
verify(container)
|
2015-01-30 19:57:40 +00:00
|
|
|
var control = stackView.createObject(container, {width: 100, height: 100})
|
2015-08-28 12:13:02 +00:00
|
|
|
verify(control)
|
2016-05-15 01:48:29 +00:00
|
|
|
|
2015-01-30 19:57:40 +00:00
|
|
|
container.width += 10
|
|
|
|
container.height += 20
|
|
|
|
compare(control.width, 100)
|
|
|
|
compare(control.height, 100)
|
2016-05-15 01:48:29 +00:00
|
|
|
|
|
|
|
control.push(item, StackView.Immediate)
|
|
|
|
compare(item.width, control.width)
|
|
|
|
compare(item.height, control.height)
|
|
|
|
|
|
|
|
control.width = 200
|
|
|
|
control.height = 200
|
|
|
|
compare(item.width, control.width)
|
|
|
|
compare(item.height, control.height)
|
|
|
|
|
|
|
|
control.clear()
|
|
|
|
control.width += 10
|
|
|
|
control.height += 20
|
|
|
|
verify(item.width !== control.width)
|
|
|
|
verify(item.height !== control.height)
|
|
|
|
|
|
|
|
control.push(item, StackView.Immediate)
|
|
|
|
compare(item.width, control.width)
|
|
|
|
compare(item.height, control.height)
|
2015-01-30 19:57:40 +00:00
|
|
|
}
|
|
|
|
|
2018-02-12 21:37:35 +00:00
|
|
|
function test_focus_data() {
|
|
|
|
return [
|
|
|
|
{ tag: "true", focus: true, forceActiveFocus: false },
|
|
|
|
{ tag: "false", focus: false, forceActiveFocus: false },
|
|
|
|
{ tag: "forceActiveFocus()", focus: false, forceActiveFocus: true },
|
|
|
|
]
|
|
|
|
}
|
|
|
|
|
|
|
|
function test_focus(data) {
|
2016-12-20 09:49:23 +00:00
|
|
|
var control = createTemporaryObject(stackView, testCase, {initialItem: item, width: 200, height: 200})
|
2015-08-28 12:13:02 +00:00
|
|
|
verify(control)
|
2015-01-30 19:57:40 +00:00
|
|
|
|
2018-02-12 21:37:35 +00:00
|
|
|
if (data.focus)
|
|
|
|
control.focus = true
|
|
|
|
if (data.forceActiveFocus)
|
|
|
|
control.forceActiveFocus()
|
|
|
|
compare(control.activeFocus, data.focus || data.forceActiveFocus)
|
2015-01-30 19:57:40 +00:00
|
|
|
|
2018-02-12 21:37:35 +00:00
|
|
|
var page = control.push(textField, StackView.Immediate)
|
|
|
|
verify(page)
|
|
|
|
compare(control.currentItem, page)
|
|
|
|
compare(page.activeFocus, control.activeFocus)
|
2015-01-30 19:57:40 +00:00
|
|
|
|
2015-09-14 14:27:24 +00:00
|
|
|
control.pop(StackView.Immediate)
|
2015-01-30 19:57:40 +00:00
|
|
|
compare(control.currentItem, item)
|
2018-02-12 21:37:35 +00:00
|
|
|
compare(item.activeFocus, data.focus || data.forceActiveFocus)
|
|
|
|
verify(!page.activeFocus)
|
2015-01-30 19:57:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function test_find() {
|
2016-12-20 09:49:23 +00:00
|
|
|
var control = createTemporaryObject(stackView, testCase)
|
2015-08-28 12:13:02 +00:00
|
|
|
verify(control)
|
2015-01-30 19:57:40 +00:00
|
|
|
|
|
|
|
var item1 = component.createObject(control, {objectName: "1"})
|
|
|
|
var item2 = component.createObject(control, {objectName: "2"})
|
|
|
|
var item3 = component.createObject(control, {objectName: "3"})
|
|
|
|
|
2015-09-14 14:27:24 +00:00
|
|
|
control.push(item1, StackView.Immediate)
|
|
|
|
control.push(item2, StackView.Immediate)
|
|
|
|
control.push(item3, StackView.Immediate)
|
2015-01-30 19:57:40 +00:00
|
|
|
|
|
|
|
compare(control.find(function(item, index) { return index === 0 }), item1)
|
|
|
|
compare(control.find(function(item) { return item.objectName === "1" }), item1)
|
|
|
|
|
|
|
|
compare(control.find(function(item, index) { return index === 1 }), item2)
|
|
|
|
compare(control.find(function(item) { return item.objectName === "2" }), item2)
|
|
|
|
|
|
|
|
compare(control.find(function(item, index) { return index === 2 }), item3)
|
|
|
|
compare(control.find(function(item) { return item.objectName === "3" }), item3)
|
|
|
|
|
|
|
|
compare(control.find(function() { return false }), null)
|
|
|
|
compare(control.find(function() { return true }), item3)
|
|
|
|
}
|
|
|
|
|
|
|
|
function test_get() {
|
2016-12-20 09:49:23 +00:00
|
|
|
var control = createTemporaryObject(stackView, testCase)
|
2015-08-28 12:13:02 +00:00
|
|
|
verify(control)
|
2015-01-30 19:57:40 +00:00
|
|
|
|
2015-09-14 14:27:24 +00:00
|
|
|
control.push([item, component, component], StackView.Immediate)
|
2015-01-30 19:57:40 +00:00
|
|
|
|
2015-09-14 14:27:24 +00:00
|
|
|
verify(control.get(0, StackView.DontLoad))
|
|
|
|
compare(control.get(0, StackView.ForceLoad), item)
|
2015-01-30 19:57:40 +00:00
|
|
|
|
2015-09-14 14:27:24 +00:00
|
|
|
verify(!control.get(1, StackView.DontLoad))
|
2015-01-30 19:57:40 +00:00
|
|
|
|
2015-09-14 14:27:24 +00:00
|
|
|
verify(control.get(2, StackView.DontLoad))
|
|
|
|
verify(control.get(2, StackView.ForceLoad))
|
2015-01-30 19:57:40 +00:00
|
|
|
}
|
|
|
|
|
2015-03-28 20:51:10 +00:00
|
|
|
function test_push() {
|
2016-12-20 09:49:23 +00:00
|
|
|
var control = createTemporaryObject(stackView, testCase)
|
2015-08-28 12:13:02 +00:00
|
|
|
verify(control)
|
2015-01-30 19:57:40 +00:00
|
|
|
|
2015-07-06 21:57:23 +00:00
|
|
|
// missing arguments
|
2021-11-04 08:53:04 +00:00
|
|
|
ignoreWarning(/QML StackView: push: missing arguments/)
|
2015-03-28 20:51:10 +00:00
|
|
|
compare(control.push(), null)
|
|
|
|
|
2015-07-06 21:57:23 +00:00
|
|
|
// nothing to push
|
2021-11-04 08:53:04 +00:00
|
|
|
ignoreWarning(/QML StackView: push: nothing to push/)
|
2015-09-14 14:27:24 +00:00
|
|
|
compare(control.push(StackView.Immediate), null)
|
2015-03-28 20:51:10 +00:00
|
|
|
|
2017-03-23 12:03:06 +00:00
|
|
|
// unsupported type
|
2021-11-04 08:53:04 +00:00
|
|
|
ignoreWarning(/QML StackView: push: QtObject is not supported. Must be Item or Component./)
|
2020-03-26 16:01:51 +00:00
|
|
|
control.push(Qt.createQmlObject('import QtQml; QtObject { }', control))
|
2017-03-23 12:03:06 +00:00
|
|
|
|
2015-03-28 20:51:10 +00:00
|
|
|
// push(item)
|
2015-01-30 19:57:40 +00:00
|
|
|
var item1 = component.createObject(control, {objectName:"1"})
|
2015-09-14 14:27:24 +00:00
|
|
|
compare(control.push(item1, StackView.Immediate), item1)
|
2015-01-30 19:57:40 +00:00
|
|
|
compare(control.depth, 1)
|
|
|
|
compare(control.currentItem, item1)
|
|
|
|
|
2015-03-28 20:51:10 +00:00
|
|
|
// push([item])
|
2015-01-30 19:57:40 +00:00
|
|
|
var item2 = component.createObject(control, {objectName:"2"})
|
2015-09-14 14:27:24 +00:00
|
|
|
compare(control.push([item2], StackView.Immediate), item2)
|
2015-01-30 19:57:40 +00:00
|
|
|
compare(control.depth, 2)
|
|
|
|
compare(control.currentItem, item2)
|
|
|
|
|
2015-03-28 20:51:10 +00:00
|
|
|
// push(item, {properties})
|
|
|
|
var item3 = component.createObject(control)
|
2015-09-14 14:27:24 +00:00
|
|
|
compare(control.push(item3, {objectName:"3"}, StackView.Immediate), item3)
|
2015-03-28 20:51:10 +00:00
|
|
|
compare(item3.objectName, "3")
|
2015-01-30 19:57:40 +00:00
|
|
|
compare(control.depth, 3)
|
|
|
|
compare(control.currentItem, item3)
|
|
|
|
|
2015-03-28 20:51:10 +00:00
|
|
|
// push([item, {properties}])
|
|
|
|
var item4 = component.createObject(control)
|
2015-09-14 14:27:24 +00:00
|
|
|
compare(control.push([item4, {objectName:"4"}], StackView.Immediate), item4)
|
2015-03-28 20:51:10 +00:00
|
|
|
compare(item4.objectName, "4")
|
|
|
|
compare(control.depth, 4)
|
2015-01-30 19:57:40 +00:00
|
|
|
compare(control.currentItem, item4)
|
|
|
|
|
2015-03-28 20:51:10 +00:00
|
|
|
// push(component, {properties})
|
2015-09-14 14:27:24 +00:00
|
|
|
var item5 = control.push(component, {objectName:"5"}, StackView.Immediate)
|
2015-03-28 20:51:10 +00:00
|
|
|
compare(item5.objectName, "5")
|
|
|
|
compare(control.depth, 5)
|
2015-01-30 19:57:40 +00:00
|
|
|
compare(control.currentItem, item5)
|
|
|
|
|
2015-03-28 20:51:10 +00:00
|
|
|
// push([component, {properties}])
|
2015-09-14 14:27:24 +00:00
|
|
|
var item6 = control.push([component, {objectName:"6"}], StackView.Immediate)
|
2015-03-28 20:51:10 +00:00
|
|
|
compare(item6.objectName, "6")
|
|
|
|
compare(control.depth, 6)
|
2015-01-30 19:57:40 +00:00
|
|
|
compare(control.currentItem, item6)
|
2015-03-28 20:51:10 +00:00
|
|
|
}
|
|
|
|
|
2021-11-04 08:53:04 +00:00
|
|
|
// Escape special Regexp characters with a '\' (backslash) prefix so that \a str can be
|
|
|
|
// used as a Regexp pattern.
|
|
|
|
function escapeRegExp(str) {
|
|
|
|
// "$&" is the last matched substring
|
|
|
|
return str.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
|
|
|
|
}
|
|
|
|
|
2015-03-28 20:51:10 +00:00
|
|
|
function test_pop() {
|
2016-12-20 09:49:23 +00:00
|
|
|
var control = createTemporaryObject(stackView, testCase)
|
2015-08-28 12:13:02 +00:00
|
|
|
verify(control)
|
2015-01-30 19:57:40 +00:00
|
|
|
|
2015-03-28 20:51:10 +00:00
|
|
|
var items = []
|
|
|
|
for (var i = 0; i < 7; ++i)
|
|
|
|
items.push(component.createObject(control, {objectName:i}))
|
2015-01-30 19:57:40 +00:00
|
|
|
|
2015-09-14 14:27:24 +00:00
|
|
|
control.push(items, StackView.Immediate)
|
2015-01-30 19:57:40 +00:00
|
|
|
|
2021-11-04 08:53:04 +00:00
|
|
|
ignoreWarning(/QML StackView: pop: too many arguments/)
|
2015-03-28 20:51:10 +00:00
|
|
|
compare(control.pop(1, 2, 3), null)
|
2015-01-30 19:57:40 +00:00
|
|
|
|
2015-03-28 20:51:10 +00:00
|
|
|
// pop the top most item
|
2015-09-14 14:27:24 +00:00
|
|
|
compare(control.pop(StackView.Immediate), items[6])
|
2015-03-28 20:51:10 +00:00
|
|
|
compare(control.depth, 6)
|
|
|
|
compare(control.currentItem, items[5])
|
2015-01-30 19:57:40 +00:00
|
|
|
|
2016-06-10 02:09:45 +00:00
|
|
|
// pop down to the current item
|
|
|
|
compare(control.pop(control.currentItem, StackView.Immediate), null)
|
|
|
|
compare(control.depth, 6)
|
|
|
|
compare(control.currentItem, items[5])
|
|
|
|
|
2015-03-28 20:51:10 +00:00
|
|
|
// pop down to (but not including) the Nth item
|
2015-09-14 14:27:24 +00:00
|
|
|
compare(control.pop(items[3], StackView.Immediate), items[5])
|
2015-03-28 20:51:10 +00:00
|
|
|
compare(control.depth, 4)
|
|
|
|
compare(control.currentItem, items[3])
|
|
|
|
|
|
|
|
// pop the top most item
|
2015-09-14 14:27:24 +00:00
|
|
|
compare(control.pop(undefined, StackView.Immediate), items[3])
|
2015-01-30 19:57:40 +00:00
|
|
|
compare(control.depth, 3)
|
2015-03-28 20:51:10 +00:00
|
|
|
compare(control.currentItem, items[2])
|
2015-01-30 19:57:40 +00:00
|
|
|
|
2015-03-28 20:51:10 +00:00
|
|
|
// don't pop non-existent item
|
2021-11-04 08:53:04 +00:00
|
|
|
ignoreWarning(new RegExp(".*QML StackView: pop: unknown argument: " + escapeRegExp(testCase.toString())))
|
2015-09-14 14:27:24 +00:00
|
|
|
compare(control.pop(testCase, StackView.Immediate), null)
|
2015-03-28 20:51:10 +00:00
|
|
|
compare(control.depth, 3)
|
|
|
|
compare(control.currentItem, items[2])
|
|
|
|
|
|
|
|
// pop all items down to (but not including) the 1st item
|
2015-09-14 14:27:24 +00:00
|
|
|
control.pop(null, StackView.Immediate)
|
2015-03-28 20:51:10 +00:00
|
|
|
compare(control.depth, 1)
|
|
|
|
compare(control.currentItem, items[0])
|
|
|
|
}
|
|
|
|
|
|
|
|
function test_replace() {
|
2016-12-20 09:49:23 +00:00
|
|
|
var control = createTemporaryObject(stackView, testCase)
|
2015-08-28 12:13:02 +00:00
|
|
|
verify(control)
|
2015-03-28 20:51:10 +00:00
|
|
|
|
2015-07-06 21:57:23 +00:00
|
|
|
// missing arguments
|
2021-11-04 08:53:04 +00:00
|
|
|
ignoreWarning(/QML StackView: replace: missing arguments/)
|
2015-03-28 20:51:10 +00:00
|
|
|
compare(control.replace(), null)
|
|
|
|
|
2015-07-06 21:57:23 +00:00
|
|
|
// nothing to push
|
2021-11-04 08:53:04 +00:00
|
|
|
ignoreWarning(/QML StackView: replace: nothing to push/)
|
2015-09-14 14:27:24 +00:00
|
|
|
compare(control.replace(StackView.Immediate), null)
|
2015-03-28 20:51:10 +00:00
|
|
|
|
2017-03-23 12:03:06 +00:00
|
|
|
// unsupported type
|
2021-11-04 08:53:04 +00:00
|
|
|
ignoreWarning(/QML StackView: replace: QtObject is not supported. Must be Item or Component./)
|
2020-03-26 16:01:51 +00:00
|
|
|
compare(control.replace(Qt.createQmlObject('import QtQml; QtObject { }', control)), null)
|
2017-03-23 12:03:06 +00:00
|
|
|
|
2015-03-28 20:51:10 +00:00
|
|
|
// replace(item)
|
|
|
|
var item1 = component.createObject(control, {objectName:"1"})
|
2015-09-14 14:27:24 +00:00
|
|
|
compare(control.replace(item1, StackView.Immediate), item1)
|
2015-01-30 19:57:40 +00:00
|
|
|
compare(control.depth, 1)
|
|
|
|
compare(control.currentItem, item1)
|
|
|
|
|
2015-03-28 20:51:10 +00:00
|
|
|
// replace([item])
|
|
|
|
var item2 = component.createObject(control, {objectName:"2"})
|
2015-09-14 14:27:24 +00:00
|
|
|
compare(control.replace([item2], StackView.Immediate), item2)
|
2015-03-28 20:51:10 +00:00
|
|
|
compare(control.depth, 1)
|
|
|
|
compare(control.currentItem, item2)
|
|
|
|
|
|
|
|
// replace(item, {properties})
|
|
|
|
var item3 = component.createObject(control)
|
2015-09-14 14:27:24 +00:00
|
|
|
compare(control.replace(item3, {objectName:"3"}, StackView.Immediate), item3)
|
2015-03-28 20:51:10 +00:00
|
|
|
compare(item3.objectName, "3")
|
|
|
|
compare(control.depth, 1)
|
|
|
|
compare(control.currentItem, item3)
|
|
|
|
|
|
|
|
// replace([item, {properties}])
|
|
|
|
var item4 = component.createObject(control)
|
2015-09-14 14:27:24 +00:00
|
|
|
compare(control.replace([item4, {objectName:"4"}], StackView.Immediate), item4)
|
2015-03-28 20:51:10 +00:00
|
|
|
compare(item4.objectName, "4")
|
|
|
|
compare(control.depth, 1)
|
|
|
|
compare(control.currentItem, item4)
|
|
|
|
|
|
|
|
// replace(component, {properties})
|
2015-09-14 14:27:24 +00:00
|
|
|
var item5 = control.replace(component, {objectName:"5"}, StackView.Immediate)
|
2015-03-28 20:51:10 +00:00
|
|
|
compare(item5.objectName, "5")
|
|
|
|
compare(control.depth, 1)
|
|
|
|
compare(control.currentItem, item5)
|
|
|
|
|
|
|
|
// replace([component, {properties}])
|
2015-09-14 14:27:24 +00:00
|
|
|
var item6 = control.replace([component, {objectName:"6"}], StackView.Immediate)
|
2015-03-28 20:51:10 +00:00
|
|
|
compare(item6.objectName, "6")
|
|
|
|
compare(control.depth, 1)
|
|
|
|
compare(control.currentItem, item6)
|
|
|
|
|
2016-01-04 18:08:27 +00:00
|
|
|
// replace the topmost item
|
|
|
|
control.push(component)
|
|
|
|
compare(control.depth, 2)
|
|
|
|
var item7 = control.replace(control.get(1), component, StackView.Immediate)
|
|
|
|
compare(control.depth, 2)
|
|
|
|
compare(control.currentItem, item7)
|
|
|
|
|
|
|
|
// replace the item in the middle
|
|
|
|
control.push(component)
|
|
|
|
control.push(component)
|
|
|
|
control.push(component)
|
|
|
|
compare(control.depth, 5)
|
|
|
|
var item8 = control.replace(control.get(2), component, StackView.Immediate)
|
|
|
|
compare(control.depth, 3)
|
|
|
|
compare(control.currentItem, item8)
|
2015-01-30 19:57:40 +00:00
|
|
|
}
|
2015-08-26 13:14:23 +00:00
|
|
|
|
2017-05-20 16:19:11 +00:00
|
|
|
function test_clear() {
|
|
|
|
var control = createTemporaryObject(stackView, testCase)
|
|
|
|
verify(control)
|
|
|
|
|
|
|
|
control.push(component, StackView.Immediate)
|
|
|
|
|
|
|
|
control.clear()
|
|
|
|
compare(control.depth, 0)
|
|
|
|
compare(control.busy, false)
|
|
|
|
|
|
|
|
control.push(component, StackView.Immediate)
|
|
|
|
|
|
|
|
control.clear(StackView.PopTransition)
|
|
|
|
compare(control.depth, 0)
|
|
|
|
compare(control.busy, true)
|
|
|
|
tryCompare(control, "busy", false)
|
|
|
|
}
|
|
|
|
|
2015-08-26 13:14:23 +00:00
|
|
|
function test_visibility_data() {
|
|
|
|
return [
|
|
|
|
{tag:"default transitions", properties: {}},
|
|
|
|
{tag:"null transitions", properties: {pushEnter: null, pushExit: null, popEnter: null, popExit: null}}
|
|
|
|
]
|
|
|
|
}
|
|
|
|
|
|
|
|
function test_visibility(data) {
|
2016-12-20 09:49:23 +00:00
|
|
|
var control = createTemporaryObject(stackView, testCase, data.properties)
|
2015-08-28 12:13:02 +00:00
|
|
|
verify(control)
|
2015-08-26 13:14:23 +00:00
|
|
|
|
|
|
|
var item1 = component.createObject(control)
|
2015-09-14 14:27:24 +00:00
|
|
|
control.push(item1, StackView.Immediate)
|
2015-08-26 13:14:23 +00:00
|
|
|
verify(item1.visible)
|
|
|
|
|
|
|
|
var item2 = component.createObject(control)
|
|
|
|
control.push(item2)
|
|
|
|
tryCompare(item1, "visible", false)
|
|
|
|
verify(item2.visible)
|
|
|
|
|
|
|
|
control.pop()
|
|
|
|
verify(item1.visible)
|
|
|
|
tryCompare(item2, "visible", false)
|
|
|
|
}
|
2015-08-26 15:53:41 +00:00
|
|
|
|
|
|
|
Component {
|
|
|
|
id: transitionView
|
|
|
|
StackView {
|
|
|
|
property int popEnterRuns
|
|
|
|
property int popExitRuns
|
|
|
|
property int pushEnterRuns
|
|
|
|
property int pushExitRuns
|
2015-09-25 19:30:59 +00:00
|
|
|
property int replaceEnterRuns
|
|
|
|
property int replaceExitRuns
|
2015-08-26 15:53:41 +00:00
|
|
|
popEnter: Transition {
|
|
|
|
PauseAnimation { duration: 1 }
|
|
|
|
onRunningChanged: if (!running) ++popEnterRuns
|
|
|
|
}
|
|
|
|
popExit: Transition {
|
|
|
|
PauseAnimation { duration: 1 }
|
|
|
|
onRunningChanged: if (!running) ++popExitRuns
|
|
|
|
}
|
|
|
|
pushEnter: Transition {
|
|
|
|
PauseAnimation { duration: 1 }
|
|
|
|
onRunningChanged: if (!running) ++pushEnterRuns
|
|
|
|
}
|
|
|
|
pushExit: Transition {
|
|
|
|
PauseAnimation { duration: 1 }
|
|
|
|
onRunningChanged: if (!running) ++pushExitRuns
|
|
|
|
}
|
2015-09-25 19:30:59 +00:00
|
|
|
replaceEnter: Transition {
|
|
|
|
PauseAnimation { duration: 1 }
|
|
|
|
onRunningChanged: if (!running) ++replaceEnterRuns
|
|
|
|
}
|
|
|
|
replaceExit: Transition {
|
|
|
|
PauseAnimation { duration: 1 }
|
|
|
|
onRunningChanged: if (!running) ++replaceExitRuns
|
|
|
|
}
|
2015-08-26 15:53:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-07-14 11:02:27 +00:00
|
|
|
function test_transitions_data() {
|
|
|
|
return [
|
|
|
|
{ tag: "undefined", operation: undefined,
|
StackView: complete animations when using StackView.Immediate transition
The idea behind using StackView.Immediate is to immediately move the
stack view item into the new state without running any animations.
However the preexisting code in QQuickStackViewPrivate::completeTransition
was doing nothing, because the animations didn't have the proper target set.
So they could never be started and/or completed.
As a result, the pushed stack view item could appear at the wrong position
and with the other properties set incorrectly (for example, opacity).
The new code uses the same approach, as in startTransition(), but simplifies
it to execute only the animations.
Instead of actually running the animations, a new
QAbstractAnimationJob::complete() method is added, which simulates the
animation progress by starting the animation, forwarding it to the end, and
completing it. This results in all the properties receiving the correct
values, and the stack view items being shown correctly.
Fixes: QTBUG-96966
Fixes: QTBUG-61496
Pick-to: 6.2
Change-Id: I990a133881c66e3ecb83887c60596a5d45e570d9
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
2021-10-13 12:10:27 +00:00
|
|
|
pushEnterRuns: [1,2,2,2], pushExitRuns: [0,1,1,1], replaceEnterRuns: [0,0,1,1], replaceExitRuns: [0,0,1,1], popEnterRuns: [0,0,0,1], popExitRuns: [0,0,0,1] },
|
2016-07-14 11:02:27 +00:00
|
|
|
{ tag: "immediate", operation: StackView.Immediate,
|
StackView: complete animations when using StackView.Immediate transition
The idea behind using StackView.Immediate is to immediately move the
stack view item into the new state without running any animations.
However the preexisting code in QQuickStackViewPrivate::completeTransition
was doing nothing, because the animations didn't have the proper target set.
So they could never be started and/or completed.
As a result, the pushed stack view item could appear at the wrong position
and with the other properties set incorrectly (for example, opacity).
The new code uses the same approach, as in startTransition(), but simplifies
it to execute only the animations.
Instead of actually running the animations, a new
QAbstractAnimationJob::complete() method is added, which simulates the
animation progress by starting the animation, forwarding it to the end, and
completing it. This results in all the properties receiving the correct
values, and the stack view items being shown correctly.
Fixes: QTBUG-96966
Fixes: QTBUG-61496
Pick-to: 6.2
Change-Id: I990a133881c66e3ecb83887c60596a5d45e570d9
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
2021-10-13 12:10:27 +00:00
|
|
|
pushEnterRuns: [1,2,2,2], pushExitRuns: [0,1,1,1], replaceEnterRuns: [0,0,1,1], replaceExitRuns: [0,0,1,1], popEnterRuns: [0,0,0,1], popExitRuns: [0,0,0,1] },
|
2016-07-14 11:02:27 +00:00
|
|
|
{ tag: "push", operation: StackView.PushTransition,
|
|
|
|
pushEnterRuns: [1,2,3,4], pushExitRuns: [0,1,2,3], replaceEnterRuns: [0,0,0,0], replaceExitRuns: [0,0,0,0], popEnterRuns: [0,0,0,0], popExitRuns: [0,0,0,0] },
|
|
|
|
{ tag: "pop", operation: StackView.PopTransition,
|
|
|
|
pushEnterRuns: [0,0,0,0], pushExitRuns: [0,0,0,0], replaceEnterRuns: [0,0,0,0], replaceExitRuns: [0,0,0,0], popEnterRuns: [1,2,3,4], popExitRuns: [0,1,2,3] },
|
|
|
|
{ tag: "replace", operation: StackView.ReplaceTransition,
|
|
|
|
pushEnterRuns: [0,0,0,0], pushExitRuns: [0,0,0,0], replaceEnterRuns: [1,2,3,4], replaceExitRuns: [0,1,2,3], popEnterRuns: [0,0,0,0], popExitRuns: [0,0,0,0] },
|
|
|
|
]
|
|
|
|
}
|
|
|
|
|
|
|
|
function test_transitions(data) {
|
2016-12-20 09:49:23 +00:00
|
|
|
var control = createTemporaryObject(transitionView, testCase)
|
2015-08-28 12:13:02 +00:00
|
|
|
verify(control)
|
2015-08-26 15:53:41 +00:00
|
|
|
|
2016-07-14 11:02:27 +00:00
|
|
|
control.push(component, data.operation)
|
2015-08-26 15:53:41 +00:00
|
|
|
tryCompare(control, "busy", false)
|
2016-07-14 11:02:27 +00:00
|
|
|
compare(control.pushEnterRuns, data.pushEnterRuns[0])
|
|
|
|
compare(control.pushExitRuns, data.pushExitRuns[0])
|
|
|
|
compare(control.replaceEnterRuns, data.replaceEnterRuns[0])
|
|
|
|
compare(control.replaceExitRuns, data.replaceExitRuns[0])
|
|
|
|
compare(control.popEnterRuns, data.popEnterRuns[0])
|
|
|
|
compare(control.popExitRuns, data.popExitRuns[0])
|
|
|
|
|
|
|
|
control.push(component, data.operation)
|
2015-09-25 19:30:59 +00:00
|
|
|
tryCompare(control, "busy", false)
|
2016-07-14 11:02:27 +00:00
|
|
|
compare(control.pushEnterRuns, data.pushEnterRuns[1])
|
|
|
|
compare(control.pushExitRuns, data.pushExitRuns[1])
|
|
|
|
compare(control.replaceEnterRuns, data.replaceEnterRuns[1])
|
|
|
|
compare(control.replaceExitRuns, data.replaceExitRuns[1])
|
|
|
|
compare(control.popEnterRuns, data.popEnterRuns[1])
|
|
|
|
compare(control.popExitRuns, data.popExitRuns[1])
|
|
|
|
|
|
|
|
control.replace(component, data.operation)
|
|
|
|
tryCompare(control, "busy", false)
|
|
|
|
compare(control.pushEnterRuns, data.pushEnterRuns[2])
|
|
|
|
compare(control.pushExitRuns, data.pushExitRuns[2])
|
|
|
|
compare(control.replaceEnterRuns, data.replaceEnterRuns[2])
|
|
|
|
compare(control.replaceExitRuns, data.replaceExitRuns[2])
|
|
|
|
compare(control.popEnterRuns, data.popEnterRuns[2])
|
|
|
|
compare(control.popExitRuns, data.popExitRuns[2])
|
|
|
|
|
|
|
|
control.pop(data.operation)
|
2015-08-26 15:53:41 +00:00
|
|
|
tryCompare(control, "busy", false)
|
2016-07-14 11:02:27 +00:00
|
|
|
compare(control.pushEnterRuns, data.pushEnterRuns[3])
|
|
|
|
compare(control.pushExitRuns, data.pushExitRuns[3])
|
|
|
|
compare(control.replaceEnterRuns, data.replaceEnterRuns[3])
|
|
|
|
compare(control.replaceExitRuns, data.replaceExitRuns[3])
|
|
|
|
compare(control.popEnterRuns, data.popEnterRuns[3])
|
|
|
|
compare(control.popExitRuns, data.popExitRuns[3])
|
2015-08-26 15:53:41 +00:00
|
|
|
}
|
2015-08-28 11:35:19 +00:00
|
|
|
|
|
|
|
TestItem {
|
|
|
|
id: indestructibleItem
|
|
|
|
}
|
|
|
|
|
|
|
|
Component {
|
|
|
|
id: destructibleComponent
|
|
|
|
TestItem { }
|
|
|
|
}
|
|
|
|
|
|
|
|
function test_ownership_data() {
|
|
|
|
return [
|
2015-09-14 14:27:24 +00:00
|
|
|
{tag:"item, transition", arg: indestructibleItem, operation: StackView.Transition, destroyed: false},
|
|
|
|
{tag:"item, immediate", arg: indestructibleItem, operation: StackView.Immediate, destroyed: false},
|
|
|
|
{tag:"component, transition", arg: destructibleComponent, operation: StackView.Transition, destroyed: true},
|
|
|
|
{tag:"component, immediate", arg: destructibleComponent, operation: StackView.Immediate, destroyed: true},
|
|
|
|
{tag:"url, transition", arg: Qt.resolvedUrl("TestItem.qml"), operation: StackView.Transition, destroyed: true},
|
|
|
|
{tag:"url, immediate", arg: Qt.resolvedUrl("TestItem.qml"), operation: StackView.Immediate, destroyed: true}
|
2015-08-28 11:35:19 +00:00
|
|
|
]
|
|
|
|
}
|
|
|
|
|
|
|
|
function test_ownership(data) {
|
2016-12-20 09:49:23 +00:00
|
|
|
var control = createTemporaryObject(transitionView, testCase, {initialItem: component})
|
2015-08-28 12:13:02 +00:00
|
|
|
verify(control)
|
2015-08-28 11:35:19 +00:00
|
|
|
|
|
|
|
// push-pop
|
2015-09-14 14:27:24 +00:00
|
|
|
control.push(data.arg, StackView.Immediate)
|
2015-08-28 11:35:19 +00:00
|
|
|
verify(control.currentItem)
|
|
|
|
verify(control.currentItem.hasOwnProperty("destroyedCallback"))
|
|
|
|
var destroyed = false
|
|
|
|
control.currentItem.destroyedCallback = function() { destroyed = true }
|
|
|
|
control.pop(data.operation)
|
|
|
|
tryCompare(control, "busy", false)
|
|
|
|
wait(0) // deferred delete
|
|
|
|
compare(destroyed, data.destroyed)
|
|
|
|
|
|
|
|
// push-replace
|
2015-09-14 14:27:24 +00:00
|
|
|
control.push(data.arg, StackView.Immediate)
|
2015-08-28 11:35:19 +00:00
|
|
|
verify(control.currentItem)
|
|
|
|
verify(control.currentItem.hasOwnProperty("destroyedCallback"))
|
|
|
|
destroyed = false
|
|
|
|
control.currentItem.destroyedCallback = function() { destroyed = true }
|
|
|
|
control.replace(component, data.operation)
|
|
|
|
tryCompare(control, "busy", false)
|
|
|
|
wait(0) // deferred delete
|
|
|
|
compare(destroyed, data.destroyed)
|
|
|
|
}
|
2015-10-10 22:04:55 +00:00
|
|
|
|
2016-08-26 10:46:42 +00:00
|
|
|
Component {
|
|
|
|
id: removeComponent
|
|
|
|
|
|
|
|
Item {
|
|
|
|
objectName: "removeItem"
|
|
|
|
StackView.onRemoved: destroy()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function test_destroyOnRemoved() {
|
2016-12-20 09:49:23 +00:00
|
|
|
var control = createTemporaryObject(stackView, testCase, { initialItem: component })
|
2016-08-26 10:46:42 +00:00
|
|
|
verify(control)
|
|
|
|
|
|
|
|
var item = removeComponent.createObject(control)
|
|
|
|
verify(item)
|
|
|
|
|
|
|
|
var removedSpy = signalSpy.createObject(control, { target: item.StackView, signalName: "removed" })
|
|
|
|
verify(removedSpy)
|
|
|
|
verify(removedSpy.valid)
|
|
|
|
|
|
|
|
var destructionSpy = signalSpy.createObject(control, { target: item.Component, signalName: "destruction" })
|
|
|
|
verify(destructionSpy)
|
|
|
|
verify(destructionSpy.valid)
|
|
|
|
|
|
|
|
// push-pop
|
|
|
|
control.push(item, StackView.Immediate)
|
|
|
|
compare(control.currentItem, item)
|
|
|
|
control.pop(StackView.Transition)
|
|
|
|
item = null
|
|
|
|
tryCompare(removedSpy, "count", 1)
|
|
|
|
tryCompare(destructionSpy, "count", 1)
|
|
|
|
compare(control.busy, false)
|
|
|
|
|
|
|
|
item = removeComponent.createObject(control)
|
|
|
|
verify(item)
|
|
|
|
|
|
|
|
removedSpy.target = item.StackView
|
|
|
|
verify(removedSpy.valid)
|
|
|
|
|
|
|
|
destructionSpy.target = item.Component
|
|
|
|
verify(destructionSpy.valid)
|
|
|
|
|
|
|
|
// push-replace
|
|
|
|
control.push(item, StackView.Immediate)
|
|
|
|
compare(control.currentItem, item)
|
|
|
|
control.replace(component, StackView.Transition)
|
|
|
|
item = null
|
|
|
|
tryCompare(removedSpy, "count", 2)
|
|
|
|
tryCompare(destructionSpy, "count", 2)
|
|
|
|
compare(control.busy, false)
|
|
|
|
}
|
|
|
|
|
2017-07-26 11:07:17 +00:00
|
|
|
function test_pushOnRemoved() {
|
|
|
|
var control = createTemporaryObject(stackView, testCase, { initialItem: component })
|
|
|
|
verify(control)
|
|
|
|
|
|
|
|
var item = control.push(component, StackView.Immediate)
|
|
|
|
verify(item)
|
|
|
|
|
2020-05-27 08:22:37 +00:00
|
|
|
item.StackView.onRemoved.connect(function() {
|
|
|
|
ignoreWarning(/.*QML StackView: cannot push while already in the process of completing a pop/)
|
|
|
|
control.push(component, StackView.Immediate)
|
|
|
|
})
|
2017-07-26 11:07:17 +00:00
|
|
|
|
|
|
|
// don't crash (QTBUG-62153)
|
|
|
|
control.pop(StackView.Immediate)
|
|
|
|
}
|
|
|
|
|
2015-10-10 22:04:55 +00:00
|
|
|
Component {
|
|
|
|
id: attachedItem
|
|
|
|
Item {
|
|
|
|
property int index: StackView.index
|
|
|
|
property StackView view: StackView.view
|
|
|
|
property int status: StackView.status
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function test_attached() {
|
2016-12-20 09:49:23 +00:00
|
|
|
var control = createTemporaryObject(stackView, testCase, {initialItem: attachedItem})
|
2015-10-10 22:04:55 +00:00
|
|
|
|
|
|
|
compare(control.get(0).index, 0)
|
|
|
|
compare(control.get(0).view, control)
|
|
|
|
compare(control.get(0).status, StackView.Active)
|
|
|
|
|
|
|
|
control.push(attachedItem, StackView.Immediate)
|
|
|
|
|
|
|
|
compare(control.get(0).index, 0)
|
|
|
|
compare(control.get(0).view, control)
|
|
|
|
compare(control.get(0).status, StackView.Inactive)
|
|
|
|
|
|
|
|
compare(control.get(1).index, 1)
|
|
|
|
compare(control.get(1).view, control)
|
|
|
|
compare(control.get(1).status, StackView.Active)
|
|
|
|
|
|
|
|
control.pop(StackView.Immediate)
|
|
|
|
|
|
|
|
compare(control.get(0).index, 0)
|
|
|
|
compare(control.get(0).view, control)
|
|
|
|
compare(control.get(0).status, StackView.Active)
|
|
|
|
}
|
2015-10-29 14:50:39 +00:00
|
|
|
|
|
|
|
Component {
|
|
|
|
id: testButton
|
|
|
|
Button {
|
|
|
|
property int clicks: 0
|
|
|
|
onClicked: ++clicks
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function test_interaction() {
|
2016-12-20 09:49:23 +00:00
|
|
|
var control = createTemporaryObject(stackView, testCase, {initialItem: testButton, width: testCase.width, height: testCase.height})
|
2015-10-29 14:50:39 +00:00
|
|
|
verify(control)
|
|
|
|
|
|
|
|
var firstButton = control.currentItem
|
|
|
|
verify(firstButton)
|
|
|
|
|
|
|
|
var firstClicks = 0
|
|
|
|
var secondClicks = 0
|
|
|
|
var thirdClicks = 0
|
|
|
|
|
|
|
|
// push - default transition
|
|
|
|
var secondButton = control.push(testButton)
|
|
|
|
compare(control.busy, true)
|
|
|
|
mouseClick(firstButton) // filtered while busy
|
|
|
|
mouseClick(secondButton) // filtered while busy
|
|
|
|
compare(firstButton.clicks, firstClicks)
|
|
|
|
compare(secondButton.clicks, secondClicks)
|
|
|
|
tryCompare(control, "busy", false)
|
|
|
|
mouseClick(secondButton)
|
|
|
|
compare(secondButton.clicks, ++secondClicks)
|
|
|
|
|
|
|
|
// replace - default transition
|
|
|
|
var thirdButton = control.replace(testButton)
|
|
|
|
compare(control.busy, true)
|
|
|
|
mouseClick(secondButton) // filtered while busy
|
|
|
|
mouseClick(thirdButton) // filtered while busy
|
|
|
|
compare(secondButton.clicks, secondClicks)
|
|
|
|
compare(thirdButton.clicks, thirdClicks)
|
|
|
|
tryCompare(control, "busy", false)
|
|
|
|
secondButton = null
|
|
|
|
secondClicks = 0
|
|
|
|
mouseClick(thirdButton)
|
|
|
|
compare(thirdButton.clicks, ++thirdClicks)
|
|
|
|
|
|
|
|
// pop - default transition
|
|
|
|
control.pop()
|
|
|
|
compare(control.busy, true)
|
|
|
|
mouseClick(firstButton) // filtered while busy
|
|
|
|
mouseClick(thirdButton) // filtered while busy
|
|
|
|
compare(firstButton.clicks, firstClicks)
|
|
|
|
compare(thirdButton.clicks, thirdClicks)
|
|
|
|
tryCompare(control, "busy", false)
|
|
|
|
thirdButton = null
|
|
|
|
thirdClicks = 0
|
|
|
|
mouseClick(firstButton)
|
|
|
|
compare(firstButton.clicks, ++firstClicks)
|
|
|
|
|
|
|
|
// push - immediate operation
|
|
|
|
secondButton = control.push(testButton, StackView.Immediate)
|
|
|
|
compare(control.busy, false)
|
|
|
|
mouseClick(secondButton)
|
|
|
|
compare(secondButton.clicks, ++secondClicks)
|
|
|
|
|
|
|
|
// replace - immediate operation
|
|
|
|
thirdButton = control.replace(testButton, StackView.Immediate)
|
|
|
|
compare(control.busy, false)
|
|
|
|
secondButton = null
|
|
|
|
secondClicks = 0
|
|
|
|
mouseClick(thirdButton)
|
|
|
|
compare(thirdButton.clicks, ++thirdClicks)
|
|
|
|
|
|
|
|
// pop - immediate operation
|
|
|
|
control.pop(StackView.Immediate)
|
|
|
|
compare(control.busy, false)
|
|
|
|
thirdButton = null
|
|
|
|
thirdClicks = 0
|
|
|
|
mouseClick(firstButton)
|
|
|
|
compare(firstButton.clicks, ++firstClicks)
|
|
|
|
|
|
|
|
// push - null transition
|
|
|
|
control.pushEnter = null
|
|
|
|
control.pushExit = null
|
|
|
|
secondButton = control.push(testButton)
|
|
|
|
compare(control.busy, false)
|
|
|
|
mouseClick(secondButton)
|
|
|
|
compare(secondButton.clicks, ++secondClicks)
|
|
|
|
|
|
|
|
// replace - null transition
|
|
|
|
control.replaceEnter = null
|
|
|
|
control.replaceExit = null
|
|
|
|
thirdButton = control.replace(testButton)
|
|
|
|
compare(control.busy, false)
|
|
|
|
secondButton = null
|
|
|
|
secondClicks = 0
|
|
|
|
mouseClick(thirdButton)
|
|
|
|
compare(thirdButton.clicks, ++thirdClicks)
|
|
|
|
|
|
|
|
// pop - null transition
|
|
|
|
control.popEnter = null
|
|
|
|
control.popExit = null
|
|
|
|
control.pop()
|
|
|
|
compare(control.busy, false)
|
|
|
|
thirdButton = null
|
|
|
|
thirdClicks = 0
|
|
|
|
mouseClick(firstButton)
|
|
|
|
compare(firstButton.clicks, ++firstClicks)
|
|
|
|
}
|
2015-12-03 17:15:00 +00:00
|
|
|
|
2016-01-21 09:47:18 +00:00
|
|
|
Component {
|
|
|
|
id: mouseArea
|
|
|
|
MouseArea {
|
|
|
|
property int presses: 0
|
|
|
|
property int releases: 0
|
|
|
|
property int clicks: 0
|
|
|
|
property int doubleClicks: 0
|
|
|
|
property int cancels: 0
|
|
|
|
onPressed: ++presses
|
|
|
|
onReleased: ++releases
|
|
|
|
onClicked: ++clicks
|
|
|
|
onDoubleClicked: ++doubleClicks
|
|
|
|
onCanceled: ++cancels
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// QTBUG-50305
|
|
|
|
function test_events() {
|
2016-12-20 09:49:23 +00:00
|
|
|
var control = createTemporaryObject(stackView, testCase, {initialItem: mouseArea, width: testCase.width, height: testCase.height})
|
2016-01-21 09:47:18 +00:00
|
|
|
verify(control)
|
|
|
|
|
|
|
|
var testItem = control.currentItem
|
|
|
|
verify(testItem)
|
|
|
|
|
|
|
|
testItem.doubleClicked.connect(function() {
|
|
|
|
control.push(mouseArea) // ungrab -> cancel
|
|
|
|
})
|
|
|
|
|
|
|
|
mouseDoubleClickSequence(testItem)
|
|
|
|
compare(testItem.presses, 2)
|
|
|
|
compare(testItem.releases, 2)
|
|
|
|
compare(testItem.clicks, 1)
|
|
|
|
compare(testItem.doubleClicks, 1)
|
|
|
|
compare(testItem.pressed, false)
|
|
|
|
compare(testItem.cancels, 0)
|
|
|
|
}
|
|
|
|
|
2017-02-23 04:40:47 +00:00
|
|
|
function test_ungrab() {
|
|
|
|
var control = createTemporaryObject(stackView, testCase, {initialItem: mouseArea, width: testCase.width, height: testCase.height})
|
|
|
|
verify(control)
|
|
|
|
|
|
|
|
var testItem = control.currentItem
|
|
|
|
verify(testItem)
|
|
|
|
|
|
|
|
mousePress(testItem)
|
|
|
|
control.push(mouseArea)
|
|
|
|
tryCompare(control, "busy", false)
|
|
|
|
mouseRelease(testItem)
|
|
|
|
|
|
|
|
compare(testItem.presses, 1)
|
|
|
|
compare(testItem.releases, 0)
|
|
|
|
compare(testItem.clicks, 0)
|
|
|
|
compare(testItem.doubleClicks, 0)
|
|
|
|
compare(testItem.pressed, false)
|
|
|
|
compare(testItem.cancels, 1)
|
|
|
|
}
|
|
|
|
|
2015-12-03 17:15:00 +00:00
|
|
|
function test_failures() {
|
2016-12-20 09:49:23 +00:00
|
|
|
var control = createTemporaryObject(stackView, testCase, {initialItem: component})
|
2015-12-03 17:15:00 +00:00
|
|
|
verify(control)
|
|
|
|
|
|
|
|
ignoreWarning("QQmlComponent: Component is not ready")
|
2021-11-04 08:53:04 +00:00
|
|
|
ignoreWarning(/QML StackView: push: .*non-existent.qml:-1 No such file or directory/)
|
2015-12-15 21:01:44 +00:00
|
|
|
control.push(Qt.resolvedUrl("non-existent.qml"))
|
|
|
|
|
2015-12-03 17:15:00 +00:00
|
|
|
ignoreWarning("QQmlComponent: Component is not ready")
|
2021-11-04 08:53:04 +00:00
|
|
|
ignoreWarning(/QML StackView: replace: .*non-existent.qml:-1 No such file or directory/)
|
2015-12-15 21:01:44 +00:00
|
|
|
control.replace(Qt.resolvedUrl("non-existent.qml"))
|
|
|
|
|
2021-11-04 08:53:04 +00:00
|
|
|
ignoreWarning(/QML StackView: push: invalid url: x:\/\/\[v\]/)
|
2017-03-23 12:03:06 +00:00
|
|
|
control.push("x://[v]")
|
|
|
|
|
2021-11-04 08:53:04 +00:00
|
|
|
ignoreWarning(/QML StackView: replace: invalid url: x:\/\/\[v\]/)
|
2017-03-23 12:03:06 +00:00
|
|
|
control.replace("x://[v]")
|
|
|
|
|
2015-12-03 17:15:00 +00:00
|
|
|
control.pop()
|
|
|
|
}
|
2015-12-08 16:31:00 +00:00
|
|
|
|
|
|
|
Component {
|
|
|
|
id: rectangle
|
|
|
|
Rectangle {
|
|
|
|
property color initialColor
|
|
|
|
Component.onCompleted: initialColor = color
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function test_properties() {
|
2016-12-20 09:49:23 +00:00
|
|
|
var control = createTemporaryObject(stackView, testCase)
|
2015-12-08 16:31:00 +00:00
|
|
|
verify(control)
|
|
|
|
|
|
|
|
var rect = control.push(rectangle, {color: "#ff0000"})
|
|
|
|
compare(rect.color, "#ff0000")
|
|
|
|
compare(rect.initialColor, "#ff0000")
|
|
|
|
}
|
2016-05-27 07:51:35 +00:00
|
|
|
|
|
|
|
Component {
|
|
|
|
id: signalTest
|
|
|
|
Control {
|
|
|
|
id: ctrl
|
|
|
|
property SignalSpy activatedSpy: SignalSpy { target: ctrl.StackView; signalName: "activated" }
|
|
|
|
property SignalSpy activatingSpy: SignalSpy { target: ctrl.StackView; signalName: "activating" }
|
|
|
|
property SignalSpy deactivatedSpy: SignalSpy { target: ctrl.StackView; signalName: "deactivated" }
|
|
|
|
property SignalSpy deactivatingSpy: SignalSpy { target: ctrl.StackView; signalName: "deactivating" }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function test_signals() {
|
2016-12-20 09:49:23 +00:00
|
|
|
var control = createTemporaryObject(stackView, testCase)
|
2016-05-27 07:51:35 +00:00
|
|
|
verify(control)
|
|
|
|
|
|
|
|
var item1 = signalTest.createObject(control)
|
|
|
|
compare(item1.StackView.status, StackView.Inactive)
|
|
|
|
control.push(item1)
|
|
|
|
compare(item1.StackView.status, StackView.Active)
|
|
|
|
compare(item1.activatedSpy.count, 1)
|
|
|
|
compare(item1.activatingSpy.count, 1)
|
|
|
|
compare(item1.deactivatedSpy.count, 0)
|
|
|
|
compare(item1.deactivatingSpy.count, 0)
|
|
|
|
|
|
|
|
var item2 = signalTest.createObject(control)
|
|
|
|
compare(item2.StackView.status, StackView.Inactive)
|
|
|
|
control.push(item2)
|
|
|
|
compare(item2.StackView.status, StackView.Activating)
|
|
|
|
compare(item2.activatedSpy.count, 0)
|
|
|
|
compare(item2.activatingSpy.count, 1)
|
|
|
|
compare(item2.deactivatedSpy.count, 0)
|
|
|
|
compare(item2.deactivatingSpy.count, 0)
|
|
|
|
compare(item1.StackView.status, StackView.Deactivating)
|
|
|
|
compare(item1.activatedSpy.count, 1)
|
|
|
|
compare(item1.activatingSpy.count, 1)
|
|
|
|
compare(item1.deactivatedSpy.count, 0)
|
|
|
|
compare(item1.deactivatingSpy.count, 1)
|
|
|
|
tryCompare(item2.activatedSpy, "count", 1)
|
|
|
|
tryCompare(item1.deactivatedSpy, "count", 1)
|
|
|
|
|
|
|
|
control.pop()
|
|
|
|
compare(item2.StackView.status, StackView.Deactivating)
|
|
|
|
compare(item2.activatedSpy.count, 1)
|
|
|
|
compare(item2.activatingSpy.count, 1)
|
|
|
|
compare(item2.deactivatedSpy.count, 0)
|
|
|
|
compare(item2.deactivatingSpy.count, 1)
|
|
|
|
compare(item1.StackView.status, StackView.Activating)
|
|
|
|
compare(item1.activatedSpy.count, 1)
|
|
|
|
compare(item1.activatingSpy.count, 2)
|
|
|
|
compare(item1.deactivatedSpy.count, 1)
|
|
|
|
compare(item1.deactivatingSpy.count, 1)
|
|
|
|
tryCompare(item1.activatedSpy, "count", 2)
|
|
|
|
}
|
2016-09-26 12:10:14 +00:00
|
|
|
|
2016-09-23 21:10:02 +00:00
|
|
|
// QTBUG-56158
|
|
|
|
function test_repeatedPop() {
|
2016-12-20 09:49:23 +00:00
|
|
|
var control = createTemporaryObject(stackView, testCase, {initialItem: component, width: testCase.width, height: testCase.height})
|
2016-09-23 21:10:02 +00:00
|
|
|
verify(control)
|
|
|
|
|
|
|
|
for (var i = 0; i < 12; ++i)
|
|
|
|
control.push(component)
|
|
|
|
tryCompare(control, "busy", false)
|
|
|
|
|
|
|
|
while (control.depth > 1) {
|
|
|
|
control.pop()
|
|
|
|
wait(50)
|
|
|
|
}
|
|
|
|
tryCompare(control, "busy", false)
|
|
|
|
}
|
2016-12-06 08:31:22 +00:00
|
|
|
|
|
|
|
function test_pushSameItem() {
|
2017-05-09 06:56:50 +00:00
|
|
|
var control = createTemporaryObject(stackView, testCase)
|
2016-12-06 08:31:22 +00:00
|
|
|
verify(control)
|
|
|
|
|
|
|
|
control.push(item, StackView.Immediate)
|
|
|
|
compare(control.currentItem, item)
|
|
|
|
compare(control.depth, 1)
|
|
|
|
|
|
|
|
// Pushing the same Item should do nothing.
|
2021-11-04 08:53:04 +00:00
|
|
|
ignoreWarning(/QML StackView: push: nothing to push/)
|
2016-12-06 08:31:22 +00:00
|
|
|
control.push(item, StackView.Immediate)
|
|
|
|
compare(control.currentItem, item)
|
|
|
|
compare(control.depth, 1)
|
|
|
|
|
|
|
|
// Push a component so that it becomes current.
|
|
|
|
var current = control.push(component, StackView.Immediate)
|
|
|
|
compare(control.currentItem, current)
|
|
|
|
compare(control.depth, 2)
|
|
|
|
|
|
|
|
// Push a bunch of stuff. "item" is already in the stack, so it should be ignored.
|
|
|
|
current = control.push(component, item, StackView.Immediate)
|
|
|
|
verify(current !== item)
|
|
|
|
compare(control.currentItem, current)
|
|
|
|
compare(control.depth, 3)
|
|
|
|
}
|
2017-01-11 14:44:16 +00:00
|
|
|
|
|
|
|
function test_visible() {
|
2017-05-09 06:56:50 +00:00
|
|
|
var control = createTemporaryObject(stackView, testCase)
|
2017-01-11 14:44:16 +00:00
|
|
|
verify(control)
|
|
|
|
|
|
|
|
var item1 = component.createObject(control)
|
|
|
|
control.push(item1, StackView.Immediate)
|
|
|
|
compare(item1.visible, true)
|
|
|
|
compare(item1.StackView.visible, item1.visible)
|
|
|
|
|
|
|
|
var item2 = component.createObject(control)
|
|
|
|
control.push(item2, StackView.Immediate)
|
|
|
|
compare(item1.visible, false)
|
|
|
|
compare(item2.visible, true)
|
|
|
|
compare(item1.StackView.visible, false)
|
|
|
|
compare(item2.StackView.visible, true)
|
|
|
|
|
|
|
|
// keep explicitly visible
|
|
|
|
item2.StackView.visible = true
|
|
|
|
control.push(component, StackView.Immediate)
|
|
|
|
compare(item2.visible, true)
|
|
|
|
compare(item2.StackView.visible, true)
|
|
|
|
|
|
|
|
// show underneath
|
|
|
|
item1.StackView.visible = true
|
|
|
|
compare(item1.visible, true)
|
|
|
|
compare(item1.StackView.visible, true)
|
|
|
|
|
|
|
|
control.pop(StackView.Immediate)
|
|
|
|
compare(item2.visible, true)
|
|
|
|
compare(item2.StackView.visible, true)
|
|
|
|
|
|
|
|
// hide the top-most
|
|
|
|
item2.StackView.visible = false
|
|
|
|
compare(item2.visible, false)
|
|
|
|
compare(item2.StackView.visible, false)
|
|
|
|
|
|
|
|
// reset the top-most
|
|
|
|
item2.StackView.visible = undefined
|
|
|
|
compare(item2.visible, true)
|
|
|
|
compare(item2.StackView.visible, true)
|
|
|
|
|
|
|
|
// reset underneath
|
|
|
|
item1.StackView.visible = undefined
|
|
|
|
compare(item1.visible, false)
|
|
|
|
compare(item1.StackView.visible, false)
|
|
|
|
|
|
|
|
control.pop(StackView.Immediate)
|
|
|
|
compare(item1.visible, true)
|
|
|
|
compare(item1.StackView.visible, true)
|
|
|
|
}
|
2017-04-03 17:56:04 +00:00
|
|
|
|
2017-08-03 18:04:57 +00:00
|
|
|
function test_resolveInitialItem() {
|
|
|
|
var control = createTemporaryObject(stackView, testCase, {initialItem: "TestItem.qml"})
|
|
|
|
verify(control)
|
|
|
|
verify(control.currentItem)
|
|
|
|
}
|
|
|
|
|
2017-04-03 17:56:04 +00:00
|
|
|
function test_resolve() {
|
|
|
|
var control = createTemporaryObject(stackView, testCase)
|
|
|
|
verify(control)
|
|
|
|
|
|
|
|
var item = control.push("TestItem.qml")
|
|
|
|
compare(control.depth, 1)
|
|
|
|
verify(item)
|
|
|
|
}
|
2017-12-19 09:33:50 +00:00
|
|
|
|
|
|
|
// QTBUG-65084
|
|
|
|
function test_mouseArea() {
|
|
|
|
var ma = createTemporaryObject(mouseArea, testCase, {width: testCase.width, height: testCase.height})
|
|
|
|
verify(ma)
|
|
|
|
|
|
|
|
var control = stackView.createObject(ma, {width: testCase.width, height: testCase.height})
|
|
|
|
verify(control)
|
|
|
|
|
|
|
|
mousePress(control)
|
|
|
|
verify(ma.pressed)
|
|
|
|
|
|
|
|
mouseRelease(control)
|
|
|
|
verify(!ma.pressed)
|
|
|
|
|
|
|
|
var touch = touchEvent(control)
|
|
|
|
touch.press(0, control).commit()
|
|
|
|
verify(ma.pressed)
|
|
|
|
|
|
|
|
touch.release(0, control).commit()
|
|
|
|
verify(!ma.pressed)
|
|
|
|
}
|
2018-03-19 09:18:57 +00:00
|
|
|
|
|
|
|
// Separate function to ensure that the temporary value created to hold the return value of the Qt.createComponent()
|
|
|
|
// call is out of scope when the caller calls gc().
|
|
|
|
function stackViewFactory()
|
|
|
|
{
|
|
|
|
return createTemporaryObject(stackView, testCase, {initialItem: Qt.createComponent("TestItem.qml")})
|
|
|
|
}
|
|
|
|
|
|
|
|
function test_initalItemOwnership()
|
|
|
|
{
|
|
|
|
var control = stackViewFactory()
|
|
|
|
verify(control)
|
|
|
|
gc()
|
|
|
|
verify(control.initialItem)
|
|
|
|
}
|
2019-11-27 10:00:59 +00:00
|
|
|
|
|
|
|
// Need to use this specific structure in order to reproduce the crash.
|
|
|
|
Component {
|
|
|
|
id: clearUponDestructionContainerComponent
|
|
|
|
|
|
|
|
Item {
|
|
|
|
id: container
|
|
|
|
objectName: "container"
|
|
|
|
|
|
|
|
property alias control: stackView
|
|
|
|
property var onDestructionCallback
|
|
|
|
|
|
|
|
property Component clearUponDestructionComponent: Component {
|
|
|
|
id: clearUponDestructionComponent
|
|
|
|
|
|
|
|
Item {
|
|
|
|
objectName: "clearUponDestructionItem"
|
2022-08-30 08:02:40 +00:00
|
|
|
onParentChanged: {
|
|
|
|
// We don't actually do this on destruction because destruction is delayed.
|
|
|
|
// Rather, we do it when we get un-parented.
|
|
|
|
if (parent === null)
|
|
|
|
container.onDestructionCallback(stackView)
|
|
|
|
}
|
2019-11-27 10:00:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
StackView {
|
|
|
|
id: stackView
|
|
|
|
initialItem: Item {
|
|
|
|
objectName: "initialItem"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// QTBUG-80353
|
|
|
|
// Tests that calling clear() in Component.onDestruction in response to that
|
|
|
|
// item being removed (e.g. via an earlier call to clear()) results in a warning and not a crash.
|
|
|
|
function test_recursiveClearClear() {
|
|
|
|
let container = createTemporaryObject(clearUponDestructionContainerComponent, testCase,
|
|
|
|
{ onDestructionCallback: function(stackView) { stackView.clear(StackView.Immediate) }})
|
|
|
|
verify(container)
|
|
|
|
|
|
|
|
let control = container.control
|
|
|
|
control.push(container.clearUponDestructionComponent, StackView.Immediate)
|
|
|
|
|
|
|
|
// Shouldn't crash.
|
2020-05-27 08:22:37 +00:00
|
|
|
ignoreWarning(/.*cannot clear while already in the process of completing a clear/)
|
2019-11-27 10:00:59 +00:00
|
|
|
control.clear(StackView.Immediate)
|
|
|
|
}
|
|
|
|
|
|
|
|
function test_recursivePopClear() {
|
|
|
|
let container = createTemporaryObject(clearUponDestructionContainerComponent, testCase,
|
|
|
|
{ onDestructionCallback: function(stackView) { stackView.clear(StackView.Immediate) }})
|
|
|
|
verify(container)
|
|
|
|
|
|
|
|
let control = container.control
|
|
|
|
control.push(container.clearUponDestructionComponent, StackView.Immediate)
|
|
|
|
|
|
|
|
// Pop all items except the first, removing the second item we pushed in the process.
|
|
|
|
// Shouldn't crash.
|
2020-05-27 08:22:37 +00:00
|
|
|
ignoreWarning(/.*cannot clear while already in the process of completing a pop/)
|
2019-11-27 10:00:59 +00:00
|
|
|
control.pop(null, StackView.Immediate)
|
|
|
|
}
|
|
|
|
|
|
|
|
function test_recursivePopPop() {
|
|
|
|
let container = createTemporaryObject(clearUponDestructionContainerComponent, testCase,
|
|
|
|
{ onDestructionCallback: function(stackView) { stackView.pop(null, StackView.Immediate) }})
|
|
|
|
verify(container)
|
|
|
|
|
|
|
|
let control = container.control
|
|
|
|
// Push an extra item so that we can call pop(null) and reproduce the conditions for the crash.
|
|
|
|
control.push(component, StackView.Immediate)
|
|
|
|
control.push(container.clearUponDestructionComponent, StackView.Immediate)
|
|
|
|
|
|
|
|
// Pop the top item, then pop down to the first item in response.
|
2020-05-27 08:22:37 +00:00
|
|
|
ignoreWarning(/.*cannot pop while already in the process of completing a pop/)
|
2019-11-27 10:00:59 +00:00
|
|
|
control.pop(StackView.Immediate)
|
|
|
|
}
|
|
|
|
|
|
|
|
function test_recursiveReplaceClear() {
|
|
|
|
let container = createTemporaryObject(clearUponDestructionContainerComponent, testCase,
|
|
|
|
{ onDestructionCallback: function(stackView) { stackView.clear(StackView.Immediate) }})
|
|
|
|
verify(container)
|
|
|
|
|
|
|
|
let control = container.control
|
|
|
|
control.push(container.clearUponDestructionComponent, StackView.Immediate)
|
|
|
|
|
|
|
|
// Replace the top item, then clear in response.
|
2020-05-27 08:22:37 +00:00
|
|
|
ignoreWarning(/.*cannot clear while already in the process of completing a replace/)
|
2019-11-27 10:00:59 +00:00
|
|
|
control.replace(component, StackView.Immediate)
|
|
|
|
}
|
|
|
|
|
|
|
|
function test_recursiveClearReplace() {
|
|
|
|
let container = createTemporaryObject(clearUponDestructionContainerComponent, testCase,
|
|
|
|
{ onDestructionCallback: function(stackView) { stackView.replace(component, StackView.Immediate) }})
|
|
|
|
verify(container)
|
|
|
|
|
|
|
|
let control = container.control
|
|
|
|
control.push(container.clearUponDestructionComponent, StackView.Immediate)
|
|
|
|
|
|
|
|
// Replace the top item, then clear in response.
|
2020-05-27 08:22:37 +00:00
|
|
|
ignoreWarning(/.*cannot replace while already in the process of completing a clear/)
|
2019-11-27 10:00:59 +00:00
|
|
|
control.clear(StackView.Immediate)
|
|
|
|
}
|
2019-09-27 08:44:42 +00:00
|
|
|
|
|
|
|
Component {
|
|
|
|
id: rectangleComponent
|
|
|
|
Rectangle {}
|
|
|
|
}
|
|
|
|
|
|
|
|
Component {
|
|
|
|
id: qtbug57267_StackViewComponent
|
|
|
|
|
|
|
|
StackView {
|
|
|
|
id: stackView
|
|
|
|
|
|
|
|
popEnter: Transition {
|
|
|
|
XAnimator { from: (stackView.mirrored ? -1 : 1) * -stackView.width; to: 0; duration: 400; easing.type: Easing.Linear }
|
|
|
|
}
|
|
|
|
popExit: Transition {
|
|
|
|
XAnimator { from: 0; to: (stackView.mirrored ? -1 : 1) * stackView.width; duration: 400; easing.type: Easing.Linear }
|
|
|
|
}
|
|
|
|
pushEnter: Transition {
|
|
|
|
XAnimator { from: (stackView.mirrored ? -1 : 1) * stackView.width; to: 0; duration: 400; easing.type: Easing.Linear }
|
|
|
|
}
|
|
|
|
pushExit: Transition {
|
|
|
|
XAnimator { from: 0; to: (stackView.mirrored ? -1 : 1) * -stackView.width; duration: 400; easing.type: Easing.Linear }
|
|
|
|
}
|
|
|
|
replaceEnter: Transition {
|
|
|
|
XAnimator { from: (stackView.mirrored ? -1 : 1) * stackView.width; to: 0; duration: 400; easing.type: Easing.Linear }
|
|
|
|
}
|
|
|
|
replaceExit: Transition {
|
|
|
|
XAnimator { from: 0; to: (stackView.mirrored ? -1 : 1) * -stackView.width; duration: 400; easing.type: Easing.Linear }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function test_qtbug57267() {
|
|
|
|
let redRect = createTemporaryObject(rectangleComponent, testCase, { color: "red" })
|
|
|
|
verify(redRect)
|
|
|
|
let blueRect = createTemporaryObject(rectangleComponent, testCase, { color: "blue" })
|
|
|
|
verify(blueRect)
|
|
|
|
let control = createTemporaryObject(qtbug57267_StackViewComponent, testCase,
|
|
|
|
{ "anchors.fill": testCase, initialItem: redRect })
|
|
|
|
verify(control)
|
|
|
|
|
|
|
|
control.replace(blueRect)
|
|
|
|
compare(control.currentItem, blueRect)
|
|
|
|
compare(control.depth, 1)
|
|
|
|
|
|
|
|
// Wait until the animation has started and then interrupt it by pushing the redRect.
|
|
|
|
tryCompare(control, "busy", true)
|
|
|
|
control.replace(redRect)
|
|
|
|
// The blue rect shouldn't be visible since we replaced it and therefore interrupted its animation.
|
|
|
|
tryCompare(blueRect, "visible", false)
|
|
|
|
// We did the replace very early on, so the transition for the redRect should still be happening.
|
|
|
|
compare(control.busy, true)
|
|
|
|
compare(redRect.visible, true)
|
|
|
|
|
|
|
|
// After finishing the transition, the red rect should still be visible.
|
|
|
|
tryCompare(control, "busy", false)
|
|
|
|
compare(redRect.visible, true)
|
|
|
|
}
|
2020-05-27 08:22:37 +00:00
|
|
|
|
|
|
|
// QTBUG-84381
|
|
|
|
function test_clearAndPushAfterDepthChange() {
|
|
|
|
var control = createTemporaryObject(stackView, testCase, {
|
|
|
|
popEnter: null, popExit: null, pushEnter: null,
|
|
|
|
pushExit: null, replaceEnter: null, replaceExit: null
|
|
|
|
})
|
|
|
|
verify(control)
|
|
|
|
|
|
|
|
control.depthChanged.connect(function() {
|
|
|
|
if (control.depth === 2) {
|
|
|
|
// Shouldn't assert.
|
|
|
|
ignoreWarning(/.*QML StackView: cannot clear while already in the process of completing a push/)
|
|
|
|
control.clear()
|
|
|
|
// Shouldn't crash.
|
|
|
|
ignoreWarning(/.*QML StackView: cannot push while already in the process of completing a push/)
|
|
|
|
control.push(component)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
control.push(component)
|
|
|
|
control.push(component)
|
|
|
|
}
|
StackView: complete animations when using StackView.Immediate transition
The idea behind using StackView.Immediate is to immediately move the
stack view item into the new state without running any animations.
However the preexisting code in QQuickStackViewPrivate::completeTransition
was doing nothing, because the animations didn't have the proper target set.
So they could never be started and/or completed.
As a result, the pushed stack view item could appear at the wrong position
and with the other properties set incorrectly (for example, opacity).
The new code uses the same approach, as in startTransition(), but simplifies
it to execute only the animations.
Instead of actually running the animations, a new
QAbstractAnimationJob::complete() method is added, which simulates the
animation progress by starting the animation, forwarding it to the end, and
completing it. This results in all the properties receiving the correct
values, and the stack view items being shown correctly.
Fixes: QTBUG-96966
Fixes: QTBUG-61496
Pick-to: 6.2
Change-Id: I990a133881c66e3ecb83887c60596a5d45e570d9
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
2021-10-13 12:10:27 +00:00
|
|
|
|
|
|
|
// QTBUG-96966
|
|
|
|
// Generate a stack view with complex transition animations and make sure
|
|
|
|
// that the item's state is restored correctly when StackView.Immediate
|
|
|
|
// operation is used
|
|
|
|
Component {
|
|
|
|
id: qtbug96966_stackViewComponent
|
|
|
|
StackView {
|
|
|
|
id: qtbug96966_stackView
|
|
|
|
pushEnter: Transition {
|
|
|
|
ParallelAnimation {
|
|
|
|
NumberAnimation {
|
|
|
|
property: "x"
|
|
|
|
from: qtbug96966_stackView.width
|
|
|
|
to: 0
|
|
|
|
duration: 100
|
|
|
|
easing.type: Easing.OutCubic
|
|
|
|
}
|
|
|
|
NumberAnimation {
|
|
|
|
property: "opacity"
|
|
|
|
from: 0
|
|
|
|
to: 1
|
|
|
|
duration: 100
|
|
|
|
easing.type: Easing.OutCubic
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
pushExit: Transition {
|
|
|
|
ParallelAnimation {
|
|
|
|
NumberAnimation {
|
|
|
|
property: "x"
|
|
|
|
from: 0
|
|
|
|
to: -qtbug96966_stackView.width
|
|
|
|
duration: 100
|
|
|
|
easing.type: Easing.OutCubic
|
|
|
|
}
|
|
|
|
NumberAnimation {
|
|
|
|
property: "opacity"
|
|
|
|
from: 1
|
|
|
|
to: 0
|
|
|
|
duration: 100
|
|
|
|
easing.type: Easing.OutCubic
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
popExit: Transition {
|
|
|
|
ParallelAnimation {
|
|
|
|
NumberAnimation {
|
|
|
|
property: "x"
|
|
|
|
from: 0
|
|
|
|
to: qtbug96966_stackView.width
|
|
|
|
duration: 100
|
|
|
|
easing.type: Easing.OutCubic
|
|
|
|
}
|
|
|
|
NumberAnimation {
|
|
|
|
property: "opacity"
|
|
|
|
from: 1
|
|
|
|
to: 0
|
|
|
|
duration: 100
|
|
|
|
easing.type: Easing.OutCubic
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
popEnter: Transition {
|
|
|
|
ParallelAnimation {
|
|
|
|
NumberAnimation {
|
|
|
|
property: "x"
|
|
|
|
from: -qtbug96966_stackView.width
|
|
|
|
to: 0
|
|
|
|
duration: 100
|
|
|
|
easing.type: Easing.OutCubic
|
|
|
|
}
|
|
|
|
NumberAnimation {
|
|
|
|
property: "opacity"
|
|
|
|
from: 0
|
|
|
|
to: 1
|
|
|
|
duration: 100
|
|
|
|
easing.type: Easing.OutCubic
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function test_immediateTransitionPropertiesApplied() {
|
|
|
|
let redRect = createTemporaryObject(rectangleComponent, testCase, { color: "red" })
|
|
|
|
verify(redRect)
|
|
|
|
let blueRect = createTemporaryObject(rectangleComponent, testCase, { color: "blue" })
|
|
|
|
verify(blueRect)
|
|
|
|
let control = createTemporaryObject(qtbug96966_stackViewComponent, testCase,
|
|
|
|
{ "anchors.fill": testCase, initialItem: redRect })
|
|
|
|
verify(control)
|
|
|
|
|
|
|
|
control.push(blueRect)
|
|
|
|
// wait until the animation is finished
|
|
|
|
tryCompare(control, "busy", true)
|
|
|
|
tryCompare(control, "busy", false)
|
|
|
|
// Now the red rectangle should become invisible and move to the left
|
|
|
|
compare(redRect.x, -200) // the window width is 200
|
|
|
|
compare(redRect.opacity, 0)
|
|
|
|
|
|
|
|
control.pop(null, StackView.Immediate)
|
|
|
|
// The red rectangle immediately restores its initial state (both
|
|
|
|
// position and opacity).
|
|
|
|
compare(redRect.x, 0)
|
|
|
|
compare(redRect.opacity, 1)
|
|
|
|
// Blue rectangle is moved to the right and becomes invisible
|
|
|
|
compare(blueRect.x, 200)
|
|
|
|
compare(blueRect.opacity, 0)
|
|
|
|
}
|
2020-05-18 09:11:07 +00:00
|
|
|
|
|
|
|
function test_requiredProperties() {
|
|
|
|
var control = createTemporaryObject(stackView, testCase)
|
|
|
|
verify(control)
|
|
|
|
let failedPush = control.push(withRequired)
|
|
|
|
compare(failedPush, null);
|
|
|
|
control.push(withRequired, {"i": 42})
|
|
|
|
verify(control.currentItem.i === 42)
|
|
|
|
control.pop(StackView.Immediate)
|
|
|
|
}
|
2022-06-22 06:36:47 +00:00
|
|
|
|
|
|
|
// QTBUG-104491
|
|
|
|
// Tests that correctly set a busy state when the transition is stolen(canceled)
|
|
|
|
function test_continuousTransition() {
|
|
|
|
let redRect = createTemporaryObject(rectangleComponent, testCase, { color: "red" })
|
|
|
|
verify(redRect)
|
|
|
|
let blueRect = createTemporaryObject(rectangleComponent, testCase, { color: "blue" })
|
|
|
|
verify(blueRect)
|
|
|
|
let greenRect = createTemporaryObject(rectangleComponent, testCase, { color: "green" })
|
|
|
|
verify(greenRect)
|
|
|
|
let yellowRect = createTemporaryObject(rectangleComponent, testCase, { color: "yellow" })
|
|
|
|
verify(yellowRect)
|
|
|
|
let control = createTemporaryObject(qtbug96966_stackViewComponent, testCase,
|
|
|
|
{ "anchors.fill": testCase, initialItem: redRect })
|
|
|
|
verify(control)
|
|
|
|
|
|
|
|
control.push(blueRect)
|
|
|
|
control.pop()
|
|
|
|
tryCompare(control, "busy", true)
|
|
|
|
tryCompare(control, "busy", false)
|
|
|
|
|
|
|
|
control.push(blueRect)
|
|
|
|
control.push(greenRect)
|
|
|
|
control.push(yellowRect)
|
|
|
|
tryCompare(control, "busy", true)
|
|
|
|
tryCompare(control, "busy", false)
|
|
|
|
|
|
|
|
control.pop()
|
|
|
|
control.pop()
|
|
|
|
control.pop()
|
|
|
|
tryCompare(control, "busy", true)
|
|
|
|
tryCompare(control, "busy", false)
|
|
|
|
}
|
2022-08-30 08:02:40 +00:00
|
|
|
|
|
|
|
Component {
|
|
|
|
id: cppComponent
|
|
|
|
|
|
|
|
StackView {
|
|
|
|
id: stackView
|
|
|
|
anchors.fill: parent
|
|
|
|
initialItem: cppComponent
|
|
|
|
|
|
|
|
property Component cppComponent: ComponentCreator.createComponent("import QtQuick; Rectangle { color: \"navajowhite\" }")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Test that a component created in C++ works with StackView.
|
|
|
|
function test_componentCreatedInCpp() {
|
|
|
|
let control = createTemporaryObject(cppComponent, testCase)
|
|
|
|
verify(control)
|
|
|
|
compare(control.currentItem.color, Qt.color("navajowhite"))
|
|
|
|
|
|
|
|
control.push(control.cppComponent, { color: "tomato" })
|
|
|
|
compare(control.currentItem.color, Qt.color("tomato"))
|
|
|
|
}
|
2022-10-06 09:32:02 +00:00
|
|
|
|
|
|
|
Component {
|
|
|
|
id: noProperties
|
|
|
|
Item {}
|
|
|
|
}
|
|
|
|
|
|
|
|
Component {
|
|
|
|
id: invalidProperties
|
|
|
|
|
|
|
|
StackView {
|
|
|
|
anchors.fill: parent
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function test_invalidProperties() {
|
|
|
|
let control = createTemporaryObject(invalidProperties, testCase)
|
|
|
|
verify(control)
|
|
|
|
verify(control.empty)
|
|
|
|
ignoreWarning(/Cannot resolve property "unknownProperty.test"/)
|
|
|
|
control.push(noProperties, { "unknownProperty.test": "crashes" })
|
|
|
|
verify(!control.empty)
|
|
|
|
}
|
2015-01-30 19:57:40 +00:00
|
|
|
}
|