diff --git a/lib/src/widgets/board.dart b/lib/src/widgets/board.dart index 651e3d24..fecd679a 100644 --- a/lib/src/widgets/board.dart +++ b/lib/src/widgets/board.dart @@ -535,7 +535,8 @@ class _BoardState extends State { } void _onPointerDown(PointerDownEvent details) { - if (details.buttons != kPrimaryButton) return; + final button = details.buttons; + if (button != kPrimaryButton && button != kSecondaryMouseButton) return; final square = widget.offsetSquare(details.localPosition); if (square == null) return; @@ -545,6 +546,27 @@ class _BoardState extends State { final Piece? piece = pieces[square]; if (widget.settings.drawShape.enable) { + // draw mode takes priority over play mode when the draw mode lock is set or if using the right mouse button + final bool canDrawWithTouch = + _drawModeLockOrigin != null && + _drawModeLockOrigin!.pointer != details.pointer; + + final bool canDrawWithMouse = + details.kind == PointerDeviceKind.mouse && + button == kSecondaryMouseButton; + + if (canDrawWithTouch || canDrawWithMouse) { + _drawOrigin = details; + setState(() { + _shapeAvatar = Circle( + color: widget.settings.drawShape.newShapeColor, + orig: square, + scale: 0.80, + ); + }); + return; + } + if (_drawModeLockOrigin == null) { if (piece == null) { // Sets a lock to the draw mode if the user holds the pointer to an @@ -571,20 +593,11 @@ class _BoardState extends State { widget.settings.drawShape.onClearShapes?.call(); } } - // draw mode takes priority over play mode when the draw mode lock is set - else if (_drawModeLockOrigin!.pointer != details.pointer) { - _drawOrigin = details; - setState(() { - _shapeAvatar = Circle( - color: widget.settings.drawShape.newShapeColor, - orig: square, - scale: 0.80, - ); - }); - return; - } } + // Right mouse button is only used above to draw, we can ignore it from here + if (button == kSecondaryMouseButton) return; + if (widget.interactive == false) return; // From here on, we only allow 1 pointer to interact with the board. Other @@ -666,7 +679,8 @@ class _BoardState extends State { } void _onPointerMove(PointerMoveEvent details) { - if (details.buttons != kPrimaryButton) return; + final button = details.buttons; + if (button != kPrimaryButton && button != kSecondaryMouseButton) return; // draw mode takes priority over play mode when the draw mode lock is set if (_shapeAvatar != null && @@ -682,6 +696,9 @@ class _BoardState extends State { } } + // Right mouse button is only used above to draw, we can ignore it from here + if (button == kSecondaryMouseButton) return; + if (_currentPointerDownEvent == null || _currentPointerDownEvent!.pointer != details.pointer || widget.settings.pieceShiftMethod == PieceShiftMethod.tapTwoSquares) { diff --git a/test/widgets/board_test.dart b/test/widgets/board_test.dart index 369acb83..9ae48778 100644 --- a/test/widgets/board_test.dart +++ b/test/widgets/board_test.dart @@ -3,6 +3,7 @@ import 'package:chessground/src/widgets/animation.dart'; import 'package:chessground/src/widgets/promotion.dart'; import 'package:chessground/src/widgets/shape.dart'; import 'package:fast_immutable_collections/fast_immutable_collections.dart'; +import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:dartchess/dartchess.dart'; @@ -1467,6 +1468,90 @@ void main() { ); }); + testWidgets('draw a circle by mouse right click', (WidgetTester tester) async { + await tester.pumpWidget( + const _TestApp( + initialPlayerSide: PlayerSide.both, + enableDrawingShapes: true, + ), + ); + + await TestAsyncUtils.guard(() async { + // keep pressing an empty square to enable drawing shapes + final pressGesture = await tester.startGesture( + squareOffset(tester, Square.a3), + ); + + // drawing a circle with another tap + final tapGesture = await tester.startGesture( + squareOffset(tester, Square.e4), + ); + await tapGesture.up(); + + await pressGesture.up(); + }); + + // wait for the double tap delay to expire + await tester.pump(const Duration(milliseconds: 210)); + + expect( + find.byType(ShapeWidget), + paints..path(color: const Color(0xFF0000FF)), + ); + }); + + testWidgets('draw an arrow by mouse right click', (WidgetTester tester) async { + await tester.pumpWidget( + const _TestApp( + initialPlayerSide: PlayerSide.both, + enableDrawingShapes: true, + ), + ); + + await tester.dragFrom( + squareOffset(tester, Square.e2), + const Offset(0, -(squareSize * 2)), + buttons: kSecondaryMouseButton, + kind: PointerDeviceKind.mouse, + ); + + await tester.pump(); + + expect( + find.byType(ShapeWidget), + paints + ..line(color: const Color(0xFF0000FF)) + ..path(color: const Color(0xFF0000FF)), + ); + }); + + testWidgets('can draw shapes on an non-interactive board', ( + WidgetTester tester, + ) async { + await tester.pumpWidget( + const _TestApp( + initialPlayerSide: PlayerSide.none, + enableDrawingShapes: true, + ), + ); + + await tester.dragFrom( + squareOffset(tester, Square.e2), + const Offset(0, -(squareSize * 2)), + buttons: kSecondaryMouseButton, + kind: PointerDeviceKind.mouse, + ); + + await tester.pump(); + + expect( + find.byType(ShapeWidget), + paints + ..line(color: const Color(0xFF0000FF)) + ..path(color: const Color(0xFF0000FF)), + ); + }); + testWidgets('can draw shapes on an non-interactive board', (WidgetTester tester) async { await tester.pumpWidget(