Clean up troubleshooter

This commit is contained in:
2025-03-18 22:17:28 +01:00
parent a4ee202971
commit 71ad392fb6

View File

@@ -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;
} }
} }