Skip to content

Commit 531fd9b

Browse files
authored
Merge pull request #46 from scratchcpp/key_press_events
Handle key events
2 parents 93ebaf8 + a626dd7 commit 531fd9b

File tree

6 files changed

+151
-5
lines changed

6 files changed

+151
-5
lines changed

ScratchCPPGui/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,6 @@ qt_add_qml_module(libscratchcpp-gui
2222
renderedtarget.h
2323
targetpainter.cpp
2424
targetpainter.h
25+
keyeventhandler.cpp
26+
keyeventhandler.h
2527
)

ScratchCPPGui/keyeventhandler.cpp

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// SPDX-License-Identifier: LGPL-3.0-or-later
2+
3+
#include <QKeyEvent>
4+
5+
#include "keyeventhandler.h"
6+
7+
using namespace scratchcppgui;
8+
9+
KeyEventHandler::KeyEventHandler(QObject *parent) :
10+
QObject(parent)
11+
{
12+
}
13+
14+
bool KeyEventHandler::eventFilter(QObject *obj, QEvent *event)
15+
{
16+
switch (event->type()) {
17+
case QEvent::KeyPress: {
18+
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
19+
emit keyPressed(static_cast<Qt::Key>(keyEvent->key()), keyEvent->text());
20+
break;
21+
}
22+
23+
case QEvent::KeyRelease: {
24+
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
25+
emit keyReleased(static_cast<Qt::Key>(keyEvent->key()), keyEvent->text());
26+
break;
27+
}
28+
29+
default:
30+
break;
31+
}
32+
33+
return QObject::eventFilter(obj, event);
34+
}

ScratchCPPGui/keyeventhandler.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// SPDX-License-Identifier: LGPL-3.0-or-later
2+
3+
#pragma once
4+
5+
#include <QObject>
6+
7+
namespace scratchcppgui
8+
{
9+
10+
class KeyEventHandler : public QObject
11+
{
12+
Q_OBJECT
13+
public:
14+
explicit KeyEventHandler(QObject *parent = nullptr);
15+
16+
signals:
17+
void keyPressed(Qt::Key key, const QString &text);
18+
void keyReleased(Qt::Key key, const QString &text);
19+
20+
protected:
21+
bool eventFilter(QObject *obj, QEvent *event) override;
22+
};
23+
24+
} // namespace scratchcppgui

ScratchCPPGui/projectscene.cpp

Lines changed: 49 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,34 @@
11
// SPDX-License-Identifier: LGPL-3.0-or-later
22

3+
#include <QQuickWindow>
34
#include <scratchcpp/iengine.h>
5+
#include <scratchcpp/keyevent.h>
46

57
#include "projectscene.h"
8+
#include "keyeventhandler.h"
69

7-
namespace scratchcppgui
8-
{
10+
using namespace scratchcppgui;
11+
using namespace libscratchcpp;
12+
13+
static const std::unordered_map<Qt::Key, KeyEvent::Type> SPECIAL_KEY_MAP = {
14+
{ Qt::Key_Space, KeyEvent::Type::Space }, { Qt::Key_Left, KeyEvent::Type::Left }, { Qt::Key_Up, KeyEvent::Type::Up }, { Qt::Key_Right, KeyEvent::Type::Right },
15+
{ Qt::Key_Down, KeyEvent::Type::Down }, { Qt::Key_Return, KeyEvent::Type::Enter }, { Qt::Key_Enter, KeyEvent::Type::Enter }
16+
};
917

1018
ProjectScene::ProjectScene(QQuickItem *parent)
1119
{
20+
m_keyHandler = new KeyEventHandler(this);
21+
connect(this, &QQuickItem::windowChanged, this, &ProjectScene::installKeyHandler);
22+
connect(m_keyHandler, &KeyEventHandler::keyPressed, this, &ProjectScene::handleKeyPress);
23+
connect(m_keyHandler, &KeyEventHandler::keyReleased, this, &ProjectScene::handleKeyRelease);
1224
}
1325

14-
libscratchcpp::IEngine *ProjectScene::engine() const
26+
IEngine *ProjectScene::engine() const
1527
{
1628
return m_engine;
1729
}
1830

19-
void ProjectScene::setEngine(libscratchcpp::IEngine *newEngine)
31+
void ProjectScene::setEngine(IEngine *newEngine)
2032
{
2133
if (m_engine == newEngine)
2234
return;
@@ -45,4 +57,36 @@ void ProjectScene::handleMouseRelease()
4557
m_engine->setMousePressed(false);
4658
}
4759

48-
} // namespace scratchcppgui
60+
void ProjectScene::handleKeyPress(Qt::Key key, const QString &text)
61+
{
62+
if (m_engine) {
63+
auto it = SPECIAL_KEY_MAP.find(key);
64+
65+
if (it == SPECIAL_KEY_MAP.cend())
66+
m_engine->setKeyState(text.toStdString(), true);
67+
else {
68+
KeyEvent event(it->second);
69+
m_engine->setKeyState(event.name(), true);
70+
}
71+
}
72+
}
73+
74+
void ProjectScene::handleKeyRelease(Qt::Key key, const QString &text)
75+
{
76+
if (m_engine) {
77+
auto it = SPECIAL_KEY_MAP.find(key);
78+
79+
if (it == SPECIAL_KEY_MAP.cend())
80+
m_engine->setKeyState(text.toStdString(), false);
81+
else {
82+
KeyEvent event(it->second);
83+
m_engine->setKeyState(event.name(), false);
84+
}
85+
}
86+
}
87+
88+
void ProjectScene::installKeyHandler(QQuickWindow *window)
89+
{
90+
if (window)
91+
window->installEventFilter(m_keyHandler);
92+
}

ScratchCPPGui/projectscene.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ class IEngine;
1212
namespace scratchcppgui
1313
{
1414

15+
class KeyEventHandler;
16+
1517
class ProjectScene : public QQuickItem
1618
{
1719
Q_OBJECT
@@ -28,11 +30,17 @@ class ProjectScene : public QQuickItem
2830
Q_INVOKABLE void handleMousePress();
2931
Q_INVOKABLE void handleMouseRelease();
3032

33+
void handleKeyPress(Qt::Key key, const QString &text);
34+
void handleKeyRelease(Qt::Key key, const QString &text);
35+
3136
signals:
3237
void engineChanged();
3338

3439
private:
40+
void installKeyHandler(QQuickWindow *window);
41+
3542
libscratchcpp::IEngine *m_engine = nullptr;
43+
KeyEventHandler *m_keyHandler = nullptr;
3644
};
3745

3846
} // namespace scratchcppgui

test/projectscene/projectscene_test.cpp

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
#include <scratchcpp/keyevent.h>
12
#include <projectscene.h>
23
#include <enginemock.h>
34

@@ -55,3 +56,36 @@ TEST(ProjectScene, HandleMouseRelease)
5556
EXPECT_CALL(engine, setMousePressed(false));
5657
scene.handleMouseRelease();
5758
}
59+
60+
TEST(ProjectScene, HandleKeyPressAndRelease)
61+
{
62+
static const std::unordered_map<Qt::Key, KeyEvent::Type> SPECIAL_KEY_MAP = {
63+
{ Qt::Key_Space, KeyEvent::Type::Space }, { Qt::Key_Left, KeyEvent::Type::Left }, { Qt::Key_Up, KeyEvent::Type::Up }, { Qt::Key_Right, KeyEvent::Type::Right },
64+
{ Qt::Key_Down, KeyEvent::Type::Down }, { Qt::Key_Return, KeyEvent::Type::Enter }, { Qt::Key_Enter, KeyEvent::Type::Enter }
65+
};
66+
67+
ProjectScene scene;
68+
EngineMock engine;
69+
scene.setEngine(&engine);
70+
71+
for (const auto &[qtKey, scratchKey] : SPECIAL_KEY_MAP) {
72+
KeyEvent event(scratchKey);
73+
EXPECT_CALL(engine, setKeyState(event.name(), true));
74+
scene.handleKeyPress(qtKey, "test");
75+
76+
EXPECT_CALL(engine, setKeyState(event.name(), false));
77+
scene.handleKeyRelease(qtKey, "test");
78+
}
79+
80+
EXPECT_CALL(engine, setKeyState("a", true));
81+
scene.handleKeyPress(Qt::Key_A, "a");
82+
83+
EXPECT_CALL(engine, setKeyState("a", false));
84+
scene.handleKeyRelease(Qt::Key_A, "a");
85+
86+
EXPECT_CALL(engine, setKeyState("0", true));
87+
scene.handleKeyPress(Qt::Key_0, "0");
88+
89+
EXPECT_CALL(engine, setKeyState("0", false));
90+
scene.handleKeyRelease(Qt::Key_0, "0");
91+
}

0 commit comments

Comments
 (0)