#include "InputMapper.h" #include #include #include #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" namespace { // The building type a number-key build hotkey selects, or nullopt for the digits // that are unbound (REQ-UI-HOTKEYS). Plain digits pick the transport buildings, // Shift+digit the production ones. std::optional buildHotkeyType(int digit, bool shiftHeld) { if (!shiftHeld) { switch (digit) { case 1: return BuildingType::Belt; case 2: return BuildingType::Splitter; case 3: return BuildingType::TunnelEntry; // unified tunnel mode (REQ-BLD-TUNNEL-MODE); 4 unused } return std::nullopt; } switch (digit) { case 1: return BuildingType::Miner; case 2: return BuildingType::Smelter; case 3: return BuildingType::Assembler; case 4: return BuildingType::Shipyard; case 5: return BuildingType::SalvageBay; case 6: return BuildingType::ReprocessingPlant; } return std::nullopt; } } // namespace QString InputMapper::getBuildHotkeyLabel(BuildingType type) { // Searched out of the binding table above rather than spelled out a second time, // so a badge can never claim a key the handler does not act on. The shift glyph // is written as a code point because the sources are not guaranteed to be read // as UTF-8 by every compiler this builds with. A plain stroke arrow rather than // U+21E7 UPWARDS WHITE ARROW: the standard UI fonts do not all carry the outlined // shift glyph, and the substitute they fall back to is unreadable at badge size. const QChar shiftGlyph(0x2191); // U+2191 UPWARDS ARROW for (int digit = 1; digit <= 9; ++digit) { if (buildHotkeyType(digit, false) == type) { return QString::number(digit); } if (buildHotkeyType(digit, true) == type) { return shiftGlyph + QString::number(digit); } } return QString(); } bool InputMapper::handleKeyPress(QKeyEvent* event) { // Auto-repeat says nothing new about which keys are down, and a held action is // already held. if (event->isAutoRepeat()) { return false; } // Number-key build-mode hotkeys (REQ-UI-HOTKEYS). nativeVirtualKey gives the // physical digit independent of keyboard layout and Shift (with Shift held, key() // for the number row can arrive as Key_Exclam etc.). VK_1..VK_9 = 0x31..0x39. const quint32 virtualKey = event->nativeVirtualKey(); if (virtualKey >= 0x31 && virtualKey <= 0x39) { const std::optional type = buildHotkeyType( static_cast(virtualKey - 0x30), (event->modifiers() & Qt::ShiftModifier) != 0); if (type.has_value()) { EventManager::getInstance()->sendEventImmediately( std::make_shared(*type)); return true; } } switch (event->key()) { case Qt::Key_A: m_panLeftHeld = true; updatePanDirection(); return true; case Qt::Key_D: 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 // whether anything placeable is selected and drives placement mode from there. 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()); return true; case Qt::Key_F4: EventManager::getInstance()->addEvent( std::make_shared()); return true; default: return false; } } bool InputMapper::handleKeyRelease(QKeyEvent* event) { if (event->isAutoRepeat()) { return false; } switch (event->key()) { case Qt::Key_A: m_panLeftHeld = false; updatePanDirection(); return true; case Qt::Key_D: m_panRightHeld = false; updatePanDirection(); return true; default: return false; } } void InputMapper::releaseAll() { m_panLeftHeld = false; m_panRightHeld = false; updatePanDirection(); } void InputMapper::updatePanDirection() { // Holding both keys cancels out rather than favouring one. PanDirection direction = PanDirection::None; if (m_panLeftHeld != m_panRightHeld) { direction = m_panLeftHeld ? PanDirection::Left : PanDirection::Right; } if (direction == m_panDirection) { return; } m_panDirection = direction; EventManager::getInstance()->sendEventImmediately( std::make_shared(direction)); }