Revision control
Copy as Markdown
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtTest
import QtQuick.Controls
TestCase {
id: testCase
width: 400
height: 400
visible: true
when: windowShown
name: "RangeSlider"
Component {
id: signalSpy
SignalSpy { }
}
Component {
id: sliderComponent
RangeSlider {
id: slider
Component.onCompleted: {
first.handle.objectName = "firstHandle"
second.handle.objectName = "secondHandle"
}
Text {
text: "1"
parent: slider.first.handle
anchors.centerIn: parent
}
Text {
text: "2"
parent: slider.second.handle
anchors.centerIn: parent
}
}
}
function init() {
failOnWarning(/.?/)
}
function test_defaults() {
let control = createTemporaryObject(sliderComponent, testCase)
verify(control)
compare(control.stepSize, 0)
compare(control.snapMode, RangeSlider.NoSnap)
compare(control.orientation, Qt.Horizontal)
compare(control.horizontal, true)
compare(control.vertical, false)
}
function test_values() {
let control = createTemporaryObject(sliderComponent, testCase)
verify(control)
compare(control.first.value, 0.0)
compare(control.second.value, 1.0)
control.first.value = 0.5
compare(control.first.value, 0.5)
control.first.value = 1.0
compare(control.first.value, 1.0)
control.first.value = -1.0
compare(control.first.value, 0.0)
control.first.value = 2.0
compare(control.first.value, 1.0)
control.first.value = 0
compare(control.first.value, 0.0)
control.second.value = 0.5
compare(control.second.value, 0.5)
control.first.value = 1
compare(control.first.value, 0.5)
control.second.value = 0
compare(control.second.value, 0.5)
}
function test_range() {
let control = createTemporaryObject(sliderComponent, testCase, { from: 0, to: 100, "first.value": 50, "second.value": 100 })
verify(control)
compare(control.from, 0)
compare(control.to, 100)
compare(control.first.value, 50)
compare(control.second.value, 100)
compare(control.first.position, 0.5)
compare(control.second.position, 1.0)
control.first.value = 1000
compare(control.first.value, 100)
compare(control.first.position, 1.0)
control.first.value = -1
compare(control.first.value, 0)
compare(control.first.position, 0)
control.from = 25
compare(control.from, 25)
compare(control.first.value, 25)
compare(control.first.position, 0)
control.to = 75
compare(control.to, 75)
compare(control.second.value, 75)
compare(control.second.position, 1.0)
control.first.value = 50
compare(control.first.value, 50)
compare(control.first.position, 0.5)
}
function test_setToFromUpdatesHandles() {
let control = createTemporaryObject(sliderComponent, testCase, { from: 0, to: 100, "first.value": 50, "second.value": 75 })
verify(control)
let firstPos = control.first.position
let secondPos = control.second.position
let firstPosChangesSpy = signalSpy.createObject(control, {target: control.first, signalName: "positionChanged"})
verify(firstPosChangesSpy.valid)
let secondPosChangesSpy = signalSpy.createObject(control, {target: control.second, signalName: "positionChanged"})
verify(secondPosChangesSpy.valid)
// Increasing the 'to' value, so the positions of the handles should be
// moved to the left (become smaller)
control.to = 200;
compare(firstPosChangesSpy.count, 1)
compare(secondPosChangesSpy.count, 1)
verify(control.first.position < firstPos)
verify(control.second.position < secondPos)
// resetting the values
control.to = 100
firstPosChangesSpy.clear()
secondPosChangesSpy.clear()
firstPos = control.first.position
secondPos = control.second.position
// Decreasing the 'from' value, so the positions of the handles should
// be moved to the right (become larger)
control.from = -100
compare(firstPosChangesSpy.count, 1)
compare(secondPosChangesSpy.count, 1)
verify(control.first.position > firstPos)
verify(control.second.position > secondPos)
}
function test_setValues() {
let control = createTemporaryObject(sliderComponent, testCase)
verify(control)
compare(control.from, 0)
compare(control.to, 1)
compare(control.first.value, 0)
compare(control.second.value, 1)
compare(control.first.position, 0.0)
compare(control.second.position, 1.0)
control.setValues(100, 200)
compare(control.first.value, 1)
compare(control.second.value, 1)
compare(control.first.position, 1.0)
compare(control.second.position, 1.0)
control.to = 300;
control.setValues(100, 200)
compare(control.first.value, 100)
compare(control.second.value, 200)
compare(control.first.position, 0.333333)
compare(control.second.position, 0.666666)
}
function test_inverted() {
let control = createTemporaryObject(sliderComponent, testCase, { from: 1.0, to: -1.0 })
verify(control)
compare(control.from, 1.0)
compare(control.to, -1.0)
compare(control.first.value, 0.0)
compare(control.first.position, 0.5)
compare(control.second.value, 0.0);
compare(control.second.position, 0.5);
control.first.value = 2.0
compare(control.first.value, 1.0)
compare(control.first.position, 0.0)
compare(control.second.value, 0.0);
compare(control.second.position, 0.5);
control.first.value = -2.0
compare(control.first.value, 0.0)
compare(control.first.position, 0.5)
compare(control.second.value, 0.0);
compare(control.second.position, 0.5);
control.first.value = 0.0
compare(control.first.value, 0.0)
compare(control.first.position, 0.5)
compare(control.second.value, 0.0);
compare(control.second.position, 0.5);
}
function test_visualPosition() {
let control = createTemporaryObject(sliderComponent, testCase)
verify(control)
compare(control.first.value, 0.0)
compare(control.first.position, 0.0)
compare(control.first.visualPosition, 0.0)
compare(control.second.value, 1.0)
compare(control.second.position, 1.0)
compare(control.second.visualPosition, 1.0)
control.first.value = 0.25
compare(control.first.value, 0.25)
compare(control.first.position, 0.25)
compare(control.first.visualPosition, 0.25)
compare(control.second.value, 1.0)
compare(control.second.position, 1.0)
compare(control.second.visualPosition, 1.0)
// RTL locale
control.locale = Qt.locale("ar_EG")
compare(control.first.visualPosition, 0.25)
compare(control.second.visualPosition, 1.0)
// RTL locale + LayoutMirroring
control.LayoutMirroring.enabled = true
compare(control.first.visualPosition, 0.75)
compare(control.second.visualPosition, 0.0)
// LTR locale + LayoutMirroring
control.locale = Qt.locale("en_US")
compare(control.first.visualPosition, 0.75)
compare(control.second.visualPosition, 0.0)
// LTR locale
control.LayoutMirroring.enabled = false
compare(control.first.visualPosition, 0.25)
compare(control.second.visualPosition, 1.0)
// LayoutMirroring
control.LayoutMirroring.enabled = true
compare(control.first.visualPosition, 0.75)
compare(control.second.visualPosition, 0.0)
}
function test_orientation() {
let control = createTemporaryObject(sliderComponent, testCase)
verify(control)
compare(control.orientation, Qt.Horizontal)
compare(control.horizontal, true)
compare(control.vertical, false)
verify(control.width > control.height)
control.orientation = Qt.Vertical
compare(control.orientation, Qt.Vertical)
compare(control.horizontal, false)
compare(control.vertical, true)
verify(control.width < control.height)
}
function test_mouse_data() {
return [
{ tag: "horizontal", orientation: Qt.Horizontal, live: false },
{ tag: "vertical", orientation: Qt.Vertical, live: false },
{ tag: "horizontal:live", orientation: Qt.Horizontal, live: true },
{ tag: "vertical:live", orientation: Qt.Vertical, live: true }
]
}
function test_mouse(data) {
let control = createTemporaryObject(sliderComponent, testCase, { orientation: data.orientation, live: data.live })
verify(control)
let firstPressedSpy = signalSpy.createObject(control, {target: control.first, signalName: "pressedChanged"})
verify(firstPressedSpy.valid)
let firstMovedSpy = signalSpy.createObject(control, {target: control.first, signalName: "moved"})
verify(firstMovedSpy.valid)
let secondPressedSpy = signalSpy.createObject(control, {target: control.second, signalName: "pressedChanged"})
verify(secondPressedSpy.valid)
let secondMovedSpy = signalSpy.createObject(control, {target: control.second, signalName: "moved"})
verify(secondMovedSpy.valid)
// Press and release the first handle without moving it.
mousePress(control, control.leftPadding, control.height - control.bottomPadding - 1, Qt.LeftButton)
compare(firstPressedSpy.count, 1)
compare(firstMovedSpy.count, 0)
compare(secondPressedSpy.count, 0)
compare(secondMovedSpy.count, 0)
compare(control.first.pressed, true)
compare(control.first.value, 0.0)
compare(control.first.position, 0.0)
compare(control.second.pressed, false)
compare(control.second.value, 1.0)
compare(control.second.position, 1.0)
mouseRelease(control, control.leftPadding, control.height - control.bottomPadding - 1, Qt.LeftButton)
compare(firstPressedSpy.count, 2)
compare(firstMovedSpy.count, 0)
compare(secondPressedSpy.count, 0)
compare(secondMovedSpy.count, 0)
compare(control.first.pressed, false)
compare(control.first.value, 0.0)
compare(control.first.position, 0.0)
compare(control.second.pressed, false)
compare(control.second.value, 1.0)
compare(control.second.position, 1.0)
// Press and release the second handle without moving it.
mousePress(control, control.width - control.rightPadding - 1, control.topPadding, Qt.LeftButton)
compare(firstPressedSpy.count, 2)
compare(firstMovedSpy.count, 0)
compare(secondPressedSpy.count, 1)
compare(secondMovedSpy.count, 0)
compare(control.first.pressed, false)
compare(control.first.value, 0.0)
compare(control.first.position, 0.0)
compare(control.second.pressed, true)
compare(control.second.value, 1.0)
compare(control.second.position, 1.0)
mouseRelease(control, control.width - control.rightPadding - 1, control.topPadding, Qt.LeftButton)
compare(firstPressedSpy.count, 2)
compare(firstMovedSpy.count, 0)
compare(secondPressedSpy.count, 2)
compare(secondMovedSpy.count, 0)
compare(control.first.pressed, false)
compare(control.first.value, 0.0)
compare(control.first.position, 0.0)
compare(control.second.pressed, false)
compare(control.second.value, 1.0)
compare(control.second.position, 1.0)
// Press and release on the bottom left corner of the control without moving the handle.
mousePress(control, 0, control.height - 1, Qt.LeftButton)
compare(firstPressedSpy.count, 3)
compare(firstMovedSpy.count, 0)
compare(secondPressedSpy.count, 2)
compare(secondMovedSpy.count, 0)
compare(control.first.pressed, true)
compare(control.first.value, 0.0)
compare(control.first.position, 0.0)
compare(control.second.pressed, false)
compare(control.second.value, 1.0)
compare(control.second.position, 1.0)
mouseRelease(control, 0, control.height - 1, Qt.LeftButton)
compare(firstPressedSpy.count, 4)
compare(firstMovedSpy.count, 0)
compare(secondPressedSpy.count, 2)
compare(secondMovedSpy.count, 0)
compare(control.first.pressed, false)
compare(control.first.value, 0.0)
compare(control.first.position, 0.0)
compare(control.second.pressed, false)
compare(control.second.value, 1.0)
compare(control.second.position, 1.0)
// Drag the first handle.
mousePress(control, control.leftPadding, control.height - control.bottomPadding - 1, Qt.LeftButton)
compare(firstPressedSpy.count, 5)
compare(firstMovedSpy.count, 0)
compare(secondPressedSpy.count, 2)
compare(secondMovedSpy.count, 0)
compare(control.first.pressed, true)
compare(control.first.value, 0.0)
compare(control.first.position, 0.0)
compare(control.second.pressed, false)
compare(control.second.value, 1.0)
compare(control.second.position, 1.0)
let horizontal = control.orientation === Qt.Horizontal
let toX = horizontal ? control.width * 0.5 : control.first.handle.x
let toY = horizontal ? control.first.handle.y : control.height * 0.5
mouseMove(control, toX, toY)
compare(firstPressedSpy.count, 5)
compare(firstMovedSpy.count, 1)
compare(secondPressedSpy.count, 2)
compare(secondMovedSpy.count, 0)
compare(control.first.pressed, true)
compare(control.first.value, data.live ? 0.5 : 0.0)
compare(control.first.position, 0.5)
compare(control.first.visualPosition, 0.5)
compare(control.second.pressed, false)
compare(control.second.value, 1.0)
compare(control.second.position, 1.0)
compare(control.second.visualPosition, horizontal ? 1.0 : 0.0)
mouseRelease(control, toX, toY, Qt.LeftButton)
compare(firstPressedSpy.count, 6)
compare(firstMovedSpy.count, 1)
compare(secondPressedSpy.count, 2)
compare(secondMovedSpy.count, 0)
compare(control.first.pressed, false)
compare(control.first.value, 0.5)
compare(control.first.position, 0.5)
compare(control.first.visualPosition, 0.5)
compare(control.second.pressed, false)
compare(control.second.value, 1.0)
compare(control.second.position, 1.0)
compare(control.second.visualPosition, horizontal ? 1.0 : 0.0)
}
function test_touch_data() {
return [
{ tag: "horizontal", orientation: Qt.Horizontal, live: false },
{ tag: "vertical", orientation: Qt.Vertical, live: false },
{ tag: "horizontal:live", orientation: Qt.Horizontal, live: true },
{ tag: "vertical:live", orientation: Qt.Vertical, live: true }
]
}
function test_touch(data) {
let control = createTemporaryObject(sliderComponent, testCase, { orientation: data.orientation, live: data.live })
verify(control)
let firstPressedSpy = signalSpy.createObject(control, {target: control.first, signalName: "pressedChanged"})
verify(firstPressedSpy.valid)
let firstMovedSpy = signalSpy.createObject(control, {target: control.first, signalName: "moved"})
verify(firstMovedSpy.valid)
let secondPressedSpy = signalSpy.createObject(control, {target: control.second, signalName: "pressedChanged"})
verify(secondPressedSpy.valid)
let secondMovedSpy = signalSpy.createObject(control, {target: control.second, signalName: "moved"})
verify(secondMovedSpy.valid)
// Press and release the first handle without moving it.
let touch = touchEvent(control)
control.setValues(0, 1);
touch.press(0, control, control.leftPadding, control.height - control.bottomPadding - 1).commit()
compare(firstPressedSpy.count, 1)
compare(firstMovedSpy.count, 0)
compare(secondPressedSpy.count, 0)
compare(secondMovedSpy.count, 0)
compare(control.first.pressed, true)
compare(control.first.value, 0.0)
compare(control.first.position, 0.0)
compare(control.second.pressed, false)
compare(control.second.value, 1.0)
compare(control.second.position, 1.0)
touch.release(0, control, control.leftPadding, control.height - control.bottomPadding - 1).commit()
compare(firstPressedSpy.count, 2)
compare(firstMovedSpy.count, 0)
compare(secondPressedSpy.count, 0)
compare(secondMovedSpy.count, 0)
compare(control.first.pressed, false)
compare(control.first.value, 0.0)
compare(control.first.position, 0.0)
compare(control.second.pressed, false)
compare(control.second.value, 1.0)
compare(control.second.position, 1.0)
// Press and release the second handle without moving it.
touch.press(0, control, control.width - control.rightPadding - 1, control.topPadding).commit()
compare(firstPressedSpy.count, 2)
compare(secondPressedSpy.count, 1)
compare(control.first.pressed, false)
compare(control.first.value, 0.0)
compare(control.first.position, 0.0)
compare(control.second.pressed, true)
compare(control.second.value, 1.0)
compare(control.second.position, 1.0)
touch.release(0, control, control.width - control.rightPadding - 1, control.topPadding).commit()
compare(firstPressedSpy.count, 2)
compare(secondPressedSpy.count, 2)
compare(control.first.pressed, false)
compare(control.first.value, 0.0)
compare(control.first.position, 0.0)
compare(control.second.pressed, false)
compare(control.second.value, 1.0)
compare(control.second.position, 1.0)
// Press and release on the bottom left corner of the control without moving the handle.
touch.press(0, control, 0, control.height - 1).commit()
compare(firstPressedSpy.count, 3)
compare(secondPressedSpy.count, 2)
compare(control.first.pressed, true)
compare(control.first.value, 0.0)
compare(control.first.position, 0.0)
compare(control.second.pressed, false)
compare(control.second.value, 1.0)
compare(control.second.position, 1.0)
touch.release(0, control, 0, control.height - 1).commit()
compare(firstPressedSpy.count, 4)
compare(secondPressedSpy.count, 2)
compare(control.first.pressed, false)
compare(control.first.value, 0.0)
compare(control.first.position, 0.0)
compare(control.second.pressed, false)
compare(control.second.value, 1.0)
compare(control.second.position, 1.0)
touch.press(0, control, control.first.handle.x, control.first.handle.y).commit()
compare(firstPressedSpy.count, 5)
compare(secondPressedSpy.count, 2)
compare(control.first.pressed, true)
compare(control.first.value, 0.0)
compare(control.first.position, 0.0)
compare(control.second.pressed, false)
compare(control.second.value, 1.0)
compare(control.second.position, 1.0)
// Drag the first handle.
let horizontal = control.orientation === Qt.Horizontal
let toX = horizontal ? control.width * 0.5 : control.first.handle.x
let toY = horizontal ? control.first.handle.y : control.height * 0.5
touch.move(0, control, toX, toY).commit()
compare(firstPressedSpy.count, 5)
compare(secondPressedSpy.count, 2)
compare(control.first.pressed, true)
compare(control.first.value, data.live ? 0.5 : 0.0)
compare(control.first.position, 0.5)
compare(control.first.visualPosition, 0.5)
compare(control.second.pressed, false)
compare(control.second.value, 1.0)
compare(control.second.position, 1.0)
compare(control.second.visualPosition, horizontal ? 1.0 : 0.0)
touch.release(0, control, toX, toY).commit()
compare(firstPressedSpy.count, 6)
compare(secondPressedSpy.count, 2)
compare(control.first.pressed, false)
compare(control.first.value, 0.5)
compare(control.first.position, 0.5)
compare(control.first.visualPosition, 0.5)
compare(control.second.pressed, false)
compare(control.second.value, 1.0)
compare(control.second.position, 1.0)
compare(control.second.visualPosition, horizontal ? 1.0 : 0.0)
}
function test_multiTouch() {
let control1 = createTemporaryObject(sliderComponent, testCase)
verify(control1)
// press and move the first handle of the first slider
let touch = touchEvent(control1)
touch.press(0, control1, 1, 1).commit().move(0, control1, control1.width / 2, control1.height / 2).commit()
compare(control1.first.pressed, true)
compare(control1.first.position, 0.5)
compare(control1.second.pressed, false)
compare(control1.second.position, 1.0)
// press and move the second handle of the first slider
touch.stationary(0).press(1, control1, control1.width - 1, control1.height - 1).commit()
touch.stationary(0).move(1, control1, control1.width / 2, control1.height / 2).commit()
compare(control1.first.pressed, true)
compare(control1.first.position, 0.5)
compare(control1.second.pressed, true)
compare(control1.second.position, 0.5)
let control2 = createTemporaryObject(sliderComponent, testCase, {y: control1.height})
verify(control2)
// press and move the first handle of the second slider
touch.stationary(0).stationary(1).press(2, control2, 1, 1).commit()
touch.stationary(0).stationary(1).move(2, control2, control2.width / 2, control2.height / 2).commit()
compare(control1.first.pressed, true)
compare(control1.first.position, 0.5)
compare(control1.second.pressed, true)
compare(control1.second.position, 0.5)
compare(control2.first.pressed, true)
compare(control2.first.position, 0.5)
compare(control2.second.pressed, false)
compare(control2.second.position, 1.0)
// press and move the second handle of the second slider
touch.stationary(0).stationary(1).stationary(2).press(3, control2, control2.width - 1, control2.height - 1).commit()
touch.stationary(0).stationary(1).stationary(2).move(3, control2, control2.width / 2, control2.height / 2).commit()
compare(control1.first.pressed, true)
compare(control1.first.position, 0.5)
compare(control1.second.pressed, true)
compare(control1.second.position, 0.5)
compare(control2.first.pressed, true)
compare(control2.first.position, 0.5)
compare(control2.second.pressed, true)
compare(control2.second.position, 0.5)
// release the both handles of the both sliders
touch.release(0, control1).release(1, control1).release(2, control2).release(3, control2).commit()
compare(control1.first.pressed, false)
compare(control1.first.position, 0.5)
compare(control1.second.pressed, false)
compare(control1.second.position, 0.5)
compare(control2.first.pressed, false)
compare(control2.first.position, 0.5)
compare(control2.second.pressed, false)
compare(control2.second.position, 0.5)
}
function test_overlappingHandles() {
if ((Qt.platform.pluginName === "offscreen")
|| (Qt.platform.pluginName === "minimal"))
skip("Mouse hovering not functional on offscreen/minimal platforms")
let control = createTemporaryObject(sliderComponent, testCase)
verify(control)
// By default, we force the second handle to be after the first in
// terms of stacking order *and* z value.
compare(control.second.handle.z, 1)
compare(control.first.handle.z, 0)
control.first.value = 0
control.second.value = 0
// When both handles overlap, only the handle with the higher Z value
// should be hovered.
mouseMove(control, control.second.handle.x, control.second.handle.y)
compare(control.second.hovered, true)
compare(control.first.hovered, false)
// Both are at the same position, so it doesn't matter whose coordinates we use.
mousePress(control, control.first.handle.x, control.first.handle.y, Qt.LeftButton)
verify(control.second.pressed)
compare(control.second.handle.z, 1)
compare(control.first.handle.z, 0)
// Move the second handle out of the way.
mouseMove(control, control.width, control.first.handle.y)
mouseRelease(control, control.width, control.first.handle.y, Qt.LeftButton)
verify(!control.second.pressed)
compare(control.second.value, 1.0)
compare(control.second.handle.z, 1)
compare(control.first.handle.z, 0)
// The first handle should not be hovered.
compare(control.first.hovered, false)
// Move the first handle on top of the second.
mousePress(control, control.first.handle.x, control.first.handle.y, Qt.LeftButton)
verify(control.first.pressed)
compare(control.first.handle.z, 1)
compare(control.second.handle.z, 0)
mouseMove(control, control.width, control.first.handle.y)
mouseRelease(control, control.width, control.first.handle.y, Qt.LeftButton)
verify(!control.first.pressed)
compare(control.first.handle.z, 1)
compare(control.second.handle.z, 0)
// The most recently pressed handle (the first) should have the higher z value.
mousePress(control, control.first.handle.x, control.first.handle.y, Qt.LeftButton)
verify(control.first.pressed)
compare(control.first.handle.z, 1)
compare(control.second.handle.z, 0)
mouseRelease(control, control.first.handle.x, control.first.handle.y, Qt.LeftButton)
verify(!control.first.pressed)
compare(control.first.handle.z, 1)
compare(control.second.handle.z, 0)
}
function test_keys_data() {
return [
{ tag: "horizontal", orientation: Qt.Horizontal, decrease: Qt.Key_Left, increase: Qt.Key_Right },
{ tag: "vertical", orientation: Qt.Vertical, decrease: Qt.Key_Down, increase: Qt.Key_Up }
]
}
function test_keys(data) {
let control = createTemporaryObject(sliderComponent, testCase, { orientation: data.orientation })
verify(control)
let pressedCount = 0
let firstPressedSpy = signalSpy.createObject(control, {target: control.first, signalName: "pressedChanged"})
verify(firstPressedSpy.valid)
control.first.handle.forceActiveFocus()
verify(control.first.handle.activeFocus)
control.first.value = 0.5
for (let d1 = 1; d1 <= 10; ++d1) {
keyPress(data.decrease)
compare(control.first.pressed, true)
compare(firstPressedSpy.count, ++pressedCount)
compare(control.first.value, Math.max(0.0, 0.5 - d1 * 0.1))
compare(control.first.value, control.first.position)
keyRelease(data.decrease)
compare(control.first.pressed, false)
compare(firstPressedSpy.count, ++pressedCount)
}
for (let i1 = 1; i1 <= 20; ++i1) {
keyPress(data.increase)
compare(control.first.pressed, true)
compare(firstPressedSpy.count, ++pressedCount)
compare(control.first.value, Math.min(1.0, 0.0 + i1 * 0.1))
compare(control.first.value, control.first.position)
keyRelease(data.increase)
compare(control.first.pressed, false)
compare(firstPressedSpy.count, ++pressedCount)
}
control.first.value = 0;
control.stepSize = 0.25
pressedCount = 0;
let secondPressedSpy = signalSpy.createObject(control, {target: control.second, signalName: "pressedChanged"})
verify(secondPressedSpy.valid)
control.second.handle.forceActiveFocus()
verify(control.second.handle.activeFocus)
for (let d2 = 1; d2 <= 10; ++d2) {
keyPress(data.decrease)
compare(control.second.pressed, true)
compare(secondPressedSpy.count, ++pressedCount)
compare(control.second.value, Math.max(0.0, 1.0 - d2 * 0.25))
compare(control.second.value, control.second.position)
keyRelease(data.decrease)
compare(control.second.pressed, false)
compare(secondPressedSpy.count, ++pressedCount)
}
for (let i2 = 1; i2 <= 10; ++i2) {
keyPress(data.increase)
compare(control.second.pressed, true)
compare(secondPressedSpy.count, ++pressedCount)
compare(control.second.value, Math.min(1.0, 0.0 + i2 * 0.25))
compare(control.second.value, control.second.position)
keyRelease(data.increase)
compare(control.second.pressed, false)
compare(secondPressedSpy.count, ++pressedCount)
}
}
function test_padding() {
// test with "unbalanced" paddings (left padding != right padding) to ensure
// that the slider position calculation is done taking padding into account
// ==> the position is _not_ 0.5 in the middle of the control
let control = createTemporaryObject(sliderComponent, testCase, { leftPadding: 10, rightPadding: 20, live: false })
verify(control)
let firstPressedSpy = signalSpy.createObject(control, {target: control.first, signalName: "pressedChanged"})
verify(firstPressedSpy.valid)
mousePress(control, control.first.handle.x, control.first.handle.y, Qt.LeftButton)
compare(firstPressedSpy.count, 1)
compare(control.first.pressed, true)
compare(control.first.value, 0.0)
compare(control.first.position, 0.0)
compare(control.first.visualPosition, 0.0)
mouseMove(control, control.leftPadding + control.availableWidth * 0.5, control.height * 0.5, 0)
compare(firstPressedSpy.count, 1)
compare(control.first.pressed, true)
compare(control.first.value, 0.0)
compare(control.first.position, 0.5)
compare(control.first.visualPosition, 0.5)
mouseMove(control, control.width * 0.5, control.height * 0.5, 0)
compare(firstPressedSpy.count, 1)
compare(control.first.pressed, true)
compare(control.first.value, 0.0)
verify(control.first.position > 0.5)
verify(control.first.visualPosition > 0.5)
mouseRelease(control, control.leftPadding + control.availableWidth * 0.5, control.height * 0.5, Qt.LeftButton)
compare(firstPressedSpy.count, 2)
compare(control.first.pressed, false)
compare(control.first.value, 0.5)
compare(control.first.position, 0.5)
compare(control.first.visualPosition, 0.5)
// RTL
control.first.value = 0
control.locale = Qt.locale("ar_EG")
mousePress(control, control.first.handle.x, control.first.handle.y, Qt.LeftButton)
compare(firstPressedSpy.count, 3)
compare(control.first.pressed, true)
compare(control.first.value, 0.0)
compare(control.first.position, 0.0)
compare(control.first.visualPosition, 0.0)
mouseMove(control, control.leftPadding + control.availableWidth * 0.5, control.height * 0.5, 0)
compare(firstPressedSpy.count, 3)
compare(control.first.pressed, true)
compare(control.first.value, 0.0)
compare(control.first.position, 0.5)
compare(control.first.visualPosition, 0.5)
mouseMove(control, control.width * 0.5, control.height * 0.5, 0)
compare(firstPressedSpy.count, 3)
compare(control.first.pressed, true)
compare(control.first.value, 0.0)
verify(control.first.position > 0.5)
verify(control.first.visualPosition > 0.5)
mouseRelease(control, control.leftPadding + control.availableWidth * 0.5, control.height * 0.5, Qt.LeftButton)
compare(firstPressedSpy.count, 4)
compare(control.first.pressed, false)
compare(control.first.value, 0.5)
compare(control.first.position, 0.5)
compare(control.first.visualPosition, 0.5)
}
function test_snapMode_data(immediate) {
return [
{ tag: "NoSnap", snapMode: RangeSlider.NoSnap, from: 0, to: 2, values: [0, 0, 0.25], positions: [0, 0.1, 0.1] },
{ tag: "SnapAlways (0..2)", snapMode: RangeSlider.SnapAlways, from: 0, to: 2, values: [0.0, 0.0, 0.2], positions: [0.0, 0.1, 0.1] },
{ tag: "SnapAlways (1..3)", snapMode: RangeSlider.SnapAlways, from: 1, to: 3, values: [1.0, 1.0, 1.2], positions: [0.0, 0.1, 0.1] },
{ tag: "SnapAlways (-1..1)", snapMode: RangeSlider.SnapAlways, from: -1, to: 1, values: [0.0, 0.0, -0.8], positions: [immediate ? 0.0 : 0.5, 0.1, 0.1] },
{ tag: "SnapAlways (1..-1)", snapMode: RangeSlider.SnapAlways, from: 1, to: -1, values: [0.0, 0.0, 0.8], positions: [immediate ? 0.0 : 0.5, 0.1, 0.1] },
{ tag: "SnapOnRelease (0..2)", snapMode: RangeSlider.SnapOnRelease, from: 0, to: 2, values: [0.0, 0.0, 0.2], positions: [0.0, 0.1, 0.1] },
{ tag: "SnapOnRelease (1..3)", snapMode: RangeSlider.SnapOnRelease, from: 1, to: 3, values: [1.0, 1.0, 1.2], positions: [0.0, 0.1, 0.1] },
{ tag: "SnapOnRelease (-1..1)", snapMode: RangeSlider.SnapOnRelease, from: -1, to: 1, values: [0.0, 0.0, -0.8], positions: [immediate ? 0.0 : 0.5, 0.1, 0.1] },
{ tag: "SnapOnRelease (1..-1)", snapMode: RangeSlider.SnapOnRelease, from: 1, to: -1, values: [0.0, 0.0, 0.8], positions: [immediate ? 0.0 : 0.5, 0.1, 0.1] }
]
}
function test_snapMode_mouse_data() {
return test_snapMode_data(true)
}
function test_snapMode_mouse(data) {
let control = createTemporaryObject(sliderComponent, testCase, {snapMode: data.snapMode, from: data.from, to: data.to, stepSize: 0.2, live: false, width: testCase.width})
verify(control)
control.first.value = 0
control.second.value = data.to
let fuzz = 0.05
mousePress(control, control.leftPadding)
compare(control.first.pressed, true)
compare(control.first.value, data.values[0])
compare(control.first.position, data.positions[0])
mouseMove(control, control.leftPadding + 0.15 * (control.availableWidth + control.first.handle.width / 2))
compare(control.first.pressed, true)
fuzzyCompare(control.first.value, data.values[1], fuzz)
fuzzyCompare(control.first.position, data.positions[1], fuzz)
mouseRelease(control, control.leftPadding + 0.15 * (control.availableWidth + control.first.handle.width / 2))
compare(control.first.pressed, false)
fuzzyCompare(control.first.value, data.values[2], fuzz)
fuzzyCompare(control.first.position, data.positions[2], fuzz)
}
function test_snapMode_touch_data() {
return test_snapMode_data(false)
}
function test_snapMode_touch(data) {
let control = createTemporaryObject(sliderComponent, testCase, {snapMode: data.snapMode, from: data.from, to: data.to, stepSize: 0.2, live: false, width: testCase.width})
verify(control)
control.first.value = 0
control.second.value = data.to
let fuzz = 0.05
let touch = touchEvent(control)
touch.press(0, control, control.first.handle.x, control.first.handle.y).commit()
compare(control.first.pressed, true)
compare(control.first.value, data.values[0])
compare(control.first.position, data.positions[0])
touch.move(0, control, control.leftPadding + 0.15 * (control.availableWidth + control.first.handle.width / 2)).commit()
compare(control.first.pressed, true)
fuzzyCompare(control.first.value, data.values[1], fuzz)
fuzzyCompare(control.first.position, data.positions[1], fuzz)
touch.release(0, control, control.leftPadding + 0.15 * (control.availableWidth + control.first.handle.width / 2)).commit()
compare(control.first.pressed, false)
fuzzyCompare(control.first.value, data.values[2], fuzz)
fuzzyCompare(control.first.position, data.positions[2], fuzz)
}
function test_focus() {
let control = createTemporaryObject(sliderComponent, testCase)
verify(control)
compare(control.activeFocus, false)
// focus is forwarded to the first handle
control.forceActiveFocus()
compare(control.activeFocus, true)
compare(control.first.handle.activeFocus, true)
compare(control.second.handle.activeFocus, false)
// move focus to the second handle
control.second.handle.forceActiveFocus()
compare(control.activeFocus, true)
compare(control.first.handle.activeFocus, false)
compare(control.second.handle.activeFocus, true)
// clear focus
control.focus = false
compare(control.activeFocus, false)
compare(control.first.handle.activeFocus, false)
compare(control.second.handle.activeFocus, false)
// focus is forwarded to the second handle (where it previously was in the focus scope)
control.forceActiveFocus()
compare(control.activeFocus, true)
compare(control.first.handle.activeFocus, false)
compare(control.second.handle.activeFocus, true)
}
function test_hover_data() {
return [
{ tag: "first:true", node: "first", hoverEnabled: true },
{ tag: "first:false", node: "first", hoverEnabled: false },
{ tag: "second:true", node: "second", hoverEnabled: true },
{ tag: "second:false", node: "second", hoverEnabled: false }
]
}
function test_hover(data) {
let control = createTemporaryObject(sliderComponent, testCase, {hoverEnabled: data.hoverEnabled})
verify(control)
let node = control[data.node]
compare(control.hovered, false)
compare(node.hovered, false)
mouseMove(control, node.handle.x + node.handle.width / 2, node.handle.y + node.handle.height / 2)
compare(control.hovered, data.hoverEnabled)
compare(node.hovered, data.hoverEnabled && node.handle.enabled)
mouseMove(control, node.handle.x - 1, node.handle.y - 1)
compare(node.hovered, false)
}
function test_nullHandles() {
let control = createTemporaryObject(sliderComponent, testCase, {"second.value": 1})
verify(control)
verify(control.first.handle)
verify(control.second.handle)
control.first.handle = null
control.second.handle = null
mousePress(control, control.leftPadding, control.height / 2)
verify(control.first.pressed, true)
compare(control.second.pressed, false)
mouseRelease(control, control.leftPadding, control.height / 2)
compare(control.first.pressed, false)
compare(control.second.pressed, false)
mousePress(control, control.width - control.rightPadding - 1, control.height / 2)
compare(control.first.pressed, false)
compare(control.second.pressed, true)
mouseRelease(control, control.width - control.rightPadding - 1, control.height / 2)
compare(control.first.pressed, false)
compare(control.second.pressed, false)
}
function test_touchDragThreshold_data() {
let d1 = 3; let d2 = 7;
return [
{ tag: "horizontal", orientation: Qt.Horizontal, dx1: d1, dy1: 0, dx2: d2, dy2: 0 },
{ tag: "vertical", orientation: Qt.Vertical, dx1: 0, dy1: -d1, dx2: 0, dy2: -d2 },
{ tag: "horizontal2", orientation: Qt.Horizontal, dx1: -d1, dy1: 0, dx2: -d2, dy2: 0 },
{ tag: "vertical2", orientation: Qt.Vertical, dx1: 0, dy1: d1, dx2: 0, dy2: d2 },
]
}
function test_touchDragThreshold(data) {
let control = createTemporaryObject(sliderComponent, testCase, {touchDragThreshold: 10, live: true, orientation: data.orientation, "first.value": 0, "second.value": 1})
verify(control)
compare(control.touchDragThreshold, 10)
let valueChangedCount = 0
let valueChangedSpy = signalSpy.createObject(control, {target: control, signalName: "touchDragThresholdChanged"})
verify(valueChangedSpy.valid)
control.touchDragThreshold = undefined
compare(control.touchDragThreshold, -1) // reset to -1
compare(valueChangedSpy.count, ++valueChangedCount)
let t = 5
control.touchDragThreshold = t
compare(control.touchDragThreshold, t)
compare(valueChangedSpy.count, ++valueChangedCount)
control.touchDragThreshold = t
compare(control.touchDragThreshold, t)
compare(valueChangedSpy.count, valueChangedCount)
let pressedCount = 0
let pressedCount2 = 0
let visualPositionCount = 0
let visualPositionCount2 = 0
let pressedSpy = signalSpy.createObject(control, {target: control.first, signalName: "pressedChanged"})
verify(pressedSpy.valid)
let pressedSpy2 = signalSpy.createObject(control, {target: control.second, signalName: "pressedChanged"})
verify(pressedSpy2.valid)
let visualPositionSpy = signalSpy.createObject(control, {target: control.first, signalName: "visualPositionChanged"})
verify(visualPositionSpy.valid)
let visualPositionSpy2 = signalSpy.createObject(control, {target: control.second, signalName: "visualPositionChanged"})
verify(visualPositionSpy2.valid)
let touch = touchEvent(control)
control.first.value = 0.4
control.second.value = 1
let x0 = control.first.handle.x + control.first.handle.width * 0.5
let y0 = control.first.handle.y + control.first.handle.height * 0.5
touch.press(0, control, x0, y0).commit()
compare(pressedSpy.count, ++pressedCount)
compare(control.first.pressed, true)
compare(visualPositionSpy.count, ++visualPositionCount)
touch.move(0, control, x0 + data.dx1, y0 + data.dy1).commit()
compare(pressedSpy.count, pressedCount)
compare(control.first.pressed, true)
compare(visualPositionSpy.count, visualPositionCount)
touch.move(0, control, x0 + data.dx2, y0 + data.dy2).commit()
compare(pressedSpy.count, pressedCount)
compare(control.first.pressed, true)
compare(visualPositionSpy.count, ++visualPositionCount)
touch.release(0, control, x0 + data.dx2, y0 + data.dy2).commit()
control.first.value = 0
control.second.value = 0.6
x0 = control.second.handle.x + control.second.handle.width * 0.5
y0 = control.second.handle.y + control.second.handle.height * 0.5
touch.press(0, control, x0, y0).commit()
compare(pressedSpy2.count, ++pressedCount2)
compare(control.second.pressed, true)
compare(visualPositionSpy2.count, ++visualPositionCount2)
touch.move(0, control, x0 + data.dx1, y0 + data.dy1).commit()
compare(pressedSpy2.count, pressedCount2)
compare(control.second.pressed, true)
compare(visualPositionSpy2.count, visualPositionCount2)
touch.move(0, control, x0 + data.dx2, y0 + data.dy2).commit()
compare(pressedSpy2.count, pressedCount2)
compare(control.second.pressed, true)
compare(visualPositionSpy2.count, ++visualPositionCount2)
touch.release(0, control, x0 + data.dx2, y0 + data.dy2).commit()
}
function test_valueAt_data() {
return [
{ tag: "0.0..1.0", properties: { from: 0.0, to: 1.0 }, values: [0.0, 0.2, 0.5, 1.0] },
{ tag: "0..100", properties: { from: 0, to: 100 }, values: [0, 20, 50, 100] },
{ tag: "100..-100", properties: { from: 100, to: -100 }, values: [100, 60, 0, -100] },
{ tag: "-7..7", properties: { from: -7, to: 7, stepSize: 1.0 }, values: [-7.0, -4.0, 0.0, 7.0] },
{ tag: "-3..7", properties: { from: -3, to: 7, stepSize: 5.0 }, values: [-3.0, -3.0, 2.0, 7.0] },
]
}
function test_valueAt(data) {
let control = createTemporaryObject(sliderComponent, testCase, data.properties)
verify(control)
compare(control.valueAt(0.0), data.values[0])
compare(control.valueAt(0.2), data.values[1])
compare(control.valueAt(0.5), data.values[2])
compare(control.valueAt(1.0), data.values[3])
}
function test_updatePositionOnPress() {
let control = createTemporaryObject(sliderComponent, testCase)
verify(control)
let firstPressedSpy = signalSpy.createObject(control, {target: control.first, signalName: "pressedChanged"})
verify(firstPressedSpy.valid)
let firstMovedSpy = signalSpy.createObject(control, {target: control.first, signalName: "moved"})
verify(firstMovedSpy.valid)
let secondPressedSpy = signalSpy.createObject(control, {target: control.second, signalName: "pressedChanged"})
verify(secondPressedSpy.valid)
let secondMovedSpy = signalSpy.createObject(control, {target: control.second, signalName: "moved"})
verify(secondMovedSpy.valid)
// Touch press and release on the left corner
control.setValues(0.2, 0.8)
compare(control.first.value, 0.2)
compare(control.second.value, 0.8)
let touch = touchEvent(control)
touch.press(0, control, 0, control.height * 0.75).commit()
compare(firstPressedSpy.count, 1)
compare(firstMovedSpy.count, 0)
compare(secondPressedSpy.count, 0)
compare(secondMovedSpy.count, 0)
compare(control.first.pressed, true)
compare(control.first.value, 0.2)
compare(control.first.position, 0.2)
compare(control.second.pressed, false)
compare(control.second.value, 0.8)
compare(control.second.position, 0.8)
touch.release(0, control, 0, control.height * 0.75).commit()
compare(firstPressedSpy.count, 2)
compare(firstMovedSpy.count, 1)
compare(secondPressedSpy.count, 0)
compare(secondMovedSpy.count, 0)
compare(control.first.pressed, false)
compare(control.first.value, 0.0)
compare(control.first.position, 0.0)
compare(control.second.pressed, false)
compare(control.second.value, 0.8)
compare(control.second.position, 0.8)
firstPressedSpy.clear()
firstMovedSpy.clear()
secondPressedSpy.clear()
secondMovedSpy.clear()
// Touch press and release on the right corner
// On touch, the handle position is updated on release
control.setValues(0.2, 0.8)
compare(control.first.value, 0.2)
compare(control.second.value, 0.8)
touch = touchEvent(control)
touch.press(0, control, control.width - control.rightPadding - 1, control.height * 0.75).commit()
compare(secondPressedSpy.count, 1)
compare(secondMovedSpy.count, 0)
compare(firstPressedSpy.count, 0)
compare(firstMovedSpy.count, 0)
compare(control.second.pressed, true)
compare(control.second.value, 0.8)
compare(control.second.position, 0.8)
compare(control.first.pressed, false)
compare(control.first.value, 0.2)
compare(control.first.position, 0.2)
touch.release(0, control, control.width - control.rightPadding - 1, control.height * 0.75).commit()
compare(secondPressedSpy.count, 2)
compare(secondMovedSpy.count, 1)
compare(firstPressedSpy.count, 0)
compare(firstMovedSpy.count, 0)
compare(control.second.pressed, false)
compare(control.second.value, 1.0)
compare(control.second.position, 1.0)
compare(control.first.pressed, false)
compare(control.first.value, 0.2)
compare(control.first.position, 0.2)
firstPressedSpy.clear()
firstMovedSpy.clear()
secondPressedSpy.clear()
secondMovedSpy.clear()
// Mouse press and release on the left corner
// On mouse, the position is immediately updated on press
control.setValues(0.2, 0.8)
compare(control.first.value, 0.2)
compare(control.second.value, 0.8)
mousePress(control, 0, control.height * 0.75, Qt.LeftButton)
compare(firstPressedSpy.count, 1)
compare(firstMovedSpy.count, 1)
compare(secondPressedSpy.count, 0)
compare(secondMovedSpy.count, 0)
compare(control.first.pressed, true)
compare(control.first.value, 0.0)
compare(control.first.position, 0.0)
compare(control.second.pressed, false)
compare(control.second.value, 0.8)
compare(control.second.position, 0.8)
mouseRelease(control, 0, control.height * 0.75, Qt.LeftButton)
compare(firstPressedSpy.count, 2)
compare(firstMovedSpy.count, 1)
compare(secondPressedSpy.count, 0)
compare(secondMovedSpy.count, 0)
compare(control.first.pressed, false)
compare(control.first.value, 0.0)
compare(control.first.position, 0.0)
compare(control.second.pressed, false)
compare(control.second.value, 0.8)
compare(control.second.position, 0.8)
firstPressedSpy.clear()
firstMovedSpy.clear()
secondPressedSpy.clear()
secondMovedSpy.clear()
// Mouse press and release on the right corner
control.setValues(0.2, 0.8)
compare(control.first.value, 0.2)
compare(control.second.value, 0.8)
mousePress(control, control.width - control.rightPadding - 1, control.height * 0.75, Qt.LeftButton)
compare(secondPressedSpy.count, 1)
compare(secondMovedSpy.count, 1)
compare(firstPressedSpy.count, 0)
compare(firstMovedSpy.count, 0)
compare(control.second.pressed, true)
compare(control.second.value, 1.0)
compare(control.second.position, 1.0)
compare(control.first.pressed, false)
compare(control.first.value, 0.2)
compare(control.first.position, 0.2)
mouseRelease(control, control.width - control.rightPadding - 1, control.height * 0.75, Qt.LeftButton)
compare(secondPressedSpy.count, 2)
compare(secondMovedSpy.count, 1)
compare(firstPressedSpy.count, 0)
compare(firstMovedSpy.count, 0)
compare(control.second.pressed, false)
compare(control.second.value, 1.0)
compare(control.second.position, 1.0)
compare(control.first.pressed, false)
compare(control.first.value, 0.2)
compare(control.first.position, 0.2)
}
function test_clickFocus() {
let control = createTemporaryObject(sliderComponent, testCase)
verify(control)
// Click on the second handle - it should get focus on press.
mousePress(control, control.second.handle.x, control.second.handle.y, Qt.LeftButton)
if (Qt.platform.os === "osx")
verify(!control.activeFocus)
else
verify(control.activeFocus)
mouseRelease(control, control.second.handle.x, control.second.handle.y, Qt.LeftButton)
// Click on the first handle - it should get focus on press.
mousePress(control, control.first.handle.x, control.first.handle.y, Qt.LeftButton)
if (Qt.platform.os === "osx")
verify(!control.activeFocus)
else
verify(control.activeFocus)
mouseRelease(control, control.first.handle.x, control.first.handle.y, Qt.LeftButton)
}
function mouseXForPosition(control, position) {
position = Math.min(Math.max(position, 0.0), 1.0)
// Shouldn't matter which handle we use for the width, assuming they're both the same.
compare(control.first.handle.width, control.second.handle.width)
return control.leftPadding + control.first.handle.width * 0.5
+ position * (control.availableWidth - control.first.handle.width)
}
function test_integerStepping_data() {
return [
{ tag: "mouse" },
{ tag: "keyboard" }
]
}
function test_integerStepping(data) {
let control = createTemporaryObject(sliderComponent, testCase,
{ stepSize: Slider.SnapAlways })
verify(control)
control.from = 0
control.to = 7
control.stepSize = 1
const useMouse = data.tag === "mouse"
if (useMouse) {
// Start a drag on the second handle, which starts at 1.
mousePress(control, mouseXForPosition(control, 1 / control.to))
} else {
control.second.handle.forceActiveFocus()
verify(control.second.handle.activeFocus)
}
for (let i = 1; i < control.to; ++i) {
if (useMouse)
mouseMove(control, mouseXForPosition(control, i / control.to))
// Compare as strings to avoid a fuzzy compare; we want an exact match.
compare("" + control.second.value, "" + i)
if (!useMouse)
keyClick(Qt.Key_Right)
}
if (useMouse)
mouseRelease(control)
}
function test_crossingEnabled_default() {
let control = createTemporaryObject(sliderComponent, testCase)
verify(control)
compare(control.crossingEnabled, false)
compare(control.handlesCrossed, false)
}
function test_crossingEnabled_basic() {
let control = createTemporaryObject(sliderComponent, testCase, {
crossingEnabled: true,
from: 0,
to: 100
})
verify(control)
control.first.value = 30
control.second.value = 70
compare(control.handlesCrossed, false)
compare(control.first.value, 30)
compare(control.second.value, 70)
// Move first handle past second
control.first.value = 80
compare(control.handlesCrossed, true)
compare(control.first.value, 80)
compare(control.second.value, 70)
}
function test_crossingEnabled_mouse_data() {
return [
{ tag: "horizontal", orientation: Qt.Horizontal },
{ tag: "vertical", orientation: Qt.Vertical }
]
}
function test_crossingEnabled_mouse(data) {
let control = createTemporaryObject(sliderComponent, testCase, {
orientation: data.orientation,
crossingEnabled: true,
from: 0,
to: 100
})
verify(control)
control.first.value = 30
control.second.value = 70
compare(control.handlesCrossed, false)
compare(control.first.value, 30)
compare(control.second.value, 70)
let crossedSpy = signalSpy.createObject(control, {
target: control,
signalName: "handlesCrossedChanged"
})
verify(crossedSpy.valid)
let firstHandleItem = control.first.handle
let secondHandleItem = control.second.handle
// Store initial handle positions
let fromX = firstHandleItem.x + firstHandleItem.width / 2
let fromY = firstHandleItem.y + firstHandleItem.height / 2
mousePress(control, fromX, fromY)
// Drag first handle past second
let toX = data.orientation === Qt.Horizontal ? control.width - control.rightPadding : fromX
let toY = data.orientation === Qt.Vertical ? control.topPadding : fromY
mouseMove(control, toX, toY)
compare(crossedSpy.count, 1)
compare(control.handlesCrossed, true)
compare(control.first.value, 100)
compare(control.second.value, 70)
mouseRelease(control, toX, toY)
compare(control.first.value, 100)
compare(control.second.value, 70)
}
function test_crossingEnabled_invertedFromTo() {
let control = createTemporaryObject(sliderComponent, testCase, {
crossingEnabled: true,
from: 100,
to: 0
})
verify(control)
control.first.value = 70
control.second.value = 30
compare(control.handlesCrossed, false)
control.first.value = 20
compare(control.handlesCrossed, true)
compare(control.first.value, 20)
compare(control.second.value, 30)
}
function test_crossingEnabled_disable_while_crossed() {
let control = createTemporaryObject(sliderComponent, testCase, {
crossingEnabled: true,
from: 0,
to: 100
})
verify(control)
control.first.value = 30
control.second.value = 70
compare(control.handlesCrossed, false)
// Cross the handles
control.first.value = 80
compare(control.handlesCrossed, true)
compare(control.first.value, 80)
compare(control.second.value, 70)
// Disable crossing while crossed - should swap values and uncross
control.crossingEnabled = false
compare(control.handlesCrossed, false)
// Values should be swapped
compare(control.first.value, 70)
compare(control.second.value, 80)
// But the constraint should now prevent crossing again
control.first.value = 90
compare(control.first.value, 80) // Clamped to second.value
}
function test_crossingEnabled_disable_while_crossed_inverted() {
let control = createTemporaryObject(sliderComponent, testCase, {
crossingEnabled: true,
from: 100,
to: 0
})
verify(control)
control.first.value = 70
control.second.value = 30
compare(control.handlesCrossed, false)
// Cross the handles
control.first.value = 20
compare(control.handlesCrossed, true)
compare(control.first.value, 20)
compare(control.second.value, 30)
// Disable crossing while crossed - should swap and uncross
control.crossingEnabled = false
compare(control.handlesCrossed, false)
compare(control.first.value, 30)
compare(control.second.value, 20)
}
function test_crossingEnabled_setValues() {
let control = createTemporaryObject(sliderComponent, testCase, {
crossingEnabled: true
})
verify(control)
// Set values in crossed order
control.setValues(0.8, 0.2)
compare(control.handlesCrossed, true)
compare(control.first.value, 0.8)
compare(control.second.value, 0.2)
}
function test_crossingEnabled_keyboard() {
let control = createTemporaryObject(sliderComponent, testCase, {
crossingEnabled: true,
stepSize: 0.1
})
verify(control)
control.first.value = 0.4
control.second.value = 0.6
let crossedSpy = signalSpy.createObject(control, {
target: control,
signalName: "handlesCrossedChanged"
})
verify(crossedSpy.valid)
control.first.handle.forceActiveFocus()
// Increase first handle past second
for (let i = 0; i < 5; i++) {
control.first.increase()
}
compare(crossedSpy.count, 1)
compare(control.handlesCrossed, true)
}
function test_crossingEnabled_liveMode() {
let control = createTemporaryObject(sliderComponent, testCase, {
crossingEnabled: true,
live: true
})
verify(control)
control.first.value = 0.3
control.second.value = 0.7
let crossedSpy = signalSpy.createObject(control, {
target: control,
signalName: "handlesCrossedChanged"
})
verify(crossedSpy.valid)
// Drag first handle past second in live mode
let firstHandleItem = control.first.handle
let fromX = firstHandleItem.x + firstHandleItem.width / 2
let fromY = firstHandleItem.y + firstHandleItem.height / 2
mousePress(control, fromX, fromY)
let toX = control.width - control.rightPadding
mouseMove(control, toX, fromY)
// In live mode, crossing should be detected during drag
compare(crossedSpy.count, 1)
compare(control.handlesCrossed, true)
mouseRelease(control, toX, fromY)
}
function test_effectiveValues_notCrossed() {
let control = createTemporaryObject(sliderComponent, testCase, {
crossingEnabled: true,
from: 0,
to: 100
})
verify(control)
control.first.value = 30
control.second.value = 70
// When not crossed, effective values match semantic values
compare(control.handlesCrossed, false)
compare(control.effectiveFirstValue, 30) // Leftmost = first
compare(control.effectiveSecondValue, 70) // Rightmost = second
}
function test_effectiveValues_crossed() {
let control = createTemporaryObject(sliderComponent, testCase, {
crossingEnabled: true,
from: 0,
to: 100
})
verify(control)
control.first.value = 30
control.second.value = 70
let firstSpy = signalSpy.createObject(control, {
target: control,
signalName: "effectiveFirstValueChanged"
})
let secondSpy = signalSpy.createObject(control, {
target: control,
signalName: "effectiveSecondValueChanged"
})
verify(firstSpy.valid)
verify(secondSpy.valid)
// Cross the handles
control.first.value = 80
compare(control.handlesCrossed, true)
// After crossing, effective values represent visual positions
// Visual left = second node (value 70)
// Visual right = first node (value 80)
compare(control.effectiveFirstValue, 70) // Leftmost = second (after cross)
compare(control.effectiveSecondValue, 80) // Rightmost = first (after cross)
// Signals should have been emitted
compare(firstSpy.count, 1) // Once for crossing
compare(secondSpy.count, 2) // Once for value change, once for crossing
}
function test_effectiveValues_invertedFromTo() {
let control = createTemporaryObject(sliderComponent, testCase, {
crossingEnabled: true,
from: 100,
to: 0
})
verify(control)
control.first.value = 70
control.second.value = 30
compare(control.handlesCrossed, false)
// In inverted range, visual positions are reversed
// But effective values still represent visual left/bottom and right/top
compare(control.effectiveFirstValue, 70) // Bottommost = first
compare(control.effectiveSecondValue, 30) // Topmost = second
// Cross the handles
control.first.value = 20
compare(control.handlesCrossed, true)
// After crossing
compare(control.effectiveFirstValue, 30) // Bottommost = second (after cross)
compare(control.effectiveSecondValue, 20) // Topmost = first (after cross)
}
function test_effectiveValues_signalEmission() {
let control = createTemporaryObject(sliderComponent, testCase, {
crossingEnabled: true,
from: 0,
to: 100
})
verify(control)
let firstSpy = signalSpy.createObject(control, {
target: control,
signalName: "effectiveFirstValueChanged"
})
let secondSpy = signalSpy.createObject(control, {
target: control,
signalName: "effectiveSecondValueChanged"
})
control.first.value = 30
control.second.value = 70
firstSpy.clear()
secondSpy.clear()
// Changing first value should emit effectiveFirstValue change (not crossed)
control.first.value = 40
compare(firstSpy.count, 1)
compare(secondSpy.count, 0)
firstSpy.clear()
// Changing second value should emit effectiveSecondValue change (not crossed)
control.second.value = 80
compare(firstSpy.count, 0)
compare(secondSpy.count, 1)
secondSpy.clear()
// Cross the handles - both effective values swap
control.first.value = 90
verify(control.handlesCrossed)
compare(firstSpy.count, 1) // Once for crossing
compare(secondSpy.count, 2) // Once for value change, once for crossing
firstSpy.clear()
secondSpy.clear()
// After crossing, first node changes affect effectiveSecondValue
control.first.value = 95
compare(firstSpy.count, 0)
compare(secondSpy.count, 1) // First node now maps to effectiveSecond
}
function test_effectiveValues_withoutCrossing() {
let control = createTemporaryObject(sliderComponent, testCase, {
crossingEnabled: false,
from: 0,
to: 100
})
verify(control)
// Second value needs to be set first to avoid clamping with first handle default value 1.0
control.second.value = 70
control.first.value = 30
// Without crossing enabled, effective values are always same as semantic
compare(control.first.value, 30)
compare(control.second.value, 70)
compare(control.effectiveFirstValue, 30)
compare(control.effectiveSecondValue, 70)
// Try to cross (should fail)
control.first.value = 80
compare(control.first.value, 70) // Clamped
compare(control.effectiveFirstValue, 70)
compare(control.effectiveSecondValue, 70)
}
function test_focus_tabNavigation() {
// Test tab navigation without crossing enabled
let control = createTemporaryObject(sliderComponent, testCase, {
crossingEnabled: false,
from: 0,
to: 100
})
verify(control)
control.first.value = 30
control.second.value = 70
// Start with no focus
compare(control.activeFocus, false)
compare(control.first.handle.activeFocus, false)
compare(control.second.handle.activeFocus, false)
// Give focus to the control - should go to first handle
control.forceActiveFocus()
verify(control.activeFocus)
verify(control.first.handle.activeFocus)
// Tab to next handle (second)
keyPress(Qt.Key_Tab)
keyRelease(Qt.Key_Tab)
verify(control.activeFocus)
verify(control.second.handle.activeFocus)
// Shift+Tab back to first handle
keyPress(Qt.Key_Tab, Qt.ShiftModifier)
keyRelease(Qt.Key_Tab, Qt.ShiftModifier)
verify(control.activeFocus)
verify(control.first.handle.activeFocus)
}
function test_focus_tabNavigation_withCrossing() {
// Test tab navigation with crossing enabled
let control = createTemporaryObject(sliderComponent, testCase, {
crossingEnabled: true,
from: 0,
to: 100
})
verify(control)
control.first.value = 30
control.second.value = 70
// Give focus to first handle
control.first.handle.forceActiveFocus()
verify(control.first.handle.activeFocus)
// Tab to second handle
keyPress(Qt.Key_Tab)
keyRelease(Qt.Key_Tab)
verify(control.second.handle.activeFocus)
// Shift+Tab back to first
keyPress(Qt.Key_Tab, Qt.ShiftModifier)
keyRelease(Qt.Key_Tab, Qt.ShiftModifier)
verify(control.first.handle.activeFocus)
}
function test_focus_tabNavigation_afterCrossing() {
// Test tab navigation after handles have crossed
let control = createTemporaryObject(sliderComponent, testCase, {
crossingEnabled: true,
from: 0,
to: 100
})
verify(control)
control.first.value = 30
control.second.value = 70
// Cross the handles
control.first.value = 80
compare(control.handlesCrossed, true)
compare(control.first.value, 80)
compare(control.second.value, 70)
// Give focus to first handle (now visually on the right)
control.first.handle.forceActiveFocus()
verify(control.first.handle.activeFocus)
// Shift+Tab should move to second handle (visually on the left)
keyPress(Qt.Key_Tab, Qt.ShiftModifier)
keyRelease(Qt.Key_Tab, Qt.ShiftModifier)
verify(control.second.handle.activeFocus)
// Tab should move to first handle (visually on the right)
keyPress(Qt.Key_Tab)
keyRelease(Qt.Key_Tab)
verify(control.first.handle.activeFocus)
// Uncross the handles
control.first.value = 50
compare(control.handlesCrossed, false)
// Tab navigation should still work after uncrossing
verify(control.first.handle.activeFocus)
keyPress(Qt.Key_Tab)
keyRelease(Qt.Key_Tab)
verify(control.second.handle.activeFocus)
}
function test_focus_tabNavigation_multipleCrossings() {
// Test tab navigation through multiple crossing events
let control = createTemporaryObject(sliderComponent, testCase, {
crossingEnabled: true,
from: 0,
to: 100,
stepSize: 10
})
verify(control)
control.first.value = 40
control.second.value = 60
// Start with first handle focused
control.first.handle.forceActiveFocus()
verify(control.first.handle.activeFocus)
// Tab to second handle
keyPress(Qt.Key_Tab)
keyRelease(Qt.Key_Tab)
verify(control.second.handle.activeFocus)
// Move second handle to cross (move left past first)
control.second.value = 30
compare(control.handlesCrossed, true)
verify(control.second.handle.activeFocus) // Focus maintained
// Tab should still work after crossing
keyPress(Qt.Key_Tab)
keyRelease(Qt.Key_Tab)
verify(control.first.handle.activeFocus)
// Move first handle to uncross
control.first.value = 25
compare(control.handlesCrossed, false)
// Tab should still work after uncrossing
keyPress(Qt.Key_Tab)
keyRelease(Qt.Key_Tab)
verify(control.second.handle.activeFocus)
// Cross again by moving second past first
control.second.value = 20
compare(control.handlesCrossed, true)
// Tab navigation should remain consistent
keyPress(Qt.Key_Tab)
keyRelease(Qt.Key_Tab)
verify(control.first.handle.activeFocus)
}
}