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

Improve Accessibility #71

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
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
65 changes: 65 additions & 0 deletions lib/src/widgets/board.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'dart:async';
import 'package:chessground/src/widgets/geometry.dart';
import 'package:dartchess/dartchess.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/semantics.dart';
import 'package:flutter/widgets.dart';
import 'package:fast_immutable_collections/fast_immutable_collections.dart';

Expand All @@ -20,6 +21,12 @@ import '../fen.dart';
import '../premove.dart';
import '../board_settings.dart';

typedef SemanticBuilder =
String? Function(
({Square square, Piece? piece}) highlighted,
({Square square, Piece? piece})? selected,
);

/// Number of logical pixels that have to be dragged before a drag starts.
const double _kDragDistanceThreshold = 3.0;

Expand Down Expand Up @@ -48,6 +55,8 @@ class Chessboard extends StatefulWidget with ChessboardGeometry {
required this.game,
this.shapes,
this.annotations,
this.squareSemanticValueBuilder,
this.squareSemanticHintBuilder,
}) : _size = size;

/// Creates a new chessboard widget that cannot be interacted with.
Expand All @@ -65,6 +74,8 @@ class Chessboard extends StatefulWidget with ChessboardGeometry {
this.onTouchedSquare,
this.shapes,
this.annotations,
this.squareSemanticValueBuilder,
this.squareSemanticHintBuilder,
}) : _size = size,
game = null,
opponentsPiecesUpsideDown = false;
Expand Down Expand Up @@ -111,6 +122,10 @@ class Chessboard extends StatefulWidget with ChessboardGeometry {
/// Move annotations to be displayed on the board.
final IMap<Square, Annotation>? annotations;

final SemanticBuilder? squareSemanticValueBuilder;

final SemanticBuilder? squareSemanticHintBuilder;

/// Whether the pieces can be moved by one side or both.
bool get interactive => game != null && game!.playerSide != PlayerSide.none;

Expand Down Expand Up @@ -223,6 +238,47 @@ class _BoardState extends State<Chessboard> {
dimension: widget.size,
child: background,
),

for (final square in Square.values)
PositionedSquare(
key: ValueKey('${square.name}-accessibility'),
size: widget.size,
orientation: widget.orientation,
square: square,
child: Semantics(
onTap: () {
_onSquarePointerDown(square);
_onSquarePointerUp(square);
},
attributedLabel: AttributedString(
square.name,
attributes: [
SpellOutStringAttribute(
range: TextRange(start: 0, end: square.name.length),
),
LocaleStringAttribute(
range: TextRange(start: 0, end: square.name.length),
locale: Localizations.localeOf(context),
),
],
),
value: widget.squareSemanticValueBuilder?.call(
(square: square, piece: pieces[square]),
selected != null
? (square: selected!, piece: pieces[selected])
: null,
),
hint: widget.squareSemanticHintBuilder?.call(
(square: square, piece: pieces[square]),
selected != null
? (square: selected!, piece: pieces[selected])
: null,
),
selected: selected == square,
child: const SizedBox.shrink(),
),
),

if (settings.showLastMove && widget.lastMove != null)
for (final square in widget.lastMove!.squares)
if (premove == null || !premove.hasSquare(square))
Expand Down Expand Up @@ -600,10 +656,15 @@ class _BoardState extends State<Chessboard> {
// pointer events
_currentPointerDownEvent = details;

_onSquarePointerDown(square);
}

void _onSquarePointerDown(Square square) {
// a piece was selected and the user taps on a different square:
// - try to move the piece to the target square
// - if the move was not possible but there is a movable piece under the
// target square, select it
final piece = pieces[square];
if (selected != null && square != selected) {
final couldMove = _tryMoveOrPremoveTo(square);
if (!couldMove && _isMovable(piece)) {
Expand Down Expand Up @@ -734,6 +795,10 @@ class _BoardState extends State<Chessboard> {

final square = widget.offsetSquare(details.localPosition);

_onSquarePointerUp(square);
}

void _onSquarePointerUp(Square? square) {
// handle pointer up while dragging a piece
if (_dragAvatar != null) {
bool shouldDeselect = true;
Expand Down
Loading