Refactor menu structure and add Flutter-specific input and persistence layers
- Moved menu-related classes to a new structure under `src/menu/`. - Introduced `WolfMenuPresentation` to handle menu art and mappings. - Added `MenuManager` tests to ensure menu state reflects game status. - Implemented `FlutterRendererSettingsPersistence` and `FlutterSaveGamePersistence` for managing settings and save files on desktop platforms. - Created `Wolf3dFlutterInput` to handle keyboard and mouse input in a Flutter environment. - Updated README to reflect new package structure and usage instructions. Signed-off-by: Hans Kokx <hans.d.kokx@gmail.com>
This commit is contained in:
@@ -0,0 +1,167 @@
|
||||
import 'package:test/test.dart';
|
||||
import 'package:wolf_3d_dart/src/menu/menu_manager.dart';
|
||||
import 'package:wolf_3d_dart/wolf_3d_engine.dart';
|
||||
|
||||
void main() {
|
||||
group('MenuManager', () {
|
||||
test('main menu row enablement reflects resumable and loadable state', () {
|
||||
final manager = MenuManager();
|
||||
|
||||
manager.showMainMenu(hasResumableGame: false, hasLoadableSave: false);
|
||||
|
||||
expect(
|
||||
_entryFor(manager, WolfMenuMainAction.loadGame).isEnabled,
|
||||
isFalse,
|
||||
);
|
||||
expect(
|
||||
_entryFor(manager, WolfMenuMainAction.saveGame).isEnabled,
|
||||
isFalse,
|
||||
);
|
||||
expect(
|
||||
_entryFor(manager, WolfMenuMainAction.changeView).isEnabled,
|
||||
isTrue,
|
||||
);
|
||||
|
||||
manager.setLoadGameAvailable(true);
|
||||
|
||||
expect(_entryFor(manager, WolfMenuMainAction.loadGame).isEnabled, isTrue);
|
||||
expect(
|
||||
_entryFor(manager, WolfMenuMainAction.saveGame).isEnabled,
|
||||
isFalse,
|
||||
);
|
||||
|
||||
manager.showMainMenu(hasResumableGame: true, hasLoadableSave: true);
|
||||
|
||||
expect(_entryFor(manager, WolfMenuMainAction.saveGame).isEnabled, isTrue);
|
||||
expect(_entryFor(manager, WolfMenuMainAction.endGame).isEnabled, isTrue);
|
||||
expect(
|
||||
manager.mainMenuEntries.any(
|
||||
(entry) => entry.action == WolfMenuMainAction.backToGame,
|
||||
),
|
||||
isTrue,
|
||||
);
|
||||
});
|
||||
|
||||
test('change-view navigation skips disabled renderer rows and options', () {
|
||||
final manager = MenuManager();
|
||||
|
||||
manager.setChangeViewEntries(const <WolfMenuRendererEntry>[
|
||||
WolfMenuRendererEntry(
|
||||
mode: WolfRendererMode.software,
|
||||
label: 'SOFTWARE',
|
||||
hasOptions: false,
|
||||
isEnabled: false,
|
||||
),
|
||||
WolfMenuRendererEntry(
|
||||
mode: WolfRendererMode.ascii,
|
||||
label: 'ASCII',
|
||||
hasOptions: true,
|
||||
),
|
||||
]);
|
||||
manager.setRendererOptionEntries(
|
||||
title: 'ASCII OPTIONS',
|
||||
entries: const <WolfMenuRendererOptionEntry>[
|
||||
WolfMenuRendererOptionEntry(
|
||||
id: WolfRendererOptionId.asciiTheme,
|
||||
label: 'THEME',
|
||||
isEnabled: false,
|
||||
),
|
||||
WolfMenuRendererOptionEntry(
|
||||
id: WolfRendererOptionId.fpsCounter,
|
||||
label: 'FPS',
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
manager.showChangeViewMenu();
|
||||
|
||||
expect(manager.selectedChangeViewIndex, 1);
|
||||
|
||||
manager.updateChangeViewMenu(const EngineInput(isMovingBackward: true));
|
||||
manager.updateChangeViewMenu(const EngineInput());
|
||||
|
||||
expect(manager.selectedChangeViewIndex, 3);
|
||||
});
|
||||
|
||||
test(
|
||||
'renderer option selection is preserved by option id when entries refresh',
|
||||
() {
|
||||
final manager = MenuManager();
|
||||
|
||||
manager.setChangeViewEntries(const <WolfMenuRendererEntry>[
|
||||
WolfMenuRendererEntry(
|
||||
mode: WolfRendererMode.ascii,
|
||||
label: 'ASCII',
|
||||
hasOptions: true,
|
||||
),
|
||||
]);
|
||||
manager.setRendererOptionEntries(
|
||||
title: 'ASCII OPTIONS',
|
||||
entries: const <WolfMenuRendererOptionEntry>[
|
||||
WolfMenuRendererOptionEntry(
|
||||
id: WolfRendererOptionId.asciiTheme,
|
||||
label: 'THEME',
|
||||
),
|
||||
WolfMenuRendererOptionEntry(
|
||||
id: WolfRendererOptionId.fpsCounter,
|
||||
label: 'FPS',
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
manager.showRendererOptionsMenu();
|
||||
manager.updateRendererOptionsMenu(
|
||||
const EngineInput(isMovingBackward: true),
|
||||
);
|
||||
manager.updateRendererOptionsMenu(const EngineInput());
|
||||
|
||||
expect(manager.selectedRendererOptionIndex, 1);
|
||||
|
||||
manager.setRendererOptionEntries(
|
||||
title: 'ASCII OPTIONS',
|
||||
entries: const <WolfMenuRendererOptionEntry>[
|
||||
WolfMenuRendererOptionEntry(
|
||||
id: WolfRendererOptionId.fpsCounter,
|
||||
label: 'FPS',
|
||||
),
|
||||
WolfMenuRendererOptionEntry(
|
||||
id: WolfRendererOptionId.asciiTheme,
|
||||
label: 'THEME',
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
expect(manager.selectedRendererOptionIndex, 0);
|
||||
expect(
|
||||
manager.rendererOptionEntries[manager.selectedRendererOptionIndex].id,
|
||||
WolfRendererOptionId.fpsCounter,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
test('transition locks navigation and reports none when idle', () {
|
||||
final manager = MenuManager();
|
||||
|
||||
manager.showMainMenu(hasResumableGame: false);
|
||||
manager.startTransition(WolfMenuScreen.difficultySelect);
|
||||
|
||||
final result = manager.updateMainMenu(
|
||||
const EngineInput(isMovingBackward: true, isInteracting: true),
|
||||
);
|
||||
|
||||
expect(result.selected, isNull);
|
||||
expect(result.goBack, isFalse);
|
||||
expect(manager.transitionEffect, WolfTransitionEffect.normalFade);
|
||||
|
||||
manager.tickTransition(MenuManager.transitionDurationMs);
|
||||
|
||||
expect(manager.isTransitioning, isFalse);
|
||||
expect(manager.transitionEffect, WolfTransitionEffect.none);
|
||||
expect(manager.activeMenu, WolfMenuScreen.difficultySelect);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
WolfMenuMainEntry _entryFor(MenuManager manager, WolfMenuMainAction action) {
|
||||
return manager.mainMenuEntries.firstWhere((entry) => entry.action == action);
|
||||
}
|
||||
Reference in New Issue
Block a user