Feat: Bouton de clôture de session (80/20) sur l'accueil et reset de l'espacement
This commit is contained in:
@@ -51,14 +51,14 @@ class TargetCalibrationState extends State<TargetCalibration> {
|
||||
late int _ringCount;
|
||||
late List<double> _ringRadii;
|
||||
|
||||
// CORRECTION : Variable dédiée pour piloter la jauge orange sans conflit
|
||||
late double _currentEspacementRatio;
|
||||
|
||||
bool _isDraggingCenter = false;
|
||||
bool _isDraggingRadius = false;
|
||||
bool _isDraggingInnerRadius = false;
|
||||
|
||||
// Contrôle de l'affichage du scroll d'espacement (désactivé par défaut)
|
||||
bool _showEspacement = false;
|
||||
|
||||
// Variable temporaire pour stocker la valeur du rayon au début du pincement tactile
|
||||
double _baseRadiusBeforeScale = 1.0;
|
||||
|
||||
@override
|
||||
@@ -69,6 +69,9 @@ class TargetCalibrationState extends State<TargetCalibration> {
|
||||
_radius = widget.initialRadius;
|
||||
_innerRadius = widget.initialInnerRadius;
|
||||
_ringCount = widget.initialRingCount;
|
||||
|
||||
// Initialisation du ratio par défaut
|
||||
_currentEspacementRatio = (_radius > 0) ? (_innerRadius / _radius).clamp(0.01, 0.9) : 0.1;
|
||||
_initRingRadii();
|
||||
}
|
||||
|
||||
@@ -77,11 +80,9 @@ class TargetCalibrationState extends State<TargetCalibration> {
|
||||
widget.initialRingRadii!.length == _ringCount) {
|
||||
_ringRadii = List.from(widget.initialRingRadii!);
|
||||
} else {
|
||||
// Initialize with default proportional radii interpolated between inner and outer
|
||||
_ringRadii = List.generate(_ringCount, (i) {
|
||||
if (_ringCount <= 1) return 1.0;
|
||||
final ratio = _innerRadius / _radius;
|
||||
return ratio + (1.0 - ratio) * i / (_ringCount - 1);
|
||||
return _currentEspacementRatio + (1.0 - _currentEspacementRatio) * i / (_ringCount - 1);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -91,12 +92,10 @@ class TargetCalibrationState extends State<TargetCalibration> {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
bool shouldReinit = false;
|
||||
|
||||
if (widget.initialCenterX != oldWidget.initialCenterX &&
|
||||
!_isDraggingCenter) {
|
||||
if (widget.initialCenterX != oldWidget.initialCenterX && !_isDraggingCenter) {
|
||||
_centerX = widget.initialCenterX;
|
||||
}
|
||||
if (widget.initialCenterY != oldWidget.initialCenterY &&
|
||||
!_isDraggingCenter) {
|
||||
if (widget.initialCenterY != oldWidget.initialCenterY && !_isDraggingCenter) {
|
||||
_centerY = widget.initialCenterY;
|
||||
}
|
||||
if (widget.initialRingCount != oldWidget.initialRingCount) {
|
||||
@@ -107,12 +106,12 @@ class TargetCalibrationState extends State<TargetCalibration> {
|
||||
_radius = widget.initialRadius;
|
||||
shouldReinit = true;
|
||||
}
|
||||
if (widget.initialInnerRadius != oldWidget.initialInnerRadius &&
|
||||
!_isDraggingInnerRadius) {
|
||||
if (widget.initialInnerRadius != oldWidget.initialInnerRadius && !_isDraggingInnerRadius) {
|
||||
_innerRadius = widget.initialInnerRadius;
|
||||
_currentEspacementRatio = (_radius > 0) ? (_innerRadius / _radius).clamp(0.01, 0.9) : 0.1;
|
||||
shouldReinit = true;
|
||||
}
|
||||
if (widget.initialRingRadii != oldWidget.initialRingRadii) {
|
||||
if (widget.initialRingRadii != oldWidget.initialRingRadii && widget.initialRingRadii != null) {
|
||||
shouldReinit = true;
|
||||
}
|
||||
|
||||
@@ -129,7 +128,6 @@ class TargetCalibrationState extends State<TargetCalibration> {
|
||||
|
||||
return Stack(
|
||||
children: [
|
||||
// Utilisation d'un Gesture Detector basé sur le Scale pour synchroniser à 1 ou 2 doigts
|
||||
GestureDetector(
|
||||
onScaleStart: (details) {
|
||||
_baseRadiusBeforeScale = _radius;
|
||||
@@ -162,7 +160,6 @@ class TargetCalibrationState extends State<TargetCalibration> {
|
||||
),
|
||||
),
|
||||
|
||||
// Panneau de configuration des Sliders en haut
|
||||
Positioned(
|
||||
top: 10,
|
||||
left: 40,
|
||||
@@ -170,7 +167,6 @@ class TargetCalibrationState extends State<TargetCalibration> {
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
// Ligne d'activation pour afficher ou masquer l'espacement
|
||||
Container(
|
||||
margin: const EdgeInsets.only(bottom: 8),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 2),
|
||||
@@ -201,7 +197,6 @@ class TargetCalibrationState extends State<TargetCalibration> {
|
||||
),
|
||||
),
|
||||
|
||||
// Slider pour la taille (toujours visible)
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 4),
|
||||
decoration: BoxDecoration(
|
||||
@@ -221,6 +216,7 @@ class TargetCalibrationState extends State<TargetCalibration> {
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
_radius = value;
|
||||
_innerRadius = _radius * _currentEspacementRatio;
|
||||
_initRingRadii(forceRecalculate: true);
|
||||
});
|
||||
_notifyChange();
|
||||
@@ -232,11 +228,10 @@ class TargetCalibrationState extends State<TargetCalibration> {
|
||||
),
|
||||
),
|
||||
|
||||
// Affichage conditionnel du slider d'espacement orange
|
||||
if (_showEspacement) ...[
|
||||
const SizedBox(height: 8),
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 4),
|
||||
padding: const EdgeInsets.fromLTRB(16, 4, 4, 4),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.black54,
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
@@ -246,21 +241,51 @@ class TargetCalibrationState extends State<TargetCalibration> {
|
||||
const Text('Espacement ', style: TextStyle(color: Colors.white, fontSize: 12, fontWeight: FontWeight.bold)),
|
||||
const Icon(Icons.compress, color: Colors.white, size: 16),
|
||||
Expanded(
|
||||
child: Slider(
|
||||
value: (_innerRadius / _radius).clamp(0.01, 0.9),
|
||||
min: 0.01,
|
||||
max: 0.9,
|
||||
activeColor: Colors.orange,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
_innerRadius = _radius * value;
|
||||
_initRingRadii(forceRecalculate: true);
|
||||
});
|
||||
_notifyChange();
|
||||
},
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Slider(
|
||||
value: _currentEspacementRatio,
|
||||
min: 0.01,
|
||||
max: 0.9,
|
||||
activeColor: Colors.orange,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
_currentEspacementRatio = value;
|
||||
_innerRadius = _radius * _currentEspacementRatio;
|
||||
_initRingRadii(forceRecalculate: true);
|
||||
});
|
||||
_notifyChange();
|
||||
},
|
||||
),
|
||||
),
|
||||
const Icon(Icons.expand, color: Colors.white, size: 16),
|
||||
],
|
||||
),
|
||||
),
|
||||
const Icon(Icons.expand, color: Colors.white, size: 16),
|
||||
// CORRECTION DU BOUTON RESET : Force visuellement le ratio par défaut
|
||||
// CORRECTION RADICALE : On force le ratio à 0.1 (10% standard) en dur
|
||||
IconButton(
|
||||
icon: const Icon(Icons.refresh, color: Colors.white70, size: 20),
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
// 1. On force la jauge orange à se remettre pile à 10% (la valeur d'usine)
|
||||
_currentEspacementRatio = 0.1;
|
||||
|
||||
// 2. On réaligne l'innerRadius sur la taille actuelle
|
||||
_innerRadius = _radius * _currentEspacementRatio;
|
||||
|
||||
// 3. On force la reconstruction des cercles parfaits
|
||||
_initRingRadii(forceRecalculate: true);
|
||||
});
|
||||
|
||||
// 4. On balance tout ça au provider pour qu'il s'aligne
|
||||
_notifyChange();
|
||||
},
|
||||
tooltip: 'Réinitialiser l\'espacement',
|
||||
constraints: const BoxConstraints(),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -268,7 +293,6 @@ class TargetCalibrationState extends State<TargetCalibration> {
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
],
|
||||
);
|
||||
},
|
||||
@@ -286,28 +310,16 @@ class TargetCalibrationState extends State<TargetCalibration> {
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
_buildDirectionButton(
|
||||
Icons.keyboard_arrow_up,
|
||||
() => _moveCenterByPixels(0, -1, size),
|
||||
),
|
||||
_buildDirectionButton(Icons.keyboard_arrow_up, () => _moveCenterByPixels(0, -1, size)),
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
_buildDirectionButton(
|
||||
Icons.keyboard_arrow_left,
|
||||
() => _moveCenterByPixels(-1, 0, size),
|
||||
),
|
||||
_buildDirectionButton(Icons.keyboard_arrow_left, () => _moveCenterByPixels(-1, 0, size)),
|
||||
const SizedBox(width: 40),
|
||||
_buildDirectionButton(
|
||||
Icons.keyboard_arrow_right,
|
||||
() => _moveCenterByPixels(1, 0, size),
|
||||
),
|
||||
_buildDirectionButton(Icons.keyboard_arrow_right, () => _moveCenterByPixels(1, 0, size)),
|
||||
],
|
||||
),
|
||||
_buildDirectionButton(
|
||||
Icons.keyboard_arrow_down,
|
||||
() => _moveCenterByPixels(0, 1, size),
|
||||
),
|
||||
_buildDirectionButton(Icons.keyboard_arrow_down, () => _moveCenterByPixels(0, 1, size)),
|
||||
],
|
||||
),
|
||||
);
|
||||
@@ -315,10 +327,7 @@ class TargetCalibrationState extends State<TargetCalibration> {
|
||||
|
||||
Widget _buildDirectionButton(IconData icon, VoidCallback onPressed) {
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.black87,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
decoration: BoxDecoration(color: Colors.black87, borderRadius: BorderRadius.circular(8)),
|
||||
margin: const EdgeInsets.all(2),
|
||||
child: IconButton(
|
||||
icon: Icon(icon, color: Colors.white, size: 28),
|
||||
@@ -348,22 +357,19 @@ class TargetCalibrationState extends State<TargetCalibration> {
|
||||
_notifyChange();
|
||||
}
|
||||
|
||||
// Traitement intelligent des gestes tactiles
|
||||
void _onScaleUpdate(ScaleUpdateDetails details, Size size) {
|
||||
setState(() {
|
||||
if (details.pointerCount == 2) {
|
||||
// GESTE À DEUX DOIGTS : On calcule le zoom en respectant le range strict (0.3 à 1.2)
|
||||
_radius = (_baseRadiusBeforeScale * details.scale).clamp(0.5, 1.1);
|
||||
_innerRadius = _radius * _currentEspacementRatio;
|
||||
_initRingRadii(forceRecalculate: true);
|
||||
} else if (_isDraggingCenter) {
|
||||
// GESTE À UN SEUL DOIGT : Déplacement fluide du centre de la cible
|
||||
final deltaX = details.focalPointDelta.dx / size.width;
|
||||
final deltaY = details.focalPointDelta.dy / size.height;
|
||||
_centerX = _centerX + deltaX;
|
||||
_centerY = _centerY + deltaY;
|
||||
}
|
||||
});
|
||||
|
||||
_notifyChange();
|
||||
}
|
||||
|
||||
@@ -420,31 +426,16 @@ class _CalibrationPainter extends CustomPainter {
|
||||
}
|
||||
|
||||
if (isDraggingCenter) {
|
||||
final crosshairLinePaint = Paint()
|
||||
..color = Colors.red.withValues(alpha: 0.5)
|
||||
..strokeWidth = 1;
|
||||
canvas.drawLine(
|
||||
Offset(0, centerPx.dy),
|
||||
Offset(size.width, centerPx.dy),
|
||||
crosshairLinePaint,
|
||||
);
|
||||
canvas.drawLine(
|
||||
Offset(centerPx.dx, 0),
|
||||
Offset(centerPx.dx, size.height),
|
||||
crosshairLinePaint,
|
||||
);
|
||||
final crosshairLinePaint = Paint()..color = Colors.red.withValues(alpha: 0.5)..strokeWidth = 1;
|
||||
canvas.drawLine(Offset(0, centerPx.dy), Offset(size.width, centerPx.dy), crosshairLinePaint);
|
||||
canvas.drawLine(Offset(centerPx.dx, 0), Offset(centerPx.dx, size.height), crosshairLinePaint);
|
||||
}
|
||||
|
||||
_drawCenterHandle(canvas, centerPx);
|
||||
_drawInstructions(canvas, size);
|
||||
}
|
||||
|
||||
void _drawConcentricZones(
|
||||
Canvas canvas,
|
||||
Size size,
|
||||
Offset center,
|
||||
double baseRadius,
|
||||
) {
|
||||
void _drawConcentricZones(Canvas canvas, Size size, Offset center, double baseRadius) {
|
||||
List<Color> zoneColors = [];
|
||||
for (int i = 0; i < ringCount; i++) {
|
||||
final ratio = i / ringCount;
|
||||
@@ -468,9 +459,7 @@ class _CalibrationPainter extends CustomPainter {
|
||||
..color = Colors.red.withValues(alpha: 0.8);
|
||||
|
||||
for (int i = ringCount - 1; i >= 0; i--) {
|
||||
final ringRadius = ringRadii.length > i
|
||||
? ringRadii[i]
|
||||
: (i + 1) / ringCount;
|
||||
final ringRadius = ringRadii.length > i ? ringRadii[i] : (i + 1) / ringCount;
|
||||
final zoneRadius = baseRadius * ringRadius;
|
||||
|
||||
zonePaint.color = zoneColors[i];
|
||||
@@ -481,12 +470,8 @@ class _CalibrationPainter extends CustomPainter {
|
||||
final textPainter = TextPainter(textDirection: TextDirection.ltr);
|
||||
|
||||
for (int i = 0; i < ringCount; i++) {
|
||||
final ringRadius = ringRadii.length > i
|
||||
? ringRadii[i]
|
||||
: (i + 1) / ringCount;
|
||||
final prevRingRadius = i > 0
|
||||
? (ringRadii.length > i - 1 ? ringRadii[i - 1] : i / ringCount)
|
||||
: 0.0;
|
||||
final ringRadius = ringRadii.length > i ? ringRadii[i] : (i + 1) / ringCount;
|
||||
final prevRingRadius = i > 0 ? (ringRadii.length > i - 1 ? ringRadii[i - 1] : i / ringCount) : 0.0;
|
||||
final zoneRadius = baseRadius * (ringRadius + prevRingRadius) / 2;
|
||||
|
||||
final score = 10 - i;
|
||||
@@ -506,53 +491,28 @@ class _CalibrationPainter extends CustomPainter {
|
||||
|
||||
final labelY = center.dy - textPainter.height / 2;
|
||||
if (labelY >= 0 && labelY <= size.height) {
|
||||
textPainter.paint(
|
||||
canvas,
|
||||
Offset(labelX - textPainter.width / 2, labelY),
|
||||
);
|
||||
textPainter.paint(canvas, Offset(labelX - textPainter.width / 2, labelY));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void _drawSilhouetteZones(
|
||||
Canvas canvas,
|
||||
Size size,
|
||||
Offset center,
|
||||
double radius,
|
||||
) {
|
||||
final paint = Paint()
|
||||
..style = PaintingStyle.stroke
|
||||
..strokeWidth = 2;
|
||||
|
||||
void _drawSilhouetteZones(Canvas canvas, Size size, Offset center, double radius) {
|
||||
final paint = Paint()..style = PaintingStyle.stroke..strokeWidth = 2;
|
||||
final silhouetteWidth = radius * 0.8;
|
||||
final silhouetteHeight = radius * 2;
|
||||
|
||||
paint.color = Colors.green.withValues(alpha: 0.5);
|
||||
canvas.drawRect(
|
||||
Rect.fromCenter(
|
||||
center: center,
|
||||
width: silhouetteWidth,
|
||||
height: silhouetteHeight,
|
||||
),
|
||||
paint,
|
||||
);
|
||||
canvas.drawRect(Rect.fromCenter(center: center, width: silhouetteWidth, height: silhouetteHeight), paint);
|
||||
}
|
||||
|
||||
void _drawCenterHandle(Canvas canvas, Offset center) {
|
||||
final outerPaint = Paint()
|
||||
..color = isDraggingCenter ? Colors.redAccent : Colors.red
|
||||
..style = PaintingStyle.stroke
|
||||
..strokeWidth = 3;
|
||||
final outerPaint = Paint()..color = isDraggingCenter ? Colors.redAccent : Colors.red..style = PaintingStyle.stroke..strokeWidth = 3;
|
||||
canvas.drawCircle(center, 15, outerPaint);
|
||||
|
||||
final innerPaint = Paint()
|
||||
..color = isDraggingCenter ? Colors.redAccent : Colors.red
|
||||
..style = PaintingStyle.fill;
|
||||
final innerPaint = Paint()..color = isDraggingCenter ? Colors.redAccent : Colors.red..style = PaintingStyle.fill;
|
||||
canvas.drawCircle(center, 5, innerPaint);
|
||||
|
||||
final crossPaint = Paint()
|
||||
..color = isDraggingCenter ? Colors.redAccent : Colors.red
|
||||
..strokeWidth = 2;
|
||||
final crossPaint = Paint()..color = isDraggingCenter ? Colors.redAccent : Colors.red..strokeWidth = 2;
|
||||
canvas.drawLine(Offset(center.dx - 20, center.dy), Offset(center.dx - 8, center.dy), crossPaint);
|
||||
canvas.drawLine(Offset(center.dx + 8, center.dy), Offset(center.dx + 20, center.dy), crossPaint);
|
||||
canvas.drawLine(Offset(center.dx, center.dy - 20), Offset(center.dx, center.dy - 8), crossPaint);
|
||||
@@ -561,23 +521,12 @@ class _CalibrationPainter extends CustomPainter {
|
||||
|
||||
void _drawInstructions(Canvas canvas, Size size) {
|
||||
const instruction = 'Deplacez le centre ou ajustez le rayon';
|
||||
|
||||
final textPainter = TextPainter(
|
||||
text: TextSpan(
|
||||
text: instruction,
|
||||
style: TextStyle(
|
||||
color: Colors.white.withValues(alpha: 0.9),
|
||||
fontSize: 12,
|
||||
backgroundColor: Colors.black54,
|
||||
),
|
||||
),
|
||||
text: TextSpan(text: instruction, style: TextStyle(color: Colors.white.withValues(alpha: 0.9), fontSize: 12, backgroundColor: Colors.black54)),
|
||||
textDirection: TextDirection.ltr,
|
||||
);
|
||||
textPainter.layout();
|
||||
textPainter.paint(
|
||||
canvas,
|
||||
Offset((size.width - textPainter.width) / 2, size.height - 30),
|
||||
);
|
||||
textPainter.paint(canvas, Offset((size.width - textPainter.width) / 2, size.height - 30));
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
Reference in New Issue
Block a user