import * as React from "react"; import { Option, map, expect } from "../util/OptionUtil"; import { hotkeySystem } from "./LiveSplit"; import { Circle, Trash } from "lucide-react"; import "../css/HotkeyButton.scss"; function resolveKey(keyCode: string): Promise | string { return expect( hotkeySystem, "The Hotkey System should always be initialized.", ).resolve(keyCode); } export interface Props { value: Option; setValue: (value: Option) => void; } export interface State { listener: Option; intervalHandle: Option; resolvedKey: Option; } export default class HotkeyButton extends React.Component { constructor(props: Props) { super(props); this.state = { listener: null, intervalHandle: null, resolvedKey: null, }; this.updateResolvedKey(props.value); } public componentDidUpdate(previousProps: Props) { if (previousProps.value !== this.props.value) { this.updateResolvedKey(this.props.value); } } private async updateResolvedKey(value: Option): Promise { let resolvedKey = ""; if (value != null) { const matches = value.match(/(.+)\+\s*(.+)$/); if (matches != null) { resolvedKey = `${matches[1]}+ ${await resolveKey(matches[2])}`; } else { resolvedKey = await resolveKey(value); } } this.setState({ resolvedKey }); } public render() { let buttonText: Option | React.JSX.Element = null; if (this.props.value != null) { buttonText = this.state.resolvedKey; } else if (this.state.listener != null) { buttonText = ( ); } return (
{map(this.props.value, () => ( this.props.setValue(null)} /> ))} {this.state.listener != null && (
this.blurButton()} /> )}
); } private focusButton() { let listener = this.state.listener; let intervalHandle = this.state.intervalHandle; if (listener === null) { listener = { handleEvent: (ev: KeyboardEvent) => { if (ev.repeat) { return; } let text = ""; if ( ev.ctrlKey && ev.code !== "ControlLeft" && ev.code !== "ControlRight" ) { text += "Ctrl + "; } if ( ev.altKey && ev.code !== "AltLeft" && ev.code !== "AltRight" ) { text += "Alt + "; } if ( ev.metaKey && ev.code !== "MetaLeft" && ev.code !== "MetaRight" ) { text += "Meta + "; } if ( ev.shiftKey && ev.code !== "ShiftLeft" && ev.code !== "ShiftRight" ) { text += "Shift + "; } text += ev.code; this.props.setValue(text); ev.preventDefault(); }, }; window.addEventListener("keydown", listener); } if (intervalHandle === null) { const oldButtonState: boolean[][] = []; intervalHandle = window.setInterval(() => { const gamepads = navigator.getGamepads(); let gamepadIdx = 0; for (const gamepad of gamepads) { if (gamepadIdx >= oldButtonState.length) { oldButtonState[gamepadIdx] = []; } if (gamepad !== null) { let buttonIdx = 0; for (const button of gamepad.buttons) { const oldState = oldButtonState[gamepadIdx]?.[buttonIdx] ?? false; if (button.pressed && !oldState) { this.props.setValue(`Gamepad${buttonIdx}`); } oldButtonState[gamepadIdx][buttonIdx] = button.pressed; buttonIdx++; } } gamepadIdx++; } }, 1000 / 60.0); } this.setState({ listener, intervalHandle, }); } private blurButton() { if (this.state.listener != null) { window.removeEventListener("keydown", this.state.listener); } if (this.state.intervalHandle != null) { window.clearTimeout(this.state.intervalHandle); } this.setState({ listener: null, intervalHandle: null, }); } }