Moved all widgets and logic from gui app to Flutter package

- Implemented DebugToolsScreen for navigation to asset galleries.
- Created GameScreen to manage gameplay and renderer integrations.
- Added NoGameDataScreen to handle scenarios with missing game data.
- Developed SpriteGallery for visual browsing of sprite assets.
- Introduced VgaGallery for displaying VGA images from game data.
- Added GalleryGameSelector widget for selecting game variants in galleries.
- Created Wolf3dApp as the main application shell for managing game states.
- Implemented WolfMenuShell for consistent menu layouts across screens.
- Enhanced Wolf3d class to support debug mode and related functionalities.
- Updated pubspec.yaml to include window_manager dependency.
- Added tests for game screen lifecycle and debug mode functionalities.

Signed-off-by: Hans Kokx <hans.d.kokx@gmail.com>
This commit is contained in:
2026-03-23 18:44:32 +01:00
parent cbe2633ceb
commit 5a2681e89b
21 changed files with 963 additions and 590 deletions
@@ -0,0 +1,128 @@
/// Visual browser for decoded VGA pictures and UI art.
library;
import 'package:flutter/material.dart';
import 'package:wolf_3d_dart/wolf_3d_data_types.dart';
import 'package:wolf_3d_flutter/renderer/wolf_3d_asset_painter.dart';
import 'package:wolf_3d_flutter/wolf_3d_flutter.dart';
/// Shows each VGA image extracted from the currently selected game data set.
class VgaGallery extends StatefulWidget {
/// Shared app facade used to access available game data sets.
final Wolf3d wolf3d;
/// Creates the gallery for the currently selected or browsed game.
const VgaGallery({super.key, required this.wolf3d});
@override
State<VgaGallery> createState() => _VgaGalleryState();
}
class _VgaGalleryState extends State<VgaGallery> {
late WolfensteinData _selectedGame;
List<VgaImage> get _images => _selectedGame.vgaImages;
@override
void initState() {
super.initState();
_selectedGame =
widget.wolf3d.maybeActiveGame ?? widget.wolf3d.availableGames.first;
}
void _selectGame(WolfensteinData game) {
if (identical(_selectedGame, game)) {
return;
}
widget.wolf3d.setActiveGame(game);
setState(() {
_selectedGame = game;
});
}
void _showImagePreviewDialog(BuildContext context, int index) {
final VgaImage image = _images[index];
showDialog<void>(
context: context,
builder: (dialogContext) {
return AlertDialog(
title: Text(
'Index: $index ${image.width} x ${image.height}',
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
),
),
actions: [
TextButton(
onPressed: () => Navigator.of(dialogContext).pop(),
child: const Text('Close'),
),
],
content: Center(
child: AspectRatio(
aspectRatio: image.width / image.height,
child: WolfAssetPainter.vga(image),
),
),
);
},
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('VGA Image Gallery'),
actions: [
if (widget.wolf3d.availableGames.length > 1)
GalleryGameSelector(
wolf3d: widget.wolf3d,
selectedGame: _selectedGame,
onSelected: _selectGame,
),
],
),
backgroundColor: Colors.black,
body: GridView.builder(
gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 150,
crossAxisSpacing: 8,
mainAxisSpacing: 8,
),
itemCount: _images.length,
itemBuilder: (context, index) {
return Card(
color: Colors.blueGrey,
child: InkWell(
onTap: () => _showImagePreviewDialog(context, index),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
spacing: 8,
children: [
Text(
'Index: $index\n${_images[index].width} x ${_images[index].height}',
style: const TextStyle(color: Colors.white, fontSize: 12),
textAlign: TextAlign.center,
),
Expanded(
child: Center(
child: AspectRatio(
aspectRatio:
_images[index].width / _images[index].height,
child: WolfAssetPainter.vga(_images[index]),
),
),
),
],
),
),
);
},
),
);
}
}