Implement mouse events

This commit is contained in:
feedc0de committed 2026-11-14 15:58:48 +01:00
1 parent defb83c9c3
commit f6fc000287
10 files changed
+491 -24

No files matched your search

+72
View File
@@ -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(); });
+5 -1
View File
@@ -32,6 +32,10 @@
<property name="text">
<string>&amp;Mouse</string>
</property>
<property name="icon">
<iconset resource="../resources_editor.qrc">
<normaloff>:/qtgameengine/icons/event-mouse.png</normaloff>:/qtgameengine/icons/event-mouse.png</iconset>
</property>
</widget>
</item>
<item row="4" column="1">
@@ -133,7 +137,7 @@
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="standardButtons">
<set>QDialogButtonBox::Cancel</set>
<set>QDialogButtonBox::StandardButton::Cancel</set>
</property>
<property name="centerButtons">
<bool>true</bool>
Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

+117 -23
View File
@@ -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);
+1
View File
@@ -47,6 +47,7 @@
<file>icons/event-create.png</file>
<file>icons/event-destroy.png</file>
<file>icons/event-draw.png</file>
<file>icons/event-mouse.png</file>
<file>icons/event-step.png</file>
<file>icons/exit.png</file>
<file>icons/export-resources.png</file>
+80
View File
@@ -3,6 +3,9 @@
#include <algorithm>
#include <QEventLoop>
#include <QEnterEvent>
#include <QMouseEvent>
#include <QWheelEvent>
#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<QWindow *>(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<QEnterEvent *>(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<QMouseEvent *>(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<QWheelEvent *>(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())
+4
View File
@@ -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{};
+142
View File
@@ -26,6 +26,37 @@ QPointF motion(double degrees, double speed)
return {speed * std::cos(radians), -speed * std::sin(radians)};
}
std::optional<Object::EventType> 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<Object::EventType> 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.
+23
View File
@@ -11,6 +11,7 @@
#include <QRectF>
#include <QSize>
#include <QString>
#include <Qt>
#include "projectcontainer.h"
@@ -39,6 +40,7 @@ struct ObjectInstance
int depth{};
bool visible{true};
bool pendingDestroy{};
bool mouseOver{};
std::array<int, 12> alarms;
void step();
@@ -71,6 +73,11 @@ struct GameScene
std::vector<const Instance *> 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<Instance> 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<MouseInput> 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();
};
+47
View File
@@ -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<EventType, ActionsContainer>;