diff --git a/autotests/ktooltipwidgettest.h b/autotests/ktooltipwidgettest.h --- a/autotests/ktooltipwidgettest.h +++ b/autotests/ktooltipwidgettest.h @@ -19,18 +19,28 @@ #define KTOOLTIPWIDGETTEST_H #include +#include class KToolTipWidgetTest : public QObject { Q_OBJECT private Q_SLOTS: + void initTestCase(); + void showTooltipShouldShowContent(); void hideLaterShouldHideAfterDelay(); void setZeroDelayShouldHideImmediately(); void shouldHideImmediatelyIfContentDestroyed(); void shouldNotTakeOwnershipOfContent(); + + void shouldNotObscureTarget_data(); + void shouldNotObscureTarget(); + +private: + QRect m_screenGeometry; + const int m_offset = 10; }; #endif diff --git a/autotests/ktooltipwidgettest.cpp b/autotests/ktooltipwidgettest.cpp --- a/autotests/ktooltipwidgettest.cpp +++ b/autotests/ktooltipwidgettest.cpp @@ -1,29 +1,40 @@ /* * Copyright (c) 2017 Elvis Angelaccio - * + * Copyright (c) 2018 Michael Heidelbach + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #include "ktooltipwidgettest.h" #include +#include +#include +#include +#include +#include #include #include #include #include +void KToolTipWidgetTest::initTestCase() +{ + m_screenGeometry = QGuiApplication::primaryScreen()->geometry(); +} + void KToolTipWidgetTest::showTooltipShouldShowContent() { KToolTipWidget tooltip; @@ -106,6 +117,123 @@ QCOMPARE(spy.count(), 1); } +void KToolTipWidgetTest::shouldNotObscureTarget_data() +{ + QTest::addColumn("position"); + QTest::addColumn("content"); + + const QMap positions{ + {QStringLiteral("topleft"), QPoint(m_offset, m_offset)}, + {QStringLiteral("topright"), QPoint(-m_offset, m_offset)}, + {QStringLiteral("bottomright"), QPoint(-m_offset, -m_offset)}, + {QStringLiteral("bottomleft"), QPoint(m_offset, -m_offset)}, + {QStringLiteral("centered"), + QPoint(m_screenGeometry.width() / 2, m_screenGeometry.height() / 2)} + }; + + QMapIterator i(positions); + while (i.hasNext()) { + i.next(); + QTest::newRow(qPrintable(QStringLiteral("small/%1").arg(i.key())) ) + << i.value() + << QStringLiteral("dummy text"); + + QTest::newRow(qPrintable(QStringLiteral("multiline/%1").arg(i.key()))) + << i.value() + << QStringLiteral("dummy text\nLine 1\nLine 2\nLine 3"); + + QTest::newRow(qPrintable(QStringLiteral("one long line/%1").arg(i.key()))) + << i.value() + << QStringLiteral( + "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam nec felis sed elit auctor lobortis non a urna. Quisque non posuere mauris. Suspendisse potenti. In diam leo, lobortis at placerat nec, sagittis at tortor. Pellentesque scelerisque enim vel elementum scelerisque. Integer eget lectus vitae lorem pulvinar hendrerit. Suspendisse auctor sapien vel semper porta. Vestibulum fringilla aliquet tincidunt. Maecenas mollis mauris et erat viverra mollis. Proin suscipit felis nisi, a dapibus est hendrerit euismod. Suspendisse quis faucibus quam. Fusce eu cursus magna, et egestas purus. Duis enim sapien, feugiat id facilisis non, rhoncus ut lectus. Aliquam at nisi vel ligula interdum ultricies. Donec condimentum ante quam, eu congue lectus pulvinar in. Cras interdum, neque quis fermentum consequat, lectus tellus eleifend turpis." + ); + + if (m_screenGeometry.width() >= 1600 && m_screenGeometry.height() >= 900) { + QTest::newRow(qPrintable(QStringLiteral("super large/%1").arg(i.key()))) + << i.value() + << QStringLiteral( + "dummy 0 text\nLine 1\nLine 2\nLine 3" + "dummy 1 text\nLine 1\nLine 2\nLine 3" + "dummy 2 text\nLine 1\nLine 2\nLine 3" + "dummy 3 text\nLine 1\nLine 2\nLine 3" + "dummy 4 text\nLine 1\nLine 2\nLine 3" + + "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam nec felis sed elit auctor lobortis non a urna. Quisque non posuere mauris. Suspendisse potenti. In diam leo, lobortis at placerat nec, sagittis at tortor. Pellentesque scelerisque enim vel elementum scelerisque. Integer eget lectus vitae lorem pulvinar hendrerit. Suspendisse auctor sapien vel semper porta. Vestibulum fringilla aliquet tincidunt. Maecenas mollis mauris et erat viverra mollis. Proin suscipit felis nisi, a dapibus est hendrerit euismod. Suspendisse quis faucibus quam. Fusce eu cursus magna, et egestas purus. Duis enim sapien, feugiat id facilisis non, rhoncus ut lectus. Aliquam at nisi vel ligula interdum ultricies. Donec condimentum ante quam, eu congue lectus pulvinar in. Cras interdum, neque quis fermentum consequat, lectus tellus eleifend turpis." + ); + } + } +} + +void KToolTipWidgetTest::shouldNotObscureTarget() +{ + QFETCH(QPoint, position); + QFETCH(QString, content); + + QWidget* containerWidget = new QWidget(); + const int margin = containerWidget->style()->pixelMetric(QStyle::PM_ToolTipLabelFrameWidth); + QLabel* targetWidget = new QLabel(QStringLiteral("dummy file")); + QLayout* layout = new QHBoxLayout(containerWidget); + layout->addWidget(targetWidget); + + containerWidget->adjustSize(); + containerWidget->move( + position.x() >= 0 + ? position.x() + : m_screenGeometry.right() + position.x() - containerWidget->width(), + position.y() >= 0 + ? position.y() + : m_screenGeometry.bottom() + position.y() - containerWidget->height() + ); + containerWidget->show(); + QVERIFY(QTest::qWaitForWindowActive(containerWidget)); + QVERIFY(targetWidget->isVisible()); + + QRect targetRect = QRect(targetWidget->frameGeometry()); + targetRect.moveTo(targetWidget->mapToGlobal(QPoint(0, 0))); + + QLabel* contentWidget = new QLabel(content); + const QSize shrinkSize(2 * m_offset, 2 * m_offset); + contentWidget->setMaximumSize(m_screenGeometry.size() - shrinkSize); + + QVERIFY2(containerWidget->windowHandle(), "Container's window handle is invalid"); + KToolTipWidget tooltipWidget; + tooltipWidget.showBelow(targetRect, contentWidget, containerWidget->windowHandle()); + QVERIFY(QTest::qWaitForWindowActive(&tooltipWidget)); + const QRect tooltipRect = tooltipWidget.frameGeometry(); + + qDebug() << QStringLiteral("tooltip: %1x%2 x=%3, y=%4") + .arg(tooltipRect.width()) + .arg(tooltipRect.height()) + .arg(tooltipRect.left()) + .arg(tooltipRect.top()); + qDebug() << QStringLiteral("target: %1x%2 x=%3, y=%4") + .arg(targetRect.width()) + .arg(targetRect.height()) + .arg(targetRect.left()) + .arg(targetRect.top()); + + QVERIFY2(!tooltipRect.intersects(targetRect), "Target obscured"); + QCOMPARE(tooltipRect.intersected(m_screenGeometry), tooltipRect); + + // Check margins + if (tooltipRect.bottom() < targetRect.top()) { + QCOMPARE(margin, targetRect.top() - tooltipRect.bottom()); + } else if (tooltipRect.top() > targetRect.bottom()) { + QCOMPARE(margin, tooltipRect.top() - targetRect.bottom()); + } else if (tooltipRect.right() < targetRect.left()) { + QCOMPARE(margin, targetRect.left() - tooltipRect.right()); + } else if (tooltipRect.left() > targetRect.right() ) { + QCOMPARE(margin, tooltipRect.left() - targetRect.right()); + } + + tooltipWidget.hide(); + QVERIFY(tooltipWidget.isHidden()); + + delete contentWidget; + delete containerWidget; +} + + QTEST_MAIN(KToolTipWidgetTest) diff --git a/src/ktooltipwidget.cpp b/src/ktooltipwidget.cpp --- a/src/ktooltipwidget.cpp +++ b/src/ktooltipwidget.cpp @@ -119,11 +119,11 @@ // It must be assured that: // - the content is fully visible // - the content is not drawn inside rect - - const QSize size = content->sizeHint(); + + const QSize size = q->sizeHint(); const int margin = q->style()->pixelMetric(QStyle::PM_ToolTipLabelFrameWidth); const QRect screenGeometry = screen->geometry(); - + const bool hasRoomToLeft = (rect.left() - size.width() - margin >= screenGeometry.left()); const bool hasRoomToRight = (rect.right() + size.width() + margin <= screenGeometry.right()); const bool hasRoomAbove = (rect.top() - size.height() - margin >= screenGeometry.top()); @@ -142,14 +142,14 @@ if (hasRoomBelow) { y = rect.bottom() + margin; } else { - y = rect.top() - size.height() - margin; + y = rect.top() - size.height() - margin + 1; } } else { Q_ASSERT(hasRoomToLeft || hasRoomToRight); if (hasRoomToRight) { x = rect.right() + margin; } else { - x = rect.left() - size.width() - margin; + x = rect.left() - size.width() - margin + 1; } // Put the tooltip at the bottom of the screen. The x-coordinate has already // been adjusted, so that no overlapping with rect occurs.