finished bullet adjustment
This commit is contained in:
+181
-153
@@ -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(),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
Reference in New Issue
Block a user