Skip to content

Commit

Permalink
Merge branch 'tom-anders-flipDragOffset'
Browse files Browse the repository at this point in the history
  • Loading branch information
veloce committed Sep 2, 2024
2 parents 9f05635 + ab934ac commit 43e4a32
Show file tree
Hide file tree
Showing 6 changed files with 157 additions and 16 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,16 @@
- Promotion state is now lifted up to the parent widget, in order to allow more
control over the promotion dialog.
- `ChessboardEditor` now supports highlighting squares.
- Flip `BoardSettings.dragFeedbackOffset.dy` for flipped pieces.
Support displaying all pieces upside down based on side to move.
- Fix: ensure the board background does not overflow the board.

### Breaking changes:
- `Chessboard` now require a `game` parameter of type `GameData` instead
of `BoardData`.
- Added required parameters `piece` and `pieceAssets` to `PieceShape`, removed `role`. Added optional
`opacity` parameter.
- Remove 'ChessboardState.opponentsPiecesUpsideDown' in favor of `ChessboardSettings.pieceOrientationBehavior`.

## 4.0.0

Expand Down
2 changes: 1 addition & 1 deletion example/android/build.gradle
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
buildscript {
ext.kotlin_version = '1.6.10'
ext.kotlin_version = '1.7.10'
repositories {
google()
mavenCentral()
Expand Down
4 changes: 3 additions & 1 deletion example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -307,9 +307,11 @@ class _HomePageState extends State<HomePage> {
),
pieceShiftMethod: pieceShiftMethod,
autoQueenPromotionOnPremove: false,
pieceOrientationBehavior: playMode == Mode.freePlay
? PieceOrientationBehavior.opponentUpsideDown
: PieceOrientationBehavior.facingUser,
),
orientation: orientation,
opponentsPiecesUpsideDown: playMode == Mode.freePlay,
fen: fen,
lastMove: lastMove,
game: GameData(
Expand Down
21 changes: 21 additions & 0 deletions lib/src/board_settings.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,18 @@ enum PieceShiftMethod {
either;
}

/// Describes how pieces on the board are oriented.
enum PieceOrientationBehavior {
/// Pieces are always facing user (the default).
facingUser,

/// Opponent's pieces are upside down, for over the board play face to face.
opponentUpsideDown,

/// Piece orientation matches side to play, for over the board play where each user grabs the device in turn.
sideToPlay,
}

/// Board settings that controls visual aspects and behavior of the board.
///
/// This is meant for fixed settings that don't change during a game. Sensible
Expand All @@ -37,6 +49,7 @@ class ChessboardSettings {
this.blindfoldMode = false,
this.dragFeedbackScale = 2.0,
this.dragFeedbackOffset = const Offset(0.0, -1.0),
this.pieceOrientationBehavior = PieceOrientationBehavior.facingUser,

// shape drawing
this.drawShape = const DrawShapeOptions(),
Expand Down Expand Up @@ -81,6 +94,9 @@ class ChessboardSettings {
// Offset for the piece currently under drag
final Offset dragFeedbackOffset;

/// Controls if any pieces are displayed upside down.
final PieceOrientationBehavior pieceOrientationBehavior;

/// Whether castling is enabled with a premove.
final bool enablePremoveCastling;

Expand Down Expand Up @@ -118,6 +134,7 @@ class ChessboardSettings {
other.blindfoldMode == blindfoldMode &&
other.dragFeedbackScale == dragFeedbackScale &&
other.dragFeedbackOffset == dragFeedbackOffset &&
other.pieceOrientationBehavior == pieceOrientationBehavior &&
other.enablePremoveCastling == enablePremoveCastling &&
other.autoQueenPromotion == autoQueenPromotion &&
other.autoQueenPromotionOnPremove == autoQueenPromotionOnPremove &&
Expand All @@ -138,6 +155,7 @@ class ChessboardSettings {
blindfoldMode,
dragFeedbackScale,
dragFeedbackOffset,
pieceOrientationBehavior,
enablePremoveCastling,
autoQueenPromotion,
autoQueenPromotionOnPremove,
Expand All @@ -157,6 +175,7 @@ class ChessboardSettings {
bool? blindfoldMode,
double? dragFeedbackScale,
Offset? dragFeedbackOffset,
PieceOrientationBehavior? pieceOrientationBehavior,
bool? enablePremoveCastling,
bool? autoQueenPromotion,
bool? autoQueenPromotionOnPremove,
Expand All @@ -175,6 +194,8 @@ class ChessboardSettings {
blindfoldMode: blindfoldMode ?? this.blindfoldMode,
dragFeedbackScale: dragFeedbackScale ?? this.dragFeedbackScale,
dragFeedbackOffset: dragFeedbackOffset ?? this.dragFeedbackOffset,
pieceOrientationBehavior:
pieceOrientationBehavior ?? this.pieceOrientationBehavior,
enablePremoveCastling:
enablePremoveCastling ?? this.enablePremoveCastling,
autoQueenPromotionOnPremove:
Expand Down
31 changes: 19 additions & 12 deletions lib/src/widgets/board.dart
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,7 @@ class _BoardState extends State<Chessboard> {
size: widget.squareSize,
pieceAssets: widget.settings.pieceAssets,
blindfoldMode: widget.settings.blindfoldMode,
upsideDown: _isUpsideDown(entry.value),
upsideDown: _isUpsideDown(entry.value.color),
onComplete: () {
setState(() {
fadingPieces.remove(entry.key);
Expand All @@ -291,7 +291,7 @@ class _BoardState extends State<Chessboard> {
size: widget.squareSize,
pieceAssets: widget.settings.pieceAssets,
blindfoldMode: widget.settings.blindfoldMode,
upsideDown: _isUpsideDown(entry.value),
upsideDown: _isUpsideDown(entry.value.color),
),
),
for (final entry in translatingPieces.entries)
Expand All @@ -315,7 +315,7 @@ class _BoardState extends State<Chessboard> {
size: widget.squareSize,
pieceAssets: widget.settings.pieceAssets,
blindfoldMode: widget.settings.blindfoldMode,
upsideDown: _isUpsideDown(entry.value.piece),
upsideDown: _isUpsideDown(entry.value.piece.color),
),
),
),
Expand Down Expand Up @@ -378,8 +378,7 @@ class _BoardState extends State<Chessboard> {
size: widget.size,
color: widget.game!.sideToMove,
orientation: widget.orientation,
piecesUpsideDown: widget.opponentsPiecesUpsideDown &&
widget.game?.sideToMove != widget.orientation,
piecesUpsideDown: _isUpsideDown(widget.game!.sideToMove),
onSelect: widget.game!.onPromotionSelection,
onCancel: () {
widget.game!.onPromotionSelection(null);
Expand Down Expand Up @@ -767,6 +766,10 @@ class _BoardState extends State<Chessboard> {
_draggedPieceSquare = square;
});
_renderBox ??= context.findRenderObject()! as RenderBox;

final dragFeedbackOffsetY = (_isUpsideDown(piece.color) ? -1 : 1) *
widget.settings.dragFeedbackOffset.dy;

_dragAvatar = _DragAvatar(
overlayState: Overlay.of(context, debugRequiredFor: widget),
initialPosition: origin.position,
Expand All @@ -783,14 +786,14 @@ class _BoardState extends State<Chessboard> {
pieceFeedback: Transform.translate(
offset: Offset(
((widget.settings.dragFeedbackOffset.dx - 1) * feedbackSize) / 2,
((widget.settings.dragFeedbackOffset.dy - 1) * feedbackSize) / 2,
((dragFeedbackOffsetY - 1) * feedbackSize) / 2,
),
child: PieceWidget(
piece: piece,
size: feedbackSize,
pieceAssets: widget.settings.pieceAssets,
blindfoldMode: widget.settings.blindfoldMode,
upsideDown: _isUpsideDown(piece),
upsideDown: _isUpsideDown(piece.color),
),
),
);
Expand All @@ -817,12 +820,16 @@ class _BoardState extends State<Chessboard> {
_shouldCancelPremoveOnTapUp = false;
}

/// Whether the piece should be displayed upside down, according to the
/// Whether the piece with this color should be displayed upside down, according to the
/// widget settings.
bool _isUpsideDown(Piece piece) {
return widget.opponentsPiecesUpsideDown &&
piece.color != widget.orientation;
}
bool _isUpsideDown(Side pieceColor) =>
switch (widget.settings.pieceOrientationBehavior) {
PieceOrientationBehavior.facingUser => false,
PieceOrientationBehavior.opponentUpsideDown =>
pieceColor == widget.orientation.opposite,
PieceOrientationBehavior.sideToPlay =>
widget.game?.sideToMove == widget.orientation.opposite,
};

/// Whether the piece is movable by the current side to move.
bool _isMovable(Piece? piece) {
Expand Down
112 changes: 110 additions & 2 deletions test/widgets/board_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1195,12 +1195,120 @@ void main() {
expect(find.byType(ShapeWidget), findsNothing);
});
});

group('piece orientation behavior', () {
void checkUpsideDownPieces(
WidgetTester tester, {
required bool expectWhiteUpsideDown,
required bool expectBlackUpsideDown,
}) {
final pieceWidgets =
tester.widgetList<PieceWidget>(find.byType(PieceWidget));
expect(pieceWidgets, hasLength(32));
for (final pieceWidget in pieceWidgets) {
if (pieceWidget.piece.color == Side.white) {
expect(pieceWidget.upsideDown, expectWhiteUpsideDown);
} else {
expect(pieceWidget.upsideDown, expectBlackUpsideDown);
}
}
}

testWidgets('facing user', (WidgetTester tester) async {
for (final orientation in Side.values) {
await tester.pumpWidget(
buildBoard(
settings: const ChessboardSettings(
animationDuration: Duration.zero,
),
initialPlayerSide: PlayerSide.both,
orientation: orientation,
),
);

checkUpsideDownPieces(
tester,
expectWhiteUpsideDown: false,
expectBlackUpsideDown: false,
);

await makeMove(tester, Square.e2, Square.e4);

checkUpsideDownPieces(
tester,
expectWhiteUpsideDown: false,
expectBlackUpsideDown: false,
);
}
});

testWidgets('opponent upside down', (WidgetTester tester) async {
for (final orientation in Side.values) {
await tester.pumpWidget(
buildBoard(
initialPlayerSide: PlayerSide.both,
orientation: orientation,
settings: const ChessboardSettings(
animationDuration: Duration.zero,
pieceOrientationBehavior:
PieceOrientationBehavior.opponentUpsideDown,
),
),
);

checkUpsideDownPieces(
tester,
expectWhiteUpsideDown: orientation != Side.white,
expectBlackUpsideDown: orientation == Side.white,
);

await makeMove(tester, Square.e2, Square.e4);

checkUpsideDownPieces(
tester,
expectWhiteUpsideDown: orientation != Side.white,
expectBlackUpsideDown: orientation == Side.white,
);
}
});

testWidgets('side to play', (WidgetTester tester) async {
for (final orientation in Side.values) {
await tester.pumpWidget(
buildBoard(
initialPlayerSide: PlayerSide.both,
orientation: orientation,
settings: const ChessboardSettings(
animationDuration: Duration.zero,
pieceOrientationBehavior: PieceOrientationBehavior.sideToPlay,
),
),
);

checkUpsideDownPieces(
tester,
expectWhiteUpsideDown: orientation != Side.white,
expectBlackUpsideDown: orientation != Side.white,
);

await makeMove(tester, Square.e2, Square.e4);

checkUpsideDownPieces(
tester,
expectWhiteUpsideDown: orientation == Side.white,
expectBlackUpsideDown: orientation == Side.white,
);
}
});
});
}

Future<void> makeMove(WidgetTester tester, Square from, Square to) async {
await tester.tapAt(squareOffset(from));
final orientation =
tester.widget<Chessboard>(find.byType(Chessboard)).orientation;
await tester.tapAt(squareOffset(from, orientation: orientation));
await tester.pump();
await tester.tapAt(squareOffset(to));
await tester.tapAt(squareOffset(to, orientation: orientation));
await tester.pump();
}

Expand Down

0 comments on commit 43e4a32

Please sign in to comment.