diff --git a/src/editor/dialogs/addeventdialog.cpp b/src/editor/dialogs/addeventdialog.cpp
index 0734da2..d088f09 100644
--- a/src/editor/dialogs/addeventdialog.cpp
+++ b/src/editor/dialogs/addeventdialog.cpp
@@ -82,6 +82,78 @@ AddEventDialog::AddEventDialog(ProjectTreeModel &projectModel, MainWindow &mainW
m_ui->pushButtonCollision->setMenu(menu);
}
+ {
+ auto menu = new QMenu;
+ menu->addAction(tr("Left button"), this, [this](){ m_eventType = Object::EventType::MouseLeftButton; accept(); });
+ menu->addAction(tr("Right button"), this, [this](){ m_eventType = Object::EventType::MouseRightButton; accept(); });
+ menu->addAction(tr("Middle button"), this, [this](){ m_eventType = Object::EventType::MouseMiddleButton; accept(); });
+ menu->addSeparator();
+ menu->addAction(tr("No button"), this, [this](){ m_eventType = Object::EventType::MouseNoButton; accept(); });
+ menu->addSeparator();
+ menu->addAction(tr("Left pressed"), this, [this](){ m_eventType = Object::EventType::MouseLeftPressed; accept(); });
+ menu->addAction(tr("Right pressed"), this, [this](){ m_eventType = Object::EventType::MouseRightPressed; accept(); });
+ menu->addAction(tr("Middle pressed"), this, [this](){ m_eventType = Object::EventType::MouseMiddlePressed; accept(); });
+ menu->addSeparator();
+ menu->addAction(tr("Left released"), this, [this](){ m_eventType = Object::EventType::MouseLeftReleased; accept(); });
+ menu->addAction(tr("Right released"), this, [this](){ m_eventType = Object::EventType::MouseRightReleased; accept(); });
+ menu->addAction(tr("Middle released"), this, [this](){ m_eventType = Object::EventType::MouseMiddleReleased; accept(); });
+ menu->addSeparator();
+ menu->addAction(tr("Mouse enter"), this, [this](){ m_eventType = Object::EventType::MouseEnter; accept(); });
+ menu->addAction(tr("Mouse leave"), this, [this](){ m_eventType = Object::EventType::MouseLeave; accept(); });
+ menu->addSeparator();
+ menu->addAction(tr("Mouse wheel up"), this, [this](){ m_eventType = Object::EventType::MouseWheelUp; accept(); });
+ menu->addAction(tr("Mouse wheel down"), this, [this](){ m_eventType = Object::EventType::MouseWheelDown; accept(); });
+ menu->addSeparator();
+ {
+ auto subMenu = menu->addMenu(tr("Global mouse"));
+ subMenu->addAction(tr("Global Left button"), this, [this](){ m_eventType = Object::EventType::MouseGlobalLeftButton; accept(); });
+ subMenu->addAction(tr("Global Right button"), this, [this](){ m_eventType = Object::EventType::MouseGlobalRightButton; accept(); });
+ subMenu->addAction(tr("Global Middle button"), this, [this](){ m_eventType = Object::EventType::MouseGlobalMiddleButton; accept(); });
+ subMenu->addSeparator();
+ subMenu->addAction(tr("Global Left pressed"), this, [this](){ m_eventType = Object::EventType::MouseGlobalLeftPressed; accept(); });
+ subMenu->addAction(tr("Global Right pressed"), this, [this](){ m_eventType = Object::EventType::MouseGlobalRightPressed; accept(); });
+ subMenu->addAction(tr("Global Middle pressed"), this, [this](){ m_eventType = Object::EventType::MouseGlobalMiddlePressed; accept(); });
+ subMenu->addSeparator();
+ subMenu->addAction(tr("Global Left released"), this, [this](){ m_eventType = Object::EventType::MouseGlobalLeftReleased; accept(); });
+ subMenu->addAction(tr("Global Right released"), this, [this](){ m_eventType = Object::EventType::MouseGlobalRightReleased; accept(); });
+ subMenu->addAction(tr("Global Middle released"), this, [this](){ m_eventType = Object::EventType::MouseGlobalMiddleReleased; accept(); });
+ }
+ menu->addSeparator();
+ {
+ auto subMenu = menu->addMenu(tr("Joystick 1"));
+ subMenu->addAction(tr("Left"), this, [this](){ m_eventType = Object::EventType::MouseJoystick1Left; accept(); });
+ subMenu->addAction(tr("Right"), this, [this](){ m_eventType = Object::EventType::MouseJoystick1Right; accept(); });
+ subMenu->addAction(tr("Up"), this, [this](){ m_eventType = Object::EventType::MouseJoystick1Up; accept(); });
+ subMenu->addAction(tr("Down"), this, [this](){ m_eventType = Object::EventType::MouseJoystick1Down; accept(); });
+ subMenu->addSeparator();
+ subMenu->addAction(tr("Button 1"), this, [this](){ m_eventType = Object::EventType::MouseJoystick1Button1; accept(); });
+ subMenu->addAction(tr("Button 2"), this, [this](){ m_eventType = Object::EventType::MouseJoystick1Button2; accept(); });
+ subMenu->addAction(tr("Button 3"), this, [this](){ m_eventType = Object::EventType::MouseJoystick1Button3; accept(); });
+ subMenu->addAction(tr("Button 4"), this, [this](){ m_eventType = Object::EventType::MouseJoystick1Button4; accept(); });
+ subMenu->addAction(tr("Button 5"), this, [this](){ m_eventType = Object::EventType::MouseJoystick1Button5; accept(); });
+ subMenu->addAction(tr("Button 6"), this, [this](){ m_eventType = Object::EventType::MouseJoystick1Button6; accept(); });
+ subMenu->addAction(tr("Button 7"), this, [this](){ m_eventType = Object::EventType::MouseJoystick1Button7; accept(); });
+ subMenu->addAction(tr("Button 8"), this, [this](){ m_eventType = Object::EventType::MouseJoystick1Button8; accept(); });
+ }
+ {
+ auto subMenu = menu->addMenu(tr("Joystick 2"));
+ subMenu->addAction(tr("Left"), this, [this](){ m_eventType = Object::EventType::MouseJoystick2Left; accept(); });
+ subMenu->addAction(tr("Right"), this, [this](){ m_eventType = Object::EventType::MouseJoystick2Right; accept(); });
+ subMenu->addAction(tr("Up"), this, [this](){ m_eventType = Object::EventType::MouseJoystick2Up; accept(); });
+ subMenu->addAction(tr("Down"), this, [this](){ m_eventType = Object::EventType::MouseJoystick2Down; accept(); });
+ subMenu->addSeparator();
+ subMenu->addAction(tr("Button 1"), this, [this](){ m_eventType = Object::EventType::MouseJoystick2Button1; accept(); });
+ subMenu->addAction(tr("Button 2"), this, [this](){ m_eventType = Object::EventType::MouseJoystick2Button2; accept(); });
+ subMenu->addAction(tr("Button 3"), this, [this](){ m_eventType = Object::EventType::MouseJoystick2Button3; accept(); });
+ subMenu->addAction(tr("Button 4"), this, [this](){ m_eventType = Object::EventType::MouseJoystick2Button4; accept(); });
+ subMenu->addAction(tr("Button 5"), this, [this](){ m_eventType = Object::EventType::MouseJoystick2Button5; accept(); });
+ subMenu->addAction(tr("Button 6"), this, [this](){ m_eventType = Object::EventType::MouseJoystick2Button6; accept(); });
+ subMenu->addAction(tr("Button 7"), this, [this](){ m_eventType = Object::EventType::MouseJoystick2Button7; accept(); });
+ subMenu->addAction(tr("Button 8"), this, [this](){ m_eventType = Object::EventType::MouseJoystick2Button8; accept(); });
+ }
+ m_ui->pushButtonMouse->setMenu(menu);
+ }
+
{
auto menu = new QMenu;
menu->addAction(tr("Add/Modify Triggers..."), this, [this](){ m_mainWindow.showDefineTriggers(); reject(); });
diff --git a/src/editor/dialogs/addeventdialog.ui b/src/editor/dialogs/addeventdialog.ui
index beb6733..fa16330 100644
--- a/src/editor/dialogs/addeventdialog.ui
+++ b/src/editor/dialogs/addeventdialog.ui
@@ -32,6 +32,10 @@
&Mouse
+
+
+ :/qtgameengine/icons/event-mouse.png:/qtgameengine/icons/event-mouse.png
+
-
@@ -133,7 +137,7 @@
-
- QDialogButtonBox::Cancel
+ QDialogButtonBox::StandardButton::Cancel
true
diff --git a/src/editor/icons/event-mouse.png b/src/editor/icons/event-mouse.png
new file mode 100644
index 0000000..3c2b676
Binary files /dev/null and b/src/editor/icons/event-mouse.png differ
diff --git a/src/editor/models/objecteventsmodel.cpp b/src/editor/models/objecteventsmodel.cpp
index c7f7984..4d52205 100644
--- a/src/editor/models/objecteventsmodel.cpp
+++ b/src/editor/models/objecteventsmodel.cpp
@@ -38,24 +38,71 @@ QVariant ObjectEventsModel::data(const QModelIndex &index, int role) const
case Qt::EditRole:
switch (pair.first)
{
- case Object::EventType::Create: return tr("Create");
- case Object::EventType::Destroy: return tr("Destroy");
- case Object::EventType::Alarm0: return tr("Alarm 0");
- case Object::EventType::Alarm1: return tr("Alarm 1");
- case Object::EventType::Alarm2: return tr("Alarm 2");
- case Object::EventType::Alarm3: return tr("Alarm 3");
- case Object::EventType::Alarm4: return tr("Alarm 4");
- case Object::EventType::Alarm5: return tr("Alarm 5");
- case Object::EventType::Alarm6: return tr("Alarm 6");
- case Object::EventType::Alarm7: return tr("Alarm 7");
- case Object::EventType::Alarm8: return tr("Alarm 8");
- case Object::EventType::Alarm9: return tr("Alarm 9");
- case Object::EventType::Alarm10: return tr("Alarm 10");
- case Object::EventType::Alarm11: return tr("Alarm 11");
- case Object::EventType::Step: return tr("Step");
- case Object::EventType::BeginStep: return tr("Begin Step");
- case Object::EventType::EndStep: return tr("End Step");
- case Object::EventType::Draw: return tr("Draw");
+ case Object::EventType::Create: return tr("Create");
+ case Object::EventType::Destroy: return tr("Destroy");
+ case Object::EventType::Alarm0: return tr("Alarm 0");
+ case Object::EventType::Alarm1: return tr("Alarm 1");
+ case Object::EventType::Alarm2: return tr("Alarm 2");
+ case Object::EventType::Alarm3: return tr("Alarm 3");
+ case Object::EventType::Alarm4: return tr("Alarm 4");
+ case Object::EventType::Alarm5: return tr("Alarm 5");
+ case Object::EventType::Alarm6: return tr("Alarm 6");
+ case Object::EventType::Alarm7: return tr("Alarm 7");
+ case Object::EventType::Alarm8: return tr("Alarm 8");
+ case Object::EventType::Alarm9: return tr("Alarm 9");
+ case Object::EventType::Alarm10: return tr("Alarm 10");
+ case Object::EventType::Alarm11: return tr("Alarm 11");
+ case Object::EventType::Step: return tr("Step");
+ case Object::EventType::BeginStep: return tr("Begin Step");
+ case Object::EventType::EndStep: return tr("End Step");
+ case Object::EventType::Draw: return tr("Draw");
+ case Object::EventType::MouseLeftButton: return tr("Left button");
+ case Object::EventType::MouseRightButton: return tr("Right Button");
+ case Object::EventType::MouseMiddleButton: return tr("Middle Button");
+ case Object::EventType::MouseNoButton: return tr("No Button");
+ case Object::EventType::MouseLeftPressed: return tr("Left Pressed");
+ case Object::EventType::MouseRightPressed: return tr("Right Pressed");
+ case Object::EventType::MouseMiddlePressed: return tr("Middle Pressed");
+ case Object::EventType::MouseLeftReleased: return tr("Left Released");
+ case Object::EventType::MouseRightReleased: return tr("Right Released");
+ case Object::EventType::MouseMiddleReleased: return tr("Middle Released");
+ case Object::EventType::MouseEnter: return tr("Enter");
+ case Object::EventType::MouseLeave: return tr("Leave");
+ case Object::EventType::MouseWheelUp: return tr("Wheel Up");
+ case Object::EventType::MouseWheelDown: return tr("Wheel Down");
+ case Object::EventType::MouseGlobalLeftButton: return tr("Global Left Button");
+ case Object::EventType::MouseGlobalRightButton: return tr("Global Right Button");
+ case Object::EventType::MouseGlobalMiddleButton: return tr("Global Middle Button");
+ case Object::EventType::MouseGlobalLeftPressed: return tr("Global Left Pressed");
+ case Object::EventType::MouseGlobalRightPressed: return tr("Global Right Pressed");
+ case Object::EventType::MouseGlobalMiddlePressed: return tr("Global Middle Pressed");
+ case Object::EventType::MouseGlobalLeftReleased: return tr("Global Left Released");
+ case Object::EventType::MouseGlobalRightReleased: return tr("Global Right Released");
+ case Object::EventType::MouseGlobalMiddleReleased: return tr("Global Middle Released");
+ case Object::EventType::MouseJoystick1Left: return tr("Joystick 1 Left");
+ case Object::EventType::MouseJoystick1Right: return tr("Joystick 1 Right");
+ case Object::EventType::MouseJoystick1Up: return tr("Joystick 1 Up");
+ case Object::EventType::MouseJoystick1Down: return tr("Joystick 1 Down");
+ case Object::EventType::MouseJoystick1Button1: return tr("Joystick 1 Button 1");
+ case Object::EventType::MouseJoystick1Button2: return tr("Joystick 1 Button 2");
+ case Object::EventType::MouseJoystick1Button3: return tr("Joystick 1 Button 3");
+ case Object::EventType::MouseJoystick1Button4: return tr("Joystick 1 Button 4");
+ case Object::EventType::MouseJoystick1Button5: return tr("Joystick 1 Button 5");
+ case Object::EventType::MouseJoystick1Button6: return tr("Joystick 1 Button 6");
+ case Object::EventType::MouseJoystick1Button7: return tr("Joystick 1 Button 7");
+ case Object::EventType::MouseJoystick1Button8: return tr("Joystick 1 Button 8");
+ case Object::EventType::MouseJoystick2Left: return tr("Joystick 2 Left");
+ case Object::EventType::MouseJoystick2Right: return tr("Joystick 2 Right");
+ case Object::EventType::MouseJoystick2Up: return tr("Joystick 2 Up");
+ case Object::EventType::MouseJoystick2Down: return tr("Joystick 2 Down");
+ case Object::EventType::MouseJoystick2Button1: return tr("Joystick 2 Button 1");
+ case Object::EventType::MouseJoystick2Button2: return tr("Joystick 2 Button 2");
+ case Object::EventType::MouseJoystick2Button3: return tr("Joystick 2 Button 3");
+ case Object::EventType::MouseJoystick2Button4: return tr("Joystick 2 Button 4");
+ case Object::EventType::MouseJoystick2Button5: return tr("Joystick 2 Button 5");
+ case Object::EventType::MouseJoystick2Button6: return tr("Joystick 2 Button 6");
+ case Object::EventType::MouseJoystick2Button7: return tr("Joystick 2 Button 7");
+ case Object::EventType::MouseJoystick2Button8: return tr("Joystick 2 Button 8");
}
qWarning() << "unknown event type" << std::to_underlying(pair.first);
@@ -63,8 +110,8 @@ QVariant ObjectEventsModel::data(const QModelIndex &index, int role) const
case Qt::DecorationRole:
switch (pair.first)
{
- case Object::EventType::Create: return QIcon{":/qtgameengine/icons/event-create.png"};
- case Object::EventType::Destroy: return QIcon{":/qtgameengine/icons/event-destroy.png"};
+ case Object::EventType::Create: return QIcon{":/qtgameengine/icons/event-create.png"};
+ case Object::EventType::Destroy: return QIcon{":/qtgameengine/icons/event-destroy.png"};
case Object::EventType::Alarm0:
case Object::EventType::Alarm1:
case Object::EventType::Alarm2:
@@ -76,11 +123,58 @@ QVariant ObjectEventsModel::data(const QModelIndex &index, int role) const
case Object::EventType::Alarm8:
case Object::EventType::Alarm9:
case Object::EventType::Alarm10:
- case Object::EventType::Alarm11: return QIcon{":/qtgameengine/icons/event-alarm.png"};
+ case Object::EventType::Alarm11: return QIcon{":/qtgameengine/icons/event-alarm.png"};
case Object::EventType::Step:
case Object::EventType::BeginStep:
- case Object::EventType::EndStep: return QIcon{":/qtgameengine/icons/event-step.png"};
- case Object::EventType::Draw: return QIcon{":/qtgameengine/icons/event-draw.png"};
+ case Object::EventType::EndStep: return QIcon{":/qtgameengine/icons/event-step.png"};
+ case Object::EventType::Draw: return QIcon{":/qtgameengine/icons/event-draw.png"};
+ case Object::EventType::MouseLeftButton:
+ case Object::EventType::MouseRightButton:
+ case Object::EventType::MouseMiddleButton:
+ case Object::EventType::MouseNoButton:
+ case Object::EventType::MouseLeftPressed:
+ case Object::EventType::MouseRightPressed:
+ case Object::EventType::MouseMiddlePressed:
+ case Object::EventType::MouseLeftReleased:
+ case Object::EventType::MouseRightReleased:
+ case Object::EventType::MouseMiddleReleased:
+ case Object::EventType::MouseEnter:
+ case Object::EventType::MouseLeave:
+ case Object::EventType::MouseWheelUp:
+ case Object::EventType::MouseWheelDown:
+ case Object::EventType::MouseGlobalLeftButton:
+ case Object::EventType::MouseGlobalRightButton:
+ case Object::EventType::MouseGlobalMiddleButton:
+ case Object::EventType::MouseGlobalLeftPressed:
+ case Object::EventType::MouseGlobalRightPressed:
+ case Object::EventType::MouseGlobalMiddlePressed:
+ case Object::EventType::MouseGlobalLeftReleased:
+ case Object::EventType::MouseGlobalRightReleased:
+ case Object::EventType::MouseGlobalMiddleReleased:
+ case Object::EventType::MouseJoystick1Left:
+ case Object::EventType::MouseJoystick1Right:
+ case Object::EventType::MouseJoystick1Up:
+ case Object::EventType::MouseJoystick1Down:
+ case Object::EventType::MouseJoystick1Button1:
+ case Object::EventType::MouseJoystick1Button2:
+ case Object::EventType::MouseJoystick1Button3:
+ case Object::EventType::MouseJoystick1Button4:
+ case Object::EventType::MouseJoystick1Button5:
+ case Object::EventType::MouseJoystick1Button6:
+ case Object::EventType::MouseJoystick1Button7:
+ case Object::EventType::MouseJoystick1Button8:
+ case Object::EventType::MouseJoystick2Left:
+ case Object::EventType::MouseJoystick2Right:
+ case Object::EventType::MouseJoystick2Up:
+ case Object::EventType::MouseJoystick2Down:
+ case Object::EventType::MouseJoystick2Button1:
+ case Object::EventType::MouseJoystick2Button2:
+ case Object::EventType::MouseJoystick2Button3:
+ case Object::EventType::MouseJoystick2Button4:
+ case Object::EventType::MouseJoystick2Button5:
+ case Object::EventType::MouseJoystick2Button6:
+ case Object::EventType::MouseJoystick2Button7:
+ case Object::EventType::MouseJoystick2Button8: return QIcon{":/qtgameengine/icons/event-mouse.png"};
}
qWarning() << "unknown event type" << std::to_underlying(pair.first);
diff --git a/src/editor/resources_editor.qrc b/src/editor/resources_editor.qrc
index 7964bc1..5af7df6 100644
--- a/src/editor/resources_editor.qrc
+++ b/src/editor/resources_editor.qrc
@@ -47,6 +47,7 @@
icons/event-create.png
icons/event-destroy.png
icons/event-draw.png
+ icons/event-mouse.png
icons/event-step.png
icons/exit.png
icons/export-resources.png
diff --git a/src/engine/gameengine.cpp b/src/engine/gameengine.cpp
index 54b4cea..778db73 100644
--- a/src/engine/gameengine.cpp
+++ b/src/engine/gameengine.cpp
@@ -3,6 +3,9 @@
#include
#include
+#include
+#include
+#include
#include "closeeventfilter.h"
#include "projectcontainer.h"
@@ -22,6 +25,8 @@ GameEngine::GameEngine(const ProjectContainer &project, QObject *parent) :
m_glGameWindow.setModality(Qt::WindowModal);
m_vulkanGameWindow.setModality(Qt::WindowModal);
+ m_glGameWindow.installEventFilter(this);
+ m_vulkanGameWindow.installEventFilter(this);
m_frameTimer.setTimerType(Qt::PreciseTimer);
m_frameTimer.setSingleShot(true);
@@ -41,6 +46,81 @@ GameEngine::GameEngine(const ProjectContainer &project, QObject *parent) :
});
}
+bool GameEngine::eventFilter(QObject *watched, QEvent *event)
+{
+ if (watched != &m_glGameWindow && watched != &m_vulkanGameWindow)
+ return QObject::eventFilter(watched, event);
+
+ auto *window = static_cast(watched);
+ const auto roomPosition = [this, window](QPointF position) {
+ return QPointF{position.x() * m_scene.roomSize.width() / std::max(1, window->width()),
+ position.y() * m_scene.roomSize.height() / std::max(1, window->height())};
+ };
+
+ switch (event->type())
+ {
+ case QEvent::FocusIn:
+ if (m_mouseSource != window)
+ {
+ m_scene.resetMouseInput();
+ m_mouseSource = window;
+ }
+ break;
+ case QEvent::FocusOut:
+ case QEvent::Hide:
+ if (m_mouseSource == window)
+ {
+ m_scene.resetMouseInput();
+ m_mouseSource = nullptr;
+ }
+ break;
+ case QEvent::Enter:
+ if (!m_mouseSource)
+ m_mouseSource = window;
+ if (m_mouseSource == window)
+ m_scene.queueMouseMove(roomPosition(static_cast(event)->position()));
+ break;
+ case QEvent::Leave:
+ if (m_mouseSource == window)
+ m_scene.queueMouseLeave();
+ break;
+ case QEvent::MouseMove:
+ case QEvent::MouseButtonPress:
+ case QEvent::MouseButtonRelease:
+ case QEvent::MouseButtonDblClick:
+ {
+ auto *mouse = static_cast(event);
+ if (event->type() == QEvent::MouseButtonPress || event->type() == QEvent::MouseButtonDblClick)
+ {
+ if (m_mouseSource != window)
+ {
+ m_scene.resetMouseInput();
+ m_mouseSource = window;
+ }
+ }
+ if (m_mouseSource != window)
+ break;
+ const QPointF position = roomPosition(mouse->position());
+ if (event->type() == QEvent::MouseMove)
+ m_scene.queueMouseMove(position);
+ else
+ m_scene.queueMouseButton(position, mouse->button(),
+ event->type() != QEvent::MouseButtonRelease);
+ break;
+ }
+ case QEvent::Wheel:
+ if (m_mouseSource == window)
+ {
+ auto *wheel = static_cast(event);
+ m_scene.queueMouseWheel(roomPosition(wheel->position()), wheel->angleDelta().y());
+ }
+ break;
+ default:
+ break;
+ }
+ return QObject::eventFilter(watched, event);
+}
+
void GameEngine::run()
{
if (m_project.rooms.empty())
diff --git a/src/engine/gameengine.h b/src/engine/gameengine.h
index eae0efa..da06c90 100644
--- a/src/engine/gameengine.h
+++ b/src/engine/gameengine.h
@@ -21,6 +21,9 @@ public:
explicit GameEngine(const ProjectContainer &project, QObject *parent = nullptr);
void run();
+protected:
+ bool eventFilter(QObject *watched, QEvent *event) override;
+
private:
const ProjectContainer &m_project;
GameScene m_scene;
@@ -28,6 +31,7 @@ private:
GlGameWindow m_glGameWindow;
VulkanGameWindow m_vulkanGameWindow;
bool m_vulkanAvailable{};
+ QWindow *m_mouseSource{};
QTimer m_frameTimer;
QElapsedTimer m_elapsed;
qint64 m_nextFrameNanoseconds{};
diff --git a/src/engine/gamescene.cpp b/src/engine/gamescene.cpp
index 93dd82d..bf7940c 100644
--- a/src/engine/gamescene.cpp
+++ b/src/engine/gamescene.cpp
@@ -26,6 +26,37 @@ QPointF motion(double degrees, double speed)
return {speed * std::cos(radians), -speed * std::sin(radians)};
}
+std::optional mouseButtonEvent(Qt::MouseButton button, bool pressed, bool global)
+{
+ using Event = Object::EventType;
+ switch (button)
+ {
+ case Qt::LeftButton:
+ return global ? (pressed ? Event::MouseGlobalLeftPressed : Event::MouseGlobalLeftReleased)
+ : (pressed ? Event::MouseLeftPressed : Event::MouseLeftReleased);
+ case Qt::RightButton:
+ return global ? (pressed ? Event::MouseGlobalRightPressed : Event::MouseGlobalRightReleased)
+ : (pressed ? Event::MouseRightPressed : Event::MouseRightReleased);
+ case Qt::MiddleButton:
+ return global ? (pressed ? Event::MouseGlobalMiddlePressed : Event::MouseGlobalMiddleReleased)
+ : (pressed ? Event::MouseMiddlePressed : Event::MouseMiddleReleased);
+ default:
+ return std::nullopt;
+ }
+}
+
+std::optional mouseHeldEvent(Qt::MouseButton button, bool global)
+{
+ using Event = Object::EventType;
+ switch (button)
+ {
+ case Qt::LeftButton: return global ? Event::MouseGlobalLeftButton : Event::MouseLeftButton;
+ case Qt::RightButton: return global ? Event::MouseGlobalRightButton : Event::MouseRightButton;
+ case Qt::MiddleButton: return global ? Event::MouseGlobalMiddleButton : Event::MouseMiddleButton;
+ default: return std::nullopt;
+ }
+}
+
} // namespace
void ObjectInstance::executeActions(const ActionsContainer &actions, GameScene &scene,
@@ -260,6 +291,115 @@ void GameScene::removeDestroyed()
std::erase_if(instances, [](const Instance &instance) { return instance.pendingDestroy; });
}
+void GameScene::queueMouseMove(QPointF position)
+{
+ m_mouseInputs.push_back({MouseInput::Kind::Move, position});
+}
+
+void GameScene::queueMouseButton(QPointF position, Qt::MouseButton button, bool pressed)
+{
+ m_mouseInputs.push_back({pressed ? MouseInput::Kind::Press : MouseInput::Kind::Release,
+ position, button});
+}
+
+void GameScene::queueMouseWheel(QPointF position, int angleDeltaY)
+{
+ m_mouseInputs.push_back({MouseInput::Kind::Wheel, position, Qt::NoButton, angleDeltaY});
+}
+
+void GameScene::queueMouseLeave()
+{
+ m_mouseInputs.push_back({MouseInput::Kind::Leave, {}});
+}
+
+void GameScene::resetMouseInput()
+{
+ m_mouseInputs.push_back({MouseInput::Kind::Reset, {}});
+}
+
+void GameScene::dispatchMouseEvent(Object::EventType type, QPointF position, bool global)
+{
+ for (Instance &instance : instances)
+ if (!instance.pendingDestroy &&
+ (global || collisionRect(instance).contains(position)))
+ instance.executeEvent(type, *this, m_executor);
+}
+
+void GameScene::updateMouseHover()
+{
+ for (Instance &instance : instances)
+ {
+ if (instance.pendingDestroy)
+ continue;
+ const bool over = m_mouseInside && collisionRect(instance).contains(m_mousePosition);
+ if (over == instance.mouseOver)
+ continue;
+ instance.mouseOver = over;
+ instance.executeEvent(over ? Object::EventType::MouseEnter : Object::EventType::MouseLeave,
+ *this, m_executor);
+ }
+}
+
+void GameScene::processMouseInput()
+{
+ const auto inputs = std::exchange(m_mouseInputs, {});
+ const QRectF room{QPointF{}, QSizeF{roomSize}};
+ for (const MouseInput &input : inputs)
+ {
+ if (input.kind == MouseInput::Kind::Leave || input.kind == MouseInput::Kind::Reset)
+ {
+ m_mouseInside = false;
+ if (input.kind == MouseInput::Kind::Reset)
+ m_mouseButtons = Qt::NoButton;
+ updateMouseHover();
+ continue;
+ }
+
+ m_mousePosition = input.position;
+ m_mouseInside = room.contains(input.position);
+ updateMouseHover();
+ if (input.kind == MouseInput::Kind::Press || input.kind == MouseInput::Kind::Release)
+ {
+ const bool pressed = input.kind == MouseInput::Kind::Press;
+ if (pressed)
+ m_mouseButtons |= input.button;
+ else
+ m_mouseButtons &= ~Qt::MouseButtons{input.button};
+ if (const auto local = mouseButtonEvent(input.button, pressed, false); local && m_mouseInside)
+ dispatchMouseEvent(*local, input.position, false);
+ if (const auto global = mouseButtonEvent(input.button, pressed, true); global && m_mouseInside)
+ dispatchMouseEvent(*global, input.position, true);
+ }
+ else if (input.kind == MouseInput::Kind::Wheel && m_mouseInside)
+ {
+ m_wheelRemainder += input.wheelDelta;
+ while (m_wheelRemainder >= 120)
+ {
+ dispatchMouseEvent(Object::EventType::MouseWheelUp, input.position, true);
+ m_wheelRemainder -= 120;
+ }
+ while (m_wheelRemainder <= -120)
+ {
+ dispatchMouseEvent(Object::EventType::MouseWheelDown, input.position, true);
+ m_wheelRemainder += 120;
+ }
+ }
+ }
+
+ updateMouseHover(); // Instances can also move under a stationary cursor.
+ if (!m_mouseInside)
+ return;
+ if (m_mouseButtons == Qt::NoButton)
+ dispatchMouseEvent(Object::EventType::MouseNoButton, m_mousePosition, false);
+ for (Qt::MouseButton button : {Qt::LeftButton, Qt::RightButton, Qt::MiddleButton})
+ {
+ if (!(m_mouseButtons & button))
+ continue;
+ dispatchMouseEvent(*mouseHeldEvent(button, false), m_mousePosition, false);
+ dispatchMouseEvent(*mouseHeldEvent(button, true), m_mousePosition, true);
+ }
+}
+
void GameScene::advanceTo(qint64 elapsedMilliseconds)
{
const qint64 targetStep = elapsedMilliseconds * roomSpeed / 1000;
@@ -284,6 +424,7 @@ void GameScene::advanceTo(qint64 elapsedMilliseconds)
break;
}
}
+ processMouseInput();
for (Instance &instance : instances)
if (!instance.pendingDestroy)
instance.executeEvent(Object::EventType::Step, *this, m_executor);
@@ -295,6 +436,7 @@ void GameScene::advanceTo(qint64 elapsedMilliseconds)
for (Instance &instance : instances)
if (!instance.pendingDestroy)
instance.step();
+ updateMouseHover();
// Detect every pair at its moved position before collision actions can
// change either instance. A sprite-size rectangle is our first mask.
diff --git a/src/engine/gamescene.h b/src/engine/gamescene.h
index 96db84c..c30a692 100644
--- a/src/engine/gamescene.h
+++ b/src/engine/gamescene.h
@@ -11,6 +11,7 @@
#include
#include
#include
+#include
#include "projectcontainer.h"
@@ -39,6 +40,7 @@ struct ObjectInstance
int depth{};
bool visible{true};
bool pendingDestroy{};
+ bool mouseOver{};
std::array alarms;
void step();
@@ -71,6 +73,11 @@ struct GameScene
std::vector drawOrder() const;
void start(const ActionExecutor &executor);
void destroyInstance(Instance &instance);
+ void queueMouseMove(QPointF position);
+ void queueMouseButton(QPointF position, Qt::MouseButton button, bool pressed);
+ void queueMouseWheel(QPointF position, int angleDeltaY);
+ void queueMouseLeave();
+ void resetMouseInput();
void advanceTo(qint64 elapsedMilliseconds);
void draw();
@@ -82,7 +89,23 @@ struct GameScene
std::vector instances;
private:
+ struct MouseInput
+ {
+ enum class Kind { Move, Press, Release, Wheel, Leave, Reset } kind;
+ QPointF position;
+ Qt::MouseButton button{Qt::NoButton};
+ int wheelDelta{};
+ };
+
qint64 m_stepsDone{};
ActionExecutor m_executor;
+ std::vector m_mouseInputs;
+ QPointF m_mousePosition;
+ Qt::MouseButtons m_mouseButtons{Qt::NoButton};
+ bool m_mouseInside{};
+ int m_wheelRemainder{};
void removeDestroyed();
+ void updateMouseHover();
+ void dispatchMouseEvent(Object::EventType type, QPointF position, bool global);
+ void processMouseInput();
};
diff --git a/src/projectcontainer.h b/src/projectcontainer.h
index 9123673..ee378a9 100644
--- a/src/projectcontainer.h
+++ b/src/projectcontainer.h
@@ -262,6 +262,53 @@ struct Object
BeginStep,
EndStep,
Draw,
+ MouseLeftButton,
+ MouseRightButton,
+ MouseMiddleButton,
+ MouseNoButton,
+ MouseLeftPressed,
+ MouseRightPressed,
+ MouseMiddlePressed,
+ MouseLeftReleased,
+ MouseRightReleased,
+ MouseMiddleReleased,
+ MouseEnter,
+ MouseLeave,
+ MouseWheelUp,
+ MouseWheelDown,
+ MouseGlobalLeftButton,
+ MouseGlobalRightButton,
+ MouseGlobalMiddleButton,
+ MouseGlobalLeftPressed,
+ MouseGlobalRightPressed,
+ MouseGlobalMiddlePressed,
+ MouseGlobalLeftReleased,
+ MouseGlobalRightReleased,
+ MouseGlobalMiddleReleased,
+ MouseJoystick1Left,
+ MouseJoystick1Right,
+ MouseJoystick1Up,
+ MouseJoystick1Down,
+ MouseJoystick1Button1,
+ MouseJoystick1Button2,
+ MouseJoystick1Button3,
+ MouseJoystick1Button4,
+ MouseJoystick1Button5,
+ MouseJoystick1Button6,
+ MouseJoystick1Button7,
+ MouseJoystick1Button8,
+ MouseJoystick2Left,
+ MouseJoystick2Right,
+ MouseJoystick2Up,
+ MouseJoystick2Down,
+ MouseJoystick2Button1,
+ MouseJoystick2Button2,
+ MouseJoystick2Button3,
+ MouseJoystick2Button4,
+ MouseJoystick2Button5,
+ MouseJoystick2Button6,
+ MouseJoystick2Button7,
+ MouseJoystick2Button8,
};
using events_container_t = std::map;