finished bullet adjustment

This commit is contained in:
2025-04-07 20:02:16 +02:00
parent 208819dba9
commit aeda7ddd2e
+181 -153
View File
@@ -47,7 +47,7 @@ class _PreviewScreenState extends State<PreviewScreen> {
final List<int> _manualBulletScores = []; final List<int> _manualBulletScores = [];
Color _bulletColor = Colors.blue; // default fallback Color _bulletColor = Colors.blue; // default fallback
double get _bulletOpacity => 0.7; double get _bulletOpacity => 0.8;
bool _isEditMode = false; bool _isEditMode = false;
int? _selectedBulletIndexInEdit; int? _selectedBulletIndexInEdit;
@@ -204,35 +204,23 @@ Color _getColorFromName(String colorName) {
_cachedTargetImage = Uint8List.fromList(img.encodePng(_targetImageDecoded!)); _cachedTargetImage = Uint8List.fromList(img.encodePng(_targetImageDecoded!));
} }
// 10. Update UI // 10. Update state with mapped bullets
final missing = widget.bulletCount - mappedBullets.length;
setState(() { setState(() {
_mappedBullets = mappedBullets; _mappedBullets = mappedBullets;
_isLoading = false; _isLoading = false;
}); });
if (missing > 0) {
final result = await _showMismatchOptionsDialog(mappedBullets.length);
if (result == 'retry') {
Navigator.pop(context); // Go back to camera screen
} else if (result == 'manual') {
_manualScoreEntry(); // Reuse your original manual dialog
}
}
} catch (e, stack) { } catch (e, stack) {
debugPrint("❌ Error during detection mapping: $e"); debugPrint("❌ Error during detection mapping: $e");
debugPrint(stack.toString()); debugPrint(stack.toString());
setState(() => _isLoading = false); setState(() {
_mappedBullets = [];
_isLoading = false;
});
} }
} }
Future<String?> _showMismatchOptionsDialog(int detectedCount) async { Future<String?> _showMismatchOptionsDialog(int detectedCount) async {
return showDialog<String>( return showDialog<String>(
context: context, context: context,
@@ -780,6 +768,24 @@ void _moveBullet(Offset offsetDelta) {
return autoScore + manualScore; return autoScore + manualScore;
} }
Color _getBulletButtonColor(int index) {
final isSelected = _isEditMode
? index == _selectedBulletIndexInEdit
: index == _highlightedBulletIndex;
if (isSelected) return Colors.red;
final manualStart = _mappedBullets.length - _manualBulletScores.length;
final isManual = index >= manualStart;
if (isManual) return Colors.redAccent; // ⬅️ Light red for manual bullets
if (_isEditMode) return _editColor;
return _bulletColor;
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
@@ -822,6 +828,51 @@ Widget build(BuildContext context) {
? const Center(child: CircularProgressIndicator()) ? const Center(child: CircularProgressIndicator())
: Stack( : Stack(
children: [ children: [
if (_mappedBullets.isNotEmpty)
Padding(
padding: const EdgeInsets.fromLTRB(16, 12, 16, 8),
child: SizedBox(
width: double.infinity,
child: ElevatedButton.icon(
onPressed: () async {
if (!_isEditMode && _mappedBullets.length < widget.bulletCount) {
final result = await _showMismatchOptionsDialog(_mappedBullets.length);
if (result == 'retry') {
Navigator.pop(context);
return;
} else if (result == 'manual') {
_manualScoreEntry();
return;
} else {
return;
}
}
setState(() {
_isEditMode = !_isEditMode;
_selectedBulletIndexInEdit = null;
});
},
icon: Icon(
_isEditMode ? Icons.check : Icons.edit,
color: Colors.white,
size: 20,
),
label: Text(
_isEditMode ? "Done Editing" : "Edit Bullets",
style: const TextStyle(color: Colors.white, fontSize: 16),
),
style: ElevatedButton.styleFrom(
backgroundColor: _activeColor,
padding: const EdgeInsets.symmetric(vertical: 14),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
),
),
),
Column( Column(
children: [ children: [
Expanded( Expanded(
@@ -918,6 +969,14 @@ Widget build(BuildContext context) {
final isHighlighted = index == _highlightedBulletIndex; final isHighlighted = index == _highlightedBulletIndex;
final isSelectedForEdit = index == _selectedBulletIndexInEdit; final isSelectedForEdit = index == _selectedBulletIndexInEdit;
final manualStart = _mappedBullets.length - _manualBulletScores.length;
final isManual = index >= manualStart;
final Color bulletFillColor = isSelectedForEdit || (!_isEditMode && isHighlighted)
? Colors.red.withOpacity(_bulletOpacity)
: isManual
? Colors.red.withOpacity(_bulletOpacity)
: _activeColorWithOpacity;
return Positioned( return Positioned(
left: dx - bulletSize / 2, left: dx - bulletSize / 2,
@@ -926,17 +985,15 @@ Widget build(BuildContext context) {
width: bulletSize, width: bulletSize,
height: bulletSize, height: bulletSize,
decoration: BoxDecoration( decoration: BoxDecoration(
color: _activeColorWithOpacity, color: bulletFillColor,
shape: BoxShape.circle, shape: BoxShape.circle,
border: Border.all( border: Border.all(
color: isSelectedForEdit color: (isSelectedForEdit || (!_isEditMode && isHighlighted))
? _bulletColor ? Colors.red
: (!_isEditMode && isHighlighted) : Colors.black,
? Colors.red width: (isSelectedForEdit || (!_isEditMode && isHighlighted)) ? 3 : 1,
: Colors.black, ),
width: (isSelectedForEdit || (!_isEditMode && isHighlighted)) ? 3 : 1,
), ),
),
), ),
); );
}).toList(), }).toList(),
@@ -947,147 +1004,118 @@ Widget build(BuildContext context) {
), ),
const SizedBox(height: 12), const SizedBox(height: 12),
if (_mappedBullets.isNotEmpty) if (_mappedBullets.isNotEmpty)
Padding( Column(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6), children: [
child: Row( // Score buttons
children: List.generate( Padding(
_mappedBullets.length, padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
(index) { child: Row(
final bullet = _mappedBullets[index]; children: List.generate(
final isManual = index >= (_mappedBullets.length - _manualBulletScores.length); _mappedBullets.length,
final score = isManual (index) {
? _manualBulletScores[index - (_mappedBullets.length - _manualBulletScores.length)] final bullet = _mappedBullets[index];
: _scoreForBullet( final isManual = index >= (_mappedBullets.length - _manualBulletScores.length);
bullet, final score = isManual
_targetImageCenterForScoring ?? fixedRingTargetCenter, ? _manualBulletScores[index - (_mappedBullets.length - _manualBulletScores.length)]
); : _scoreForBullet(
final isSelected = index == (_isEditMode ? _selectedBulletIndexInEdit : _highlightedBulletIndex); bullet,
_targetImageCenterForScoring ?? fixedRingTargetCenter,
);
return Expanded( return Expanded(
child: Padding( child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 6), padding: const EdgeInsets.symmetric(horizontal: 6),
child: GestureDetector( child: GestureDetector(
onTap: () { onTap: () {
if (_isEditMode) { if (_isEditMode) {
setState(() => _selectedBulletIndexInEdit = index); setState(() => _selectedBulletIndexInEdit = index);
} }
}, },
onTapDown: (_) { onTapDown: (_) {
if (!_isEditMode) { if (!_isEditMode) {
setState(() => _highlightedBulletIndex = index); setState(() => _highlightedBulletIndex = index);
} }
}, },
onTapUp: (_) { onTapUp: (_) {
if (!_isEditMode) { if (!_isEditMode) {
setState(() => _highlightedBulletIndex = null); setState(() => _highlightedBulletIndex = null);
} }
}, },
onTapCancel: () { onTapCancel: () {
if (!_isEditMode) { if (!_isEditMode) {
setState(() => _highlightedBulletIndex = null); setState(() => _highlightedBulletIndex = null);
} }
}, },
child: AnimatedContainer( child: AnimatedContainer(
duration: const Duration(milliseconds: 200), duration: const Duration(milliseconds: 200),
height: 100, height: 100,
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: BorderRadius.circular(16), borderRadius: BorderRadius.circular(16),
boxShadow: const [ boxShadow: const [
BoxShadow( BoxShadow(
color: Colors.black26, color: Colors.black26,
offset: Offset(0, 2), offset: Offset(0, 2),
blurRadius: 6, blurRadius: 6,
),
],
),
child: Column(
children: [
Container(
height: 30,
decoration: BoxDecoration(
color: Colors.grey[300],
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(16),
topRight: Radius.circular(16),
), ),
), ],
alignment: Alignment.center,
child: Text(
'${index + 1}',
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 14,
color: Colors.black87,
),
),
), ),
Expanded( child: Column(
child: Container( children: [
decoration: BoxDecoration( Container(
color: isSelected height: 30,
? _editColor decoration: BoxDecoration(
: _activeColor, color: Colors.grey[300],
borderRadius: const BorderRadius.only( borderRadius: const BorderRadius.only(
bottomLeft: Radius.circular(16), topLeft: Radius.circular(16),
bottomRight: Radius.circular(16), topRight: Radius.circular(16),
),
),
alignment: Alignment.center,
child: Text(
'${index + 1}',
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 14,
color: Colors.black87,
),
), ),
), ),
alignment: Alignment.center, Expanded(
child: Text( child: Container(
'$score', decoration: BoxDecoration(
style: const TextStyle( color: _getBulletButtonColor(index),
fontSize: 28, borderRadius: const BorderRadius.only(
fontWeight: FontWeight.bold, bottomLeft: Radius.circular(16),
color: Colors.white, bottomRight: Radius.circular(16),
),
),
alignment: Alignment.center,
child: Text(
'$score',
style: const TextStyle(
fontSize: 28,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
), ),
), ),
), ],
), ),
], ),
), ),
), ),
), );
), },
); ),
}, ),
), ),
), ],
), ),
const SizedBox(height: 12), const SizedBox(height: 12),
], ],
), ),
Positioned(
top: 12,
right: 12,
child: ElevatedButton.icon(
onPressed: () {
setState(() {
_isEditMode = !_isEditMode;
_selectedBulletIndexInEdit = null;
});
},
icon: Icon(
_isEditMode ? Icons.check : Icons.edit,
color: Colors.white,
size: 20,
),
label: Text(
_isEditMode ? "Done" : "Edit Bullets",
style: const TextStyle(color: Colors.white),
),
style: ElevatedButton.styleFrom(
backgroundColor: _activeColor, // ← dynamic color depending on mode
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
elevation: 4,
),
),
),
_buildEditController(), _buildEditController(),
], ],
), ),