Skip to content

Commit 080d9fe

Browse files
authored
Merge pull request #54 from scratchcpp/refactor_mouse_events
Refactor mouse events
2 parents 26db0eb + 3ad5aac commit 080d9fe

20 files changed

+832
-8
lines changed

ScratchCPPGui/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ qt_add_qml_module(libscratchcpp-gui
2222
renderedtarget.h
2323
targetpainter.cpp
2424
targetpainter.h
25+
scenemousearea.cpp
26+
scenemousearea.h
27+
mouseeventhandler.cpp
28+
mouseeventhandler.h
2529
keyeventhandler.cpp
2630
keyeventhandler.h
2731
)

ScratchCPPGui/ProjectPlayer.qml

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -111,12 +111,13 @@ ProjectScene {
111111
}
112112
}
113113

114-
MouseArea {
114+
SceneMouseArea {
115115
id: mouseArea
116116
anchors.fill: parent
117-
hoverEnabled: true
118-
onPositionChanged: root.handleMouseMove(mouseX, mouseY)
119-
onPressed: root.handleMousePress()
120-
onReleased: root.handleMouseRelease()
117+
stage: stageTarget
118+
spriteRepeater: sprites
119+
onMouseMoved: (x, y)=> root.handleMouseMove(x, y)
120+
onMousePressed: root.handleMousePress()
121+
onMouseReleased: root.handleMouseRelease()
121122
}
122123
}

ScratchCPPGui/irenderedtarget.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ class IRenderedTarget : public QNanoQuickItem
5353
virtual qreal height() const = 0;
5454
virtual void setHeight(qreal width) = 0;
5555

56+
virtual QPointF mapFromScene(const QPointF &point) const = 0;
57+
5658
virtual QBuffer *bitmapBuffer() = 0;
5759
virtual const QString &bitmapUniqueKey() const = 0;
5860

ScratchCPPGui/keyeventhandler.h

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,11 @@ class KeyEventHandler : public QObject
1313
public:
1414
explicit KeyEventHandler(QObject *parent = nullptr);
1515

16+
bool eventFilter(QObject *obj, QEvent *event) override;
17+
1618
signals:
1719
void keyPressed(Qt::Key key, const QString &text);
1820
void keyReleased(Qt::Key key, const QString &text);
19-
20-
protected:
21-
bool eventFilter(QObject *obj, QEvent *event) override;
2221
};
2322

2423
} // namespace scratchcppgui
Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
// SPDX-License-Identifier: LGPL-3.0-or-later
2+
3+
#include <QMouseEvent>
4+
#include <scratchcpp/target.h>
5+
6+
#include "mouseeventhandler.h"
7+
#include "renderedtarget.h"
8+
9+
using namespace scratchcppgui;
10+
11+
MouseEventHandler::MouseEventHandler(QObject *parent) :
12+
QObject(parent)
13+
{
14+
}
15+
16+
IRenderedTarget *MouseEventHandler::stage() const
17+
{
18+
return m_stage;
19+
}
20+
21+
void MouseEventHandler::setStage(IRenderedTarget *stage)
22+
{
23+
m_stage = stage;
24+
}
25+
26+
QQuickItem *MouseEventHandler::spriteRepeater() const
27+
{
28+
return m_spriteRepeater;
29+
}
30+
31+
void MouseEventHandler::setSpriteRepeater(QQuickItem *repeater)
32+
{
33+
m_spriteRepeater = repeater;
34+
}
35+
36+
bool MouseEventHandler::eventFilter(QObject *obj, QEvent *event)
37+
{
38+
switch (event->type()) {
39+
case QEvent::HoverEnter:
40+
case QEvent::HoverLeave:
41+
forwardPointEvent(static_cast<QSinglePointEvent *>(event));
42+
return true;
43+
44+
case QEvent::HoverMove:
45+
case QEvent::MouseMove: {
46+
QSinglePointEvent *pointEvent = static_cast<QSinglePointEvent *>(event);
47+
forwardPointEvent(pointEvent);
48+
QPointF pos = pointEvent->position();
49+
emit mouseMoved(pos.x(), pos.y());
50+
return true;
51+
}
52+
53+
case QEvent::MouseButtonPress:
54+
emit mousePressed();
55+
forwardPointEvent(static_cast<QSinglePointEvent *>(event));
56+
return true;
57+
58+
case QEvent::MouseButtonRelease:
59+
emit mouseReleased();
60+
forwardPointEvent(static_cast<QSinglePointEvent *>(event));
61+
return true;
62+
63+
default:
64+
break;
65+
}
66+
67+
return QObject::eventFilter(obj, event);
68+
}
69+
70+
void MouseEventHandler::forwardPointEvent(QSinglePointEvent *event)
71+
{
72+
Q_ASSERT(m_spriteRepeater);
73+
74+
if (!m_spriteRepeater)
75+
return;
76+
77+
// Create list of sprites
78+
std::vector<IRenderedTarget *> sprites;
79+
int count = m_spriteRepeater->property("count").toInt();
80+
sprites.reserve(count);
81+
82+
for (int i = 0; i < count; i++) {
83+
QQuickItem *sprite = nullptr;
84+
QMetaObject::invokeMethod(m_spriteRepeater, "itemAt", Qt::DirectConnection, Q_RETURN_ARG(QQuickItem *, sprite), Q_ARG(int, i));
85+
Q_ASSERT(sprite);
86+
Q_ASSERT(dynamic_cast<IRenderedTarget *>(sprite));
87+
Q_ASSERT(dynamic_cast<IRenderedTarget *>(sprite)->scratchTarget());
88+
sprites.push_back(dynamic_cast<IRenderedTarget *>(sprite));
89+
}
90+
91+
// Sort the list by layer order
92+
std::sort(sprites.begin(), sprites.end(), [](IRenderedTarget *t1, IRenderedTarget *t2) { return t1->scratchTarget()->layerOrder() > t2->scratchTarget()->layerOrder(); });
93+
94+
// Send the event to the hovered sprite
95+
for (IRenderedTarget *sprite : sprites) {
96+
// contains() expects position in the item's coordinate system
97+
QPointF localPos = sprite->mapFromScene(event->scenePosition());
98+
99+
if (sprite->contains(localPos)) {
100+
sendPointEventToItem(event, sprite);
101+
return;
102+
}
103+
}
104+
105+
// If there wasn't any hovered sprite, send the event to the stage
106+
Q_ASSERT(m_stage);
107+
108+
if (m_stage)
109+
sendPointEventToItem(event, m_stage);
110+
}
111+
112+
void MouseEventHandler::sendPointEventToItem(QSinglePointEvent *event, QQuickItem *item)
113+
{
114+
Q_ASSERT(event);
115+
Q_ASSERT(item);
116+
Q_ASSERT(m_stage);
117+
118+
switch (event->type()) {
119+
case QEvent::HoverEnter:
120+
case QEvent::HoverLeave:
121+
QCoreApplication::sendEvent(item, event);
122+
break;
123+
124+
case QEvent::HoverMove:
125+
// Send HoverLeave to the previously hovered item and HoverEnter to the hovered item
126+
if (item != m_hoveredItem) {
127+
if (m_hoveredItem)
128+
sendHoverEventToItem(static_cast<QHoverEvent *>(event), QEvent::HoverLeave, m_hoveredItem);
129+
130+
sendHoverEventToItem(static_cast<QHoverEvent *>(event), QEvent::HoverEnter, item);
131+
m_hoveredItem = item;
132+
}
133+
134+
QCoreApplication::sendEvent(item, event);
135+
break;
136+
137+
case QEvent::MouseButtonPress:
138+
case QEvent::MouseButtonRelease:
139+
case QEvent::MouseMove: {
140+
QMouseEvent itemEvent(
141+
event->type(),
142+
item->mapFromScene(event->scenePosition()),
143+
event->scenePosition(),
144+
event->globalPosition(),
145+
event->button(),
146+
event->buttons(),
147+
event->modifiers(),
148+
static_cast<const QPointingDevice *>(event->device()));
149+
150+
QCoreApplication::sendEvent(item, &itemEvent);
151+
break;
152+
}
153+
154+
default:
155+
Q_ASSERT(false);
156+
break;
157+
}
158+
}
159+
160+
void MouseEventHandler::sendHoverEventToItem(QHoverEvent *originalEvent, QEvent::Type newType, QQuickItem *item)
161+
{
162+
Q_ASSERT(newType == QEvent::HoverEnter || newType == QEvent::HoverLeave || newType == QEvent::HoverMove);
163+
164+
QHoverEvent event(
165+
newType,
166+
originalEvent->scenePosition(),
167+
originalEvent->globalPosition(),
168+
originalEvent->oldPosF(),
169+
originalEvent->modifiers(),
170+
static_cast<const QPointingDevice *>(originalEvent->device()));
171+
172+
QCoreApplication::sendEvent(item, &event);
173+
}

ScratchCPPGui/mouseeventhandler.h

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// SPDX-License-Identifier: LGPL-3.0-or-later
2+
3+
#pragma once
4+
5+
#include <QObject>
6+
#include <QEvent>
7+
8+
class QQuickItem;
9+
class QSinglePointEvent;
10+
class QHoverEvent;
11+
12+
namespace scratchcppgui
13+
{
14+
15+
class IRenderedTarget;
16+
17+
class MouseEventHandler : public QObject
18+
{
19+
Q_OBJECT
20+
public:
21+
explicit MouseEventHandler(QObject *parent = nullptr);
22+
23+
IRenderedTarget *stage() const;
24+
void setStage(IRenderedTarget *stage);
25+
26+
QQuickItem *spriteRepeater() const;
27+
void setSpriteRepeater(QQuickItem *repeater);
28+
29+
bool eventFilter(QObject *obj, QEvent *event) override;
30+
31+
signals:
32+
void mouseMoved(qreal x, qreal y);
33+
void mousePressed();
34+
void mouseReleased();
35+
36+
private:
37+
void forwardPointEvent(QSinglePointEvent *event);
38+
void sendPointEventToItem(QSinglePointEvent *event, QQuickItem *item);
39+
void sendHoverEventToItem(QHoverEvent *originalEvent, QEvent::Type newType, QQuickItem *item);
40+
41+
IRenderedTarget *m_stage = nullptr;
42+
QQuickItem *m_hoveredItem = nullptr;
43+
QQuickItem *m_spriteRepeater = nullptr;
44+
};
45+
46+
} // namespace scratchcppgui

ScratchCPPGui/renderedtarget.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,11 @@ void RenderedTarget::setHeight(qreal height)
216216
QNanoQuickItem::setHeight(height);
217217
}
218218

219+
QPointF RenderedTarget::mapFromScene(const QPointF &point) const
220+
{
221+
return QNanoQuickItem::mapFromScene(point);
222+
}
223+
219224
QNanoQuickItemPainter *RenderedTarget::createItemPainter() const
220225
{
221226
return new TargetPainter();

ScratchCPPGui/renderedtarget.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ class RenderedTarget : public IRenderedTarget
4848
qreal height() const override;
4949
void setHeight(qreal height) override;
5050

51+
QPointF mapFromScene(const QPointF &point) const override;
52+
5153
QBuffer *bitmapBuffer() override;
5254
const QString &bitmapUniqueKey() const override;
5355

ScratchCPPGui/scenemousearea.cpp

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// SPDX-License-Identifier: LGPL-3.0-or-later
2+
3+
#include "scenemousearea.h"
4+
#include "mouseeventhandler.h"
5+
6+
using namespace scratchcppgui;
7+
8+
SceneMouseArea::SceneMouseArea(QQuickItem *parent) :
9+
QQuickItem(parent)
10+
{
11+
m_mouseHandler = new MouseEventHandler(this);
12+
installEventFilter(m_mouseHandler);
13+
connect(m_mouseHandler, &MouseEventHandler::mouseMoved, this, &SceneMouseArea::mouseMoved);
14+
connect(m_mouseHandler, &MouseEventHandler::mousePressed, this, &SceneMouseArea::mousePressed);
15+
connect(m_mouseHandler, &MouseEventHandler::mouseReleased, this, &SceneMouseArea::mouseReleased);
16+
17+
setAcceptHoverEvents(true);
18+
setAcceptTouchEvents(true);
19+
setAcceptedMouseButtons(Qt::AllButtons);
20+
}
21+
22+
IRenderedTarget *SceneMouseArea::stage() const
23+
{
24+
return m_mouseHandler->stage();
25+
}
26+
27+
void SceneMouseArea::setStage(IRenderedTarget *newStage)
28+
{
29+
m_mouseHandler->setStage(newStage);
30+
emit stageChanged();
31+
}
32+
33+
QQuickItem *SceneMouseArea::spriteRepeater() const
34+
{
35+
return m_mouseHandler->spriteRepeater();
36+
}
37+
38+
void SceneMouseArea::setSpriteRepeater(QQuickItem *newSpriteRepeater)
39+
{
40+
m_mouseHandler->setSpriteRepeater(newSpriteRepeater);
41+
emit spriteRepeaterChanged();
42+
}

ScratchCPPGui/scenemousearea.h

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// SPDX-License-Identifier: LGPL-3.0-or-later
2+
3+
#pragma once
4+
5+
#include <QQuickItem>
6+
7+
namespace scratchcppgui
8+
{
9+
10+
class IRenderedTarget;
11+
class MouseEventHandler;
12+
13+
class SceneMouseArea : public QQuickItem
14+
{
15+
Q_OBJECT
16+
QML_ELEMENT
17+
Q_PROPERTY(IRenderedTarget *stage WRITE setStage NOTIFY stageChanged)
18+
Q_PROPERTY(QQuickItem *spriteRepeater READ spriteRepeater WRITE setSpriteRepeater NOTIFY spriteRepeaterChanged)
19+
20+
public:
21+
explicit SceneMouseArea(QQuickItem *parent = nullptr);
22+
23+
IRenderedTarget *stage() const;
24+
void setStage(IRenderedTarget *newStage);
25+
26+
QQuickItem *spriteRepeater() const;
27+
void setSpriteRepeater(QQuickItem *newSpriteRepeater);
28+
29+
signals:
30+
void mouseMoved(qreal x, qreal y);
31+
void mousePressed();
32+
void mouseReleased();
33+
void stageChanged();
34+
void spriteRepeaterChanged();
35+
36+
private:
37+
MouseEventHandler *m_mouseHandler = nullptr;
38+
};
39+
40+
} // namespace scratchcppgui

0 commit comments

Comments
 (0)