Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix key event #101

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@
.idea
build
dep-libs
CMakeLists.txt.user
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please don't add any non project files to the .gitignore. There is already more in there than there should be actually. For files created by your editor, it is better to use a local .gitignore file: https://gist.github.com/subfuzion/db7f57fff2fb6998a16c

1 change: 1 addition & 0 deletions examples/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ add_subdirectory(GTest)
add_subdirectory(ListGridView)
add_subdirectory(RemoteCtrl)
add_subdirectory(RepeaterLoader)
add_subdirectory(QtTest)
32 changes: 32 additions & 0 deletions examples/QtTest/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
cmake_minimum_required(VERSION 3.16)

project(QtTestExample VERSION 1.0 LANGUAGES CXX)

enable_testing()

set(CMAKE_AUTOMOC ON)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

find_package(AnyRPC REQUIRED)
find_package(Qt${SPIX_QT_MAJOR} REQUIRED COMPONENTS Test Quick)

set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

include_directories(${AnyRPC_INCLUDE_DIRS})

add_executable(QtTestExample tst_test.cpp)
add_test(NAME QtTestExample COMMAND QtTestExample)

target_link_libraries(QtTestExample PRIVATE
Qt${SPIX_QT_MAJOR}::Test
Spix

PRIVATE
AnyRPC::anyrpc
)

145 changes: 145 additions & 0 deletions examples/QtTest/tst_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
#include <QtTest>

#include "anyrpc/anyrpc.h"

#include <memory>

using namespace anyrpc;

/**
* @brief Example using QtTest for running test cases against a remote server
* It also contains some handy helper functions to call remote methods.
*
* Run e.g. the RemoteCtrl application as the application under test
*/
class QtTestExample : public QObject {
Q_OBJECT

std::shared_ptr<XmlHttpClient> client;

public:
QtTestExample();
~QtTestExample();

private slots:
void initTestCase();
void cleanupTestCase();
void test_case1();

void waitFor(int time)
{
Value paramsWait;
Value resultWait;
paramsWait[0] = time;
client->Call("wait", paramsWait, resultWait);
}

void mouseClick(std::string path)
{
Value paramsMouse;
Value resultMouse;
paramsMouse[0] = path;
client->Call("mouseClick", paramsMouse, resultMouse);
waitFor(500);
}

void screenshot(std::string path, std::string filepath, bool wait = true, bool compare = true)
{
Value params;
Value result;
params[0] = path;
params[1] = filepath;
client->Call("takeScreenshot", params, result);
}

void setProperty(std::string path, std::string field, std::string value)
{
Value params;
Value result;
params[0] = path;
params[1] = field;
params[2] = value;
client->Call("setStringProperty", params, result);
}

QString getProperty(std::string path, std::string field)
{
Value params;
Value result;
params[0] = path;
params[1] = field;
client->Call("getStringProperty", params, result);

return QString::fromStdString(result.GetString());
}

void invokeMethod(std::string path, std::string field, const std::vector<std::string> functionParameter)
{
Value params;
Value result;

Value parameters;
parameters.SetArray(functionParameter.size());
for (int i = 0; i < functionParameter.size(); ++i) {
parameters[i] = functionParameter.at(i);
}

params[0] = path;
params[1] = field;
params[2] = parameters;
client->Call("invokeMethod", params, result);
}
};

QtTestExample::QtTestExample()
{
}

QtTestExample::~QtTestExample()
{
}

void QtTestExample::initTestCase()
{
client = std::make_shared<anyrpc::XmlHttpClient>();
client->SetServer("127.0.0.1", 9000);
client->SetTimeout(10000);
}

void QtTestExample::cleanupTestCase()
{
}

void QtTestExample::test_case1()
{
setProperty("mainWindow/results", "text", "");

mouseClick("mainWindow/Button_1");
waitFor(500);
mouseClick("mainWindow/Button_2");
waitFor(500);

// in the "old" variant, this call also triggers mouseArea_2
// this is somehow correct, because the mouse area is the top component,
// but sometimes we do not want to trigger the top most component in test cases
mouseClick("mainWindow/Button_3");
waitFor(500);
mouseClick("mainWindow/Button_4");
waitFor(500);

mouseClick("mainWindow/mouseArea_1");
waitFor(500);
mouseClick("mainWindow/mouseArea_2");
waitFor(500);

auto resultText = getProperty("mainWindow/results", "text");
std::cout << resultText.toStdString() << std::endl;

QCOMPARE(resultText,
"Button 1 clicked\nButton 2 clicked\nButton 3 clicked\nButton 4 clicked\nMouseArea 1 clicked\nMouseArea 2 "
"clicked");
}

QTEST_APPLESS_MAIN(QtTestExample)

#include "tst_test.moc"
2 changes: 0 additions & 2 deletions examples/RemoteCtrl/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@
#include <Spix/AnyRpcServer.h>
#include <Spix/QtQmlBot.h>

#include <iostream>

int main(int argc, char* argv[])
{
// Init Qt Qml Application
Expand Down
77 changes: 53 additions & 24 deletions examples/RemoteCtrl/main.qml
Original file line number Diff line number Diff line change
Expand Up @@ -11,34 +11,63 @@ Window {
title: qsTr("Spix Example")
color: "#111111"

RowLayout {
ColumnLayout {
id: columnLayout

anchors {
top: parent.top
left: parent.left
topMargin: 5
leftMargin: 5
}
RowLayout {
id: rowLayout

Button {
objectName: "Button_1"
text: "Press Me"
onClicked: resultsView.appendText("Button 1 clicked")
}
Button {
objectName: "Button_2"
text: "Or Click Me"
onClicked: resultsView.appendText("Button 2 clicked")
Layout.preferredHeight: 200

Button {
objectName: "Button_1"
text: "Press Me"
onClicked: resultsView.appendText("Button 1 clicked")
}
Button {
objectName: "Button_2"
text: "Or Click Me"
onClicked: resultsView.appendText("Button 2 clicked")
}

Rectangle {
id: rectangle
color: "lightgray"

Layout.preferredWidth: 200
Layout.preferredHeight: 100

MouseArea {
id: mouseArea_1
anchors.fill: parent

onClicked: resultsView.appendText("MouseArea 1 clicked")
}

RowLayout {
Button {
objectName: "Button_3"
text: "Press Me"
onClicked: resultsView.appendText("Button 3 clicked")
}
Button {
objectName: "Button_4"
text: "Or Click Me"
onClicked: resultsView.appendText("Button 4 clicked")
}
}

MouseArea {
id: mouseArea_2
anchors.fill: parent

onClicked: resultsView.appendText("MouseArea 2 clicked")
}
}
}
}

ResultsView {
id: resultsView
anchors {
top: parent.verticalCenter
left: parent.left
right: parent.right
bottom: parent.bottom
ResultsView {
id: resultsView
}
}
}
41 changes: 38 additions & 3 deletions lib/src/Scene/Qt/QtEvents.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@
#include <QDragMoveEvent>
#include <QMimeData>

// comment this to use mouse events on window level
// if uncommented, the mouse events are triggered on the given item
// #define WINDOW_MODE

namespace spix {

namespace {
Expand Down Expand Up @@ -82,11 +86,15 @@ void sendQtKeyEvent(Item* item, bool press, int keyCode, KeyModifier mod)
if (!qtitem)
return;

auto window = qtitem->qquickitem()->window();

auto qtmod = getQtKeyboardModifiers(mod);
auto keyEvent = new QKeyEvent(press ? QEvent::KeyPress : QEvent::KeyRelease, keyCode, qtmod);

#ifdef WINDOW_MODE
auto window = qtitem->qquickitem()->window();
QGuiApplication::postEvent(window, keyEvent);
#else
QGuiApplication::postEvent(qtitem->qquickitem(), keyEvent);
#endif
}

} // namespace
Expand All @@ -98,13 +106,22 @@ void QtEvents::mouseDown(Item* item, Point loc, MouseButton button)
if (!window)
return;

auto qtitem = dynamic_cast<QtItem*>(item);
if (!qtitem)
return;

m_pressedMouseButtons |= button;
Qt::MouseButton eventCausingButton = getQtMouseButtonValue(button);
Qt::MouseButtons activeButtons = getQtMouseButtonValue(m_pressedMouseButtons);

QMouseEvent* event
= new QMouseEvent(QEvent::MouseButtonPress, windowLoc, eventCausingButton, activeButtons, Qt::NoModifier);

#ifdef WINDOW_MODE
QGuiApplication::postEvent(window, event);
#else
QGuiApplication::postEvent(qtitem->qquickitem(), event);
#endif
}

void QtEvents::mouseUp(Item* item, Point loc, MouseButton button)
Expand All @@ -114,6 +131,10 @@ void QtEvents::mouseUp(Item* item, Point loc, MouseButton button)
if (!window)
return;

auto qtitem = dynamic_cast<QtItem*>(item);
if (!qtitem)
return;

#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
// Qt6 expects the mouse to be down during the event
Qt::MouseButton eventCausingButton = getQtMouseButtonValue(button);
Expand All @@ -128,7 +149,12 @@ void QtEvents::mouseUp(Item* item, Point loc, MouseButton button)

QMouseEvent* event
= new QMouseEvent(QEvent::MouseButtonRelease, windowLoc, eventCausingButton, activeButtons, Qt::NoModifier);

#ifdef WINDOW_MODE
QGuiApplication::postEvent(window, event);
#else
QGuiApplication::postEvent(qtitem->qquickitem(), event);
#endif
}

void QtEvents::mouseMove(Item* item, Point loc)
Expand All @@ -138,19 +164,28 @@ void QtEvents::mouseMove(Item* item, Point loc)
if (!window)
return;

auto qtitem = dynamic_cast<QtItem*>(item);
if (!qtitem)
return;

Qt::MouseButton activeButtons = getQtMouseButtonValue(m_pressedMouseButtons);

// Wiggle the cursor a bit. This is needed to correctly recognize drag events
windowLoc.rx() -= 1;
QMouseEvent* mouseMoveEvent
= new QMouseEvent(QEvent::MouseMove, windowLoc, Qt::MouseButton::NoButton, activeButtons, Qt::NoModifier);
QGuiApplication::postEvent(window, mouseMoveEvent);
QGuiApplication::postEvent(qtitem->qquickitem(), mouseMoveEvent);

// Wiggle the cursor a bit. This is needed to correctly recognize drag events
windowLoc.rx() += 1;
mouseMoveEvent
= new QMouseEvent(QEvent::MouseMove, windowLoc, Qt::MouseButton::NoButton, activeButtons, Qt::NoModifier);

#ifdef WINDOW_MODE
QGuiApplication::postEvent(window, mouseMoveEvent);
#else
QGuiApplication::postEvent(qtitem->qquickitem(), mouseMoveEvent);
#endif
}

void QtEvents::stringInput(Item* item, const std::string& text)
Expand Down