diff --git a/src/components/Game/Game.js b/src/components/Game/Game.js index a27ad535..4f1e4d05 100644 --- a/src/components/Game/Game.js +++ b/src/components/Game/Game.js @@ -18,6 +18,8 @@ export default class Game extends Component { this.state = { pencilMode: false, screenWidth: 0, + vimMode: false, + vimInsert: false, }; } @@ -132,6 +134,24 @@ export default class Game extends Component { this.props.gameModel.reset(scope); }; + handleKeybind = (mode) => { + this.setState({ + vimMode: mode === 'vim', + }); + }; + + handleVimInsert = () => { + this.setState({ + vimInsert: true, + }); + }; + + handleVimNormal = () => { + this.setState({ + vimInsert: false, + }); + }; + handleTogglePencil = () => { this.setState({ pencilMode: !this.state.pencilMode, @@ -232,6 +252,10 @@ export default class Game extends Component { addPing={this.handleAddPing} onPressEnter={this.handlePressEnter} onPressPeriod={this.handlePressPeriod} + vimMode={this.state.vimMode} + vimInsert={this.state.vimInsert} + onVimInsert={this.handleVimInsert} + onVimNormal={this.handleVimNormal} mobile={mobile} pickups={this.props.pickups} optimisticCounter={optimisticCounter} @@ -244,7 +268,7 @@ export default class Game extends Component { if (!this.game) return; const {clock} = this.game; const {mobile} = this.props; - const {pencilMode} = this.state; + const {pencilMode, vimMode, vimInsert} = this.state; const {lastUpdated: startTime, totalTime: pausedTime, paused: isPaused} = clock; return ( { + const actionKeys = { + ArrowLeft: 'left', + ArrowUp: 'up', + ArrowDown: 'down', + ArrowRight: 'right', + Backspace: 'backspace', + '{del}': 'backspace', + Delete: 'delete', + Tab: 'tab', + ' ': 'space', + '[': 'backward', + ']': 'forward', + }; + + const normalModeActionKeys = { + h: 'left', + j: 'down', + k: 'up', + l: 'right', + x: 'delete', + }; + + const {onVimNormal, onVimInsert, vimInsert, onPressEnter, onPressPeriod} = this.props; + if (key in actionKeys) { + this.handleAction(actionKeys[key], shiftKey); + return true; + } else if (!vimInsert) { + if (key in normalModeActionKeys) { + this.handleAction(normalModeActionKeys[key], shiftKey); + } else if (key === 'w') { + this.selectNextClue(false); + } else if (key === 'b') { + this.selectNextClue(true); + } else if (key === 'i') { + onVimInsert && onVimInsert(); + } + } else if (key === '.') { + onPressPeriod && onPressPeriod(); + return true; + } else if (key === 'Enter') { + onPressEnter && onPressEnter(); + return true; + } else if (key === 'Escape') { + onVimNormal && onVimNormal(); + } else if (vimInsert && !this.props.frozen) { + const letter = key.toUpperCase(); + if (this.validLetter(letter)) { + this.typeLetter(letter, shiftKey); + return true; + } + } + }; + // takes in a Keyboard Event handleKeyDown(ev) { + const {vimMode} = this.props; + const _handleKeyDown = vimMode ? this._handleKeyDownVim : this._handleKeyDown; + if (ev.target.tagName === 'INPUT' || ev.metaKey || ev.ctrlKey) { return; } - if (this._handleKeyDown(ev.key, ev.shiftKey)) { + if (_handleKeyDown(ev.key, ev.shiftKey)) { ev.preventDefault(); ev.stopPropagation(); } diff --git a/src/components/Player/Player.js b/src/components/Player/Player.js index 76ef84d2..a2d924e6 100644 --- a/src/components/Player/Player.js +++ b/src/components/Player/Player.js @@ -269,6 +269,10 @@ export default class Player extends Component { mobile, onPressEnter, onPressPeriod, + vimMode, + vimInsert, + onVimNormal, + onVimInsert, grid, clues, circles, @@ -359,6 +363,10 @@ export default class Player extends Component { ref="gridControls" onPressEnter={onPressEnter} onPressPeriod={onPressPeriod} + vimMode={vimMode} + vimInsert={vimInsert} + onVimInsert={onVimInsert} + onVimNormal={onVimNormal} selected={selected} direction={direction} onSetDirection={this._setDirection} diff --git a/src/components/Toolbar/index.js b/src/components/Toolbar/index.js index a1e6cb84..6086a0fe 100644 --- a/src/components/Toolbar/index.js +++ b/src/components/Toolbar/index.js @@ -98,6 +98,20 @@ export default class Toolbar extends Component { ); } + renderKeybindMenu() { + const {vimMode, vimInsert} = this.props; + return ( + + ); + } + renderChatButton() { return ; } @@ -207,6 +221,10 @@ export default class Toolbar extends Component { this.props.onReset(scopeString); } + keybind(mode) { + this.props.onKeybind(mode); + } + resetPuzzleAndTimer() { this.reset('puzzle'); this.props.onResetClock(); @@ -253,6 +271,7 @@ export default class Toolbar extends Component { {solved ? null : this.renderCheckMenu()} {solved ? null : this.renderRevealMenu()}
{this.renderResetMenu()}
+ {this.renderKeybindMenu()} {this.renderPencil()} {this.renderInfo()}