update keyboard shortcuts

This commit is contained in:
2026-06-23 21:43:34 +02:00
parent f818c90af0
commit 31a8915b0f
9 changed files with 151 additions and 81 deletions

View File

@@ -130,3 +130,20 @@ void BuildButtonGrid::handleEvent(std::shared_ptr<const DemolishModeChangedEvent
{
m_demolishButton->setChecked(event->active);
}
void BuildButtonGrid::handleEvent(std::shared_ptr<const BuildHotkeyPressedEvent> event)
{
for (std::size_t i = 0; i < m_types.size(); ++i)
{
if (m_types[i] == event->type)
{
// Equivalent to clicking the build button: a disabled (unaffordable)
// button cannot be clicked, so the hotkey is likewise inert.
if (m_buttons[i]->isEnabled())
{
onBuildButton(static_cast<int>(i));
}
return;
}
}
}

View File

@@ -6,6 +6,7 @@
#include <QWidget>
#include "BuilderModeExitedEvent.h"
#include "BuildHotkeyPressedEvent.h"
#include "BuildingType.h"
#include "DemolishModeChangedEvent.h"
#include "EventHandler.h"
@@ -15,7 +16,8 @@ class QPushButton;
class BuildButtonGrid : public QWidget,
public CombinedEventHandler<BuilderModeExitedEvent,
DemolishModeChangedEvent>
DemolishModeChangedEvent,
BuildHotkeyPressedEvent>
{
Q_OBJECT
@@ -29,6 +31,7 @@ public:
private:
void handleEvent(std::shared_ptr<const BuilderModeExitedEvent> event) override;
void handleEvent(std::shared_ptr<const DemolishModeChangedEvent> event) override;
void handleEvent(std::shared_ptr<const BuildHotkeyPressedEvent> event) override;
private slots:
void onBuildButton(int index);

View File

@@ -47,6 +47,7 @@
#include "Tick.h"
#include "EscapeMenuRequestedEvent.h"
#include "TracePrintRequestedEvent.h"
#include "BuildHotkeyPressedEvent.h"
#include "BossWaveUpdatedEvent.h"
#include "BuilderModeExitedEvent.h"
#include "BlueprintModeExitedEvent.h"
@@ -1206,6 +1207,45 @@ void GameWorldView::keyPressEvent(QKeyEvent* event)
return;
}
// 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 int digit = static_cast<int>(virtualKey - 0x30);
const bool shift = (event->modifiers() & Qt::ShiftModifier) != 0;
std::optional<BuildingType> type;
if (!shift)
{
switch (digit)
{
case 1: type = BuildingType::Belt; break;
case 2: type = BuildingType::Splitter; break;
case 3: type = BuildingType::TunnelEntry; break;
case 4: type = BuildingType::TunnelExit; break;
}
}
else
{
switch (digit)
{
case 1: type = BuildingType::Miner; break;
case 2: type = BuildingType::Smelter; break;
case 3: type = BuildingType::Assembler; break;
case 4: type = BuildingType::Shipyard; break;
case 5: type = BuildingType::SalvageBay; break;
case 6: type = BuildingType::ReprocessingPlant; break;
}
}
if (type.has_value())
{
EventManager::getInstance()->sendEventImmediately(
std::make_shared<BuildHotkeyPressedEvent>(*type));
return;
}
}
switch (event->key())
{
case Qt::Key_A:
@@ -1231,69 +1271,24 @@ void GameWorldView::keyPressEvent(QKeyEvent* event)
case Qt::Key_S:
stepSpeed(-1);
break;
case Qt::Key_E:
if (m_builderType.has_value())
{
m_ghostRotation = rotateClockwise(m_ghostRotation);
m_ghostValid = isValidPlacement(*m_builderType, m_ghostTile, m_ghostRotation);
}
else if (m_blueprintMode.has_value())
{
for (BlueprintBuilding& bb : m_blueprintMode->buildings)
{
const BuildingDef* def = findBuildingDef(bb.type);
if (!def) { continue; }
const ParsedSurfaceMask mask = parseSurfaceMask(def->surfaceMask, bb.rotation);
int minX = INT_MAX, minY = INT_MAX;
for (const QPoint& cell : mask.bodyCells)
{
const QPoint abs = bb.offset + cell;
minX = std::min(minX, -abs.y());
minY = std::min(minY, abs.x());
}
bb.offset = QPoint(minX, minY);
bb.rotation = rotateClockwise(bb.rotation);
}
}
case Qt::Key_R:
rotateGhost(event->modifiers() & Qt::ShiftModifier);
break;
case Qt::Key_Q:
if (m_builderType.has_value())
{
m_ghostRotation = rotateCounterClockwise(m_ghostRotation);
m_ghostValid = isValidPlacement(*m_builderType, m_ghostTile, m_ghostRotation);
}
else if (m_blueprintMode.has_value())
{
for (BlueprintBuilding& bb : m_blueprintMode->buildings)
{
const BuildingDef* def = findBuildingDef(bb.type);
if (!def) { continue; }
const ParsedSurfaceMask mask = parseSurfaceMask(def->surfaceMask, bb.rotation);
int minX = INT_MAX, minY = INT_MAX;
for (const QPoint& cell : mask.bodyCells)
{
const QPoint abs = bb.offset + cell;
minX = std::min(minX, abs.y());
minY = std::min(minY, -abs.x());
}
bb.offset = QPoint(minX, minY);
bb.rotation = rotateCounterClockwise(bb.rotation);
}
}
if (m_builderType.has_value()) { exitBuilderMode(); }
else if (m_blueprintMode.has_value()) { exitBlueprintMode(); }
else { toggleDemolishMode(); }
break;
case Qt::Key_Escape:
EventManager::getInstance()->sendEventImmediately(
std::make_shared<EscapeMenuRequestedEvent>());
break;
case Qt::Key_Backspace:
toggleDemolishMode();
break;
case Qt::Key_M:
case Qt::Key_F3:
m_debugDraw = !m_debugDraw;
EventManager::getInstance()->sendEventImmediately(
std::make_shared<DebugDrawToggledEvent>(m_debugDraw));
break;
case Qt::Key_L:
case Qt::Key_F4:
EventManager::getInstance()->addEvent(std::make_shared<TracePrintRequestedEvent>());
break;
default:
@@ -1545,6 +1540,43 @@ void GameWorldView::toggleDemolishMode()
std::make_shared<DemolishModeChangedEvent>(m_demolishMode));
}
void GameWorldView::rotateGhost(bool clockwise)
{
if (m_builderType.has_value())
{
m_ghostRotation = clockwise ? rotateClockwise(m_ghostRotation)
: rotateCounterClockwise(m_ghostRotation);
m_ghostValid = isValidPlacement(*m_builderType, m_ghostTile, m_ghostRotation);
}
else if (m_blueprintMode.has_value())
{
for (BlueprintBuilding& bb : m_blueprintMode->buildings)
{
const BuildingDef* def = findBuildingDef(bb.type);
if (!def) { continue; }
const ParsedSurfaceMask mask = parseSurfaceMask(def->surfaceMask, bb.rotation);
int minX = INT_MAX, minY = INT_MAX;
for (const QPoint& cell : mask.bodyCells)
{
const QPoint abs = bb.offset + cell;
if (clockwise)
{
minX = std::min(minX, -abs.y());
minY = std::min(minY, abs.x());
}
else
{
minX = std::min(minX, abs.y());
minY = std::min(minY, -abs.x());
}
}
bb.offset = QPoint(minX, minY);
bb.rotation = clockwise ? rotateClockwise(bb.rotation)
: rotateCounterClockwise(bb.rotation);
}
}
}
void GameWorldView::enterBuilderMode(BuildingType type)
{
m_builderType = type;

View File

@@ -140,6 +140,7 @@ private:
void enterBlueprintMode(Blueprint blueprint);
void exitBlueprintMode();
void toggleDemolishMode();
void rotateGhost(bool clockwise);
struct ActiveBeam
{

View File

@@ -583,27 +583,29 @@ std::optional<ShipLayoutConfig> ShipLayoutDialog::result() const
void ShipLayoutDialog::keyPressEvent(QKeyEvent* event)
{
if (event->key() == Qt::Key_Q)
if (event->key() == Qt::Key_R)
{
// Rotate CCW = 3 CW steps.
switch (m_currentRotation)
if (event->modifiers() & Qt::ShiftModifier)
{
case Rotation::East: m_currentRotation = Rotation::North; break;
case Rotation::North: m_currentRotation = Rotation::West; break;
case Rotation::West: m_currentRotation = Rotation::South; break;
case Rotation::South: m_currentRotation = Rotation::East; break;
// Shift+R: rotate CW.
switch (m_currentRotation)
{
case Rotation::East: m_currentRotation = Rotation::South; break;
case Rotation::South: m_currentRotation = Rotation::West; break;
case Rotation::West: m_currentRotation = Rotation::North; break;
case Rotation::North: m_currentRotation = Rotation::East; break;
}
}
updateGridWidget();
}
else if (event->key() == Qt::Key_E)
{
// Rotate CW.
switch (m_currentRotation)
else
{
case Rotation::East: m_currentRotation = Rotation::South; break;
case Rotation::South: m_currentRotation = Rotation::West; break;
case Rotation::West: m_currentRotation = Rotation::North; break;
case Rotation::North: m_currentRotation = Rotation::East; break;
// R: rotate CCW.
switch (m_currentRotation)
{
case Rotation::East: m_currentRotation = Rotation::North; break;
case Rotation::North: m_currentRotation = Rotation::West; break;
case Rotation::West: m_currentRotation = Rotation::South; break;
case Rotation::South: m_currentRotation = Rotation::East; break;
}
}
updateGridWidget();
}