From 71c07531f0f0ef8c2780dbd2bb3097447a645d63 Mon Sep 17 00:00:00 2001 From: Malte Langkabel Date: Wed, 5 Aug 2026 14:02:35 +0200 Subject: [PATCH] move the remaining hotkeys into the InputMapper Space, W, S, R, Q and F3 all acted on GameWorldView state, so each needed a request event before it could move. One event per action rather than a shared action enum, matching how the existing request events are named and keeping subscribers from waking on actions they do not care about. The split is deliberate about where knowledge lives. Each event says only what the player asked for, never how to satisfy it: - PauseToggleRequestedEvent carries no speed; which speed to restore is remembered by the receiver. - SpeedStepRequestedEvent is a relative notch, because the ladder of speeds belongs to the receiver (unlike SpeedChangeRequestedEvent, which the speed buttons send with an absolute multiplier). - ModeCancelRequestedEvent names no mode; which of builder, blueprint or deconstruct is active is state only the receiver has. - DebugDrawToggleRequestedEvent is the request to flip the flag, where the existing DebugDrawToggledEvent is the announcement that it was flipped, so the flag keeps a single owner. GameWorldView::keyPressEvent is now nothing but a forward to the mapper. Shift release stays behind on purpose: it is the modifier of a mouse gesture (REQ-BLD-COPY-CONFIG), not a keyboard action, and modelling it as one would misrepresent it. Bindings are still hard-coded, and behaviour is unchanged. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01JcReq7hVk4KUPhTDKWAG7K --- src/lib/eventsystem/event/CMakeLists.txt | 5 ++ .../event/DebugDrawToggleRequestedEvent.h | 12 +++ .../event/GhostRotationRequestedEvent.h | 13 +++ .../event/ModeCancelRequestedEvent.h | 13 +++ .../event/PauseToggleRequestedEvent.h | 12 +++ .../event/SpeedStepRequestedEvent.h | 14 ++++ src/ui/GameWorldView.cpp | 81 ++++++++++--------- src/ui/GameWorldView.h | 17 ++++ src/ui/InputMapper.cpp | 31 +++++++ 9 files changed, 160 insertions(+), 38 deletions(-) create mode 100644 src/lib/eventsystem/event/DebugDrawToggleRequestedEvent.h create mode 100644 src/lib/eventsystem/event/GhostRotationRequestedEvent.h create mode 100644 src/lib/eventsystem/event/ModeCancelRequestedEvent.h create mode 100644 src/lib/eventsystem/event/PauseToggleRequestedEvent.h create mode 100644 src/lib/eventsystem/event/SpeedStepRequestedEvent.h diff --git a/src/lib/eventsystem/event/CMakeLists.txt b/src/lib/eventsystem/event/CMakeLists.txt index f82197f..b6c692b 100644 --- a/src/lib/eventsystem/event/CMakeLists.txt +++ b/src/lib/eventsystem/event/CMakeLists.txt @@ -17,6 +17,11 @@ SET(HDRS ${CMAKE_CURRENT_SOURCE_DIR}/BlueprintModeExitedEvent.h ${CMAKE_CURRENT_SOURCE_DIR}/EscapeMenuRequestedEvent.h ${CMAKE_CURRENT_SOURCE_DIR}/PanDirectionChangedEvent.h + ${CMAKE_CURRENT_SOURCE_DIR}/PauseToggleRequestedEvent.h + ${CMAKE_CURRENT_SOURCE_DIR}/SpeedStepRequestedEvent.h + ${CMAKE_CURRENT_SOURCE_DIR}/GhostRotationRequestedEvent.h + ${CMAKE_CURRENT_SOURCE_DIR}/ModeCancelRequestedEvent.h + ${CMAKE_CURRENT_SOURCE_DIR}/DebugDrawToggleRequestedEvent.h ${CMAKE_CURRENT_SOURCE_DIR}/DeconstructModeChangedEvent.h ${CMAKE_CURRENT_SOURCE_DIR}/BuildingTypeSelectedEvent.h ${CMAKE_CURRENT_SOURCE_DIR}/BuildHotkeyPressedEvent.h diff --git a/src/lib/eventsystem/event/DebugDrawToggleRequestedEvent.h b/src/lib/eventsystem/event/DebugDrawToggleRequestedEvent.h new file mode 100644 index 0000000..83f2e4f --- /dev/null +++ b/src/lib/eventsystem/event/DebugDrawToggleRequestedEvent.h @@ -0,0 +1,12 @@ +#pragma once + +#include "Event.h" + +// The player asked to toggle the debug overlays (REQ-UI-HOTKEYS). The request to +// flip the flag; DebugDrawToggledEvent is the announcement that it was flipped and +// what it now is. Splitting the two keeps the flag itself in one owner. +class DebugDrawToggleRequestedEvent : public Event +{ +public: + DebugDrawToggleRequestedEvent() = default; +}; diff --git a/src/lib/eventsystem/event/GhostRotationRequestedEvent.h b/src/lib/eventsystem/event/GhostRotationRequestedEvent.h new file mode 100644 index 0000000..3318e84 --- /dev/null +++ b/src/lib/eventsystem/event/GhostRotationRequestedEvent.h @@ -0,0 +1,13 @@ +#pragma once + +#include "Event.h" + +// The player asked to rotate the placement ghost (REQ-BLD-ROTATE, REQ-UI-HOTKEYS). +// Sent whether or not a builder or blueprint mode is actually active; deciding +// there is nothing to rotate is the receiver's job. +class GhostRotationRequestedEvent : public Event +{ +public: + explicit GhostRotationRequestedEvent(bool clockwise) : clockwise(clockwise) {} + const bool clockwise; +}; diff --git a/src/lib/eventsystem/event/ModeCancelRequestedEvent.h b/src/lib/eventsystem/event/ModeCancelRequestedEvent.h new file mode 100644 index 0000000..42b5530 --- /dev/null +++ b/src/lib/eventsystem/event/ModeCancelRequestedEvent.h @@ -0,0 +1,13 @@ +#pragma once + +#include "Event.h" + +// The player pressed the one "get me out of the current mode" key (REQ-UI-HOTKEYS). +// Intentionally says only that, not which mode to leave: which of builder, +// blueprint placement, or deconstruct is active — and that the key falls through to +// entering deconstruct mode when none of them is — is state only the receiver has. +class ModeCancelRequestedEvent : public Event +{ +public: + ModeCancelRequestedEvent() = default; +}; diff --git a/src/lib/eventsystem/event/PauseToggleRequestedEvent.h b/src/lib/eventsystem/event/PauseToggleRequestedEvent.h new file mode 100644 index 0000000..a0bc8dc --- /dev/null +++ b/src/lib/eventsystem/event/PauseToggleRequestedEvent.h @@ -0,0 +1,12 @@ +#pragma once + +#include "Event.h" + +// The player asked to pause or unpause (REQ-UI-HOTKEYS). Carries no speed: which +// speed to restore on unpause is the receiver's business, since it is the one that +// remembers what was running before the pause. +class PauseToggleRequestedEvent : public Event +{ +public: + PauseToggleRequestedEvent() = default; +}; diff --git a/src/lib/eventsystem/event/SpeedStepRequestedEvent.h b/src/lib/eventsystem/event/SpeedStepRequestedEvent.h new file mode 100644 index 0000000..70f5249 --- /dev/null +++ b/src/lib/eventsystem/event/SpeedStepRequestedEvent.h @@ -0,0 +1,14 @@ +#pragma once + +#include "Event.h" + +// The player asked to step the game speed one notch (REQ-UI-HOTKEYS): +1 faster, +// -1 slower. A relative step rather than a target speed, because the ladder of +// available speeds belongs to the receiver — contrast SpeedChangeRequestedEvent, +// which names an absolute multiplier and is what the speed buttons send. +class SpeedStepRequestedEvent : public Event +{ +public: + explicit SpeedStepRequestedEvent(int delta) : delta(delta) {} + const int delta; +}; diff --git a/src/ui/GameWorldView.cpp b/src/ui/GameWorldView.cpp index 595dba2..9c272df 100644 --- a/src/ui/GameWorldView.cpp +++ b/src/ui/GameWorldView.cpp @@ -2311,46 +2311,12 @@ void GameWorldView::keyPressEvent(QKeyEvent* event) return; } - // Keys the input mapper owns are turned into actions and published from there - // (REQ-UI-HOTKEYS); this widget reacts to those as an ordinary subscriber. + // Keys are turned into actions and published by the input mapper + // (REQ-UI-HOTKEYS); this widget reacts to those as an ordinary subscriber, so + // nothing is handled here directly. if (m_inputMapper.handleKeyPress(event)) { return; } - switch (event->key()) - { - case Qt::Key_Space: - if (m_gameSpeedMultiplier > 0.0) - { - m_prevNonZeroSpeed = m_gameSpeedMultiplier; - setGameSpeed(0.0); - } - else - { - setGameSpeed(m_prevNonZeroSpeed); - } - break; - case Qt::Key_W: - stepSpeed(+1); - break; - case Qt::Key_S: - stepSpeed(-1); - break; - case Qt::Key_R: - rotateGhost(event->modifiers() & Qt::ShiftModifier); - break; - case Qt::Key_Q: - if (m_builderType.has_value()) { exitBuilderMode(); } - else if (m_blueprintMode.has_value()) { exitBlueprintMode(); } - else { toggleDeconstructMode(); } - break; - case Qt::Key_F3: - m_debugDraw = !m_debugDraw; - EventManager::getInstance()->sendEventImmediately( - std::make_shared(m_debugDraw)); - break; - default: - QOpenGLWidget::keyPressEvent(event); - break; - } + QOpenGLWidget::keyPressEvent(event); } void GameWorldView::keyReleaseEvent(QKeyEvent* event) @@ -3113,6 +3079,45 @@ void GameWorldView::handleEvent(std::shared_ptr m_panDirection = event->direction; } +void GameWorldView::handleEvent(std::shared_ptr /*event*/) +{ + if (m_gameSpeedMultiplier > 0.0) + { + m_prevNonZeroSpeed = m_gameSpeedMultiplier; + setGameSpeed(0.0); + } + else + { + setGameSpeed(m_prevNonZeroSpeed); + } +} + +void GameWorldView::handleEvent(std::shared_ptr event) +{ + stepSpeed(event->delta); +} + +void GameWorldView::handleEvent(std::shared_ptr event) +{ + rotateGhost(event->clockwise); +} + +void GameWorldView::handleEvent(std::shared_ptr /*event*/) +{ + // One key backs out of whichever mode is active, and enters deconstruct mode + // when none is (REQ-UI-HOTKEYS). + if (m_builderType.has_value()) { exitBuilderMode(); } + else if (m_blueprintMode.has_value()) { exitBlueprintMode(); } + else { toggleDeconstructMode(); } +} + +void GameWorldView::handleEvent(std::shared_ptr /*event*/) +{ + m_debugDraw = !m_debugDraw; + EventManager::getInstance()->sendEventImmediately( + std::make_shared(m_debugDraw)); +} + void GameWorldView::handleEvent(std::shared_ptr event) { // Other widgets (MainWindow, SelectedBuildingPanel) request commands via this diff --git a/src/ui/GameWorldView.h b/src/ui/GameWorldView.h index 1aa8858..5085d09 100644 --- a/src/ui/GameWorldView.h +++ b/src/ui/GameWorldView.h @@ -31,8 +31,13 @@ #include "EventHandler.h" #include "ExitBlueprintModeRequestedEvent.h" #include "ExitBuilderModeRequestedEvent.h" +#include "DebugDrawToggleRequestedEvent.h" +#include "GhostRotationRequestedEvent.h" #include "InputMapper.h" +#include "ModeCancelRequestedEvent.h" #include "PanDirectionChangedEvent.h" +#include "PauseToggleRequestedEvent.h" +#include "SpeedStepRequestedEvent.h" #include "DebugDrawToggledEvent.h" #include "ArtifactCountChangedEvent.h" #include "BeamFiredEvent.h" @@ -83,6 +88,11 @@ class GameWorldView : public QOpenGLWidget, ExitBlueprintModeRequestedEvent, SpeedChangeRequestedEvent, PanDirectionChangedEvent, + PauseToggleRequestedEvent, + SpeedStepRequestedEvent, + GhostRotationRequestedEvent, + ModeCancelRequestedEvent, + DebugDrawToggleRequestedEvent, CommandRequestedEvent> { Q_OBJECT @@ -105,6 +115,8 @@ public: protected: void initializeGL() override; void paintGL() override; + // Only forwards to the input mapper; every key this widget acts on reaches it + // as a published action instead (REQ-UI-HOTKEYS). void keyPressEvent(QKeyEvent* event) override; void keyReleaseEvent(QKeyEvent* event) override; // Key-up never arrives for a key that was still held when focus moved away — @@ -127,6 +139,11 @@ private: void handleEvent(std::shared_ptr event) override; void handleEvent(std::shared_ptr event) override; void handleEvent(std::shared_ptr event) override; + void handleEvent(std::shared_ptr event) override; + void handleEvent(std::shared_ptr event) override; + void handleEvent(std::shared_ptr event) override; + void handleEvent(std::shared_ptr event) override; + void handleEvent(std::shared_ptr event) override; void handleEvent(std::shared_ptr event) override; // Enqueue a sim command onto the CommandManager (the single mutation path). diff --git a/src/ui/InputMapper.cpp b/src/ui/InputMapper.cpp index 3a5f473..ba0ac2a 100644 --- a/src/ui/InputMapper.cpp +++ b/src/ui/InputMapper.cpp @@ -7,9 +7,14 @@ #include "BuildHotkeyPressedEvent.h" #include "BuildingType.h" +#include "DebugDrawToggleRequestedEvent.h" #include "EscapeMenuRequestedEvent.h" #include "EventManager.h" +#include "GhostRotationRequestedEvent.h" +#include "ModeCancelRequestedEvent.h" #include "PanDirectionChangedEvent.h" +#include "PauseToggleRequestedEvent.h" +#include "SpeedStepRequestedEvent.h" #include "TemporaryBlueprintRequestedEvent.h" #include "TracePrintRequestedEvent.h" @@ -79,6 +84,28 @@ bool InputMapper::handleKeyPress(QKeyEvent* event) m_panRightHeld = true; updatePanDirection(); return true; + case Qt::Key_Space: + EventManager::getInstance()->sendEventImmediately( + std::make_shared()); + return true; + case Qt::Key_W: + EventManager::getInstance()->sendEventImmediately( + std::make_shared(+1)); + return true; + case Qt::Key_S: + EventManager::getInstance()->sendEventImmediately( + std::make_shared(-1)); + return true; + case Qt::Key_R: + // Shift reverses the rotation direction (REQ-BLD-ROTATE). + EventManager::getInstance()->sendEventImmediately( + std::make_shared( + (event->modifiers() & Qt::ShiftModifier) != 0)); + return true; + case Qt::Key_Q: + EventManager::getInstance()->sendEventImmediately( + std::make_shared()); + return true; case Qt::Key_T: // Request a temporary blueprint from the current selection (REQ-UI-BLUEPRINT-TEMP). // The BlueprintPanel owns the selection and blueprint-capture logic; it decides @@ -86,6 +113,10 @@ bool InputMapper::handleKeyPress(QKeyEvent* event) EventManager::getInstance()->sendEventImmediately( std::make_shared()); return true; + case Qt::Key_F3: + EventManager::getInstance()->sendEventImmediately( + std::make_shared()); + return true; case Qt::Key_Escape: EventManager::getInstance()->sendEventImmediately( std::make_shared());