diff --git a/packages/pinball_components/lib/src/components/backboard/backboard_game_over.dart b/packages/pinball_components/lib/src/components/backboard/backboard_game_over.dart index 469988cf..10448395 100644 --- a/packages/pinball_components/lib/src/components/backboard/backboard_game_over.dart +++ b/packages/pinball_components/lib/src/components/backboard/backboard_game_over.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'dart:math'; import 'package:flame/components.dart'; import 'package:flutter/material.dart'; @@ -10,8 +11,7 @@ import 'package:pinball_components/pinball_components.dart'; /// [PositionComponent] that handles the user input on the /// game over display view. /// {@endtemplate} -class BackboardGameOver extends PositionComponent - with HasGameRef, KeyboardHandler { +class BackboardGameOver extends PositionComponent with HasGameRef { /// {@macro backboard_game_over} BackboardGameOver({ required int score, @@ -76,12 +76,29 @@ class BackboardGameOver extends PositionComponent ), ); } + + unawaited( + add( + KeyboardInputController( + keyUp: { + LogicalKeyboardKey.arrowLeft: () => _movePrompt(true), + LogicalKeyboardKey.arrowRight: () => _movePrompt(false), + }, + ), + ), + ); } - @override - bool onKeyEvent(RawKeyEvent event, Set keysPressed) { - final isUp = event is RawKeyUpEvent; + bool _movePrompt(bool left) { + final prompts = children.whereType().toList(); + + final current = prompts.firstWhere((prompt) => prompt.hasFocus) + ..hasFocus = false; + var index = prompts.indexOf(current) + (left ? -1 : 1); + index = min(max(0, index), prompts.length - 1); + + prompts[index].hasFocus = true; - return true; + return false; } } diff --git a/packages/pinball_components/lib/src/components/backboard/backboard_letter_prompt.dart b/packages/pinball_components/lib/src/components/backboard/backboard_letter_prompt.dart index 76169c62..554a27c4 100644 --- a/packages/pinball_components/lib/src/components/backboard/backboard_letter_prompt.dart +++ b/packages/pinball_components/lib/src/components/backboard/backboard_letter_prompt.dart @@ -1,8 +1,9 @@ import 'dart:async'; -import 'dart:ui'; +import 'dart:math'; import 'package:flame/components.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:pinball_components/pinball_components.dart'; /// {@template backboard_letter_prompt} @@ -20,10 +21,10 @@ class BackboardLetterPrompt extends PositionComponent { ); static const _alphabetCode = 65; - static const _alphabetLength = 26; + static const _alphabetLength = 25; + var _charIndex = 0; bool _hasFocus; - String _char = ''; late RectangleComponent _underscore; late TextComponent _input; @@ -43,9 +44,9 @@ class BackboardLetterPrompt extends PositionComponent { unawaited(add(_underscore)); _input = TextComponent( - text: _hasFocus ? 'A' : '', - textRenderer: Backboard.textPaint, - anchor: Anchor.center, + text: 'A', + textRenderer: Backboard.textPaint, + anchor: Anchor.center, ); unawaited(add(_input)); @@ -61,14 +62,42 @@ class BackboardLetterPrompt extends PositionComponent { ); unawaited(add(_underscoreBlinker)); - } - void up() { + unawaited( + add( + KeyboardInputController( + keyUp: { + LogicalKeyboardKey.arrowUp: () => _cycle(true), + LogicalKeyboardKey.arrowDown: () => _cycle(false), + }, + ), + ), + ); } - void down() { + bool _cycle(bool up) { + if (_hasFocus) { + final newCharCode = + min(max(_charIndex + (up ? 1 : -1), 0), _alphabetLength); + _input.text = String.fromCharCode(_alphabetCode + newCharCode); + _charIndex = newCharCode; + + return false; + } + return true; } - void nextPrompt() { + /// Returns if this prompt has focus on it + bool get hasFocus => _hasFocus; + + /// Updates this prompt focus + set hasFocus(bool hasFocus) { + if (hasFocus) { + _underscoreBlinker.timer.resume(); + } else { + _underscoreBlinker.timer.pause(); + } + _underscore.paint.color = Colors.white; + _hasFocus = hasFocus; } } diff --git a/packages/pinball_components/lib/src/flame/flame.dart b/packages/pinball_components/lib/src/flame/flame.dart index 9af8dba6..9b766995 100644 --- a/packages/pinball_components/lib/src/flame/flame.dart +++ b/packages/pinball_components/lib/src/flame/flame.dart @@ -1,2 +1,3 @@ export 'blueprint.dart'; +export 'keyboard_input_controller.dart'; export 'priority.dart'; diff --git a/packages/pinball_components/lib/src/flame/keyboard_input_controller.dart b/packages/pinball_components/lib/src/flame/keyboard_input_controller.dart new file mode 100644 index 00000000..5fcdbca1 --- /dev/null +++ b/packages/pinball_components/lib/src/flame/keyboard_input_controller.dart @@ -0,0 +1,36 @@ +import 'package:flame/components.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; + +/// The signature for a key handle function +typedef KeyHandlerCallback = bool Function(); + +/// {@template keyboard_input_controller} +/// A that receives keyboard input and execute registered methods +/// {@endtemplate} +class KeyboardInputController extends Component with KeyboardHandler { + + /// {@macro keyboard_input_controller} + KeyboardInputController({ + Map keyUp = const {}, + Map keyDown = const {}, + }) : _keyUp = keyUp, + _keyDown = keyDown; + + final Map _keyUp; + final Map _keyDown; + + @override + bool onKeyEvent(RawKeyEvent event, Set keysPressed) { + final isUp = event is RawKeyUpEvent; + + final handlers = isUp ? _keyUp : _keyDown; + final handler = handlers[event.logicalKey]; + + if (handler != null) { + return handler(); + } + + return true; + } +}