Files
wolf_dart/apps/wolf_3d_gui/lib/screens/sprite_gallery.dart
Hans Kokx 03dd871a46 feat: Implement game selection in sprite and VGA galleries with GalleryGameSelector
refactor: Update VgaGallery and SpriteGallery to use selected game data
chore: Remove unused plugins from generated plugin registrant and CMake files
chore: Clean up pubspec.yaml by removing super_clipboard dependency

Signed-off-by: Hans Kokx <hans.d.kokx@gmail.com>
2026-03-20 15:53:24 +01:00

156 lines
4.4 KiB
Dart

/// Visual browser for decoded sprite assets and their inferred gameplay roles.
library;
import 'package:flutter/material.dart';
import 'package:wolf_3d_dart/wolf_3d_data_types.dart';
import 'package:wolf_3d_dart/wolf_3d_entities.dart';
import 'package:wolf_3d_flutter/wolf_3d_flutter.dart';
import 'package:wolf_3d_gui/screens/gallery_game_selector.dart';
import 'package:wolf_3d_renderer/wolf_3d_asset_painter.dart';
/// Displays every sprite frame in the active game along with enemy metadata.
class SpriteGallery extends StatefulWidget {
/// Shared application facade used to access the active game's sprite set.
final Wolf3d wolf3d;
/// Creates the sprite gallery for [wolf3d].
const SpriteGallery({super.key, required this.wolf3d});
@override
State<SpriteGallery> createState() => _SpriteGalleryState();
}
class _SpriteGalleryState extends State<SpriteGallery> {
late WolfensteinData _selectedGame;
@override
void initState() {
super.initState();
_selectedGame =
widget.wolf3d.maybeActiveGame ?? widget.wolf3d.availableGames.first;
}
bool get isShareware => _selectedGame.version == GameVersion.shareware;
List<Sprite> get _sprites => _selectedGame.sprites;
void _selectGame(WolfensteinData game) {
if (identical(_selectedGame, game)) {
return;
}
widget.wolf3d.setActiveGame(game);
setState(() {
_selectedGame = game;
});
}
String _buildSpriteLabel(int index) {
String label = 'Sprite Index: $index';
for (final enemy in EnemyType.values) {
// The gallery infers likely ownership from sprite index ranges so
// debugging art packs does not require cross-referencing source.
if (enemy.claimsSpriteIndex(index, isShareware: isShareware)) {
final EnemyAnimation? animation = enemy.getAnimationFromSprite(
index,
isShareware: isShareware,
);
label += '\n${enemy.name}';
if (animation != null) {
label += '\n${animation.name}';
}
break;
}
}
return label;
}
void _showImagePreviewDialog(BuildContext context, int index) {
final Sprite sprite = _sprites[index];
showDialog<void>(
context: context,
builder: (dialogContext) {
return AlertDialog(
title: Text(
'${_buildSpriteLabel(index)}\n64 x 64',
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
),
),
actions: [
TextButton(
onPressed: () => Navigator.of(dialogContext).pop(),
child: const Text('Close'),
),
],
content: Center(
child: AspectRatio(
aspectRatio: 1,
child: WolfAssetPainter.sprite(sprite),
),
),
);
},
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Sprite Gallery'),
automaticallyImplyLeading: true,
actions: [
if (widget.wolf3d.availableGames.length > 1)
GalleryGameSelector(
wolf3d: widget.wolf3d,
selectedGame: _selectedGame,
onSelected: _selectGame,
),
],
),
backgroundColor: Colors.black,
body: GridView.builder(
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 8,
crossAxisSpacing: 8,
mainAxisSpacing: 8,
),
itemCount: _sprites.length,
itemBuilder: (context, index) {
final String label = _buildSpriteLabel(index);
return Card(
color: Colors.blueGrey,
child: InkWell(
onTap: () => _showImagePreviewDialog(context, index),
child: Column(
spacing: 8,
children: [
Text(
label,
style: const TextStyle(color: Colors.white, fontSize: 10),
textAlign: TextAlign.center,
),
Expanded(
child: Center(
child: AspectRatio(
aspectRatio: 1,
child: WolfAssetPainter.sprite(_sprites[index]),
),
),
),
],
),
),
);
},
),
);
}
}