1 /*
2     SPDX-FileCopyrightText: 2017 Elvis Angelaccio <elvis.angelaccio@kde.org>
3     SPDX-FileCopyrightText: 2018 Michael Heidelbach <ottwolt@gmail.com>
4 
5     SPDX-License-Identifier: LGPL-2.1-or-later
6 */
7 
8 #include "ktooltipwidgettest.h"
9 
10 #include <KToolTipWidget>
11 
12 #include <QLabel>
13 #include <QLayout>
14 #include <QLineEdit>
15 #include <QScreen>
16 #include <QSignalSpy>
17 #include <QStyle>
18 #include <QTest>
19 
initTestCase()20 void KToolTipWidgetTest::initTestCase()
21 {
22     m_screenGeometry = QGuiApplication::primaryScreen()->geometry();
23 }
24 
showTooltipShouldShowContent()25 void KToolTipWidgetTest::showTooltipShouldShowContent()
26 {
27     KToolTipWidget tooltip;
28     QLabel label;
29 
30     tooltip.showAt(QPoint(10, 10), &label, nullptr);
31     QVERIFY(tooltip.isVisible());
32     QVERIFY(label.isVisible());
33 }
34 
hideLaterShouldHideAfterDelay()35 void KToolTipWidgetTest::hideLaterShouldHideAfterDelay()
36 {
37     KToolTipWidget tooltip;
38     QLabel label;
39 
40     tooltip.showAt(QPoint(10, 10), &label, nullptr);
41     QVERIFY(tooltip.isVisible());
42     QVERIFY(label.isVisible());
43 
44     tooltip.hideLater();
45     QVERIFY(tooltip.isVisible());
46     QVERIFY(label.isVisible());
47 
48     QSignalSpy spy(&tooltip, &KToolTipWidget::hidden);
49     connect(&tooltip, &KToolTipWidget::hidden, this, [&]() {
50         QVERIFY(tooltip.isHidden());
51         QVERIFY(!label.isVisible());
52     });
53 
54     QEventLoop eventLoop;
55     connect(&tooltip, &KToolTipWidget::hidden, &eventLoop, &QEventLoop::quit);
56     QCOMPARE(eventLoop.exec(), 0);
57     QCOMPARE(spy.count(), 1);
58 }
59 
setZeroDelayShouldHideImmediately()60 void KToolTipWidgetTest::setZeroDelayShouldHideImmediately()
61 {
62     KToolTipWidget tooltip;
63     QLabel label;
64 
65     tooltip.setHideDelay(0);
66     tooltip.showAt(QPoint(10, 10), &label, nullptr);
67     QVERIFY(tooltip.isVisible());
68     QVERIFY(label.isVisible());
69 
70     tooltip.hideLater();
71     QVERIFY(tooltip.isHidden());
72     QVERIFY(!label.isVisible());
73 }
74 
shouldHideImmediatelyIfContentDestroyed()75 void KToolTipWidgetTest::shouldHideImmediatelyIfContentDestroyed()
76 {
77     KToolTipWidget tooltip;
78     auto lineEdit = new QLineEdit();
79 
80     tooltip.showAt(QPoint(10, 10), lineEdit, nullptr);
81     QVERIFY(tooltip.isVisible());
82 
83     delete lineEdit;
84     QVERIFY(tooltip.isHidden());
85 }
86 
shouldNotTakeOwnershipOfContent()87 void KToolTipWidgetTest::shouldNotTakeOwnershipOfContent()
88 {
89     auto parent = new QWidget();
90     auto label = new QLabel(parent);
91     auto tooltip = new KToolTipWidget();
92 
93     tooltip->showAt(QPoint(10, 10), label, nullptr);
94     QCOMPARE(label->parent(), tooltip);
95     QVERIFY(label->isVisible());
96 
97     QSignalSpy spy(label, &QWidget::destroyed);
98     delete tooltip;
99     QVERIFY(!label->isVisible());
100     QCOMPARE(label->parent(), parent);
101     QCOMPARE(spy.count(), 0);
102 
103     delete parent;
104     QCOMPARE(spy.count(), 1);
105 }
106 
shouldNotObscureTarget_data()107 void KToolTipWidgetTest::shouldNotObscureTarget_data()
108 {
109     qDebug() << "Style used" << QApplication::style()->objectName();
110 
111     QTest::addColumn<QPoint>("position");
112     QTest::addColumn<QString>("content");
113 
114     const QMap<QString, QPoint> positions{{QStringLiteral("topleft"), QPoint(m_offset, m_offset)},
115                                           {QStringLiteral("topright"), QPoint(-m_offset, m_offset)},
116                                           {QStringLiteral("bottomright"), QPoint(-m_offset, -m_offset)},
117                                           {QStringLiteral("bottomleft"), QPoint(m_offset, -m_offset)},
118                                           {QStringLiteral("centered"), QPoint(m_screenGeometry.width() / 2, m_screenGeometry.height() / 2)}};
119 
120     QMapIterator<QString, QPoint> i(positions);
121     while (i.hasNext()) {
122         i.next();
123         QTest::newRow(qPrintable(QStringLiteral("small/%1").arg(i.key()))) << i.value() << QStringLiteral("dummy text");
124 
125         QTest::newRow(qPrintable(QStringLiteral("multiline/%1").arg(i.key()))) << i.value() << QStringLiteral("dummy text\nLine 1\nLine 2\nLine 3");
126 
127         QTest::newRow(qPrintable(QStringLiteral("one long line/%1").arg(i.key())))
128             << i.value()
129             << QStringLiteral(
130                    "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam nec felis sed elit auctor lobortis non a urna. Quisque non posuere mauris. "
131                    "Suspendisse potenti. In diam leo, lobortis at placerat nec, sagittis at tortor. Pellentesque scelerisque enim vel elementum scelerisque. "
132                    "Integer eget lectus vitae lorem pulvinar hendrerit. Suspendisse auctor sapien vel semper porta. Vestibulum fringilla aliquet tincidunt. "
133                    "Maecenas mollis mauris et erat viverra mollis. Proin suscipit felis nisi, a dapibus est hendrerit euismod. Suspendisse quis faucibus quam. "
134                    "Fusce eu cursus magna, et egestas purus. Duis enim sapien, feugiat id facilisis non, rhoncus ut lectus. Aliquam at nisi vel ligula "
135                    "interdum ultricies. Donec condimentum ante quam, eu congue lectus pulvinar in. Cras interdum, neque quis fermentum consequat, lectus "
136                    "tellus eleifend turpis.");
137 
138         if (m_screenGeometry.width() >= 1600 && m_screenGeometry.height() >= 900) {
139             QTest::newRow(qPrintable(QStringLiteral("super large/%1").arg(i.key())))
140                 << i.value()
141                 << QStringLiteral(
142                        "dummy 0 text\nLine 1\nLine 2\nLine 3"
143                        "dummy 1 text\nLine 1\nLine 2\nLine 3"
144                        "dummy 2 text\nLine 1\nLine 2\nLine 3"
145                        "dummy 3 text\nLine 1\nLine 2\nLine 3"
146                        "dummy 4 text\nLine 1\nLine 2\nLine 3"
147 
148                        "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam nec felis sed elit auctor lobortis non a urna. Quisque non posuere "
149                        "mauris. Suspendisse potenti. In diam leo, lobortis at placerat nec, sagittis at tortor. Pellentesque scelerisque enim vel elementum "
150                        "scelerisque. Integer eget lectus vitae lorem pulvinar hendrerit. Suspendisse auctor sapien vel semper porta. Vestibulum fringilla "
151                        "aliquet tincidunt. Maecenas mollis mauris et erat viverra mollis. Proin suscipit felis nisi, a dapibus est hendrerit euismod. "
152                        "Suspendisse quis faucibus quam. Fusce eu cursus magna, et egestas purus. Duis enim sapien, feugiat id facilisis non, rhoncus ut "
153                        "lectus. Aliquam at nisi vel ligula interdum ultricies. Donec condimentum ante quam, eu congue lectus pulvinar in. Cras interdum, neque "
154                        "quis fermentum consequat, lectus tellus eleifend turpis.");
155         }
156     }
157 }
158 
shouldNotObscureTarget()159 void KToolTipWidgetTest::shouldNotObscureTarget()
160 {
161     QFETCH(QPoint, position);
162     QFETCH(QString, content);
163 
164     QWidget *containerWidget = new QWidget();
165     const int margin = containerWidget->style()->pixelMetric(QStyle::PM_ToolTipLabelFrameWidth);
166     QLabel *targetWidget = new QLabel(QStringLiteral("dummy file"));
167     QLayout *layout = new QHBoxLayout(containerWidget);
168     layout->addWidget(targetWidget);
169 
170     containerWidget->adjustSize();
171     containerWidget->move(position.x() >= 0 ? position.x() : m_screenGeometry.right() + position.x() - containerWidget->width(),
172                           position.y() >= 0 ? position.y() : m_screenGeometry.bottom() + position.y() - containerWidget->height());
173     containerWidget->show();
174     QVERIFY(QTest::qWaitForWindowExposed(containerWidget));
175     QVERIFY(targetWidget->isVisible());
176 
177     QRect targetRect = QRect(targetWidget->frameGeometry());
178     targetRect.moveTo(targetWidget->mapToGlobal(QPoint(0, 0)));
179 
180     QLabel *contentWidget = new QLabel(content);
181 
182     QVERIFY2(containerWidget->windowHandle(), "Container's window handle is invalid");
183     KToolTipWidget tooltipWidget;
184     tooltipWidget.showBelow(targetRect, contentWidget, containerWidget->windowHandle());
185     QVERIFY(QTest::qWaitForWindowExposed(&tooltipWidget));
186     const QRect tooltipRect = tooltipWidget.frameGeometry();
187 
188     qDebug() << QStringLiteral("tooltip: %1x%2 x=%3, y=%4").arg(tooltipRect.width()).arg(tooltipRect.height()).arg(tooltipRect.left()).arg(tooltipRect.top());
189     qDebug() << QStringLiteral("target:  %1x%2 x=%3, y=%4").arg(targetRect.width()).arg(targetRect.height()).arg(targetRect.left()).arg(targetRect.top());
190 
191     QVERIFY2(!tooltipRect.intersects(targetRect), "Target obscured");
192     QCOMPARE(tooltipRect.intersected(m_screenGeometry), tooltipRect);
193 
194     // Check margins
195     if (tooltipRect.bottom() < targetRect.top()) {
196         QCOMPARE(margin, targetRect.top() - tooltipRect.bottom());
197     } else if (tooltipRect.top() > targetRect.bottom()) {
198         QCOMPARE(margin, tooltipRect.top() - targetRect.bottom());
199     } else if (tooltipRect.right() < targetRect.left()) {
200         QCOMPARE(margin, targetRect.left() - tooltipRect.right());
201     } else if (tooltipRect.left() > targetRect.right()) {
202         QCOMPARE(margin, tooltipRect.left() - targetRect.right());
203     }
204 
205     tooltipWidget.hide();
206     QVERIFY(tooltipWidget.isHidden());
207 
208     delete contentWidget;
209     delete containerWidget;
210 }
211 
212 QTEST_MAIN(KToolTipWidgetTest)
213