258 lines
11 KiB
C++
258 lines
11 KiB
C++
// SPDX-License-Identifier: MIT
|
|
#include "panelcontroller.h"
|
|
#include <QJSEngine>
|
|
#include <QMouseEvent>
|
|
#include <QQuickWindow>
|
|
#include <QSignalSpy>
|
|
#include <QTest>
|
|
#include <QWheelEvent>
|
|
#include <QStyleHints>
|
|
|
|
using namespace Qt::StringLiterals;
|
|
|
|
class PanelWindow : public QQuickWindow
|
|
{
|
|
public:
|
|
int delivered = 0;
|
|
bool event(QEvent *event) override
|
|
{
|
|
if (event->type() == QEvent::Wheel || event->type() == QEvent::MouseButtonPress
|
|
|| event->type() == QEvent::MouseButtonRelease || event->type() == QEvent::MouseMove) {
|
|
++delivered;
|
|
return true;
|
|
}
|
|
return QQuickWindow::event(event);
|
|
}
|
|
};
|
|
|
|
class PanelControllerTest : public QObject
|
|
{
|
|
Q_OBJECT
|
|
private:
|
|
static void setup(PanelController &controller, PanelWindow &window, QJSEngine &engine)
|
|
{
|
|
controller.setParentItem(window.contentItem());
|
|
controller.setProperty("active", true);
|
|
controller.setProperty("locateArea", QVariant::fromValue(engine.evaluate(
|
|
u"(function(x,y) { return x < 100 ? 'empty' : 'tasks'; })"_s)));
|
|
controller.setProperty("scrollActions", QVariantMap{{u"empty"_s, u"volume"_s}, {u"tasks"_s, u"brightness"_s}});
|
|
}
|
|
static void wheel(PanelWindow &window, int delta, int x = 20, bool pixels = false)
|
|
{
|
|
QWheelEvent event(QPointF(x, 10), QPointF(x, 10),
|
|
pixels ? QPoint(0, delta) : QPoint(), pixels ? QPoint() : QPoint(0, delta),
|
|
Qt::NoButton, Qt::NoModifier, Qt::NoScrollPhase, false);
|
|
QCoreApplication::sendEvent(&window, &event);
|
|
}
|
|
static void mouse(PanelWindow &window, QEvent::Type type, int x, Qt::MouseButton button = Qt::LeftButton)
|
|
{
|
|
QMouseEvent event(type, QPointF(x, 10), QPointF(x, 10),
|
|
type == QEvent::MouseMove ? Qt::NoButton : button,
|
|
type == QEvent::MouseButtonRelease ? Qt::NoButton : button, Qt::NoModifier);
|
|
QCoreApplication::sendEvent(&window, &event);
|
|
}
|
|
private Q_SLOTS:
|
|
void scrollHitTestingDoesNotExpandClickTargets()
|
|
{
|
|
QJSEngine engine;
|
|
PanelWindow window;
|
|
PanelController controller;
|
|
setup(controller, window, engine);
|
|
controller.setLocateArea(engine.evaluate(u"(function(x,y,scrolling) { return scrolling ? 'launcher' : 'empty'; })"_s));
|
|
controller.setProperty("scrollActions", QVariantMap{{u"launcher"_s, u"brightness"_s}, {u"empty"_s, u"volume"_s}});
|
|
controller.setProperty("clickActions", QVariantMap{{u"leftClick"_s, u"mute"_s}});
|
|
QSignalSpy scrolling(&controller, &PanelController::actionRequested);
|
|
QSignalSpy clicking(&controller, &PanelController::clickRequested);
|
|
wheel(window, 120);
|
|
QCOMPARE(scrolling.count(), 1);
|
|
QCOMPARE(scrolling.first(), QVariantList({u"brightness"_s, 1}));
|
|
mouse(window, QEvent::MouseButtonPress, 20);
|
|
mouse(window, QEvent::MouseButtonRelease, 20);
|
|
QCOMPARE(clicking.count(), 1);
|
|
QCOMPARE(clicking.first(), QVariantList({u"leftClick"_s}));
|
|
}
|
|
void wheelOverridesAndNormalBehaviour()
|
|
{
|
|
QJSEngine engine;
|
|
PanelWindow window;
|
|
PanelController controller;
|
|
setup(controller, window, engine);
|
|
QSignalSpy actions(&controller, &PanelController::actionRequested);
|
|
wheel(window, 120);
|
|
wheel(window, -120, 150);
|
|
QCOMPARE(actions.count(), 2);
|
|
QCOMPARE(actions.at(0), QVariantList({u"volume"_s, 1}));
|
|
QCOMPARE(actions.at(1), QVariantList({u"brightness"_s, -1}));
|
|
QCOMPARE(window.delivered, 0);
|
|
controller.setProperty("scrollActions", QVariantMap{{u"empty"_s, u"normal"_s}});
|
|
wheel(window, 120);
|
|
QCOMPARE(window.delivered, 1);
|
|
QCOMPARE(actions.count(), 2);
|
|
}
|
|
void smoothScrollingAndAreaChanges()
|
|
{
|
|
QJSEngine engine;
|
|
PanelWindow window;
|
|
PanelController controller;
|
|
setup(controller, window, engine);
|
|
QSignalSpy actions(&controller, &PanelController::actionRequested);
|
|
wheel(window, 60);
|
|
QCOMPARE(actions.count(), 0);
|
|
wheel(window, 60);
|
|
QCOMPARE(actions.count(), 1);
|
|
wheel(window, 60);
|
|
wheel(window, 60, 150);
|
|
QCOMPARE(actions.count(), 1); // partial gestures cannot cross areas
|
|
wheel(window, 60, 150);
|
|
QCOMPARE(actions.count(), 2);
|
|
wheel(window, 20, 20, true);
|
|
QCOMPARE(actions.count(), 2);
|
|
wheel(window, 20, 20, true);
|
|
QCOMPARE(actions.count(), 3);
|
|
}
|
|
void onlyEmptySpaceClicksAreOverridden()
|
|
{
|
|
QJSEngine engine;
|
|
PanelWindow window;
|
|
PanelController controller;
|
|
setup(controller, window, engine);
|
|
controller.setProperty("clickActions", QVariantMap{{u"leftClick"_s, u"mute"_s}});
|
|
QSignalSpy actions(&controller, &PanelController::clickRequested);
|
|
mouse(window, QEvent::MouseButtonPress, 20);
|
|
QCOMPARE(actions.count(), 0);
|
|
mouse(window, QEvent::MouseButtonRelease, 20);
|
|
QCOMPARE(actions.count(), 1);
|
|
QCOMPARE(actions.at(0), QVariantList({u"leftClick"_s}));
|
|
QCOMPARE(window.delivered, 0);
|
|
mouse(window, QEvent::MouseButtonPress, 150);
|
|
mouse(window, QEvent::MouseButtonRelease, 150);
|
|
mouse(window, QEvent::MouseButtonPress, 20, Qt::RightButton);
|
|
mouse(window, QEvent::MouseButtonRelease, 20, Qt::RightButton);
|
|
QCOMPARE(window.delivered, 4);
|
|
QCOMPARE(actions.count(), 1);
|
|
}
|
|
void draggingCancelsClickEvenWhenReturning()
|
|
{
|
|
QJSEngine engine;
|
|
PanelWindow window;
|
|
PanelController controller;
|
|
setup(controller, window, engine);
|
|
controller.setProperty("clickActions", QVariantMap{{u"leftClick"_s, u"mute"_s}});
|
|
QSignalSpy actions(&controller, &PanelController::clickRequested);
|
|
mouse(window, QEvent::MouseButtonPress, 20);
|
|
mouse(window, QEvent::MouseMove, 80);
|
|
mouse(window, QEvent::MouseMove, 20);
|
|
mouse(window, QEvent::MouseButtonRelease, 20);
|
|
QCOMPARE(actions.count(), 0);
|
|
}
|
|
void disabledAndOtherWindowsAreUntouched()
|
|
{
|
|
QJSEngine engine;
|
|
PanelWindow window;
|
|
PanelWindow popup;
|
|
PanelController controller;
|
|
setup(controller, window, engine);
|
|
QSignalSpy actions(&controller, &PanelController::actionRequested);
|
|
wheel(popup, 120);
|
|
QCOMPARE(popup.delivered, 1);
|
|
controller.setProperty("active", false);
|
|
wheel(window, 120);
|
|
QCOMPARE(window.delivered, 1);
|
|
QCOMPARE(actions.count(), 0);
|
|
controller.setParentItem(popup.contentItem());
|
|
controller.setProperty("active", true);
|
|
wheel(window, 120);
|
|
QCOMPARE(window.delivered, 2);
|
|
wheel(popup, 120);
|
|
QCOMPARE(actions.count(), 1);
|
|
}
|
|
void doubleClicks_data()
|
|
{
|
|
QTest::addColumn<int>("button");
|
|
QTest::addColumn<QString>("singleSlot");
|
|
QTest::addColumn<QString>("doubleSlot");
|
|
QTest::addColumn<bool>("precedingPress");
|
|
QTest::newRow("left") << int(Qt::LeftButton) << u"leftClick"_s << u"leftDoubleClick"_s << false;
|
|
QTest::newRow("middle") << int(Qt::MiddleButton) << u"middleClick"_s << u"middleDoubleClick"_s << false;
|
|
QTest::newRow("qt-window-left") << int(Qt::LeftButton) << u"leftClick"_s << u"leftDoubleClick"_s << true;
|
|
QTest::newRow("qt-window-middle") << int(Qt::MiddleButton) << u"middleClick"_s << u"middleDoubleClick"_s << true;
|
|
}
|
|
void doubleClicks()
|
|
{
|
|
QFETCH(int, button);
|
|
QFETCH(QString, singleSlot);
|
|
QFETCH(QString, doubleSlot);
|
|
QFETCH(bool, precedingPress);
|
|
const auto mouseButton = Qt::MouseButton(button);
|
|
QJSEngine engine;
|
|
PanelWindow window;
|
|
PanelController controller;
|
|
setup(controller, window, engine);
|
|
controller.setProperty("clickActions", QVariantMap{{singleSlot, u"mute"_s}, {doubleSlot, u"application"_s}});
|
|
QSignalSpy actions(&controller, &PanelController::clickRequested);
|
|
mouse(window, QEvent::MouseButtonPress, 20, mouseButton);
|
|
mouse(window, QEvent::MouseButtonRelease, 20, mouseButton);
|
|
QCOMPARE(actions.count(), 0);
|
|
// QGuiApplication sends both Press and DblClick for the second down.
|
|
if (precedingPress) mouse(window, QEvent::MouseButtonPress, 20, mouseButton);
|
|
mouse(window, QEvent::MouseButtonDblClick, 20, mouseButton);
|
|
mouse(window, QEvent::MouseButtonRelease, 20, mouseButton);
|
|
QCOMPARE(actions.count(), 1);
|
|
QCOMPARE(actions.at(0), QVariantList({doubleSlot}));
|
|
QTest::qWait(QGuiApplication::styleHints()->mouseDoubleClickInterval() + 30);
|
|
QCOMPARE(actions.count(), 1); // no single-click action after the double
|
|
|
|
mouse(window, QEvent::MouseButtonPress, 20, mouseButton);
|
|
mouse(window, QEvent::MouseButtonRelease, 20, mouseButton);
|
|
QCOMPARE(actions.count(), 1);
|
|
QTRY_COMPARE(actions.count(), 2);
|
|
QCOMPARE(actions.at(1), QVariantList({singleSlot}));
|
|
}
|
|
void distantClicksDoNotCombine()
|
|
{
|
|
QJSEngine engine;
|
|
PanelWindow window;
|
|
PanelController controller;
|
|
setup(controller, window, engine);
|
|
controller.setProperty("clickActions", QVariantMap{{u"leftClick"_s, u"mute"_s}, {u"leftDoubleClick"_s, u"overview"_s}});
|
|
QSignalSpy actions(&controller, &PanelController::clickRequested);
|
|
mouse(window, QEvent::MouseButtonPress, 10);
|
|
mouse(window, QEvent::MouseButtonRelease, 10);
|
|
mouse(window, QEvent::MouseButtonPress, 90);
|
|
mouse(window, QEvent::MouseButtonRelease, 90);
|
|
QTRY_COMPARE(actions.count(), 2);
|
|
QCOMPARE(actions.at(0), QVariantList({u"leftClick"_s}));
|
|
QCOMPARE(actions.at(1), QVariantList({u"leftClick"_s}));
|
|
}
|
|
void doubleOnlyAndDisablingPendingClicks()
|
|
{
|
|
QJSEngine engine;
|
|
PanelWindow window;
|
|
PanelController controller;
|
|
setup(controller, window, engine);
|
|
controller.setProperty("clickActions", QVariantMap{{u"leftDoubleClick"_s, u"command"_s}});
|
|
QSignalSpy actions(&controller, &PanelController::clickRequested);
|
|
mouse(window, QEvent::MouseButtonPress, 20);
|
|
mouse(window, QEvent::MouseButtonRelease, 20);
|
|
// Some platforms deliver the second press without a DblClick event.
|
|
mouse(window, QEvent::MouseButtonPress, 20);
|
|
mouse(window, QEvent::MouseButtonRelease, 20);
|
|
QCOMPARE(actions.count(), 1);
|
|
QCOMPARE(actions.at(0), QVariantList({u"leftDoubleClick"_s}));
|
|
mouse(window, QEvent::MouseButtonPress, 20);
|
|
mouse(window, QEvent::MouseButtonRelease, 20);
|
|
mouse(window, QEvent::MouseButtonPress, 20);
|
|
mouse(window, QEvent::MouseMove, 80);
|
|
mouse(window, QEvent::MouseMove, 20);
|
|
mouse(window, QEvent::MouseButtonRelease, 20);
|
|
QCOMPARE(actions.count(), 1); // dragging the second press cancels the double
|
|
mouse(window, QEvent::MouseButtonPress, 20);
|
|
mouse(window, QEvent::MouseButtonRelease, 20);
|
|
controller.setProperty("active", false);
|
|
QTest::qWait(QGuiApplication::styleHints()->mouseDoubleClickInterval() + 30);
|
|
QCOMPARE(actions.count(), 1);
|
|
}
|
|
};
|
|
QTEST_MAIN(PanelControllerTest)
|
|
#include "test-panelcontroller.moc"
|