| /**************************************************************************** |
| ** |
| ** Copyright (C) 2017 The Qt Company Ltd. |
| ** Contact: https://www.qt.io/licensing/ |
| ** |
| ** This file is part of the test suite of the Qt Toolkit. |
| ** |
| ** $QT_BEGIN_LICENSE:BSD$ |
| ** Commercial License Usage |
| ** Licensees holding valid commercial Qt licenses may use this file in |
| ** accordance with the commercial license agreement provided with the |
| ** Software or, alternatively, in accordance with the terms contained in |
| ** a written agreement between you and The Qt Company. For licensing terms |
| ** and conditions see https://www.qt.io/terms-conditions. For further |
| ** information use the contact form at https://www.qt.io/contact-us. |
| ** |
| ** BSD License Usage |
| ** Alternatively, you may use this file under the terms of the BSD license |
| ** as follows: |
| ** |
| ** "Redistribution and use in source and binary forms, with or without |
| ** modification, are permitted provided that the following conditions are |
| ** met: |
| ** * Redistributions of source code must retain the above copyright |
| ** notice, this list of conditions and the following disclaimer. |
| ** * Redistributions in binary form must reproduce the above copyright |
| ** notice, this list of conditions and the following disclaimer in |
| ** the documentation and/or other materials provided with the |
| ** distribution. |
| ** * Neither the name of The Qt Company Ltd nor the names of its |
| ** contributors may be used to endorse or promote products derived |
| ** from this software without specific prior written permission. |
| ** |
| ** |
| ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." |
| ** |
| ** $QT_END_LICENSE$ |
| ** |
| ****************************************************************************/ |
| |
| import QtQuick 2.12 |
| import QtTest 1.0 |
| import QtQuick.Controls 2.12 |
| |
| TestCase { |
| id: testCase |
| width: 400 |
| height: 400 |
| visible: true |
| when: windowShown |
| name: "ToolTip" |
| |
| Component { |
| id: toolTip |
| ToolTip { } |
| } |
| |
| Component { |
| id: mouseArea |
| MouseArea { } |
| } |
| |
| Component { |
| id: signalSpy |
| SignalSpy { } |
| } |
| |
| QtObject { |
| id: object |
| } |
| |
| SignalSpy { |
| id: sharedSpy |
| target: ToolTip.toolTip |
| } |
| |
| function test_properties_data() { |
| return [ |
| {tag: "text", property: "text", defaultValue: "", setValue: "Hello", signalName: "textChanged"}, |
| {tag: "delay", property: "delay", defaultValue: 0, setValue: 1000, signalName: "delayChanged"}, |
| {tag: "timeout", property: "timeout", defaultValue: -1, setValue: 2000, signalName: "timeoutChanged"} |
| ] |
| } |
| |
| function test_properties(data) { |
| var control = createTemporaryObject(toolTip, testCase) |
| verify(control) |
| |
| compare(control[data.property], data.defaultValue) |
| |
| var spy = createTemporaryObject(signalSpy, testCase, {target: control, signalName: data.signalName}) |
| verify(spy.valid) |
| |
| control[data.property] = data.setValue |
| compare(control[data.property], data.setValue) |
| compare(spy.count, 1) |
| } |
| |
| function test_attached_data() { |
| return [ |
| {tag: "text", property: "text", defaultValue: "", setValue: "Hello", signalName: "textChanged"}, |
| {tag: "delay", property: "delay", defaultValue: 0, setValue: 1000, signalName: "delayChanged"}, |
| {tag: "timeout", property: "timeout", defaultValue: -1, setValue: 2000, signalName: "timeoutChanged"} |
| ] |
| } |
| |
| function test_attached(data) { |
| var item1 = createTemporaryObject(mouseArea, testCase) |
| verify(item1) |
| |
| var item2 = createTemporaryObject(mouseArea, testCase) |
| verify(item2) |
| |
| // Reset the properties to the expected default values, in case |
| // we're not the first test that uses attached properties to be run. |
| var sharedTip = ToolTip.toolTip |
| sharedTip[data.property] = data.defaultValue |
| |
| compare(item1.ToolTip[data.property], data.defaultValue) |
| compare(item2.ToolTip[data.property], data.defaultValue) |
| |
| var spy1 = signalSpy.createObject(item1, {target: item1.ToolTip, signalName: data.signalName}) |
| verify(spy1.valid) |
| |
| var spy2 = signalSpy.createObject(item2, {target: item2.ToolTip, signalName: data.signalName}) |
| verify(spy2.valid) |
| |
| sharedSpy.signalName = data.signalName |
| verify(sharedSpy.valid) |
| sharedSpy.clear() |
| |
| // change attached properties while the shared tooltip is not visible |
| item1.ToolTip[data.property] = data.setValue |
| compare(item1.ToolTip[data.property], data.setValue) |
| compare(spy1.count, 1) |
| |
| compare(spy2.count, 0) |
| compare(item2.ToolTip[data.property], data.defaultValue) |
| |
| // the shared tooltip is not visible for item1, so the attached |
| // property change should therefore not apply to the shared instance |
| compare(sharedSpy.count, 0) |
| compare(sharedTip[data.property], data.defaultValue) |
| |
| // show the shared tooltip for item2 |
| item2.ToolTip.visible = true |
| verify(item2.ToolTip.visible) |
| verify(sharedTip.visible) |
| |
| // change attached properties while the shared tooltip is visible |
| item2.ToolTip[data.property] = data.setValue |
| compare(item2.ToolTip[data.property], data.setValue) |
| compare(spy2.count, 1) |
| |
| // the shared tooltip is visible for item2, so the attached |
| // property change should apply to the shared instance |
| compare(sharedSpy.count, 1) |
| compare(sharedTip[data.property], data.setValue) |
| } |
| |
| function test_delay_data() { |
| return [ |
| {tag: "imperative:0", delay: 0, imperative: true}, |
| {tag: "imperative:100", delay: 100, imperative: true}, |
| {tag: "declarative:0", delay: 0, imperative: false}, |
| {tag: "declarative:100", delay: 100, imperative: false} |
| ] |
| } |
| |
| function test_delay(data) { |
| var control = createTemporaryObject(toolTip, testCase, {delay: data.delay}) |
| |
| compare(control.visible, false) |
| if (data.imperative) |
| control.open() |
| else |
| control.visible = true |
| compare(control.visible, data.delay <= 0) |
| tryCompare(control, "visible", true) |
| } |
| |
| function test_timeout_data() { |
| return [ |
| {tag: "imperative", imperative: true}, |
| {tag: "declarative", imperative: false} |
| ] |
| } |
| |
| function test_timeout(data) { |
| var control = createTemporaryObject(toolTip, testCase, {timeout: 100}) |
| |
| compare(control.visible, false) |
| if (data.imperative) |
| control.open() |
| else |
| control.visible = true |
| compare(control.visible, true) |
| // wait a bit to make sure that it's still visible |
| wait(50) |
| compare(control.visible, true) |
| // re-arm for another 200 ms |
| control.timeout = 200 |
| compare(control.visible, true) |
| // ensure that it's still visible after 150 ms (where old timeout < 150 < new timeout) |
| wait(150) |
| compare(control.visible, true) |
| tryCompare(control, "visible", false) |
| } |
| |
| function test_warning() { |
| ignoreWarning(Qt.resolvedUrl("tst_tooltip.qml") + ":78:5: QML QtObject: ToolTip must be attached to an Item") |
| ignoreWarning("<Unknown File>:1:30: QML ToolTip: cannot find any window to open popup in.") |
| object.ToolTip.show("") // don't crash (QTBUG-56243) |
| } |
| |
| Component { |
| id: toolTipWithExitTransition |
| |
| ToolTip { |
| Component.onCompleted: contentItem.objectName = "contentItem" |
| |
| enter: Transition { |
| NumberAnimation { property: "opacity"; from: 0.0; to: 1.0; duration: 100 } |
| } |
| exit: Transition { |
| NumberAnimation { property: "opacity"; from: 1.0; to: 0.0; duration: 500 } |
| } |
| } |
| } |
| |
| function test_openDuringExitTransitionWithTimeout() { |
| let control = createTemporaryObject(toolTipWithExitTransition, testCase, { timeout: 250 }) |
| verify(control) |
| |
| let openedSpy = signalSpy.createObject(control, { target: control, signalName: "opened" }) |
| verify(openedSpy.valid) |
| |
| control.open() |
| verify(control.visible) |
| // Can't be fully open yet because the enter transition has only just started. |
| compare(control.opened, false) |
| compare(control.enter.running, true) |
| // Wait for it to have opened. We don't check that the opened property is still true |
| // because it can result in hard-to-reproduce flakiness. Instead we just check that |
| // it was opened at some point. |
| tryCompare(openedSpy, "count", 1) |
| |
| // Let it timeout and begin the exit transition. |
| tryCompare(control, "opened", false) |
| verify(control.visible) |
| tryCompare(control.exit, "running", true) |
| verify(control.visible) |
| |
| // Quickly open it again; it should still timeout eventually. |
| control.open() |
| tryCompare(openedSpy, "count", 2) |
| tryCompare(control.exit, "running", true) |
| } |
| |
| function test_makeVisibleWhileExitTransitionRunning_data() { |
| return [ |
| { tag: "imperative", imperative: true }, |
| { tag: "declarative", imperative: false } |
| ] |
| } |
| |
| function test_makeVisibleWhileExitTransitionRunning(data) { |
| var control = createTemporaryObject(toolTipWithExitTransition, testCase) |
| |
| // Show, hide, and show the tooltip again. Its exit transition should |
| // start and get cancelled, and then its enter transition should run. |
| if (data.imperative) |
| control.open() |
| else |
| control.visible = true |
| tryCompare(control, "opacity", 1) |
| |
| if (data.imperative) |
| control.close() |
| else |
| control.visible = false |
| verify(control.exit.running) |
| tryVerify(function() { return control.opacity < 1; }) |
| |
| if (data.imperative) |
| control.open() |
| else |
| control.visible = true |
| tryCompare(control, "opacity", 1) |
| } |
| |
| Component { |
| id: buttonAndShortcutComponent |
| |
| Item { |
| property alias shortcut: shortcut |
| property alias button: button |
| |
| Shortcut { |
| id: shortcut |
| sequence: "A" |
| } |
| |
| Button { |
| id: button |
| text: "Just a button" |
| focusPolicy: Qt.NoFocus |
| |
| ToolTip.visible: button.hovered |
| ToolTip.text: qsTr("Some helpful text") |
| } |
| } |
| } |
| |
| function test_activateShortcutWhileToolTipVisible() { |
| if ((Qt.platform.pluginName === "offscreen") |
| || (Qt.platform.pluginName === "minimal")) |
| skip("Mouse hoovering not functional on offscreen/minimal platforms") |
| |
| var root = createTemporaryObject(buttonAndShortcutComponent, testCase) |
| verify(root) |
| |
| mouseMove(root.button, root.button.width / 2, root.button.height / 2) |
| tryCompare(root.button.ToolTip.toolTip, "visible", true) |
| |
| var shortcutActivatedSpy = signalSpy.createObject(root, { target: root.shortcut, signalName: "activated" }) |
| verify(shortcutActivatedSpy.valid) |
| keyPress(Qt.Key_A) |
| compare(shortcutActivatedSpy.count, 1) |
| } |
| |
| Component { |
| id: hoverComponent |
| MouseArea { |
| id: hoverArea |
| property alias tooltip: tooltip |
| hoverEnabled: true |
| width: testCase.width |
| height: testCase.height |
| ToolTip { |
| id: tooltip |
| x: 10; y: 10 |
| width: 10; height: 10 |
| visible: hoverArea.containsMouse |
| } |
| } |
| } |
| |
| // QTBUG-63644 |
| function test_hover() { |
| var root = createTemporaryObject(hoverComponent, testCase) |
| verify(root) |
| |
| var tooltip = root.tooltip |
| verify(tooltip) |
| |
| for (var pos = 0; pos <= 25; pos += 5) { |
| mouseMove(root, pos, pos) |
| verify(tooltip.visible) |
| } |
| } |
| |
| Component { |
| id: nonAttachedToolTipComponent |
| ToolTip { } |
| } |
| |
| function test_nonAttachedToolTipShowAndHide() { |
| var tip = createTemporaryObject(nonAttachedToolTipComponent, testCase) |
| verify(tip) |
| tip.show("hello"); |
| verify(tip.visible) |
| verify(tip.text === "hello") |
| tip.hide() |
| tryCompare(tip, "visible", false) |
| tip.show("delay", 200) |
| verify(tip.visible) |
| tryCompare(tip, "visible", false) |
| } |
| |
| Component { |
| id: timeoutButtonRowComponent |
| |
| Row { |
| Button { |
| text: "Timeout: 1" |
| ToolTip.text: text |
| ToolTip.visible: down |
| ToolTip.timeout: 1 |
| } |
| |
| Button { |
| text: "Timeout: -1" |
| ToolTip.text: text |
| ToolTip.visible: down |
| } |
| } |
| } |
| |
| // QTBUG-74226 |
| function test_attachedTimeout() { |
| var row = createTemporaryObject(timeoutButtonRowComponent, testCase) |
| verify(row) |
| |
| // Press the button that has no timeout; it should stay visible. |
| var button2 = row.children[1] |
| mousePress(button2) |
| compare(button2.down, true) |
| tryCompare(button2.ToolTip.toolTip, "opened", true) |
| |
| // Wait a bit to make sure that it's still visible. |
| wait(50) |
| compare(button2.ToolTip.toolTip.opened, true) |
| |
| // Release and should close. |
| mouseRelease(button2) |
| compare(button2.down, false) |
| tryCompare(button2.ToolTip, "visible", false) |
| |
| // Now, press the first button that does have a timeout; it should close on its own eventually. |
| var button1 = row.children[0] |
| mousePress(button1) |
| compare(button1.down, true) |
| // We use a short timeout to speed up the test, but tryCompare(...opened, true) then |
| // fails because the dialog has already been hidden by that point, so just check that it's |
| // immediately visible, which is more or less the same thing. |
| compare(button1.ToolTip.visible, true) |
| tryCompare(button1.ToolTip, "visible", false) |
| mouseRelease(button2) |
| |
| // Now, hover over the second button again. It should still stay visible until the mouse is released. |
| mousePress(button2) |
| compare(button2.down, true) |
| tryCompare(button2.ToolTip.toolTip, "opened", true) |
| |
| // Wait a bit to make sure that it's still visible. |
| wait(50) |
| compare(button2.ToolTip.toolTip.opened, true) |
| |
| // Release and should close. |
| mouseRelease(button2) |
| compare(button2.down, false) |
| tryCompare(button2.ToolTip, "visible", false) |
| } |
| |
| Component { |
| id: wrapComponent |
| |
| Item { |
| ToolTip.text: "This is some very very very very very very very very very very very very" |
| + " very very very very very very very very very very very very very very" |
| + " very very very very very very very very very very very very long text" |
| } |
| } |
| |
| // QTBUG-62350 |
| function test_wrap() { |
| var item = createTemporaryObject(wrapComponent, testCase) |
| verify(item) |
| |
| // Avoid "cannot find window to popup in" warning that can occur if it's made visible too early. |
| item.ToolTip.visible = true |
| tryCompare(item.ToolTip.toolTip, "opened", true) |
| compare(item.ToolTip.toolTip.contentItem.wrapMode, Text.Wrap) |
| verify(item.ToolTip.toolTip.contentItem.width < item.ToolTip.toolTip.contentItem.implicitWidth) |
| } |
| } |