diff --git a/lib/game/pinball_game.dart b/lib/game/pinball_game.dart index 62e2976e..8f1628be 100644 --- a/lib/game/pinball_game.dart +++ b/lib/game/pinball_game.dart @@ -24,7 +24,8 @@ class PinballGame extends PinballForge2DGame required GameBloc gameBloc, required AppLocalizations l10n, required PinballPlayer player, - }) : _gameBloc = gameBloc, + }) : focusNode = FocusNode(), + _gameBloc = gameBloc, _player = player, _characterTheme = characterTheme, _l10n = l10n, @@ -40,6 +41,8 @@ class PinballGame extends PinballForge2DGame @override Color backgroundColor() => Colors.transparent; + final FocusNode focusNode; + final CharacterTheme _characterTheme; final PinballPlayer _player; diff --git a/lib/game/view/pinball_game_page.dart b/lib/game/view/pinball_game_page.dart index c0d5d1d8..076ed336 100644 --- a/lib/game/view/pinball_game_page.dart +++ b/lib/game/view/pinball_game_page.dart @@ -118,19 +118,27 @@ class PinballGameLoadedView extends StatelessWidget { child: Stack( children: [ Positioned.fill( - child: GameWidget( - game: game, - initialActiveOverlays: const [PinballGame.playButtonOverlay], - overlayBuilderMap: { - PinballGame.playButtonOverlay: (context, game) { - return const Positioned( - bottom: 20, - right: 0, - left: 0, - child: PlayButtonOverlay(), - ); - }, + child: MouseRegion( + onHover: (_) { + if (!game.focusNode.hasFocus) { + game.focusNode.requestFocus(); + } }, + child: GameWidget( + game: game, + focusNode: game.focusNode, + initialActiveOverlays: const [PinballGame.playButtonOverlay], + overlayBuilderMap: { + PinballGame.playButtonOverlay: (context, game) { + return const Positioned( + bottom: 20, + right: 0, + left: 0, + child: PlayButtonOverlay(), + ); + }, + }, + ), ), ), const _PositionedGameHud(), diff --git a/packages/pinball_components/assets/images/error_background.png b/packages/pinball_components/assets/images/error_background.png new file mode 100644 index 00000000..5aa6595f Binary files /dev/null and b/packages/pinball_components/assets/images/error_background.png differ diff --git a/packages/pinball_components/lib/gen/assets.gen.dart b/packages/pinball_components/lib/gen/assets.gen.dart index cac04cc0..0d603727 100644 --- a/packages/pinball_components/lib/gen/assets.gen.dart +++ b/packages/pinball_components/lib/gen/assets.gen.dart @@ -3,8 +3,6 @@ /// FlutterGen /// ***************************************************** -// ignore_for_file: directives_ordering,unnecessary_import - import 'package:flutter/widgets.dart'; class $AssetsImagesGen { @@ -14,14 +12,13 @@ class $AssetsImagesGen { $AssetsImagesBackboxGen get backbox => const $AssetsImagesBackboxGen(); $AssetsImagesBallGen get ball => const $AssetsImagesBallGen(); $AssetsImagesBaseboardGen get baseboard => const $AssetsImagesBaseboardGen(); - - /// File path: assets/images/board-background.png AssetGenImage get boardBackground => const AssetGenImage('assets/images/board-background.png'); - $AssetsImagesBoundaryGen get boundary => const $AssetsImagesBoundaryGen(); $AssetsImagesDashGen get dash => const $AssetsImagesDashGen(); $AssetsImagesDinoGen get dino => const $AssetsImagesDinoGen(); + AssetGenImage get errorBackground => + const AssetGenImage('assets/images/error_background.png'); $AssetsImagesFlapperGen get flapper => const $AssetsImagesFlapperGen(); $AssetsImagesFlipperGen get flipper => const $AssetsImagesFlipperGen(); $AssetsImagesGoogleWordGen get googleWord => @@ -54,11 +51,8 @@ class $AssetsImagesAndroidGen { class $AssetsImagesBackboxGen { const $AssetsImagesBackboxGen(); - /// File path: assets/images/backbox/display-divider.png AssetGenImage get displayDivider => const AssetGenImage('assets/images/backbox/display-divider.png'); - - /// File path: assets/images/backbox/marquee.png AssetGenImage get marquee => const AssetGenImage('assets/images/backbox/marquee.png'); } @@ -66,10 +60,6 @@ class $AssetsImagesBackboxGen { class $AssetsImagesBallGen { const $AssetsImagesBallGen(); - /// File path: assets/images/ball/ball.png - AssetGenImage get ball => const AssetGenImage('assets/images/ball/ball.png'); - - /// File path: assets/images/ball/flame_effect.png AssetGenImage get flameEffect => const AssetGenImage('assets/images/ball/flame_effect.png'); } @@ -77,11 +67,8 @@ class $AssetsImagesBallGen { class $AssetsImagesBaseboardGen { const $AssetsImagesBaseboardGen(); - /// File path: assets/images/baseboard/left.png AssetGenImage get left => const AssetGenImage('assets/images/baseboard/left.png'); - - /// File path: assets/images/baseboard/right.png AssetGenImage get right => const AssetGenImage('assets/images/baseboard/right.png'); } @@ -89,15 +76,10 @@ class $AssetsImagesBaseboardGen { class $AssetsImagesBoundaryGen { const $AssetsImagesBoundaryGen(); - /// File path: assets/images/boundary/bottom.png AssetGenImage get bottom => const AssetGenImage('assets/images/boundary/bottom.png'); - - /// File path: assets/images/boundary/outer-bottom.png AssetGenImage get outerBottom => const AssetGenImage('assets/images/boundary/outer-bottom.png'); - - /// File path: assets/images/boundary/outer.png AssetGenImage get outer => const AssetGenImage('assets/images/boundary/outer.png'); } @@ -105,10 +87,8 @@ class $AssetsImagesBoundaryGen { class $AssetsImagesDashGen { const $AssetsImagesDashGen(); - /// File path: assets/images/dash/animatronic.png AssetGenImage get animatronic => const AssetGenImage('assets/images/dash/animatronic.png'); - $AssetsImagesDashBumperGen get bumper => const $AssetsImagesDashBumperGen(); } @@ -117,16 +97,10 @@ class $AssetsImagesDinoGen { $AssetsImagesDinoAnimatronicGen get animatronic => const $AssetsImagesDinoAnimatronicGen(); - - /// File path: assets/images/dino/bottom-wall.png AssetGenImage get bottomWall => const AssetGenImage('assets/images/dino/bottom-wall.png'); - - /// File path: assets/images/dino/top-wall-tunnel.png AssetGenImage get topWallTunnel => const AssetGenImage('assets/images/dino/top-wall-tunnel.png'); - - /// File path: assets/images/dino/top-wall.png AssetGenImage get topWall => const AssetGenImage('assets/images/dino/top-wall.png'); } @@ -134,15 +108,10 @@ class $AssetsImagesDinoGen { class $AssetsImagesFlapperGen { const $AssetsImagesFlapperGen(); - /// File path: assets/images/flapper/back-support.png AssetGenImage get backSupport => const AssetGenImage('assets/images/flapper/back-support.png'); - - /// File path: assets/images/flapper/flap.png AssetGenImage get flap => const AssetGenImage('assets/images/flapper/flap.png'); - - /// File path: assets/images/flapper/front-support.png AssetGenImage get frontSupport => const AssetGenImage('assets/images/flapper/front-support.png'); } @@ -150,11 +119,8 @@ class $AssetsImagesFlapperGen { class $AssetsImagesFlipperGen { const $AssetsImagesFlipperGen(); - /// File path: assets/images/flipper/left.png AssetGenImage get left => const AssetGenImage('assets/images/flipper/left.png'); - - /// File path: assets/images/flipper/right.png AssetGenImage get right => const AssetGenImage('assets/images/flipper/right.png'); } @@ -186,15 +152,10 @@ class $AssetsImagesKickerGen { class $AssetsImagesLaunchRampGen { const $AssetsImagesLaunchRampGen(); - /// File path: assets/images/launch_ramp/background-railing.png AssetGenImage get backgroundRailing => const AssetGenImage('assets/images/launch_ramp/background-railing.png'); - - /// File path: assets/images/launch_ramp/foreground-railing.png AssetGenImage get foregroundRailing => const AssetGenImage('assets/images/launch_ramp/foreground-railing.png'); - - /// File path: assets/images/launch_ramp/ramp.png AssetGenImage get ramp => const AssetGenImage('assets/images/launch_ramp/ramp.png'); } @@ -202,11 +163,8 @@ class $AssetsImagesLaunchRampGen { class $AssetsImagesMultiballGen { const $AssetsImagesMultiballGen(); - /// File path: assets/images/multiball/dimmed.png AssetGenImage get dimmed => const AssetGenImage('assets/images/multiball/dimmed.png'); - - /// File path: assets/images/multiball/lit.png AssetGenImage get lit => const AssetGenImage('assets/images/multiball/lit.png'); } @@ -224,11 +182,8 @@ class $AssetsImagesMultiplierGen { class $AssetsImagesPlungerGen { const $AssetsImagesPlungerGen(); - /// File path: assets/images/plunger/plunger.png AssetGenImage get plunger => const AssetGenImage('assets/images/plunger/plunger.png'); - - /// File path: assets/images/plunger/rocket.png AssetGenImage get rocket => const AssetGenImage('assets/images/plunger/rocket.png'); } @@ -236,19 +191,12 @@ class $AssetsImagesPlungerGen { class $AssetsImagesScoreGen { const $AssetsImagesScoreGen(); - /// File path: assets/images/score/five-thousand.png AssetGenImage get fiveThousand => const AssetGenImage('assets/images/score/five-thousand.png'); - - /// File path: assets/images/score/one-million.png AssetGenImage get oneMillion => const AssetGenImage('assets/images/score/one-million.png'); - - /// File path: assets/images/score/twenty-thousand.png AssetGenImage get twentyThousand => const AssetGenImage('assets/images/score/twenty-thousand.png'); - - /// File path: assets/images/score/two-hundred-thousand.png AssetGenImage get twoHundredThousand => const AssetGenImage('assets/images/score/two-hundred-thousand.png'); } @@ -256,19 +204,12 @@ class $AssetsImagesScoreGen { class $AssetsImagesSignpostGen { const $AssetsImagesSignpostGen(); - /// File path: assets/images/signpost/active1.png AssetGenImage get active1 => const AssetGenImage('assets/images/signpost/active1.png'); - - /// File path: assets/images/signpost/active2.png AssetGenImage get active2 => const AssetGenImage('assets/images/signpost/active2.png'); - - /// File path: assets/images/signpost/active3.png AssetGenImage get active3 => const AssetGenImage('assets/images/signpost/active3.png'); - - /// File path: assets/images/signpost/inactive.png AssetGenImage get inactive => const AssetGenImage('assets/images/signpost/inactive.png'); } @@ -276,19 +217,12 @@ class $AssetsImagesSignpostGen { class $AssetsImagesSkillShotGen { const $AssetsImagesSkillShotGen(); - /// File path: assets/images/skill_shot/decal.png AssetGenImage get decal => const AssetGenImage('assets/images/skill_shot/decal.png'); - - /// File path: assets/images/skill_shot/dimmed.png AssetGenImage get dimmed => const AssetGenImage('assets/images/skill_shot/dimmed.png'); - - /// File path: assets/images/skill_shot/lit.png AssetGenImage get lit => const AssetGenImage('assets/images/skill_shot/lit.png'); - - /// File path: assets/images/skill_shot/pin.png AssetGenImage get pin => const AssetGenImage('assets/images/skill_shot/pin.png'); } @@ -296,11 +230,8 @@ class $AssetsImagesSkillShotGen { class $AssetsImagesSlingshotGen { const $AssetsImagesSlingshotGen(); - /// File path: assets/images/slingshot/lower.png AssetGenImage get lower => const AssetGenImage('assets/images/slingshot/lower.png'); - - /// File path: assets/images/slingshot/upper.png AssetGenImage get upper => const AssetGenImage('assets/images/slingshot/upper.png'); } @@ -308,10 +239,8 @@ class $AssetsImagesSlingshotGen { class $AssetsImagesSparkyGen { const $AssetsImagesSparkyGen(); - /// File path: assets/images/sparky/animatronic.png AssetGenImage get animatronic => const AssetGenImage('assets/images/sparky/animatronic.png'); - $AssetsImagesSparkyBumperGen get bumper => const $AssetsImagesSparkyBumperGen(); $AssetsImagesSparkyComputerGen get computer => @@ -332,11 +261,8 @@ class $AssetsImagesAndroidBumperGen { class $AssetsImagesAndroidRailGen { const $AssetsImagesAndroidRailGen(); - /// File path: assets/images/android/rail/exit.png AssetGenImage get exit => const AssetGenImage('assets/images/android/rail/exit.png'); - - /// File path: assets/images/android/rail/main.png AssetGenImage get main => const AssetGenImage('assets/images/android/rail/main.png'); } @@ -346,20 +272,12 @@ class $AssetsImagesAndroidRampGen { $AssetsImagesAndroidRampArrowGen get arrow => const $AssetsImagesAndroidRampArrowGen(); - - /// File path: assets/images/android/ramp/board-opening.png AssetGenImage get boardOpening => const AssetGenImage('assets/images/android/ramp/board-opening.png'); - - /// File path: assets/images/android/ramp/main.png AssetGenImage get main => const AssetGenImage('assets/images/android/ramp/main.png'); - - /// File path: assets/images/android/ramp/railing-background.png AssetGenImage get railingBackground => const AssetGenImage('assets/images/android/ramp/railing-background.png'); - - /// File path: assets/images/android/ramp/railing-foreground.png AssetGenImage get railingForeground => const AssetGenImage('assets/images/android/ramp/railing-foreground.png'); } @@ -367,15 +285,10 @@ class $AssetsImagesAndroidRampGen { class $AssetsImagesAndroidSpaceshipGen { const $AssetsImagesAndroidSpaceshipGen(); - /// File path: assets/images/android/spaceship/animatronic.png AssetGenImage get animatronic => const AssetGenImage('assets/images/android/spaceship/animatronic.png'); - - /// File path: assets/images/android/spaceship/light-beam.png AssetGenImage get lightBeam => const AssetGenImage('assets/images/android/spaceship/light-beam.png'); - - /// File path: assets/images/android/spaceship/saucer.png AssetGenImage get saucer => const AssetGenImage('assets/images/android/spaceship/saucer.png'); } @@ -392,11 +305,8 @@ class $AssetsImagesDashBumperGen { class $AssetsImagesDinoAnimatronicGen { const $AssetsImagesDinoAnimatronicGen(); - /// File path: assets/images/dino/animatronic/head.png AssetGenImage get head => const AssetGenImage('assets/images/dino/animatronic/head.png'); - - /// File path: assets/images/dino/animatronic/mouth.png AssetGenImage get mouth => const AssetGenImage('assets/images/dino/animatronic/mouth.png'); } @@ -404,11 +314,8 @@ class $AssetsImagesDinoAnimatronicGen { class $AssetsImagesGoogleWordLetter1Gen { const $AssetsImagesGoogleWordLetter1Gen(); - /// File path: assets/images/google_word/letter1/dimmed.png AssetGenImage get dimmed => const AssetGenImage('assets/images/google_word/letter1/dimmed.png'); - - /// File path: assets/images/google_word/letter1/lit.png AssetGenImage get lit => const AssetGenImage('assets/images/google_word/letter1/lit.png'); } @@ -416,11 +323,8 @@ class $AssetsImagesGoogleWordLetter1Gen { class $AssetsImagesGoogleWordLetter2Gen { const $AssetsImagesGoogleWordLetter2Gen(); - /// File path: assets/images/google_word/letter2/dimmed.png AssetGenImage get dimmed => const AssetGenImage('assets/images/google_word/letter2/dimmed.png'); - - /// File path: assets/images/google_word/letter2/lit.png AssetGenImage get lit => const AssetGenImage('assets/images/google_word/letter2/lit.png'); } @@ -428,11 +332,8 @@ class $AssetsImagesGoogleWordLetter2Gen { class $AssetsImagesGoogleWordLetter3Gen { const $AssetsImagesGoogleWordLetter3Gen(); - /// File path: assets/images/google_word/letter3/dimmed.png AssetGenImage get dimmed => const AssetGenImage('assets/images/google_word/letter3/dimmed.png'); - - /// File path: assets/images/google_word/letter3/lit.png AssetGenImage get lit => const AssetGenImage('assets/images/google_word/letter3/lit.png'); } @@ -440,11 +341,8 @@ class $AssetsImagesGoogleWordLetter3Gen { class $AssetsImagesGoogleWordLetter4Gen { const $AssetsImagesGoogleWordLetter4Gen(); - /// File path: assets/images/google_word/letter4/dimmed.png AssetGenImage get dimmed => const AssetGenImage('assets/images/google_word/letter4/dimmed.png'); - - /// File path: assets/images/google_word/letter4/lit.png AssetGenImage get lit => const AssetGenImage('assets/images/google_word/letter4/lit.png'); } @@ -452,11 +350,8 @@ class $AssetsImagesGoogleWordLetter4Gen { class $AssetsImagesGoogleWordLetter5Gen { const $AssetsImagesGoogleWordLetter5Gen(); - /// File path: assets/images/google_word/letter5/dimmed.png AssetGenImage get dimmed => const AssetGenImage('assets/images/google_word/letter5/dimmed.png'); - - /// File path: assets/images/google_word/letter5/lit.png AssetGenImage get lit => const AssetGenImage('assets/images/google_word/letter5/lit.png'); } @@ -464,11 +359,8 @@ class $AssetsImagesGoogleWordLetter5Gen { class $AssetsImagesGoogleWordLetter6Gen { const $AssetsImagesGoogleWordLetter6Gen(); - /// File path: assets/images/google_word/letter6/dimmed.png AssetGenImage get dimmed => const AssetGenImage('assets/images/google_word/letter6/dimmed.png'); - - /// File path: assets/images/google_word/letter6/lit.png AssetGenImage get lit => const AssetGenImage('assets/images/google_word/letter6/lit.png'); } @@ -476,11 +368,8 @@ class $AssetsImagesGoogleWordLetter6Gen { class $AssetsImagesKickerLeftGen { const $AssetsImagesKickerLeftGen(); - /// File path: assets/images/kicker/left/dimmed.png AssetGenImage get dimmed => const AssetGenImage('assets/images/kicker/left/dimmed.png'); - - /// File path: assets/images/kicker/left/lit.png AssetGenImage get lit => const AssetGenImage('assets/images/kicker/left/lit.png'); } @@ -488,11 +377,8 @@ class $AssetsImagesKickerLeftGen { class $AssetsImagesKickerRightGen { const $AssetsImagesKickerRightGen(); - /// File path: assets/images/kicker/right/dimmed.png AssetGenImage get dimmed => const AssetGenImage('assets/images/kicker/right/dimmed.png'); - - /// File path: assets/images/kicker/right/lit.png AssetGenImage get lit => const AssetGenImage('assets/images/kicker/right/lit.png'); } @@ -500,11 +386,8 @@ class $AssetsImagesKickerRightGen { class $AssetsImagesMultiplierX2Gen { const $AssetsImagesMultiplierX2Gen(); - /// File path: assets/images/multiplier/x2/dimmed.png AssetGenImage get dimmed => const AssetGenImage('assets/images/multiplier/x2/dimmed.png'); - - /// File path: assets/images/multiplier/x2/lit.png AssetGenImage get lit => const AssetGenImage('assets/images/multiplier/x2/lit.png'); } @@ -512,11 +395,8 @@ class $AssetsImagesMultiplierX2Gen { class $AssetsImagesMultiplierX3Gen { const $AssetsImagesMultiplierX3Gen(); - /// File path: assets/images/multiplier/x3/dimmed.png AssetGenImage get dimmed => const AssetGenImage('assets/images/multiplier/x3/dimmed.png'); - - /// File path: assets/images/multiplier/x3/lit.png AssetGenImage get lit => const AssetGenImage('assets/images/multiplier/x3/lit.png'); } @@ -524,11 +404,8 @@ class $AssetsImagesMultiplierX3Gen { class $AssetsImagesMultiplierX4Gen { const $AssetsImagesMultiplierX4Gen(); - /// File path: assets/images/multiplier/x4/dimmed.png AssetGenImage get dimmed => const AssetGenImage('assets/images/multiplier/x4/dimmed.png'); - - /// File path: assets/images/multiplier/x4/lit.png AssetGenImage get lit => const AssetGenImage('assets/images/multiplier/x4/lit.png'); } @@ -536,11 +413,8 @@ class $AssetsImagesMultiplierX4Gen { class $AssetsImagesMultiplierX5Gen { const $AssetsImagesMultiplierX5Gen(); - /// File path: assets/images/multiplier/x5/dimmed.png AssetGenImage get dimmed => const AssetGenImage('assets/images/multiplier/x5/dimmed.png'); - - /// File path: assets/images/multiplier/x5/lit.png AssetGenImage get lit => const AssetGenImage('assets/images/multiplier/x5/lit.png'); } @@ -548,11 +422,8 @@ class $AssetsImagesMultiplierX5Gen { class $AssetsImagesMultiplierX6Gen { const $AssetsImagesMultiplierX6Gen(); - /// File path: assets/images/multiplier/x6/dimmed.png AssetGenImage get dimmed => const AssetGenImage('assets/images/multiplier/x6/dimmed.png'); - - /// File path: assets/images/multiplier/x6/lit.png AssetGenImage get lit => const AssetGenImage('assets/images/multiplier/x6/lit.png'); } @@ -568,15 +439,10 @@ class $AssetsImagesSparkyBumperGen { class $AssetsImagesSparkyComputerGen { const $AssetsImagesSparkyComputerGen(); - /// File path: assets/images/sparky/computer/base.png AssetGenImage get base => const AssetGenImage('assets/images/sparky/computer/base.png'); - - /// File path: assets/images/sparky/computer/glow.png AssetGenImage get glow => const AssetGenImage('assets/images/sparky/computer/glow.png'); - - /// File path: assets/images/sparky/computer/top.png AssetGenImage get top => const AssetGenImage('assets/images/sparky/computer/top.png'); } @@ -584,11 +450,8 @@ class $AssetsImagesSparkyComputerGen { class $AssetsImagesAndroidBumperAGen { const $AssetsImagesAndroidBumperAGen(); - /// File path: assets/images/android/bumper/a/dimmed.png AssetGenImage get dimmed => const AssetGenImage('assets/images/android/bumper/a/dimmed.png'); - - /// File path: assets/images/android/bumper/a/lit.png AssetGenImage get lit => const AssetGenImage('assets/images/android/bumper/a/lit.png'); } @@ -596,11 +459,8 @@ class $AssetsImagesAndroidBumperAGen { class $AssetsImagesAndroidBumperBGen { const $AssetsImagesAndroidBumperBGen(); - /// File path: assets/images/android/bumper/b/dimmed.png AssetGenImage get dimmed => const AssetGenImage('assets/images/android/bumper/b/dimmed.png'); - - /// File path: assets/images/android/bumper/b/lit.png AssetGenImage get lit => const AssetGenImage('assets/images/android/bumper/b/lit.png'); } @@ -608,11 +468,8 @@ class $AssetsImagesAndroidBumperBGen { class $AssetsImagesAndroidBumperCowGen { const $AssetsImagesAndroidBumperCowGen(); - /// File path: assets/images/android/bumper/cow/dimmed.png AssetGenImage get dimmed => const AssetGenImage('assets/images/android/bumper/cow/dimmed.png'); - - /// File path: assets/images/android/bumper/cow/lit.png AssetGenImage get lit => const AssetGenImage('assets/images/android/bumper/cow/lit.png'); } @@ -620,27 +477,16 @@ class $AssetsImagesAndroidBumperCowGen { class $AssetsImagesAndroidRampArrowGen { const $AssetsImagesAndroidRampArrowGen(); - /// File path: assets/images/android/ramp/arrow/active1.png AssetGenImage get active1 => const AssetGenImage('assets/images/android/ramp/arrow/active1.png'); - - /// File path: assets/images/android/ramp/arrow/active2.png AssetGenImage get active2 => const AssetGenImage('assets/images/android/ramp/arrow/active2.png'); - - /// File path: assets/images/android/ramp/arrow/active3.png AssetGenImage get active3 => const AssetGenImage('assets/images/android/ramp/arrow/active3.png'); - - /// File path: assets/images/android/ramp/arrow/active4.png AssetGenImage get active4 => const AssetGenImage('assets/images/android/ramp/arrow/active4.png'); - - /// File path: assets/images/android/ramp/arrow/active5.png AssetGenImage get active5 => const AssetGenImage('assets/images/android/ramp/arrow/active5.png'); - - /// File path: assets/images/android/ramp/arrow/inactive.png AssetGenImage get inactive => const AssetGenImage('assets/images/android/ramp/arrow/inactive.png'); } @@ -648,11 +494,8 @@ class $AssetsImagesAndroidRampArrowGen { class $AssetsImagesDashBumperAGen { const $AssetsImagesDashBumperAGen(); - /// File path: assets/images/dash/bumper/a/active.png AssetGenImage get active => const AssetGenImage('assets/images/dash/bumper/a/active.png'); - - /// File path: assets/images/dash/bumper/a/inactive.png AssetGenImage get inactive => const AssetGenImage('assets/images/dash/bumper/a/inactive.png'); } @@ -660,11 +503,8 @@ class $AssetsImagesDashBumperAGen { class $AssetsImagesDashBumperBGen { const $AssetsImagesDashBumperBGen(); - /// File path: assets/images/dash/bumper/b/active.png AssetGenImage get active => const AssetGenImage('assets/images/dash/bumper/b/active.png'); - - /// File path: assets/images/dash/bumper/b/inactive.png AssetGenImage get inactive => const AssetGenImage('assets/images/dash/bumper/b/inactive.png'); } @@ -672,11 +512,8 @@ class $AssetsImagesDashBumperBGen { class $AssetsImagesDashBumperMainGen { const $AssetsImagesDashBumperMainGen(); - /// File path: assets/images/dash/bumper/main/active.png AssetGenImage get active => const AssetGenImage('assets/images/dash/bumper/main/active.png'); - - /// File path: assets/images/dash/bumper/main/inactive.png AssetGenImage get inactive => const AssetGenImage('assets/images/dash/bumper/main/inactive.png'); } @@ -684,11 +521,8 @@ class $AssetsImagesDashBumperMainGen { class $AssetsImagesSparkyBumperAGen { const $AssetsImagesSparkyBumperAGen(); - /// File path: assets/images/sparky/bumper/a/dimmed.png AssetGenImage get dimmed => const AssetGenImage('assets/images/sparky/bumper/a/dimmed.png'); - - /// File path: assets/images/sparky/bumper/a/lit.png AssetGenImage get lit => const AssetGenImage('assets/images/sparky/bumper/a/lit.png'); } @@ -696,11 +530,8 @@ class $AssetsImagesSparkyBumperAGen { class $AssetsImagesSparkyBumperBGen { const $AssetsImagesSparkyBumperBGen(); - /// File path: assets/images/sparky/bumper/b/dimmed.png AssetGenImage get dimmed => const AssetGenImage('assets/images/sparky/bumper/b/dimmed.png'); - - /// File path: assets/images/sparky/bumper/b/lit.png AssetGenImage get lit => const AssetGenImage('assets/images/sparky/bumper/b/lit.png'); } @@ -708,11 +539,8 @@ class $AssetsImagesSparkyBumperBGen { class $AssetsImagesSparkyBumperCGen { const $AssetsImagesSparkyBumperCGen(); - /// File path: assets/images/sparky/bumper/c/dimmed.png AssetGenImage get dimmed => const AssetGenImage('assets/images/sparky/bumper/c/dimmed.png'); - - /// File path: assets/images/sparky/bumper/c/lit.png AssetGenImage get lit => const AssetGenImage('assets/images/sparky/bumper/c/lit.png'); } diff --git a/packages/pinball_components/lib/gen/fonts.gen.dart b/packages/pinball_components/lib/gen/fonts.gen.dart index 5f77da16..b15f2dd0 100644 --- a/packages/pinball_components/lib/gen/fonts.gen.dart +++ b/packages/pinball_components/lib/gen/fonts.gen.dart @@ -3,14 +3,9 @@ /// FlutterGen /// ***************************************************** -// ignore_for_file: directives_ordering,unnecessary_import - class FontFamily { FontFamily._(); - /// Font family: PixeloidMono static const String pixeloidMono = 'PixeloidMono'; - - /// Font family: PixeloidSans static const String pixeloidSans = 'PixeloidSans'; } diff --git a/packages/pinball_components/lib/src/components/components.dart b/packages/pinball_components/lib/src/components/components.dart index db2f7d38..55fe6bb5 100644 --- a/packages/pinball_components/lib/src/components/components.dart +++ b/packages/pinball_components/lib/src/components/components.dart @@ -12,6 +12,7 @@ export 'chrome_dino/chrome_dino.dart'; export 'dash_animatronic.dart'; export 'dash_nest_bumper/dash_nest_bumper.dart'; export 'dino_walls.dart'; +export 'error_component.dart'; export 'fire_effect.dart'; export 'flapper/flapper.dart'; export 'flipper.dart'; diff --git a/packages/pinball_components/lib/src/components/error_component.dart b/packages/pinball_components/lib/src/components/error_component.dart new file mode 100644 index 00000000..49be8069 --- /dev/null +++ b/packages/pinball_components/lib/src/components/error_component.dart @@ -0,0 +1,95 @@ +import 'package:flame/components.dart'; +import 'package:flutter/material.dart'; +import 'package:pinball_components/pinball_components.dart'; +import 'package:pinball_ui/pinball_ui.dart'; + +final _boldLabelTextPaint = TextPaint( + style: const TextStyle( + fontSize: 1.8, + color: PinballColors.white, + fontFamily: PinballFonts.pixeloidSans, + fontWeight: FontWeight.w700, + ), +); + +final _labelTextPaint = TextPaint( + style: const TextStyle( + fontSize: 1.8, + color: PinballColors.white, + fontFamily: PinballFonts.pixeloidSans, + fontWeight: FontWeight.w400, + ), +); + +/// {@template error_component} +/// A plain visual component used to show errors for the user. +/// {@endtemplate} +class ErrorComponent extends SpriteComponent with HasGameRef { + /// {@macro error_component} + ErrorComponent({required this.label, Vector2? position}) + : _textPaint = _labelTextPaint, + super( + position: position, + ); + + /// {@macro error_component} + ErrorComponent.bold({required this.label, Vector2? position}) + : _textPaint = _boldLabelTextPaint, + super( + position: position, + ); + + /// Text shown on the error message. + final String label; + final TextPaint _textPaint; + + List _splitInLines() { + final maxWidth = size.x - 8; + final lines = []; + var currentLine = ''; + final words = label.split(' '); + while (words.isNotEmpty) { + final word = words.removeAt(0); + + if (_textPaint.measureTextWidth('$currentLine $word') <= maxWidth) { + currentLine = '$currentLine $word'.trim(); + } else { + lines.add(currentLine); + currentLine = word; + } + } + + lines.add(currentLine); + return lines; + } + + @override + Future onLoad() async { + anchor = Anchor.center; + final sprite = await gameRef.loadSprite( + Assets.images.errorBackground.keyName, + ); + + size = sprite.originalSize / 20; + this.sprite = sprite; + + final lines = _splitInLines(); + + // Calculates vertical offset based on the number of lines of text to be + // displayed. This offset is used to keep the middle of the multi-line text + // at the center of the [ErrorComponent]. + final yOffset = ((size.y / 2.2) / lines.length) * 1.5; + + for (var i = 0; i < lines.length; i++) { + await add( + TextComponent( + position: Vector2(size.x / 2, yOffset + 2.2 * i), + size: Vector2(size.x - 4, 2.2), + text: lines[i], + textRenderer: _textPaint, + anchor: Anchor.center, + ), + ); + } + } +} diff --git a/packages/pinball_components/pubspec.yaml b/packages/pinball_components/pubspec.yaml index 4f66c220..3fffaa88 100644 --- a/packages/pinball_components/pubspec.yaml +++ b/packages/pinball_components/pubspec.yaml @@ -11,7 +11,7 @@ dependencies: flame: ^1.1.1 flame_forge2d: git: - url: https://github.com/flame-engine/flame/ + url: https://github.com/flame-engine/flame path: packages/flame_forge2d/ ref: a50d4a1e7d9eaf66726ed1bb9894c9d495547d8f flutter: @@ -23,6 +23,8 @@ dependencies: path: ../pinball_flame pinball_theme: path: ../pinball_theme + pinball_ui: + path: ../pinball_ui dev_dependencies: bloc_test: ^9.0.3 @@ -33,6 +35,7 @@ dev_dependencies: very_good_analysis: ^2.4.0 flutter: + uses-material-design: true generate: true fonts: - family: PixeloidSans diff --git a/packages/pinball_components/sandbox/lib/main.dart b/packages/pinball_components/sandbox/lib/main.dart index ccb1b0bc..714bbee5 100644 --- a/packages/pinball_components/sandbox/lib/main.dart +++ b/packages/pinball_components/sandbox/lib/main.dart @@ -8,6 +8,7 @@ void main() { addBallStories(dashbook); addLayerStories(dashbook); addEffectsStories(dashbook); + addErrorComponentStories(dashbook); addFlutterForestStories(dashbook); addSparkyScorchStories(dashbook); addAndroidAcresStories(dashbook); diff --git a/packages/pinball_components/sandbox/lib/stories/error_component/error_component_game.dart b/packages/pinball_components/sandbox/lib/stories/error_component/error_component_game.dart new file mode 100644 index 00000000..c64e6d48 --- /dev/null +++ b/packages/pinball_components/sandbox/lib/stories/error_component/error_component_game.dart @@ -0,0 +1,24 @@ +import 'package:flame/components.dart'; +import 'package:pinball_components/pinball_components.dart'; +import 'package:sandbox/common/common.dart'; + +class ErrorComponentGame extends AssetsGame { + ErrorComponentGame({required this.text}); + + static const description = 'Shows how ErrorComponents are rendered.'; + + final String text; + + @override + Future onLoad() async { + camera.followVector2(Vector2.zero()); + + await add(ErrorComponent(label: text)); + await add( + ErrorComponent.bold( + label: text, + position: Vector2(0, 10), + ), + ); + } +} diff --git a/packages/pinball_components/sandbox/lib/stories/error_component/stories.dart b/packages/pinball_components/sandbox/lib/stories/error_component/stories.dart new file mode 100644 index 00000000..cecd9dae --- /dev/null +++ b/packages/pinball_components/sandbox/lib/stories/error_component/stories.dart @@ -0,0 +1,16 @@ +import 'package:dashbook/dashbook.dart'; +import 'package:sandbox/common/common.dart'; +import 'package:sandbox/stories/error_component/error_component_game.dart'; + +void addErrorComponentStories(Dashbook dashbook) { + dashbook.storiesOf('ErrorComponent').addGame( + title: 'Basic', + description: ErrorComponentGame.description, + gameBuilder: (context) => ErrorComponentGame( + text: context.textProperty( + 'label', + 'Oh no, something went wrong!', + ), + ), + ); +} diff --git a/packages/pinball_components/sandbox/lib/stories/stories.dart b/packages/pinball_components/sandbox/lib/stories/stories.dart index b48770ba..0a514eb9 100644 --- a/packages/pinball_components/sandbox/lib/stories/stories.dart +++ b/packages/pinball_components/sandbox/lib/stories/stories.dart @@ -4,6 +4,7 @@ export 'bottom_group/stories.dart'; export 'boundaries/stories.dart'; export 'dino_desert/stories.dart'; export 'effects/stories.dart'; +export 'error_component/stories.dart'; export 'flutter_forest/stories.dart'; export 'google_word/stories.dart'; export 'launch_ramp/stories.dart'; diff --git a/packages/pinball_components/sandbox/pubspec.lock b/packages/pinball_components/sandbox/pubspec.lock index d2500fbe..0357656d 100644 --- a/packages/pinball_components/sandbox/pubspec.lock +++ b/packages/pinball_components/sandbox/pubspec.lock @@ -112,7 +112,7 @@ packages: path: "packages/flame_forge2d" ref: a50d4a1e7d9eaf66726ed1bb9894c9d495547d8f resolved-ref: a50d4a1e7d9eaf66726ed1bb9894c9d495547d8f - url: "https://github.com/flame-engine/flame/" + url: "https://github.com/flame-engine/flame" source: git version: "0.11.0" flutter: @@ -270,6 +270,13 @@ packages: relative: true source: path version: "1.0.0+1" + pinball_ui: + dependency: transitive + description: + path: "../../pinball_ui" + relative: true + source: path + version: "1.0.0+1" platform: dependency: transitive description: @@ -407,7 +414,7 @@ packages: name: url_launcher url: "https://pub.dartlang.org" source: hosted - version: "6.0.20" + version: "6.1.0" url_launcher_android: dependency: transitive description: diff --git a/packages/pinball_components/sandbox/pubspec.yaml b/packages/pinball_components/sandbox/pubspec.yaml index d663cb04..791020d0 100644 --- a/packages/pinball_components/sandbox/pubspec.yaml +++ b/packages/pinball_components/sandbox/pubspec.yaml @@ -11,7 +11,7 @@ dependencies: flame: ^1.1.1 flame_forge2d: git: - url: https://github.com/flame-engine/flame/ + url: https://github.com/flame-engine/flame path: packages/flame_forge2d/ ref: a50d4a1e7d9eaf66726ed1bb9894c9d495547d8f flutter: diff --git a/packages/pinball_components/test/src/components/error_component_test.dart b/packages/pinball_components/test/src/components/error_component_test.dart new file mode 100644 index 00000000..c50ac629 --- /dev/null +++ b/packages/pinball_components/test/src/components/error_component_test.dart @@ -0,0 +1,57 @@ +// ignore_for_file: cascade_invocations + +import 'package:flame/components.dart'; +import 'package:flame_test/flame_test.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:pinball_components/pinball_components.dart'; + +import '../../helpers/helpers.dart'; + +extension _IterableX on Iterable { + int countTexts(String value) { + return where( + (component) => component is TextComponent && component.text == value, + ).length; + } +} + +void main() { + group('ErrorComponent', () { + TestWidgetsFlutterBinding.ensureInitialized(); + final assets = [ + Assets.images.errorBackground.keyName, + ]; + final flameTester = FlameTester(() => TestGame(assets)); + + flameTester.test('renders correctly', (game) async { + await game.ensureAdd(ErrorComponent(label: 'Error Message')); + final count = game.descendants().countTexts('Error Message'); + + expect(count, equals(1)); + }); + + group('when the text is longer than one line', () { + flameTester.test('renders correctly', (game) async { + await game.ensureAdd( + ErrorComponent( + label: 'Error With A Longer Message', + ), + ); + final count1 = game.descendants().countTexts('Error With A'); + final count2 = game.descendants().countTexts('Longer Message'); + + expect(count1, equals(1)); + expect(count2, equals(1)); + }); + }); + + group('when using the bold font', () { + flameTester.test('renders correctly', (game) async { + await game.ensureAdd(ErrorComponent.bold(label: 'Error Message')); + final count = game.descendants().countTexts('Error Message'); + + expect(count, equals(1)); + }); + }); + }); +} diff --git a/packages/pinball_flame/lib/src/canvas/z_canvas_component.dart b/packages/pinball_flame/lib/src/canvas/z_canvas_component.dart index 5e250285..e149bf58 100644 --- a/packages/pinball_flame/lib/src/canvas/z_canvas_component.dart +++ b/packages/pinball_flame/lib/src/canvas/z_canvas_component.dart @@ -1,4 +1,5 @@ import 'dart:ui'; +import 'package:collection/collection.dart' as collection; import 'package:flame/components.dart'; import 'package:pinball_flame/src/canvas/canvas_wrapper.dart'; @@ -56,9 +57,14 @@ class _ZCanvas extends CanvasWrapper { final List _zBuffer = []; /// Postpones the rendering of [ZIndex] component and its children. - void buffer(ZIndex component) => _zBuffer - ..add(component) - ..sort((a, b) => a.zIndex.compareTo(b.zIndex)); + void buffer(ZIndex component) { + final lowerBound = collection.lowerBound( + _zBuffer, + component, + compare: (a, b) => a.zIndex.compareTo(b.zIndex), + ); + _zBuffer.insert(lowerBound, component); + } /// Renders all [ZIndex] components and their children. /// diff --git a/packages/pinball_flame/pubspec.yaml b/packages/pinball_flame/pubspec.yaml index 89caf5bb..125e7757 100644 --- a/packages/pinball_flame/pubspec.yaml +++ b/packages/pinball_flame/pubspec.yaml @@ -10,7 +10,7 @@ dependencies: flame: ^1.1.1 flame_forge2d: git: - url: https://github.com/flame-engine/flame/ + url: https://github.com/flame-engine/flame path: packages/flame_forge2d/ ref: a50d4a1e7d9eaf66726ed1bb9894c9d495547d8f flutter: diff --git a/pubspec.lock b/pubspec.lock index 96f9f2a6..68e08467 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -245,7 +245,7 @@ packages: path: "packages/flame_forge2d" ref: a50d4a1e7d9eaf66726ed1bb9894c9d495547d8f resolved-ref: a50d4a1e7d9eaf66726ed1bb9894c9d495547d8f - url: "https://github.com/flame-engine/flame/" + url: "https://github.com/flame-engine/flame" source: git version: "0.11.0" flame_test: diff --git a/pubspec.yaml b/pubspec.yaml index dcfa6f3d..c62191a9 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -18,7 +18,7 @@ dependencies: flame_bloc: ^1.4.0 flame_forge2d: git: - url: https://github.com/flame-engine/flame/ + url: https://github.com/flame-engine/flame path: packages/flame_forge2d/ ref: a50d4a1e7d9eaf66726ed1bb9894c9d495547d8f flutter: diff --git a/test/game/view/pinball_game_page_test.dart b/test/game/view/pinball_game_page_test.dart index 5aef07dd..d1ecd72d 100644 --- a/test/game/view/pinball_game_page_test.dart +++ b/test/game/view/pinball_game_page_test.dart @@ -1,5 +1,7 @@ // ignore_for_file: prefer_const_constructors +import 'dart:ui'; + import 'package:bloc_test/bloc_test.dart'; import 'package:flame/game.dart'; import 'package:flutter/material.dart'; @@ -291,5 +293,43 @@ void main() { findsNothing, ); }); + + testWidgets('keep focus on game when mouse hovers over it', (tester) async { + final startGameState = StartGameState.initial().copyWith( + status: StartGameStatus.play, + ); + final gameState = GameState.initial().copyWith( + status: GameStatus.gameOver, + ); + + whenListen( + startGameBloc, + Stream.value(startGameState), + initialState: startGameState, + ); + whenListen( + gameBloc, + Stream.value(gameState), + initialState: gameState, + ); + await tester.pumpApp( + PinballGameView(game: game), + gameBloc: gameBloc, + startGameBloc: startGameBloc, + ); + + game.focusNode.unfocus(); + await tester.pump(); + + expect(game.focusNode.hasFocus, isFalse); + + final gesture = await tester.createGesture(kind: PointerDeviceKind.mouse); + await gesture.addPointer(location: Offset.zero); + addTearDown(gesture.removePointer); + await gesture.moveTo((game.size / 2).toOffset()); + await tester.pump(); + + expect(game.focusNode.hasFocus, isTrue); + }); }); }