Skip to content
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: 3 additions & 1 deletion lib/src/constants.dart
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,10 @@ final List<BoxShadow> boardShadows = defaultTargetPlatform == TargetPlatform.iOS

const kMaxClockTextScaleFactor = 1.94;
const kEmptyWidget = SizedBox.shrink();
const kEmptyFen = '8/8/8/8/8/8/8/8 w - - 0 1';
const kTabletBoardTableSidePadding = 16.0;

/// In crazyhouse, when displaying pockets above/below the board, add this much additional side padding to make the board smaller and avoid overflows.
const kAdditionalBoardSidePaddingForPockets = 70.0;
const kBottomBarHeight = 56.0;
const kMaterialPopupMenuMaxWidth = 500.0;

Expand Down
3 changes: 3 additions & 0 deletions lib/src/model/common/chess.dart
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ const ISet<Variant> readSupportedVariants = ISetConst({
Variant.threeCheck,
Variant.racingKings,
Variant.horde,
Variant.crazyhouse,
});

/// Set of supported variants for playing a game.
Expand Down Expand Up @@ -133,6 +134,8 @@ enum Variant {

bool get isPlaySupported => playSupportedVariants.contains(this);

bool get hasDropMoves => this == Variant.crazyhouse;

static final IMap<String, Variant> nameMap = IMap(values.asNameMap());

static Variant fromRule(Rule rule) {
Expand Down
43 changes: 43 additions & 0 deletions lib/src/model/game/game_board_params.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import 'package:chessground/chessground.dart';
import 'package:dartchess/dartchess.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:lichess_mobile/src/model/common/chess.dart';
Comment thread
tom-anders marked this conversation as resolved.

part 'game_board_params.freezed.dart';

@freezed
sealed class GameBoardParams with _$GameBoardParams {
const GameBoardParams._();

const factory GameBoardParams.readonly({
required String fen,
required Variant variant,
required Pockets? pockets,
}) = ReadonlyBoardParams;

const factory GameBoardParams.interactive({
required Variant variant,
required Position position,
required PlayerSide playerSide,
required NormalMove? promotionMove,
required void Function(Move, {bool? viaDragAndDrop}) onMove,
required void Function(Role? role) onPromotionSelection,
required Premovable? premovable,
}) = InteractiveBoardParams;

static const emptyBoard = ReadonlyBoardParams(
fen: kEmptyFEN,
Comment thread
tom-anders marked this conversation as resolved.
variant: Variant.standard,
pockets: null,
);

String get fen => switch (this) {
ReadonlyBoardParams(:final fen) => fen,
InteractiveBoardParams(:final position) => position.fen,
};

Pockets? get pockets => switch (this) {
ReadonlyBoardParams(:final pockets) => pockets,
InteractiveBoardParams(:final position) => position.pockets,
};
Comment thread
tom-anders marked this conversation as resolved.
}
2 changes: 1 addition & 1 deletion lib/src/model/tv/tv_channel.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ enum TvChannel {
// atomic('Atomic', LichessIcons.atom),
horde('Horde', LichessIcons.horde),
racingKings('Racing Kings', LichessIcons.racing_kings),
// crazyhouse('Crazyhouse', LichessIcons.h_square),
crazyhouse('Crazyhouse', LichessIcons.h_square),
ultraBullet('UltraBullet', LichessIcons.ultrabullet),
bot('Bot', LichessIcons.cogs),
computer('Computer', LichessIcons.cogs);
Expand Down
9 changes: 9 additions & 0 deletions lib/src/utils/screen.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import 'package:lichess_mobile/src/constants.dart';
import 'package:lichess_mobile/src/widgets/pockets.dart';

/// Returns the estimated height of what is left after removing the height of the board from the screen.
double estimateHeightMinusBoard(MediaQueryData mediaQuery) {
Expand Down Expand Up @@ -44,6 +45,14 @@ bool isTabletOrLarger(BuildContext context) {
return getScreenType(context) >= ScreenType.tablet;
}

/// How big a square in the [PocketsMenu] should be, based on the size of the board and whether the device is a tablet.
double pocketSquareSize({required double boardSize, required bool isTablet}) {
final squareSize = boardSize / 8;
// On tablets, displaying the pockets at the same size as regular pieces
// can lead to overflows and looks weird, so reduce the size a bit.
return isTablet ? 0.7 * squareSize : squareSize;
}

enum ScreenType { watch, handset, tablet, desktop }

extension ScreenTypeComparisonOperators on ScreenType {
Expand Down
1 change: 1 addition & 0 deletions lib/src/view/analysis/analysis_board.dart
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ abstract class AnalysisBoardState<
onClearShapes: _onClearShapes,
newShapeColor: boardPrefs.shapeColor.color,
),
enableDropMoves: analysisState.variant.hasDropMoves,
),
);
}
Expand Down
64 changes: 63 additions & 1 deletion lib/src/view/analysis/analysis_layout.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import 'package:lichess_mobile/src/utils/screen.dart';
import 'package:lichess_mobile/src/view/engine/engine_gauge.dart';
import 'package:lichess_mobile/src/widgets/adaptive_action_sheet.dart';
import 'package:lichess_mobile/src/widgets/buttons.dart';
import 'package:lichess_mobile/src/widgets/pockets.dart';

/// The height of the board header or footer in the analysis layout.
const kAnalysisBoardHeaderOrFooterHeight = 26.0;
Expand Down Expand Up @@ -129,11 +130,13 @@ class AnalysisLayout extends StatelessWidget {
required this.boardBuilder,
required this.children,
required this.pov,
required this.sideToMove,
this.boardHeader,
this.boardFooter,
this.engineGaugeBuilder,
this.engineLines,
this.bottomBar,
this.pockets,
super.key,
});

Expand All @@ -146,6 +149,9 @@ class AnalysisLayout extends StatelessWidget {
/// The side the board is displayed from.
final Side pov;

/// The side to move. In crazyhouse, this enables the [PocketsMenu] of this side.
final Side? sideToMove;

/// A widget to show above the board.
///
/// The widget will included in a parent container with a height of
Expand Down Expand Up @@ -173,6 +179,11 @@ class AnalysisLayout extends StatelessWidget {
/// A widget to show at the bottom of the screen.
final Widget? bottomBar;

/// Current state of the pockets, in variants like crazyhouse.
///
/// If not null, will render a [PocketsMenu] for each player.
final Pockets? pockets;

@override
Widget build(BuildContext context) {
return Column(
Expand Down Expand Up @@ -202,6 +213,7 @@ class AnalysisLayout extends StatelessWidget {
: constraints.biggest.longestSide / kGoldenRatio -
(kTabletBoardTableSidePadding * 2)) -
headerAndFooterHeight;

return Padding(
padding: const EdgeInsets.all(kTabletBoardTableSidePadding),
child: Row(
Expand Down Expand Up @@ -269,13 +281,39 @@ class AnalysisLayout extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
if (engineLines != null) engineLines!,
if (pockets != null)
Align(
alignment: Alignment.center,
child: PocketsMenu(
side: pov.opposite,
sideToMove: sideToMove,
pockets: pockets!,
squareSize: pocketSquareSize(
boardSize: boardSize,
isTablet: isTablet,
),
),
),
Expanded(
child: Card(
clipBehavior: Clip.hardEdge,
semanticContainer: false,
child: TabBarView(controller: tabController, children: children),
),
),
if (pockets != null)
Align(
alignment: Alignment.center,
child: PocketsMenu(
side: pov,
sideToMove: sideToMove,
pockets: pockets!,
squareSize: pocketSquareSize(
boardSize: boardSize,
isTablet: isTablet,
),
),
),
],
),
),
Expand All @@ -289,7 +327,10 @@ class AnalysisLayout extends StatelessWidget {
final isSmallScreen = remainingHeight < kSmallHeightMinusBoard;
final evalGaugeSize = engineGaugeBuilder != null ? evalGaugeWidth : 0.0;
final boardSize = isTablet || isSmallScreen
? defaultBoardSize - evalGaugeSize - kTabletBoardTableSidePadding * 2
? defaultBoardSize -
evalGaugeSize -
kTabletBoardTableSidePadding * 2 -
(pockets != null ? kAdditionalBoardSidePaddingForPockets : 0.0)
: defaultBoardSize - evalGaugeSize;

return Column(
Expand All @@ -304,6 +345,16 @@ class AnalysisLayout extends StatelessWidget {
: EdgeInsets.zero,
child: Column(
children: [
if (pockets != null)
PocketsMenu(
side: pov.opposite,
sideToMove: sideToMove,
pockets: pockets!,
squareSize: pocketSquareSize(
boardSize: boardSize,
isTablet: isTablet,
),
),
if (boardHeader != null)
// This key is used to preserve the state of the board header when the pov changes
Container(
Expand All @@ -321,6 +372,7 @@ class AnalysisLayout extends StatelessWidget {
child: boardHeader,
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
boardBuilder(
context,
Expand Down Expand Up @@ -349,6 +401,16 @@ class AnalysisLayout extends StatelessWidget {
height: kAnalysisBoardHeaderOrFooterHeight,
child: boardFooter,
),
if (pockets != null)
PocketsMenu(
side: pov,
sideToMove: sideToMove,
pockets: pockets!,
squareSize: pocketSquareSize(
boardSize: boardSize,
isTablet: isTablet,
),
),
],
),
),
Expand Down
2 changes: 2 additions & 0 deletions lib/src/view/analysis/analysis_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,7 @@ class _Body extends ConsumerWidget {
child: AnalysisLayout(
tabController: controller,
pov: pov,
sideToMove: analysisState.currentPosition.turn,
boardBuilder: (context, boardSize, borderRadius) =>
GameAnalysisBoard(options: options, boardSize: boardSize, boardRadius: borderRadius),
boardHeader: boardHeader,
Expand All @@ -379,6 +380,7 @@ class _Body extends ConsumerWidget {
)
: null,
bottomBar: _BottomBar(options: options),
pockets: analysisState.currentPosition.pockets,
children: [
ExplorerView(
pov: pov,
Expand Down
1 change: 1 addition & 0 deletions lib/src/view/analysis/retro_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ class _RetroScreen extends ConsumerWidget {

return AnalysisLayout(
pov: state.pov,
sideToMove: state.currentPosition.turn,
boardBuilder: (context, boardSize, borderRadius) =>
RetroAnalysisBoard(options, boardSize: boardSize, boardRadius: borderRadius),
engineGaugeBuilder: (context) {
Expand Down
2 changes: 1 addition & 1 deletion lib/src/view/board_editor/board_editor_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -395,7 +395,7 @@ class _BottomBar extends ConsumerWidget {
BottomSheetAction(
makeLabel: (context) => Text(context.l10n.clearBoard),
onPressed: () {
ref.read(editorController.notifier).loadFen(kEmptyFen);
ref.read(editorController.notifier).loadFen(kEmptyFEN);
},
),
],
Expand Down
1 change: 1 addition & 0 deletions lib/src/view/broadcast/broadcast_game_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,7 @@ class _Body extends ConsumerWidget {

return AnalysisLayout(
pov: pov,
sideToMove: state.currentPosition.turn,
tabController: tabController,
boardBuilder: (context, boardSize, borderRadius) => BroadcastAnalysisBoard(
roundId: roundId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import 'package:lichess_mobile/src/model/common/service/sound_service.dart';
import 'package:lichess_mobile/src/model/correspondence/correspondence_game_storage.dart';
import 'package:lichess_mobile/src/model/correspondence/offline_correspondence_game.dart';
import 'package:lichess_mobile/src/model/game/game.dart';
import 'package:lichess_mobile/src/model/game/game_board_params.dart';
import 'package:lichess_mobile/src/model/game/game_status.dart';
import 'package:lichess_mobile/src/model/game/material_diff.dart';
import 'package:lichess_mobile/src/model/settings/board_preferences.dart';
Expand Down Expand Up @@ -189,7 +190,7 @@ class _BodyState extends ConsumerState<_Body> {
isBoardTurned: isBoardTurned,
),
lastMove: game.moveAt(stepCursor),
interactiveBoardParams: (
boardParams: GameBoardParams.interactive(
variant: game.meta.variant,
position: position,
playerSide: game.playable && !isReplaying
Expand Down
3 changes: 2 additions & 1 deletion lib/src/view/game/game_body.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import 'package:lichess_mobile/src/model/account/account_repository.dart';
import 'package:lichess_mobile/src/model/account/ongoing_game.dart';
import 'package:lichess_mobile/src/model/common/id.dart';
import 'package:lichess_mobile/src/model/common/speed.dart';
import 'package:lichess_mobile/src/model/game/game_board_params.dart';
import 'package:lichess_mobile/src/model/game/game_controller.dart';
import 'package:lichess_mobile/src/model/game/game_preferences.dart';
import 'package:lichess_mobile/src/model/game/playable_game.dart';
Expand Down Expand Up @@ -258,7 +259,7 @@ class GameBody extends ConsumerWidget {
isBoardTurned: isBoardTurned,
),
lastMove: gameState.game.moveAt(gameState.stepCursor),
interactiveBoardParams: (
boardParams: GameBoardParams.interactive(
variant: gameState.game.meta.variant,
position: gameState.currentPosition,
playerSide: gameState.game.playable && !gameState.isReplaying
Expand Down
Loading