From 1394c20134c95183037c1f8b805ea77b50e0e2cf Mon Sep 17 00:00:00 2001 From: Hans Kokx Date: Mon, 23 Mar 2026 19:38:34 +0100 Subject: [PATCH] feat: Add GameDataPickerManager for managing game data selection flows Signed-off-by: Hans Kokx --- .../managers/game_data_picker_manager.dart | 109 ++++++++++++++++++ .../lib/managers/wolf3d_app_manager.dart | 95 ++------------- .../wolf_3d_flutter/lib/wolf_3d_flutter.dart | 3 +- 3 files changed, 118 insertions(+), 89 deletions(-) create mode 100644 packages/wolf_3d_flutter/lib/managers/game_data_picker_manager.dart diff --git a/packages/wolf_3d_flutter/lib/managers/game_data_picker_manager.dart b/packages/wolf_3d_flutter/lib/managers/game_data_picker_manager.dart new file mode 100644 index 0000000..5625c1c --- /dev/null +++ b/packages/wolf_3d_flutter/lib/managers/game_data_picker_manager.dart @@ -0,0 +1,109 @@ +library; + +import 'dart:collection'; + +import 'package:file_selector/file_selector.dart'; +import 'package:wolf_3d_flutter/wolf_3d_flutter.dart'; + +/// Coordinates game-data selection flows for the no-data setup screen. +class GameDataPickerManager { + GameDataPickerManager({required this.engine}); + + final Wolf3dFlutterEngine engine; + + bool _isLoadingGameData = false; + String? _pickerError; + + bool get isLoadingGameData => _isLoadingGameData; + String? get pickerError => _pickerError; + + Future pickGameDataDirectory({ + required void Function() notifyChanged, + }) async { + _isLoadingGameData = true; + _pickerError = null; + notifyChanged(); + + try { + final String? directoryPath = await getDirectoryPath( + confirmButtonText: 'Use this folder', + ); + + if (directoryPath == null || directoryPath.trim().isEmpty) { + return; + } + + await engine.init(directory: directoryPath.trim()); + } catch (error) { + _pickerError = 'Unable to load selected directory: $error'; + } finally { + _isLoadingGameData = false; + notifyChanged(); + } + } + + Future pickGameDataFiles({ + required void Function() notifyChanged, + }) async { + _isLoadingGameData = true; + _pickerError = null; + notifyChanged(); + + try { + final List selectedFiles = await openFiles(); + + if (selectedFiles.isEmpty) { + return; + } + + final LinkedHashSet directories = LinkedHashSet(); + for (final XFile file in selectedFiles) { + final String directory = _directoryFromFilePath(file.path); + if (directory.isNotEmpty) { + directories.add(directory); + } + } + + if (directories.isEmpty) { + _pickerError = 'Selected files do not expose local filesystem paths.'; + return; + } + + final List orderedDirectories = directories.toList( + growable: false, + ); + await engine.init( + directory: orderedDirectories.first, + additionalDirectories: orderedDirectories.skip(1), + ); + } catch (error) { + _pickerError = 'Unable to load selected files: $error'; + } finally { + _isLoadingGameData = false; + notifyChanged(); + } + } + + String _directoryFromFilePath(String path) { + final String trimmedPath = path.trim(); + if (trimmedPath.isEmpty) { + return ''; + } + + final int slashIndex = trimmedPath.lastIndexOf('/'); + final int backslashIndex = trimmedPath.lastIndexOf(r'\'); + final int separatorIndex = slashIndex > backslashIndex + ? slashIndex + : backslashIndex; + + if (separatorIndex < 0) { + return ''; + } + + if (separatorIndex == 0) { + return trimmedPath[0]; + } + + return trimmedPath.substring(0, separatorIndex); + } +} diff --git a/packages/wolf_3d_flutter/lib/managers/wolf3d_app_manager.dart b/packages/wolf_3d_flutter/lib/managers/wolf3d_app_manager.dart index 2d54e5c..7fd1083 100644 --- a/packages/wolf_3d_flutter/lib/managers/wolf3d_app_manager.dart +++ b/packages/wolf_3d_flutter/lib/managers/wolf3d_app_manager.dart @@ -1,9 +1,7 @@ library; import 'dart:async'; -import 'dart:collection'; -import 'package:file_selector/file_selector.dart'; import 'package:wolf_3d_flutter/wolf_3d_flutter.dart'; /// Coordinates app-shell setup actions and shutdown behavior. @@ -11,13 +9,13 @@ class Wolf3dAppManager { Wolf3dAppManager({required this.engine}); final Wolf3dFlutterEngine engine; - - bool _isLoadingGameData = false; - String? _pickerError; + late final GameDataPickerManager _pickerManager = GameDataPickerManager( + engine: engine, + ); Future? _shutdownFuture; - bool get isLoadingGameData => _isLoadingGameData; - String? get pickerError => _pickerError; + bool get isLoadingGameData => _pickerManager.isLoadingGameData; + String? get pickerError => _pickerManager.pickerError; Future ensureAudioShutdown() { final existing = _shutdownFuture; @@ -33,91 +31,12 @@ class Wolf3dAppManager { Future pickGameDataDirectory({ required void Function() notifyChanged, }) async { - _isLoadingGameData = true; - _pickerError = null; - notifyChanged(); - - try { - final String? directoryPath = await getDirectoryPath( - confirmButtonText: 'Use this folder', - ); - - if (directoryPath == null || directoryPath.trim().isEmpty) { - return; - } - - await engine.init(directory: directoryPath.trim()); - } catch (error) { - _pickerError = 'Unable to load selected directory: $error'; - } finally { - _isLoadingGameData = false; - notifyChanged(); - } + await _pickerManager.pickGameDataDirectory(notifyChanged: notifyChanged); } Future pickGameDataFiles({ required void Function() notifyChanged, }) async { - _isLoadingGameData = true; - _pickerError = null; - notifyChanged(); - - try { - final List selectedFiles = await openFiles(); - - if (selectedFiles.isEmpty) { - return; - } - - final LinkedHashSet directories = LinkedHashSet(); - for (final XFile file in selectedFiles) { - final String directory = _directoryFromFilePath(file.path); - if (directory.isNotEmpty) { - directories.add(directory); - } - } - - if (directories.isEmpty) { - _pickerError = - 'Selected files do not expose local filesystem paths.'; - return; - } - - final List orderedDirectories = directories.toList( - growable: false, - ); - await engine.init( - directory: orderedDirectories.first, - additionalDirectories: orderedDirectories.skip(1), - ); - } catch (error) { - _pickerError = 'Unable to load selected files: $error'; - } finally { - _isLoadingGameData = false; - notifyChanged(); - } - } - - String _directoryFromFilePath(String path) { - final String trimmedPath = path.trim(); - if (trimmedPath.isEmpty) { - return ''; - } - - final int slashIndex = trimmedPath.lastIndexOf('/'); - final int backslashIndex = trimmedPath.lastIndexOf(r'\'); - final int separatorIndex = slashIndex > backslashIndex - ? slashIndex - : backslashIndex; - - if (separatorIndex < 0) { - return ''; - } - - if (separatorIndex == 0) { - return trimmedPath[0]; - } - - return trimmedPath.substring(0, separatorIndex); + await _pickerManager.pickGameDataFiles(notifyChanged: notifyChanged); } } diff --git a/packages/wolf_3d_flutter/lib/wolf_3d_flutter.dart b/packages/wolf_3d_flutter/lib/wolf_3d_flutter.dart index 7c6339e..2e3eeb8 100644 --- a/packages/wolf_3d_flutter/lib/wolf_3d_flutter.dart +++ b/packages/wolf_3d_flutter/lib/wolf_3d_flutter.dart @@ -18,6 +18,7 @@ export 'audio/wolf3d_platform_audio.dart' show Wolf3dPlatformAudio; export 'managers/game_app_lifecycle_manager.dart' show GameAppLifecycleManager; export 'managers/game_data_directory_persistence.dart' show DefaultGameDataDirectoryPersistence; +export 'managers/game_data_picker_manager.dart' show GameDataPickerManager; export 'managers/game_display_manager.dart' show GameDisplayManager; export 'managers/game_persistence_manager.dart' show GamePersistenceManager; export 'managers/game_renderer_mode_manager.dart' @@ -30,7 +31,7 @@ export 'managers/game_screen_input_manager.dart' HostShortcutRegistry, GameScreenInputManager, isAltEnterShortcut; - export 'managers/wolf3d_app_manager.dart' show Wolf3dAppManager; +export 'managers/wolf3d_app_manager.dart' show Wolf3dAppManager; export 'screens/audio_gallery.dart' show AudioGallery; export 'screens/debug_tools_screen.dart' show DebugToolsScreen; export 'screens/game_screen.dart' show GameScreen;