Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support input method (click, drag, either) #39

Merged
merged 1 commit into from
Jul 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 3.2.0

- Add `pieceShiftMethod` to `BoardSetttings`, with possible values: `either` (default), `drag`, or `tapTwoSquares`.

## 3.1.2

- Any simultaneous touch on the board will now cancel the current piece
Expand Down
37 changes: 37 additions & 0 deletions example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,17 @@ class MyApp extends StatelessWidget {
}
}

String pieceShiftMethodLabel(PieceShiftMethod method) {
switch (method) {
case PieceShiftMethod.drag:
return 'Drag';
case PieceShiftMethod.tapTwoSquares:
return 'Tap two squares';
case PieceShiftMethod.either:
return 'Either';
}
}

enum Mode {
botPlay,
freePlay,
Expand All @@ -51,6 +62,7 @@ class _HomePageState extends State<HomePage> {
ValidMoves validMoves = IMap(const {});
Side sideToMove = Side.white;
PieceSet pieceSet = PieceSet.merida;
PieceShiftMethod pieceShiftMethod = PieceShiftMethod.either;
BoardTheme boardTheme = BoardTheme.blue;
bool drawMode = true;
bool pieceAnimation = true;
Expand Down Expand Up @@ -129,6 +141,7 @@ class _HomePageState extends State<HomePage> {
});
},
),
pieceShiftMethod: pieceShiftMethod,
),
data: BoardData(
interactableSide: playMode == Mode.botPlay
Expand Down Expand Up @@ -242,6 +255,30 @@ class _HomePageState extends State<HomePage> {
),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
children: [
ElevatedButton(
child: Text(
'Piece shift method: ${pieceShiftMethodLabel(pieceShiftMethod)}'),
onPressed: () => _showChoicesPicker<PieceShiftMethod>(
context,
choices: PieceShiftMethod.values,
selectedItem: pieceShiftMethod,
labelBuilder: (t) => Text(pieceShiftMethodLabel(t)),
onSelectedItemChanged: (PieceShiftMethod? value) {
setState(() {
if (value != null) {
pieceShiftMethod = value;
}
});
},
),
),
const SizedBox(width: 8),
],
),
if (playMode == Mode.freePlay)
Center(
child: IconButton(
Expand Down
2 changes: 1 addition & 1 deletion example/pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ packages:
path: ".."
relative: true
source: path
version: "3.1.2"
version: "3.2.0"
clock:
dependency: transitive
description:
Expand Down
20 changes: 20 additions & 0 deletions lib/src/board_settings.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,18 @@ import 'models.dart';
import 'piece_set.dart';
import 'draw_shape_options.dart';

/// Describes how moves are made on an interactive board.
enum PieceShiftMethod {
tom-anders marked this conversation as resolved.
Show resolved Hide resolved
/// First tap the piece to be moved, then tap the target square.
tapTwoSquares,

/// Drag-and-drop the piece to the target square.
drag,

/// Both tap and drag are enabled.
either;
}

/// Board settings that control the theme, behavior and purpose of the board.
///
/// This is meant for fixed settings that don't change during a game. Sensible
Expand Down Expand Up @@ -33,6 +45,7 @@ class BoardSettings {
this.enablePremoveCastling = true,
this.autoQueenPromotion = false,
this.autoQueenPromotionOnPremove = true,
this.pieceShiftMethod = PieceShiftMethod.either,
});

/// Theme of the board
Expand Down Expand Up @@ -79,6 +92,9 @@ class BoardSettings {
/// automatically to queen only if the premove is confirmed
final bool autoQueenPromotionOnPremove;

/// Controls how moves are made.
final PieceShiftMethod pieceShiftMethod;

/// Shape drawing options object containing data about how new shapes can be drawn.
final DrawShapeOptions drawShape;

Expand All @@ -105,6 +121,7 @@ class BoardSettings {
other.enablePremoveCastling == enablePremoveCastling &&
other.autoQueenPromotion == autoQueenPromotion &&
other.autoQueenPromotionOnPremove == autoQueenPromotionOnPremove &&
other.pieceShiftMethod == pieceShiftMethod &&
other.drawShape == drawShape;
}

Expand All @@ -124,6 +141,7 @@ class BoardSettings {
enablePremoveCastling,
autoQueenPromotion,
autoQueenPromotionOnPremove,
pieceShiftMethod,
drawShape,
);

Expand All @@ -142,6 +160,7 @@ class BoardSettings {
bool? enablePremoveCastling,
bool? autoQueenPromotion,
bool? autoQueenPromotionOnPremove,
PieceShiftMethod? pieceShiftMethod,
DrawShapeOptions? drawShape,
}) {
return BoardSettings(
Expand All @@ -161,6 +180,7 @@ class BoardSettings {
autoQueenPromotionOnPremove:
autoQueenPromotionOnPremove ?? this.autoQueenPromotionOnPremove,
autoQueenPromotion: autoQueenPromotion ?? this.autoQueenPromotion,
pieceShiftMethod: pieceShiftMethod ?? this.pieceShiftMethod,
drawShape: drawShape ?? this.drawShape,
);
}
Expand Down
8 changes: 7 additions & 1 deletion lib/src/widgets/board.dart
Original file line number Diff line number Diff line change
Expand Up @@ -557,6 +557,10 @@ class _BoardState extends State<Board> {
_premoveDests = null;
});
}

if (widget.settings.pieceShiftMethod == PieceShiftMethod.drag) {
_shouldDeselectOnTapUp = true;
}
}

void _onPointerMove(PointerMoveEvent details) {
Expand All @@ -577,7 +581,9 @@ class _BoardState extends State<Board> {
}

if (_currentPointerDownEvent == null ||
_currentPointerDownEvent!.pointer != details.pointer) return;
_currentPointerDownEvent!.pointer != details.pointer ||
widget.settings.pieceShiftMethod == PieceShiftMethod.tapTwoSquares)
return;

final distance =
(details.position - _currentPointerDownEvent!.position).distance;
Expand Down
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: chessground
description: Chess board UI developed for lichess.org. It has no chess logic inside so it can be used for chess variants.
version: 3.1.2
version: 3.2.0
repository: https://github.com/lichess-org/flutter-chessground
funding:
- https://lichess.org/patron
Expand Down
63 changes: 63 additions & 0 deletions test/widgets/board_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,33 @@ void main() {
expect(find.byKey(const Key('e4-lastMove')), findsOneWidget);
});

testWidgets('Cannot move by tap if piece shift method is drag',
(WidgetTester tester) async {
await tester.pumpWidget(
buildBoard(
initialInteractableSide: InteractableSide.both,
pieceShiftMethod: PieceShiftMethod.drag,
),
);
await tester.tap(find.byKey(const Key('e2-whitePawn')));
await tester.pump();

// Tapping a square should have no effect...
expect(find.byKey(const Key('e2-selected')), findsNothing);
expect(find.byType(MoveDest), findsNothing);
tom-anders marked this conversation as resolved.
Show resolved Hide resolved

// ... but move by drag should work
await tester.dragFrom(
squareOffset('e2'),
const Offset(0, -(squareSize * 2)),
);
await tester.pumpAndSettle();
expect(find.byKey(const Key('e4-whitePawn')), findsOneWidget);
expect(find.byKey(const Key('e2-whitePawn')), findsNothing);
expect(find.byKey(const Key('e2-lastMove')), findsOneWidget);
expect(find.byKey(const Key('e4-lastMove')), findsOneWidget);
});

testWidgets('castling by taping king then rook is possible',
(WidgetTester tester) async {
await tester.pumpWidget(
Expand Down Expand Up @@ -170,6 +197,40 @@ void main() {
expect(find.byKey(const Key('e4-lastMove')), findsOneWidget);
});

testWidgets('Cannot move by drag if piece shift method is tapTwoSquares', (
WidgetTester tester,
) async {
await tester.pumpWidget(
buildBoard(
initialInteractableSide: InteractableSide.both,
pieceShiftMethod: PieceShiftMethod.tapTwoSquares,
),
);
await tester.dragFrom(
squareOffset('e2'),
const Offset(0, -(squareSize * 2)),
);
await tester.pumpAndSettle();
expect(find.byKey(const Key('e4-whitePawn')), findsNothing);
expect(find.byKey(const Key('e2-whitePawn')), findsOneWidget);
expect(find.byKey(const Key('e2-lastMove')), findsNothing);
expect(find.byKey(const Key('e4-lastMove')), findsNothing);

// Original square is still selected after drag attempt
expect(find.byKey(const Key('e2-selected')), findsOneWidget);
expect(find.byType(MoveDest), findsNWidgets(2));
tom-anders marked this conversation as resolved.
Show resolved Hide resolved

// ...so we can still tap to move
await tester.tapAt(squareOffset('e4'));
await tester.pump();
expect(find.byKey(const Key('e2-selected')), findsNothing);
expect(find.byType(MoveDest), findsNothing);
expect(find.byKey(const Key('e4-whitePawn')), findsOneWidget);
expect(find.byKey(const Key('e2-whitePawn')), findsNothing);
expect(find.byKey(const Key('e2-lastMove')), findsOneWidget);
expect(find.byKey(const Key('e4-lastMove')), findsOneWidget);
});

tom-anders marked this conversation as resolved.
Show resolved Hide resolved
testWidgets(
'2 simultaneous pointer down events will cancel current drag/selection',
(
Expand Down Expand Up @@ -859,6 +920,7 @@ Widget buildBoard({
String initialFen = dc.kInitialFEN,
ISet<Shape>? initialShapes,
bool enableDrawingShapes = false,
PieceShiftMethod pieceShiftMethod = PieceShiftMethod.either,

/// play the first available move for the opponent after a delay of 200ms
bool shouldPlayOpponentMove = false,
Expand All @@ -884,6 +946,7 @@ Widget buildBoard({
},
newShapeColor: const Color(0xFF0000FF),
),
pieceShiftMethod: pieceShiftMethod,
);

return Board(
Expand Down
Loading