Clean up troubleshooter
This commit is contained in:
@@ -1,12 +1,11 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:rimworld_modman/mod.dart';
|
|
||||||
import 'package:rimworld_modman/mod_list.dart';
|
import 'package:rimworld_modman/mod_list.dart';
|
||||||
import 'package:rimworld_modman/mod_list_troubleshooter.dart';
|
import 'package:rimworld_modman/mod_list_troubleshooter.dart';
|
||||||
|
|
||||||
import 'main.dart';
|
import 'main.dart';
|
||||||
|
|
||||||
/// A widget that provides a user interface for the mod troubleshooter functionality.
|
/// A widget that provides a user interface for the mod troubleshooter functionality.
|
||||||
///
|
///
|
||||||
/// This allows users to:
|
/// This allows users to:
|
||||||
/// - Toggle between binary and linear search modes
|
/// - Toggle between binary and linear search modes
|
||||||
/// - Navigate forward and backward through mod sets
|
/// - Navigate forward and backward through mod sets
|
||||||
@@ -17,74 +16,77 @@ class ModTroubleshooterWidget extends StatefulWidget {
|
|||||||
const ModTroubleshooterWidget({super.key});
|
const ModTroubleshooterWidget({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<ModTroubleshooterWidget> createState() => _ModTroubleshooterWidgetState();
|
State<ModTroubleshooterWidget> createState() =>
|
||||||
|
_ModTroubleshooterWidgetState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _ModTroubleshooterWidgetState extends State<ModTroubleshooterWidget> {
|
class _ModTroubleshooterWidgetState extends State<ModTroubleshooterWidget> {
|
||||||
late ModListTroubleshooter _troubleshooter;
|
late ModListTroubleshooter _troubleshooter;
|
||||||
|
|
||||||
bool _isInitialized = false;
|
bool _isInitialized = false;
|
||||||
bool _isBinaryMode = false;
|
bool _isBinaryMode = false;
|
||||||
int _stepSize = 10;
|
int _stepSize = 10;
|
||||||
|
|
||||||
// Set of mod IDs that have been checked and confirmed to be good
|
// Set of mod IDs that have been checked and confirmed to be good
|
||||||
final Set<String> _checkedMods = {};
|
final Set<String> _checkedMods = {};
|
||||||
|
|
||||||
// Set of mod IDs that are suspected to cause issues
|
// Set of mod IDs that are suspected to cause issues
|
||||||
final Set<String> _problemMods = {};
|
final Set<String> _problemMods = {};
|
||||||
|
|
||||||
// The currently selected mod IDs (for highlighting)
|
// The currently selected mod IDs (for highlighting)
|
||||||
List<String> _selectedMods = [];
|
List<String> _selectedMods = [];
|
||||||
|
|
||||||
// The next potential set of mods (from move calculation)
|
// The next potential set of mods (from move calculation)
|
||||||
Move? _nextForwardMove;
|
Move? _nextForwardMove;
|
||||||
Move? _nextBackwardMove;
|
Move? _nextBackwardMove;
|
||||||
|
|
||||||
// Controller for step size input
|
// Controller for step size input
|
||||||
late TextEditingController _stepSizeController;
|
late TextEditingController _stepSizeController;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_stepSizeController = TextEditingController(text: _stepSize.toString());
|
_stepSizeController = TextEditingController(text: _stepSize.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
_stepSizeController.dispose();
|
_stepSizeController.dispose();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
void _initialize() {
|
void _initialize() {
|
||||||
if (_isInitialized) return;
|
if (_isInitialized) return;
|
||||||
|
|
||||||
// Initialize the troubleshooter with the global mod manager
|
// Initialize the troubleshooter with the global mod manager
|
||||||
_troubleshooter = ModListTroubleshooter(modManager);
|
_troubleshooter = ModListTroubleshooter(modManager);
|
||||||
|
|
||||||
// Set initial active mods for highlighting
|
// Set initial active mods for highlighting
|
||||||
if (modManager.activeMods.isNotEmpty) {
|
if (modManager.activeMods.isNotEmpty) {
|
||||||
// Initially select all active mods
|
// Initially select all active mods
|
||||||
_selectedMods = List.from(modManager.activeMods.keys);
|
_selectedMods = List.from(modManager.activeMods.keys);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate initial moves
|
// Calculate initial moves
|
||||||
_updateNextMoves();
|
_updateNextMoves();
|
||||||
|
|
||||||
setState(() {
|
setState(() {
|
||||||
_isInitialized = true;
|
_isInitialized = true;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void _updateNextMoves() {
|
void _updateNextMoves() {
|
||||||
if (_isBinaryMode) {
|
if (_isBinaryMode) {
|
||||||
_nextForwardMove = _troubleshooter.binaryForwardMove();
|
_nextForwardMove = _troubleshooter.binaryForwardMove();
|
||||||
_nextBackwardMove = _troubleshooter.binaryBackwardMove();
|
_nextBackwardMove = _troubleshooter.binaryBackwardMove();
|
||||||
} else {
|
} else {
|
||||||
_nextForwardMove = _troubleshooter.linearForwardMove(stepSize: _stepSize);
|
_nextForwardMove = _troubleshooter.linearForwardMove(stepSize: _stepSize);
|
||||||
_nextBackwardMove = _troubleshooter.linearBackwardMove(stepSize: _stepSize);
|
_nextBackwardMove = _troubleshooter.linearBackwardMove(
|
||||||
|
stepSize: _stepSize,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _navigateForward() {
|
void _navigateForward() {
|
||||||
ModList result;
|
ModList result;
|
||||||
if (_isBinaryMode) {
|
if (_isBinaryMode) {
|
||||||
@@ -92,17 +94,17 @@ class _ModTroubleshooterWidgetState extends State<ModTroubleshooterWidget> {
|
|||||||
} else {
|
} else {
|
||||||
result = _troubleshooter.linearForward(stepSize: _stepSize);
|
result = _troubleshooter.linearForward(stepSize: _stepSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load all required dependencies for the selected mods
|
// Load all required dependencies for the selected mods
|
||||||
final loadOrder = result.loadRequired();
|
final loadOrder = result.loadRequiredBaseGame();
|
||||||
|
|
||||||
// Use the mods from the load order result
|
// Use the mods from the load order result
|
||||||
setState(() {
|
setState(() {
|
||||||
_selectedMods = loadOrder.loadOrder;
|
_selectedMods = loadOrder.loadOrder;
|
||||||
_updateNextMoves();
|
_updateNextMoves();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void _navigateBackward() {
|
void _navigateBackward() {
|
||||||
ModList result;
|
ModList result;
|
||||||
if (_isBinaryMode) {
|
if (_isBinaryMode) {
|
||||||
@@ -110,45 +112,38 @@ class _ModTroubleshooterWidgetState extends State<ModTroubleshooterWidget> {
|
|||||||
} else {
|
} else {
|
||||||
result = _troubleshooter.linearBackward(stepSize: _stepSize);
|
result = _troubleshooter.linearBackward(stepSize: _stepSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load all required dependencies for the selected mods
|
// Load all required dependencies for the selected mods
|
||||||
final loadOrder = result.loadRequired();
|
final loadOrder = result.loadRequiredBaseGame();
|
||||||
|
|
||||||
// Use the mods from the load order result
|
// Use the mods from the load order result
|
||||||
setState(() {
|
setState(() {
|
||||||
_selectedMods = loadOrder.loadOrder;
|
_selectedMods = loadOrder.loadOrder;
|
||||||
_updateNextMoves();
|
_updateNextMoves();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void _toggleSearchMode() {
|
|
||||||
setState(() {
|
|
||||||
_isBinaryMode = !_isBinaryMode;
|
|
||||||
_updateNextMoves();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void _markAsGood(String modId) {
|
void _markAsGood(String modId) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_checkedMods.add(modId);
|
_checkedMods.add(modId);
|
||||||
_problemMods.remove(modId);
|
_problemMods.remove(modId);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void _markAsProblem(String modId) {
|
void _markAsProblem(String modId) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_problemMods.add(modId);
|
_problemMods.add(modId);
|
||||||
_checkedMods.remove(modId);
|
_checkedMods.remove(modId);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void _clearMarks(String modId) {
|
void _clearMarks(String modId) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_checkedMods.remove(modId);
|
_checkedMods.remove(modId);
|
||||||
_problemMods.remove(modId);
|
_problemMods.remove(modId);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void _resetTroubleshooter() {
|
void _resetTroubleshooter() {
|
||||||
setState(() {
|
setState(() {
|
||||||
_checkedMods.clear();
|
_checkedMods.clear();
|
||||||
@@ -157,7 +152,7 @@ class _ModTroubleshooterWidgetState extends State<ModTroubleshooterWidget> {
|
|||||||
});
|
});
|
||||||
_initialize();
|
_initialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
void _saveTroubleshootingConfig() {
|
void _saveTroubleshootingConfig() {
|
||||||
// Only save if we have a valid selection
|
// Only save if we have a valid selection
|
||||||
if (_selectedMods.isEmpty) {
|
if (_selectedMods.isEmpty) {
|
||||||
@@ -169,23 +164,25 @@ class _ModTroubleshooterWidgetState extends State<ModTroubleshooterWidget> {
|
|||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// First disable all mods
|
// First disable all mods
|
||||||
modManager.disableAll();
|
modManager.disableAll();
|
||||||
|
|
||||||
// Then enable only the selected mods
|
// Then enable only the selected mods
|
||||||
modManager.enableMods(_selectedMods);
|
modManager.enableMods(_selectedMods);
|
||||||
|
|
||||||
// Save the configuration (we don't have direct access to save method, so show a message)
|
// Save the configuration (we don't have direct access to save method, so show a message)
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
SnackBar(
|
SnackBar(
|
||||||
content: Text('${_selectedMods.length} mods prepared for testing. Please use Save button in the Mods tab to save config.'),
|
content: Text(
|
||||||
|
'${_selectedMods.length} mods prepared for testing. Please use Save button in the Mods tab to save config.',
|
||||||
|
),
|
||||||
backgroundColor: Colors.orange,
|
backgroundColor: Colors.orange,
|
||||||
duration: const Duration(seconds: 4),
|
duration: const Duration(seconds: 4),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _markSelectedAsGood() {
|
void _markSelectedAsGood() {
|
||||||
if (_selectedMods.isEmpty) {
|
if (_selectedMods.isEmpty) {
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
@@ -196,14 +193,14 @@ class _ModTroubleshooterWidgetState extends State<ModTroubleshooterWidget> {
|
|||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
setState(() {
|
setState(() {
|
||||||
for (final modId in _selectedMods) {
|
for (final modId in _selectedMods) {
|
||||||
_checkedMods.add(modId);
|
_checkedMods.add(modId);
|
||||||
_problemMods.remove(modId);
|
_problemMods.remove(modId);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
SnackBar(
|
SnackBar(
|
||||||
content: Text('Marked ${_selectedMods.length} mods as good'),
|
content: Text('Marked ${_selectedMods.length} mods as good'),
|
||||||
@@ -212,7 +209,7 @@ class _ModTroubleshooterWidgetState extends State<ModTroubleshooterWidget> {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _markSelectedAsProblem() {
|
void _markSelectedAsProblem() {
|
||||||
if (_selectedMods.isEmpty) {
|
if (_selectedMods.isEmpty) {
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
@@ -223,14 +220,14 @@ class _ModTroubleshooterWidgetState extends State<ModTroubleshooterWidget> {
|
|||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
setState(() {
|
setState(() {
|
||||||
for (final modId in _selectedMods) {
|
for (final modId in _selectedMods) {
|
||||||
_problemMods.add(modId);
|
_problemMods.add(modId);
|
||||||
_checkedMods.remove(modId);
|
_checkedMods.remove(modId);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
SnackBar(
|
SnackBar(
|
||||||
content: Text('Marked ${_selectedMods.length} mods as problematic'),
|
content: Text('Marked ${_selectedMods.length} mods as problematic'),
|
||||||
@@ -239,34 +236,32 @@ class _ModTroubleshooterWidgetState extends State<ModTroubleshooterWidget> {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
// Make sure we're initialized
|
// Make sure we're initialized
|
||||||
if (!_isInitialized) {
|
if (!_isInitialized) {
|
||||||
_initialize();
|
_initialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_isInitialized || modManager.mods.isEmpty) {
|
if (!_isInitialized || modManager.mods.isEmpty) {
|
||||||
return _buildEmptyState();
|
return _buildEmptyState();
|
||||||
}
|
}
|
||||||
|
|
||||||
return Column(
|
return Column(
|
||||||
children: [
|
children: [_buildControlPanel(), Expanded(child: _buildModList())],
|
||||||
_buildControlPanel(),
|
|
||||||
Expanded(
|
|
||||||
child: _buildModList(),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildEmptyState() {
|
Widget _buildEmptyState() {
|
||||||
return Center(
|
return Center(
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
Icon(Icons.build, size: AppThemeExtension.of(context).iconSizeLarge * 2),
|
Icon(
|
||||||
|
Icons.build,
|
||||||
|
size: AppThemeExtension.of(context).iconSizeLarge * 2,
|
||||||
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
Text(
|
Text(
|
||||||
'Troubleshooting',
|
'Troubleshooting',
|
||||||
@@ -298,57 +293,7 @@ class _ModTroubleshooterWidgetState extends State<ModTroubleshooterWidget> {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildInstructionsPanel() {
|
|
||||||
return Card(
|
|
||||||
margin: AppThemeExtension.of(context).paddingRegular,
|
|
||||||
child: Padding(
|
|
||||||
padding: AppThemeExtension.of(context).paddingRegular,
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
'Mod Troubleshooter',
|
|
||||||
style: Theme.of(context).textTheme.titleLarge,
|
|
||||||
),
|
|
||||||
const SizedBox(height: 8),
|
|
||||||
Text(
|
|
||||||
'This tool helps you find problematic mods by testing different combinations. '
|
|
||||||
'Follow these steps:',
|
|
||||||
style: Theme.of(context).textTheme.bodyMedium,
|
|
||||||
),
|
|
||||||
const SizedBox(height: 8),
|
|
||||||
Text(
|
|
||||||
'1. Start RimWorld with the highlighted mods active',
|
|
||||||
style: Theme.of(context).textTheme.bodyMedium,
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
'2. If the game works correctly, mark those mods as "Good"',
|
|
||||||
style: Theme.of(context).textTheme.bodyMedium,
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
'3. If the problem occurs, use "Forward" or "Backward" to narrow down the problem',
|
|
||||||
style: Theme.of(context).textTheme.bodyMedium,
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
'4. Mods marked as "Problem" are more likely to be causing issues',
|
|
||||||
style: Theme.of(context).textTheme.bodyMedium,
|
|
||||||
),
|
|
||||||
const SizedBox(height: 8),
|
|
||||||
if (_selectedMods.isNotEmpty)
|
|
||||||
Text(
|
|
||||||
'Currently testing ${_selectedMods.length} mods',
|
|
||||||
style: TextStyle(
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: AppThemeExtension.of(context).enabledModColor,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildControlPanel() {
|
Widget _buildControlPanel() {
|
||||||
return Card(
|
return Card(
|
||||||
margin: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 4.0),
|
margin: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 4.0),
|
||||||
@@ -362,7 +307,7 @@ class _ModTroubleshooterWidgetState extends State<ModTroubleshooterWidget> {
|
|||||||
// Compact instruction
|
// Compact instruction
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Text(
|
child: Text(
|
||||||
_selectedMods.isNotEmpty
|
_selectedMods.isNotEmpty
|
||||||
? 'Testing ${_selectedMods.length} mods. Tap highlighted mods to navigate. Mark results below:'
|
? 'Testing ${_selectedMods.length} mods. Tap highlighted mods to navigate. Mark results below:'
|
||||||
: 'Click highlighted mods to begin testing. Blue→forward, purple←backward.',
|
: 'Click highlighted mods to begin testing. Blue→forward, purple←backward.',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
@@ -373,16 +318,13 @@ class _ModTroubleshooterWidgetState extends State<ModTroubleshooterWidget> {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
|
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
// Binary/Linear mode toggle
|
// Binary/Linear mode toggle
|
||||||
Text(
|
Text('Mode:', style: Theme.of(context).textTheme.bodyMedium),
|
||||||
'Mode:',
|
|
||||||
style: Theme.of(context).textTheme.bodyMedium,
|
|
||||||
),
|
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
ToggleButtons(
|
ToggleButtons(
|
||||||
isSelected: [!_isBinaryMode, _isBinaryMode],
|
isSelected: [!_isBinaryMode, _isBinaryMode],
|
||||||
@@ -403,14 +345,11 @@ class _ModTroubleshooterWidgetState extends State<ModTroubleshooterWidget> {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
||||||
// Step size input field (only for linear mode)
|
// Step size input field (only for linear mode)
|
||||||
if (!_isBinaryMode) ...[
|
if (!_isBinaryMode) ...[
|
||||||
const SizedBox(width: 16),
|
const SizedBox(width: 16),
|
||||||
Text(
|
Text('Step:', style: Theme.of(context).textTheme.bodyMedium),
|
||||||
'Step:',
|
|
||||||
style: Theme.of(context).textTheme.bodyMedium,
|
|
||||||
),
|
|
||||||
const SizedBox(width: 4),
|
const SizedBox(width: 4),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
width: 60,
|
width: 60,
|
||||||
@@ -436,41 +375,58 @@ class _ModTroubleshooterWidgetState extends State<ModTroubleshooterWidget> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
|
|
||||||
// Buttons to mark selected mods
|
// Buttons to mark selected mods
|
||||||
if (_selectedMods.isNotEmpty) ...[
|
if (_selectedMods.isNotEmpty) ...[
|
||||||
OutlinedButton.icon(
|
OutlinedButton.icon(
|
||||||
icon: Icon(Icons.error, color: Colors.red.shade300, size: 16),
|
icon: Icon(
|
||||||
|
Icons.error,
|
||||||
|
color: Colors.red.shade300,
|
||||||
|
size: 16,
|
||||||
|
),
|
||||||
label: const Text('Problem'),
|
label: const Text('Problem'),
|
||||||
style: OutlinedButton.styleFrom(
|
style: OutlinedButton.styleFrom(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 0),
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 8,
|
||||||
|
vertical: 0,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
onPressed: _markSelectedAsProblem,
|
onPressed: _markSelectedAsProblem,
|
||||||
),
|
),
|
||||||
const SizedBox(width: 4),
|
const SizedBox(width: 4),
|
||||||
OutlinedButton.icon(
|
OutlinedButton.icon(
|
||||||
icon: Icon(Icons.check_circle, color: Colors.green.shade300, size: 16),
|
icon: Icon(
|
||||||
|
Icons.check_circle,
|
||||||
|
color: Colors.green.shade300,
|
||||||
|
size: 16,
|
||||||
|
),
|
||||||
label: const Text('Good'),
|
label: const Text('Good'),
|
||||||
style: OutlinedButton.styleFrom(
|
style: OutlinedButton.styleFrom(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 0),
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 8,
|
||||||
|
vertical: 0,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
onPressed: _markSelectedAsGood,
|
onPressed: _markSelectedAsGood,
|
||||||
),
|
),
|
||||||
const SizedBox(width: 4),
|
const SizedBox(width: 4),
|
||||||
],
|
],
|
||||||
|
|
||||||
// Reset button
|
// Reset button
|
||||||
OutlinedButton.icon(
|
OutlinedButton.icon(
|
||||||
icon: const Icon(Icons.refresh, size: 16),
|
icon: const Icon(Icons.refresh, size: 16),
|
||||||
label: const Text('Reset'),
|
label: const Text('Reset'),
|
||||||
style: OutlinedButton.styleFrom(
|
style: OutlinedButton.styleFrom(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 0),
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 8,
|
||||||
|
vertical: 0,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
onPressed: _resetTroubleshooter,
|
onPressed: _resetTroubleshooter,
|
||||||
),
|
),
|
||||||
|
|
||||||
if (_selectedMods.isNotEmpty) ...[
|
if (_selectedMods.isNotEmpty) ...[
|
||||||
const SizedBox(width: 4),
|
const SizedBox(width: 4),
|
||||||
// Save config button
|
// Save config button
|
||||||
@@ -478,7 +434,10 @@ class _ModTroubleshooterWidgetState extends State<ModTroubleshooterWidget> {
|
|||||||
icon: const Icon(Icons.save, size: 16),
|
icon: const Icon(Icons.save, size: 16),
|
||||||
label: const Text('Save'),
|
label: const Text('Save'),
|
||||||
style: OutlinedButton.styleFrom(
|
style: OutlinedButton.styleFrom(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 0),
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 8,
|
||||||
|
vertical: 0,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
onPressed: _saveTroubleshootingConfig,
|
onPressed: _saveTroubleshootingConfig,
|
||||||
),
|
),
|
||||||
@@ -490,11 +449,11 @@ class _ModTroubleshooterWidgetState extends State<ModTroubleshooterWidget> {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildModList() {
|
Widget _buildModList() {
|
||||||
// Get the original mod order from mod manager
|
// Get the original mod order from mod manager
|
||||||
final fullModList = modManager.activeMods.keys.toList();
|
final fullModList = modManager.activeMods.keys.toList();
|
||||||
|
|
||||||
return Card(
|
return Card(
|
||||||
margin: AppThemeExtension.of(context).paddingRegular,
|
margin: AppThemeExtension.of(context).paddingRegular,
|
||||||
child: Column(
|
child: Column(
|
||||||
@@ -529,28 +488,32 @@ class _ModTroubleshooterWidgetState extends State<ModTroubleshooterWidget> {
|
|||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
final modId = fullModList[index];
|
final modId = fullModList[index];
|
||||||
final mod = modManager.mods[modId];
|
final mod = modManager.mods[modId];
|
||||||
|
|
||||||
if (mod == null) return const SizedBox.shrink();
|
if (mod == null) return const SizedBox.shrink();
|
||||||
|
|
||||||
// Determine if this mod is in the selection range for highlighted navigation
|
// Determine if this mod is in the selection range for highlighted navigation
|
||||||
final bool isSelected = _selectedMods.contains(modId);
|
final bool isSelected = _selectedMods.contains(modId);
|
||||||
|
|
||||||
// Check if this mod would be included in the next Forward/Backward move
|
// Check if this mod would be included in the next Forward/Backward move
|
||||||
bool isInNextForward = false;
|
bool isInNextForward = false;
|
||||||
bool isInNextBackward = false;
|
bool isInNextBackward = false;
|
||||||
|
|
||||||
if (_nextForwardMove != null && index >= _nextForwardMove!.startIndex && index < _nextForwardMove!.endIndex) {
|
if (_nextForwardMove != null &&
|
||||||
|
index >= _nextForwardMove!.startIndex &&
|
||||||
|
index < _nextForwardMove!.endIndex) {
|
||||||
isInNextForward = true;
|
isInNextForward = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_nextBackwardMove != null && index >= _nextBackwardMove!.startIndex && index < _nextBackwardMove!.endIndex) {
|
if (_nextBackwardMove != null &&
|
||||||
|
index >= _nextBackwardMove!.startIndex &&
|
||||||
|
index < _nextBackwardMove!.endIndex) {
|
||||||
isInNextBackward = true;
|
isInNextBackward = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine mod status for coloring
|
// Determine mod status for coloring
|
||||||
final bool isChecked = _checkedMods.contains(modId);
|
final bool isChecked = _checkedMods.contains(modId);
|
||||||
final bool isProblem = _problemMods.contains(modId);
|
final bool isProblem = _problemMods.contains(modId);
|
||||||
|
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
// Navigation takes precedence if this mod is in a navigation range
|
// Navigation takes precedence if this mod is in a navigation range
|
||||||
@@ -574,8 +537,8 @@ class _ModTroubleshooterWidgetState extends State<ModTroubleshooterWidget> {
|
|||||||
vertical: 4.0,
|
vertical: 4.0,
|
||||||
),
|
),
|
||||||
color: _getModCardColor(
|
color: _getModCardColor(
|
||||||
isSelected: isSelected,
|
isSelected: isSelected,
|
||||||
isChecked: isChecked,
|
isChecked: isChecked,
|
||||||
isProblem: isProblem,
|
isProblem: isProblem,
|
||||||
isInNextForward: isInNextForward,
|
isInNextForward: isInNextForward,
|
||||||
isInNextBackward: isInNextBackward,
|
isInNextBackward: isInNextBackward,
|
||||||
@@ -592,51 +555,75 @@ class _ModTroubleshooterWidgetState extends State<ModTroubleshooterWidget> {
|
|||||||
children: [
|
children: [
|
||||||
if (isSelected)
|
if (isSelected)
|
||||||
Container(
|
Container(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 0),
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 4,
|
||||||
|
vertical: 0,
|
||||||
|
),
|
||||||
margin: const EdgeInsets.only(right: 4),
|
margin: const EdgeInsets.only(right: 4),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.blue.shade800.withOpacity(0.2),
|
color: const Color(
|
||||||
|
0x28303F9F,
|
||||||
|
), // Blue with alpha 40
|
||||||
borderRadius: BorderRadius.circular(4),
|
borderRadius: BorderRadius.circular(4),
|
||||||
),
|
),
|
||||||
child: Text(
|
child: Text(
|
||||||
'TESTING',
|
'TESTING',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Colors.blue.shade200,
|
color: Colors.blue.shade200,
|
||||||
fontSize: AppThemeExtension.of(context).textSizeSmall,
|
fontSize:
|
||||||
|
AppThemeExtension.of(
|
||||||
|
context,
|
||||||
|
).textSizeSmall,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (isChecked)
|
if (isChecked)
|
||||||
Container(
|
Container(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 0),
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 4,
|
||||||
|
vertical: 0,
|
||||||
|
),
|
||||||
margin: const EdgeInsets.only(right: 4),
|
margin: const EdgeInsets.only(right: 4),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.green.shade800.withOpacity(0.2),
|
color: const Color(
|
||||||
|
0x1E2E7D32,
|
||||||
|
), // Green with alpha 30
|
||||||
borderRadius: BorderRadius.circular(4),
|
borderRadius: BorderRadius.circular(4),
|
||||||
),
|
),
|
||||||
child: Text(
|
child: Text(
|
||||||
'GOOD',
|
'GOOD',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Colors.green.shade200,
|
color: Colors.green.shade200,
|
||||||
fontSize: AppThemeExtension.of(context).textSizeSmall,
|
fontSize:
|
||||||
|
AppThemeExtension.of(
|
||||||
|
context,
|
||||||
|
).textSizeSmall,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (isProblem)
|
if (isProblem)
|
||||||
Container(
|
Container(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 0),
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 4,
|
||||||
|
vertical: 0,
|
||||||
|
),
|
||||||
margin: const EdgeInsets.only(right: 4),
|
margin: const EdgeInsets.only(right: 4),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.red.shade800.withOpacity(0.2),
|
color: const Color(
|
||||||
|
0x1EC62828,
|
||||||
|
), // Red with alpha 30
|
||||||
borderRadius: BorderRadius.circular(4),
|
borderRadius: BorderRadius.circular(4),
|
||||||
),
|
),
|
||||||
child: Text(
|
child: Text(
|
||||||
'PROBLEM',
|
'PROBLEM',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Colors.red.shade200,
|
color: Colors.red.shade200,
|
||||||
fontSize: AppThemeExtension.of(context).textSizeSmall,
|
fontSize:
|
||||||
|
AppThemeExtension.of(
|
||||||
|
context,
|
||||||
|
).textSizeSmall,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -645,7 +632,10 @@ class _ModTroubleshooterWidgetState extends State<ModTroubleshooterWidget> {
|
|||||||
child: Text(
|
child: Text(
|
||||||
mod.name,
|
mod.name,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontWeight: isSelected ? FontWeight.bold : FontWeight.normal,
|
fontWeight:
|
||||||
|
isSelected
|
||||||
|
? FontWeight.bold
|
||||||
|
: FontWeight.normal,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -666,8 +656,10 @@ class _ModTroubleshooterWidgetState extends State<ModTroubleshooterWidget> {
|
|||||||
message: 'Base Game',
|
message: 'Base Game',
|
||||||
child: Icon(
|
child: Icon(
|
||||||
Icons.home,
|
Icons.home,
|
||||||
color: AppThemeExtension.of(context).baseGameColor,
|
color:
|
||||||
size: AppThemeExtension.of(context).iconSizeSmall,
|
AppThemeExtension.of(context).baseGameColor,
|
||||||
|
size:
|
||||||
|
AppThemeExtension.of(context).iconSizeSmall,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (mod.isExpansion)
|
if (mod.isExpansion)
|
||||||
@@ -675,20 +667,26 @@ class _ModTroubleshooterWidgetState extends State<ModTroubleshooterWidget> {
|
|||||||
message: 'Expansion',
|
message: 'Expansion',
|
||||||
child: Icon(
|
child: Icon(
|
||||||
Icons.star,
|
Icons.star,
|
||||||
color: AppThemeExtension.of(context).expansionColor,
|
color:
|
||||||
size: AppThemeExtension.of(context).iconSizeSmall,
|
AppThemeExtension.of(
|
||||||
|
context,
|
||||||
|
).expansionColor,
|
||||||
|
size:
|
||||||
|
AppThemeExtension.of(context).iconSizeSmall,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (mod.dependencies.isNotEmpty)
|
if (mod.dependencies.isNotEmpty)
|
||||||
Tooltip(
|
Tooltip(
|
||||||
message: 'Dependencies:\n${mod.dependencies.join('\n')}',
|
message:
|
||||||
|
'Dependencies:\n${mod.dependencies.join('\n')}',
|
||||||
child: Icon(
|
child: Icon(
|
||||||
Icons.link,
|
Icons.link,
|
||||||
color: AppThemeExtension.of(context).linkColor,
|
color: AppThemeExtension.of(context).linkColor,
|
||||||
size: AppThemeExtension.of(context).iconSizeSmall,
|
size:
|
||||||
|
AppThemeExtension.of(context).iconSizeSmall,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
// Display status icon
|
// Display status icon
|
||||||
if (isChecked)
|
if (isChecked)
|
||||||
Tooltip(
|
Tooltip(
|
||||||
@@ -706,63 +704,87 @@ class _ModTroubleshooterWidgetState extends State<ModTroubleshooterWidget> {
|
|||||||
color: Colors.red.shade300,
|
color: Colors.red.shade300,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
const SizedBox(width: 4),
|
const SizedBox(width: 4),
|
||||||
|
|
||||||
// Show navigation indicators
|
// Show navigation indicators
|
||||||
if (isInNextForward)
|
if (isInNextForward)
|
||||||
Container(
|
Container(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 2),
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 4,
|
||||||
|
vertical: 2,
|
||||||
|
),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.blue.withOpacity(0.2),
|
color: const Color(
|
||||||
|
0x0A2196F3,
|
||||||
|
), // Blue with alpha 10
|
||||||
borderRadius: BorderRadius.circular(4),
|
borderRadius: BorderRadius.circular(4),
|
||||||
),
|
),
|
||||||
child: Tooltip(
|
child: Tooltip(
|
||||||
message: 'Click to move Forward (test this mod)',
|
message:
|
||||||
|
'Click to move Forward (test this mod)',
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
Icon(
|
Icon(
|
||||||
Icons.arrow_forward,
|
Icons.arrow_forward,
|
||||||
color: Colors.blue.shade300,
|
color: Colors.blue.shade300,
|
||||||
size: AppThemeExtension.of(context).iconSizeSmall,
|
size:
|
||||||
|
AppThemeExtension.of(
|
||||||
|
context,
|
||||||
|
).iconSizeSmall,
|
||||||
),
|
),
|
||||||
const SizedBox(width: 2),
|
const SizedBox(width: 2),
|
||||||
Text(
|
Text(
|
||||||
'Forward',
|
'Forward',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Colors.blue.shade300,
|
color: Colors.blue.shade300,
|
||||||
fontSize: AppThemeExtension.of(context).textSizeSmall,
|
fontSize:
|
||||||
|
AppThemeExtension.of(
|
||||||
|
context,
|
||||||
|
).textSizeSmall,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
if (isInNextBackward)
|
if (isInNextBackward)
|
||||||
Container(
|
Container(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 2),
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 4,
|
||||||
|
vertical: 2,
|
||||||
|
),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.purple.withOpacity(0.2),
|
color: const Color(
|
||||||
|
0x0A9C27B0,
|
||||||
|
), // Purple with alpha 10
|
||||||
borderRadius: BorderRadius.circular(4),
|
borderRadius: BorderRadius.circular(4),
|
||||||
),
|
),
|
||||||
child: Tooltip(
|
child: Tooltip(
|
||||||
message: 'Click to move Backward (test this mod)',
|
message:
|
||||||
|
'Click to move Backward (test this mod)',
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
Icon(
|
Icon(
|
||||||
Icons.arrow_back,
|
Icons.arrow_back,
|
||||||
color: Colors.purple.shade300,
|
color: Colors.purple.shade300,
|
||||||
size: AppThemeExtension.of(context).iconSizeSmall,
|
size:
|
||||||
|
AppThemeExtension.of(
|
||||||
|
context,
|
||||||
|
).iconSizeSmall,
|
||||||
),
|
),
|
||||||
const SizedBox(width: 2),
|
const SizedBox(width: 2),
|
||||||
Text(
|
Text(
|
||||||
'Back',
|
'Back',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Colors.purple.shade300,
|
color: Colors.purple.shade300,
|
||||||
fontSize: AppThemeExtension.of(context).textSizeSmall,
|
fontSize:
|
||||||
|
AppThemeExtension.of(
|
||||||
|
context,
|
||||||
|
).textSizeSmall,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -781,7 +803,7 @@ class _ModTroubleshooterWidgetState extends State<ModTroubleshooterWidget> {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Color _getModCardColor({
|
Color _getModCardColor({
|
||||||
required bool isSelected,
|
required bool isSelected,
|
||||||
required bool isChecked,
|
required bool isChecked,
|
||||||
@@ -791,22 +813,19 @@ class _ModTroubleshooterWidgetState extends State<ModTroubleshooterWidget> {
|
|||||||
}) {
|
}) {
|
||||||
// Priority: 1. Selected, 2. Navigation areas, 3. Status
|
// Priority: 1. Selected, 2. Navigation areas, 3. Status
|
||||||
if (isSelected) {
|
if (isSelected) {
|
||||||
return Colors.blue.shade800.withOpacity(0.3);
|
return const Color(0x80303F9F);
|
||||||
} else if (isInNextForward && isInNextBackward) {
|
} else if (isInNextForward && isInNextBackward) {
|
||||||
// Both forward and backward - more obvious purple
|
return const Color(0x50673AB7);
|
||||||
return Colors.deepPurple.withOpacity(0.3);
|
|
||||||
} else if (isInNextForward) {
|
} else if (isInNextForward) {
|
||||||
// Forward navigation - more obvious blue
|
return const Color(0x402196F3);
|
||||||
return Colors.blue.withOpacity(0.25);
|
|
||||||
} else if (isInNextBackward) {
|
} else if (isInNextBackward) {
|
||||||
// Backward navigation - more obvious purple
|
return const Color(0x409C27B0);
|
||||||
return Colors.purple.withOpacity(0.25);
|
|
||||||
} else if (isChecked) {
|
} else if (isChecked) {
|
||||||
return Colors.green.shade800.withOpacity(0.2);
|
return const Color(0x802E7D32);
|
||||||
} else if (isProblem) {
|
} else if (isProblem) {
|
||||||
return Colors.red.shade800.withOpacity(0.2);
|
return const Color(0x80C62828);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Colors.transparent;
|
return Colors.transparent;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user