The shift glyph U+21E7 was not covered by the UI font and rendered as an unrecognizable substitute, and the badge sat two points below the button font, which made it hard to read even where the glyph was fine. Use U+2191, a plain single-stroke arrow the standard fonts do carry, and draw the badge at the full button font in bold. It stays dimmed, so it still reads as secondary to the cost. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JcReq7hVk4KUPhTDKWAG7K
196 lines
6.3 KiB
C++
196 lines
6.3 KiB
C++
#include "InputMapper.h"
|
|
|
|
#include <memory>
|
|
#include <optional>
|
|
|
|
#include <QKeyEvent>
|
|
|
|
#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<BuildingType> 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<BuildingType> type = buildHotkeyType(
|
|
static_cast<int>(virtualKey - 0x30),
|
|
(event->modifiers() & Qt::ShiftModifier) != 0);
|
|
if (type.has_value())
|
|
{
|
|
EventManager::getInstance()->sendEventImmediately(
|
|
std::make_shared<BuildHotkeyPressedEvent>(*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<PauseToggleRequestedEvent>());
|
|
return true;
|
|
case Qt::Key_W:
|
|
EventManager::getInstance()->sendEventImmediately(
|
|
std::make_shared<SpeedStepRequestedEvent>(+1));
|
|
return true;
|
|
case Qt::Key_S:
|
|
EventManager::getInstance()->sendEventImmediately(
|
|
std::make_shared<SpeedStepRequestedEvent>(-1));
|
|
return true;
|
|
case Qt::Key_R:
|
|
// Shift reverses the rotation direction (REQ-BLD-ROTATE).
|
|
EventManager::getInstance()->sendEventImmediately(
|
|
std::make_shared<GhostRotationRequestedEvent>(
|
|
(event->modifiers() & Qt::ShiftModifier) != 0));
|
|
return true;
|
|
case Qt::Key_Q:
|
|
EventManager::getInstance()->sendEventImmediately(
|
|
std::make_shared<ModeCancelRequestedEvent>());
|
|
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<TemporaryBlueprintRequestedEvent>());
|
|
return true;
|
|
case Qt::Key_F3:
|
|
EventManager::getInstance()->sendEventImmediately(
|
|
std::make_shared<DebugDrawToggleRequestedEvent>());
|
|
return true;
|
|
case Qt::Key_Escape:
|
|
EventManager::getInstance()->sendEventImmediately(
|
|
std::make_shared<EscapeMenuRequestedEvent>());
|
|
return true;
|
|
case Qt::Key_F4:
|
|
EventManager::getInstance()->addEvent(
|
|
std::make_shared<TracePrintRequestedEvent>());
|
|
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<PanDirectionChangedEvent>(direction));
|
|
}
|