Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
153 changes: 108 additions & 45 deletions src/client/InputHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,7 @@ export class InputHandler {
* `initializePointerAndKeyboardEvents()`. */
private listenerAbort: AbortController | null = null;
private activeKeys = new Set<string>();
private shiftHeld = false;
private keybinds: Record<string, string> = {};
private keybindAndEvent: Array<[string, KeybindEntry]> = [];
private coordinateGridEnabled = false;
Expand Down Expand Up @@ -663,6 +664,7 @@ export class InputHandler {
"blur",
() => {
this.activeKeys.clear();
this.shiftHeld = false;
if (this.alternateView) {
this.alternateView = false;
this.eventBus.emit(new AlternateViewEvent(false));
Expand All @@ -683,29 +685,34 @@ export class InputHandler {
let deltaX = 0;
let deltaY = 0;

// Skip if select warship modifier is held down
if (this.activeKeys.has(this.keybinds.boxSelectWarships)) {
// Skip movement if selection box drag is active or a non-Shift warship selector is held
if (
this.selectionBoxActive ||
(this.keybinds.boxSelectWarships !== "ShiftLeft" &&
this.keybinds.boxSelectWarships !== "ShiftRight" &&
this.activeKeys.has(this.keybinds.boxSelectWarships))
) {
return;
}

if (
this.activeKeys.has(this.keybinds.moveUp) ||
this.activeKeys.has("ArrowUp")
this.isContinuousActionActive(this.keybinds.moveUp) ||
this.isContinuousActionActive(this.keybinds.moveUpArrow)
)
deltaY += this.PAN_SPEED;
if (
this.activeKeys.has(this.keybinds.moveDown) ||
this.activeKeys.has("ArrowDown")
this.isContinuousActionActive(this.keybinds.moveDown) ||
this.isContinuousActionActive(this.keybinds.moveDownArrow)
)
deltaY -= this.PAN_SPEED;
if (
this.activeKeys.has(this.keybinds.moveLeft) ||
this.activeKeys.has("ArrowLeft")
this.isContinuousActionActive(this.keybinds.moveLeft) ||
this.isContinuousActionActive(this.keybinds.moveLeftArrow)
)
deltaX += this.PAN_SPEED;
if (
this.activeKeys.has(this.keybinds.moveRight) ||
this.activeKeys.has("ArrowRight")
this.isContinuousActionActive(this.keybinds.moveRight) ||
this.isContinuousActionActive(this.keybinds.moveRightArrow)
)
deltaX -= this.PAN_SPEED;

Expand All @@ -717,16 +724,16 @@ export class InputHandler {
const cy = window.innerHeight / 2;

if (
this.activeKeys.has(this.keybinds.zoomOut) ||
this.activeKeys.has("Minus") ||
this.activeKeys.has("NumpadSubtract")
this.isContinuousActionActive(this.keybinds.zoomOut) ||
this.isContinuousActionActive(this.keybinds.zoomOutMinus) ||
this.isContinuousActionActive(this.keybinds.zoomOutNumpad)
) {
this.eventBus.emit(new ZoomEvent(cx, cy, this.ZOOM_SPEED));
}
if (
this.activeKeys.has(this.keybinds.zoomIn) ||
this.activeKeys.has("Equal") ||
this.activeKeys.has("NumpadAdd")
this.isContinuousActionActive(this.keybinds.zoomIn) ||
this.isContinuousActionActive(this.keybinds.zoomInEqual) ||
this.isContinuousActionActive(this.keybinds.zoomInNumpad)
) {
this.eventBus.emit(new ZoomEvent(cx, cy, -this.ZOOM_SPEED));
}
Expand Down Expand Up @@ -827,41 +834,42 @@ export class InputHandler {
e.code === "NumpadAdd" ||
e.code === "NumpadSubtract");

const continuousBindings = this.getContinuousBindings();

const isConfiguredKeybind =
Object.values(this.keybinds).includes(e.code) ||
this.keybindAndEvent.some(([k]) => this.keybindMatchesEvent(e, k));
Object.values(this.keybinds).some((k) =>
this.keybindMatchesEvent(e, k),
) ||
this.keybindAndEvent.some(([k]) => this.keybindMatchesEvent(e, k)) ||
continuousBindings.some((k) => this.keybindMatchesEvent(e, k));

if (isConfiguredKeybind && !isBrowserZoomCombo) {
e.preventDefault();
}

this.shiftHeld = e.shiftKey;

const continuousPhysicalCodes = continuousBindings.map(
(k) => this.parseKeybind(k).code,
);

if (
!isBrowserZoomCombo &&
[
this.keybinds.moveUp,
this.keybinds.moveDown,
this.keybinds.moveLeft,
this.keybinds.moveRight,
this.keybinds.zoomOut,
this.keybinds.zoomIn,
"ArrowUp",
"ArrowLeft",
"ArrowDown",
"ArrowRight",
"Minus",
"Equal",
"NumpadAdd",
"NumpadSubtract",
this.keybinds.attackRatioDown,
this.keybinds.attackRatioUp,
this.keybinds.centerCamera,
"ControlLeft",
"ControlRight",
this.keybinds.boxSelectWarships,
this.keybinds.emojiMenuModifier,
this.keybinds.buildMenuModifier,
this.keybinds.altKey,
].includes(e.code)
(continuousPhysicalCodes.includes(e.code) ||
[
"ShiftLeft",
"ShiftRight",
"ControlLeft",
"ControlRight",
"AltLeft",
"AltRight",
"MetaLeft",
"MetaRight",
this.keybinds.boxSelectWarships,
this.keybinds.emojiMenuModifier,
this.keybinds.buildMenuModifier,
this.keybinds.altKey,
].includes(e.code))
) {
this.activeKeys.add(e.code);
}
Expand All @@ -885,6 +893,8 @@ export class InputHandler {
return;
}

this.shiftHeld = e.shiftKey;

// When the meta (cmd) or ctrl key is released, any keys that were held
// simultaneously will have had their keyup swallowed by the browser
// (e.g. cmd+Plus for browser zoom). Clear zoom-related keys to
Expand All @@ -899,8 +909,6 @@ export class InputHandler {
this.activeKeys.delete("Equal");
this.activeKeys.delete("NumpadAdd");
this.activeKeys.delete("NumpadSubtract");
this.activeKeys.delete(this.keybinds.zoomIn);
this.activeKeys.delete(this.keybinds.zoomOut);
}

if (this.emojiTableOpen) {
Expand Down Expand Up @@ -1377,6 +1385,60 @@ export class InputHandler {
return e.code === parsed.code && e.shiftKey === parsed.shift;
}

private isShiftHeld(): boolean {
return this.shiftHeld;
}

/**
* Evaluates whether a continuous movement or zoom action is currently active.
*
* A binding is active when its physical key is held in `activeKeys` and its
* Shift-modifier condition is satisfied:
* - Shift-modified bindings (e.g. "Shift+ArrowUp") require Shift to be held.
* Releasing Shift while the physical key remains held stops the action.
* Pressing Shift while the physical key is already held activates the action.
* - Unmodified bindings (e.g. "ArrowUp") require Shift NOT to be held.
* This ensures unmodified controls do not activate when Shift is held for
* other actions (such as warship box selection).
* - Unbound ("Null" or undefined) bindings never activate.
*/
private isContinuousActionActive(keybindValue: string | undefined): boolean {
if (!keybindValue || keybindValue === "Null") {
return false;
}
const { shift, code } = this.parseKeybind(keybindValue);
if (!this.activeKeys.has(code)) {
return false;
}
const shiftHeld = this.isShiftHeld();
return shift ? shiftHeld : !shiftHeld;
}

private getContinuousBindings(): string[] {
const bindings = [
this.keybinds.moveUp,
this.keybinds.moveUpArrow,
this.keybinds.moveDown,
this.keybinds.moveDownArrow,
this.keybinds.moveLeft,
this.keybinds.moveLeftArrow,
this.keybinds.moveRight,
this.keybinds.moveRightArrow,
this.keybinds.zoomOut,
this.keybinds.zoomOutMinus,
this.keybinds.zoomOutNumpad,
this.keybinds.zoomIn,
this.keybinds.zoomInEqual,
this.keybinds.zoomInNumpad,
this.keybinds.attackRatioDown,
this.keybinds.attackRatioUp,
this.keybinds.centerCamera,
];
return bindings.filter(
(k): k is string => typeof k === "string" && k !== "" && k !== "Null",
);
}

/**
* Extracts the digit character from KeyboardEvent.code.
* Codes look like "Digit0".."Digit9" (6 chars, digit at index 5) and
Expand Down Expand Up @@ -1557,6 +1619,7 @@ export class InputHandler {
// renderer has already removed.
this.resetPointerState();
this.activeKeys.clear();
this.shiftHeld = false;
this.emojiTableOpen = false;
this.emojiKeysDown.clear();
this.keybindAndEvent = [];
Expand Down
80 changes: 80 additions & 0 deletions src/client/UserSettingModal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1589,6 +1589,26 @@ export class UserSettingModal extends BaseModal {
@change=${this.handleKeybindChange}
></setting-keybind>

<setting-keybind
action="zoomOutMinus"
label=${translateText("user_setting.zoom_out")}
description=${translateText("user_setting.zoom_out_desc")}
defaultKey=${this.defaultKeybinds.zoomOutMinus}
.value=${this.getKeyValue("zoomOutMinus")}
.display=${this.getKeyChar("zoomOutMinus")}
@change=${this.handleKeybindChange}
></setting-keybind>

<setting-keybind
action="zoomOutNumpad"
label=${translateText("user_setting.zoom_out")}
description=${translateText("user_setting.zoom_out_desc")}
defaultKey=${this.defaultKeybinds.zoomOutNumpad}
.value=${this.getKeyValue("zoomOutNumpad")}
.display=${this.getKeyChar("zoomOutNumpad")}
@change=${this.handleKeybindChange}
></setting-keybind>

Comment thread
coderabbitai[bot] marked this conversation as resolved.
<setting-keybind
action="zoomIn"
label=${translateText("user_setting.zoom_in")}
Expand All @@ -1599,6 +1619,26 @@ export class UserSettingModal extends BaseModal {
@change=${this.handleKeybindChange}
></setting-keybind>

<setting-keybind
action="zoomInEqual"
label=${translateText("user_setting.zoom_in")}
description=${translateText("user_setting.zoom_in_desc")}
defaultKey=${this.defaultKeybinds.zoomInEqual}
.value=${this.getKeyValue("zoomInEqual")}
.display=${this.getKeyChar("zoomInEqual")}
@change=${this.handleKeybindChange}
></setting-keybind>

<setting-keybind
action="zoomInNumpad"
label=${translateText("user_setting.zoom_in")}
description=${translateText("user_setting.zoom_in_desc")}
defaultKey=${this.defaultKeybinds.zoomInNumpad}
.value=${this.getKeyValue("zoomInNumpad")}
.display=${this.getKeyChar("zoomInNumpad")}
@change=${this.handleKeybindChange}
></setting-keybind>

<h2
class="text-blue-200 text-xl font-bold mt-8 mb-3 border-b border-white/10 pb-2"
>
Expand All @@ -1625,6 +1665,16 @@ export class UserSettingModal extends BaseModal {
@change=${this.handleKeybindChange}
></setting-keybind>

<setting-keybind
action="moveUpArrow"
label=${translateText("user_setting.move_up")}
description=${translateText("user_setting.move_up_desc")}
defaultKey=${this.defaultKeybinds.moveUpArrow}
.value=${this.getKeyValue("moveUpArrow")}
.display=${this.getKeyChar("moveUpArrow")}
@change=${this.handleKeybindChange}
></setting-keybind>

<setting-keybind
action="moveLeft"
label=${translateText("user_setting.move_left")}
Expand All @@ -1635,6 +1685,16 @@ export class UserSettingModal extends BaseModal {
@change=${this.handleKeybindChange}
></setting-keybind>

<setting-keybind
action="moveLeftArrow"
label=${translateText("user_setting.move_left")}
description=${translateText("user_setting.move_left_desc")}
defaultKey=${this.defaultKeybinds.moveLeftArrow}
.value=${this.getKeyValue("moveLeftArrow")}
.display=${this.getKeyChar("moveLeftArrow")}
@change=${this.handleKeybindChange}
></setting-keybind>

<setting-keybind
action="moveDown"
label=${translateText("user_setting.move_down")}
Expand All @@ -1645,6 +1705,16 @@ export class UserSettingModal extends BaseModal {
@change=${this.handleKeybindChange}
></setting-keybind>

<setting-keybind
action="moveDownArrow"
label=${translateText("user_setting.move_down")}
description=${translateText("user_setting.move_down_desc")}
defaultKey=${this.defaultKeybinds.moveDownArrow}
.value=${this.getKeyValue("moveDownArrow")}
.display=${this.getKeyChar("moveDownArrow")}
@change=${this.handleKeybindChange}
></setting-keybind>

<setting-keybind
action="moveRight"
label=${translateText("user_setting.move_right")}
Expand All @@ -1654,6 +1724,16 @@ export class UserSettingModal extends BaseModal {
.display=${this.getKeyChar("moveRight")}
@change=${this.handleKeybindChange}
></setting-keybind>

<setting-keybind
action="moveRightArrow"
label=${translateText("user_setting.move_right")}
description=${translateText("user_setting.move_right_desc")}
defaultKey=${this.defaultKeybinds.moveRightArrow}
.value=${this.getKeyValue("moveRightArrow")}
.display=${this.getKeyChar("moveRightArrow")}
@change=${this.handleKeybindChange}
></setting-keybind>
`;
}

Expand Down
8 changes: 8 additions & 0 deletions src/client/UserSettings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,19 @@ export function getDefaultKeybinds(isMac: boolean): Record<string, string> {
swapDirection: "KeyU",
zoomOut: "KeyQ",
zoomIn: "KeyE",
zoomOutMinus: "Minus",
zoomOutNumpad: "NumpadSubtract",
zoomInEqual: "Equal",
zoomInNumpad: "NumpadAdd",
centerCamera: "KeyC",
moveUp: "KeyW",
moveLeft: "KeyA",
moveDown: "KeyS",
moveRight: "KeyD",
moveUpArrow: "ArrowUp",
moveLeftArrow: "ArrowLeft",
moveDownArrow: "ArrowDown",
moveRightArrow: "ArrowRight",
buildMenuModifier: isMac ? "MetaLeft" : "ControlLeft",
emojiMenuModifier: "AltLeft",
boxSelectWarships: "ShiftLeft",
Expand Down
Loading
Loading