diff --git a/lib/features/crop/crop_screen.dart b/lib/features/crop/crop_screen.dart index 5ff0567a..1c276b77 100644 --- a/lib/features/crop/crop_screen.dart +++ b/lib/features/crop/crop_screen.dart @@ -27,6 +27,11 @@ class CropScreen extends StatefulWidget { } class _CropScreenState extends State { + // Bornes et pas de la rotation, partagés par la jauge et les boutons − / +. + static const double _minRotation = -15.0; + static const double _maxRotation = 15.0; + static const double _rotationStep = 0.1; + final ImageCropService _cropService = ImageCropService(); // États de transformation @@ -113,7 +118,7 @@ class _CropScreenState extends State { const SizedBox(width: 8), Flexible( child: Text( - 'Alignez et pivotez la cible sur la croix', + 'Zoomer au maximum puis aligner votre cible', style: TextStyle(color: Colors.white.withValues(alpha: 0.8), fontSize: 14, fontWeight: FontWeight.w500), ), ), @@ -155,7 +160,8 @@ class _CropScreenState extends State { const SizedBox(height: 8), - // CROIX DIRECTIONNELLE — déplace la photo pixel par pixel. + // CROIX DIRECTIONNELLE — déplace la photo pixel par pixel, + // encadrée par les deux boutons de rotation fine. _buildDirectionalPad(), const SizedBox(height: 16), @@ -180,12 +186,13 @@ class _CropScreenState extends State { ), Row( children: [ - const Icon(Icons.rotate_left, color: Colors.white38, size: 20), + // Les icônes de sens de rotation ont rejoint les boutons − / + + // de la croix directionnelle. Expanded( child: Slider( value: _rotation, - min: -15.0, - max: 15.0, + min: _minRotation, + max: _maxRotation, divisions: 300, label: '${_rotation.toStringAsFixed(1)}°', activeColor: const Color(0xFF1A73E8), @@ -197,7 +204,6 @@ class _CropScreenState extends State { }, ), ), - const Icon(Icons.rotate_right, color: Colors.white38, size: 20), const SizedBox(width: 4), IconButton( icon: const Icon(Icons.restart_alt, color: Colors.white54, size: 20), @@ -363,12 +369,18 @@ class _CropScreenState extends State { } // Croix directionnelle compacte pour déplacer la photo pixel par pixel. - // Les symboles « − » et « + » de part et d'autre sont purement décoratifs. + // Les boutons « − » et « + » de part et d'autre pivotent l'image de 0,1°. Widget _buildDirectionalPad() { return Row( mainAxisAlignment: MainAxisAlignment.center, children: [ - _buildCropSignLabel('−'), + _buildRotationButton( + icon: Icons.rotate_left, + sign: '−', + iconFirst: true, + tooltip: 'Pivoter vers la gauche', + onPressed: () => _rotateBy(-_rotationStep), + ), const SizedBox(width: 10), Column( mainAxisSize: MainAxisSize.min, @@ -386,7 +398,13 @@ class _CropScreenState extends State { ], ), const SizedBox(width: 10), - _buildCropSignLabel('+'), + _buildRotationButton( + icon: Icons.rotate_right, + sign: '+', + iconFirst: false, + tooltip: 'Pivoter vers la droite', + onPressed: () => _rotateBy(_rotationStep), + ), ], ); } @@ -404,10 +422,40 @@ class _CropScreenState extends State { ); } - Widget _buildCropSignLabel(String text) { - return Text( - text, - style: const TextStyle(color: Colors.white54, fontSize: 24, fontWeight: FontWeight.bold), + // Bouton de rotation : l'icône de sens est accolée au signe − / +. + Widget _buildRotationButton({ + required IconData icon, + required String sign, + required bool iconFirst, + required String tooltip, + required VoidCallback onPressed, + }) { + final content = [ + Icon(icon, color: Colors.white70, size: 20), + const SizedBox(width: 4), + Text( + sign, + style: const TextStyle(color: Colors.white, fontSize: 22, fontWeight: FontWeight.bold), + ), + ]; + + return Tooltip( + message: tooltip, + child: Material( + color: Colors.black54, + borderRadius: BorderRadius.circular(6), + child: InkWell( + onTap: onPressed, + borderRadius: BorderRadius.circular(6), + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 8), + child: Row( + mainAxisSize: MainAxisSize.min, + children: iconFirst ? content : content.reversed.toList(), + ), + ), + ), + ), ); } @@ -417,6 +465,17 @@ class _CropScreenState extends State { }); } + /// Pivote l'image de [delta] degrés, dans les mêmes bornes que la jauge. + /// + /// La valeur est arrondie au dixième pour rester calée sur les crans de la + /// jauge (300 divisions sur 30°) et sur l'affichage. + void _rotateBy(double delta) { + setState(() { + final value = (_rotation + delta).clamp(_minRotation, _maxRotation); + _rotation = (value * 10).roundToDouble() / 10; + }); + } + void _onScaleStart(ScaleStartDetails details) { _baseScale = _scale; _startFocalPoint = details.focalPoint;