Merge remote-tracking branch 'Leptopoda/linting'
* Enabled strict linting. * Fix all the linting errors and disabled the rules that needed to much restructure. * Restructured the files. Known Issues: * Tap drawer menu item will get an exception when AI is thinking Change difficulty level to 30, thinking time to 60, select AI Vs. AI and tap Start Game, and tap on the settings in the drawer menu get the exception: This widget has been unmounted, so the State no longer has a context (and should be considered defunct). * Sound problem when this option is enabled, it does not take effect: Preferences -> Sound effects -> Keep mute when taking back The sound issue is related to some async not being appropriately handled. This leads to the game_page setting Audios. isTemporaryMute to false before Audios evaluated the value. This leads to the sound being played. Some out-of-order execution. It seems to be a thing with the code in general that Futures aren't really awaited. This will be quite some stuff to do and check later. * Performance Performance testing has not yet been conducted.
This commit is contained in:
commit
b04a0bb40f
|
@ -318,7 +318,7 @@ CMakeLists.txt.user*
|
|||
*.bak
|
||||
|
||||
# visual studio code
|
||||
.vscode/
|
||||
#.vscode/
|
||||
|
||||
# markdown temp files
|
||||
*.md.Html
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
# The .vscode folder contains launch configuration and tasks you configure in
|
||||
# VS Code which you may wish to be included in version control, so this line
|
||||
# is commented out by default.
|
||||
.vscode/
|
||||
#.vscode/
|
||||
|
||||
# Visual Studio
|
||||
.vs/
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"[dart]": {
|
||||
"editor.formatOnSave": true
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
include: package:lint/analysis_options.yaml
|
||||
|
||||
linter:
|
||||
rules:
|
||||
avoid_positional_boolean_parameters: false
|
||||
constant_identifier_names: false
|
||||
avoid_escaping_inner_quotes: false
|
||||
use_build_context_synchronously: false
|
||||
|
||||
analyzer:
|
||||
exclude:
|
||||
# generated code
|
||||
- lib/generated/**
|
||||
- lib/l10n/**
|
|
@ -19,11 +19,11 @@
|
|||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:sanmill/common/config.dart';
|
||||
import 'package:sanmill/common/constants.dart';
|
||||
import 'package:sanmill/shared/common/config.dart';
|
||||
import 'package:sanmill/shared/common/constants.dart';
|
||||
import 'package:sanmill/generated/l10n.dart';
|
||||
import 'package:sanmill/style/app_theme.dart';
|
||||
import 'package:sanmill/widgets/list_item_divider.dart';
|
||||
import 'package:sanmill/shared/list_item_divider.dart';
|
||||
import 'package:sanmill/shared/theme/app_theme.dart';
|
||||
|
||||
Map<String, Strings> languageCodeToStrings = {
|
||||
"ar": ArabicStrings(),
|
||||
|
@ -505,7 +505,7 @@ class Resources {
|
|||
}
|
||||
|
||||
Strings get strings {
|
||||
Strings? ret = languageCodeToStrings[languageCode];
|
||||
final Strings? ret = languageCodeToStrings[languageCode];
|
||||
|
||||
if (ret == null) {
|
||||
return EnglishStrings();
|
||||
|
@ -519,8 +519,9 @@ class Resources {
|
|||
}
|
||||
}
|
||||
|
||||
setLanguage(BuildContext context, var callback) async {
|
||||
var languageColumn = Column(
|
||||
Future<void> setLanguage(
|
||||
BuildContext context, Function(String?)? callback) async {
|
||||
final languageColumn = Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
RadioListTile(
|
||||
|
@ -530,7 +531,7 @@ setLanguage(BuildContext context, var callback) async {
|
|||
value: Constants.defaultLanguageCodeName,
|
||||
onChanged: callback,
|
||||
),
|
||||
ListItemDivider(),
|
||||
const ListItemDivider(),
|
||||
for (var i in languageCodeToStrings.keys)
|
||||
RadioListTile(
|
||||
activeColor: AppTheme.switchListTileActiveColor,
|
||||
|
@ -563,7 +564,7 @@ enum Bidirectionality {
|
|||
}
|
||||
|
||||
Bidirectionality getBidirectionality(BuildContext context) {
|
||||
Locale currentLocale = Localizations.localeOf(context);
|
||||
final Locale currentLocale = Localizations.localeOf(context);
|
||||
if (currentLocale.languageCode == "ar" ||
|
||||
currentLocale.languageCode == "fa" ||
|
||||
currentLocale.languageCode == "he" ||
|
||||
|
@ -578,8 +579,8 @@ Bidirectionality getBidirectionality(BuildContext context) {
|
|||
|
||||
String specialCountryAndRegion = "";
|
||||
|
||||
setSpecialCountryAndRegion(BuildContext context) {
|
||||
Locale currentLocale = Localizations.localeOf(context);
|
||||
void setSpecialCountryAndRegion(BuildContext context) {
|
||||
final Locale currentLocale = Localizations.localeOf(context);
|
||||
|
||||
switch (currentLocale.countryCode) {
|
||||
case "IR":
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
import 'dart:ui';
|
||||
|
||||
|
@ -27,41 +26,39 @@ import 'package:flutter/material.dart';
|
|||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:sanmill/common/constants.dart';
|
||||
import 'package:sanmill/generated/l10n.dart';
|
||||
import 'package:sanmill/l10n/resources.dart';
|
||||
import 'package:sanmill/screens/navigation_home_screen.dart';
|
||||
import 'package:sanmill/services/audios.dart';
|
||||
import 'package:sanmill/style/app_theme.dart';
|
||||
import 'package:sanmill/widgets/navigation_home_screen.dart';
|
||||
|
||||
import 'services/audios.dart';
|
||||
import 'package:sanmill/shared/common/constants.dart';
|
||||
import 'package:sanmill/shared/theme/app_theme.dart';
|
||||
|
||||
Future<void> main() async {
|
||||
var catcher = Catcher(
|
||||
rootWidget: BetterFeedback(
|
||||
child: SanmillApp(),
|
||||
//localeOverride: Locale(Resources.of().languageCode),
|
||||
),
|
||||
ensureInitialized: true);
|
||||
final catcher = Catcher(
|
||||
rootWidget: BetterFeedback(
|
||||
child: SanmillApp(),
|
||||
//localeOverride: Locale(Resources.of().languageCode),
|
||||
),
|
||||
ensureInitialized: true,
|
||||
);
|
||||
|
||||
String externalDirStr;
|
||||
try {
|
||||
Directory? externalDir = await getExternalStorageDirectory();
|
||||
final Directory? externalDir = await getExternalStorageDirectory();
|
||||
if (externalDir != null) {
|
||||
externalDirStr = externalDir.path.toString();
|
||||
externalDirStr = externalDir.path;
|
||||
} else {
|
||||
externalDirStr = ".";
|
||||
}
|
||||
} catch (e) {
|
||||
print(e);
|
||||
debugPrint(e.toString());
|
||||
externalDirStr = ".";
|
||||
}
|
||||
String path = externalDirStr + "/" + Constants.crashLogsFileName;
|
||||
print("[env] ExternalStorageDirectory: " + externalDirStr);
|
||||
String recipients = Constants.recipients;
|
||||
final String path = "$externalDirStr/${Constants.crashLogsFileName}";
|
||||
debugPrint("[env] ExternalStorageDirectory: $externalDirStr");
|
||||
final String recipients = Constants.recipients;
|
||||
|
||||
CatcherOptions debugOptions =
|
||||
CatcherOptions(PageReportMode(showStackTrace: true), [
|
||||
final CatcherOptions debugOptions = CatcherOptions(PageReportMode(), [
|
||||
ConsoleHandler(),
|
||||
FileHandler(File(path), printLogs: true),
|
||||
EmailManualHandler([recipients], printLogs: true)
|
||||
|
@ -71,14 +68,12 @@ Future<void> main() async {
|
|||
/// Release configuration.
|
||||
/// Same as above, but once user accepts dialog,
|
||||
/// user will be prompted to send email with crash to support.
|
||||
CatcherOptions releaseOptions =
|
||||
CatcherOptions(PageReportMode(showStackTrace: true), [
|
||||
final CatcherOptions releaseOptions = CatcherOptions(PageReportMode(), [
|
||||
FileHandler(File(path), printLogs: true),
|
||||
EmailManualHandler([recipients], printLogs: true)
|
||||
]);
|
||||
|
||||
CatcherOptions profileOptions =
|
||||
CatcherOptions(PageReportMode(showStackTrace: true), [
|
||||
final CatcherOptions profileOptions = CatcherOptions(PageReportMode(), [
|
||||
ConsoleHandler(),
|
||||
FileHandler(File(path), printLogs: true),
|
||||
EmailManualHandler([recipients], printLogs: true)
|
||||
|
@ -91,16 +86,16 @@ Future<void> main() async {
|
|||
profileConfig: profileOptions,
|
||||
);
|
||||
|
||||
print(window.physicalSize);
|
||||
print(Constants.windowAspectRatio);
|
||||
debugPrint(window.physicalSize.toString());
|
||||
debugPrint(Constants.windowAspectRatio.toString());
|
||||
|
||||
SystemChrome.setPreferredOrientations(
|
||||
[DeviceOrientation.portraitUp, DeviceOrientation.portraitDown],
|
||||
);
|
||||
|
||||
if (Platform.isAndroid && isLargeScreen()) {
|
||||
if (Platform.isAndroid && isLargeScreen) {
|
||||
SystemChrome.setSystemUIOverlayStyle(
|
||||
SystemUiOverlayStyle(
|
||||
const SystemUiOverlayStyle(
|
||||
statusBarColor: Colors.transparent,
|
||||
statusBarBrightness: Brightness.light,
|
||||
statusBarIconBrightness: Brightness.dark,
|
||||
|
@ -110,34 +105,20 @@ Future<void> main() async {
|
|||
);
|
||||
}
|
||||
|
||||
if (isSmallScreen()) {
|
||||
SystemChrome.setEnabledSystemUIOverlays([]);
|
||||
if (isSmallScreen) {
|
||||
SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: []);
|
||||
}
|
||||
}
|
||||
|
||||
RouteObserver<PageRoute> routeObserver = RouteObserver<PageRoute>();
|
||||
|
||||
final globalScaffoldKey = GlobalKey<ScaffoldState>();
|
||||
|
||||
class SanmillApp extends StatefulWidget {
|
||||
@override
|
||||
_SanmillAppState createState() => _SanmillAppState();
|
||||
}
|
||||
|
||||
class _SanmillAppState extends State<SanmillApp> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
if (Platform.isWindows) {
|
||||
print("[audio] Audio Player is not support Windows.");
|
||||
return;
|
||||
} else {
|
||||
Audios.loadSounds();
|
||||
}
|
||||
}
|
||||
class SanmillApp extends StatelessWidget {
|
||||
final globalScaffoldKey = GlobalKey<ScaffoldState>();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Audios.loadSounds();
|
||||
|
||||
setSpecialCountryAndRegion(context);
|
||||
|
||||
return MaterialApp(
|
||||
|
@ -146,7 +127,7 @@ class _SanmillAppState extends State<SanmillApp> {
|
|||
navigatorKey: Catcher.navigatorKey,
|
||||
key: globalScaffoldKey,
|
||||
navigatorObservers: [routeObserver],
|
||||
localizationsDelegates: [
|
||||
localizationsDelegates: const [
|
||||
// ... app-specific localization delegate[s] here
|
||||
S.delegate,
|
||||
GlobalMaterialLocalizations.delegate,
|
||||
|
@ -159,10 +140,10 @@ class _SanmillAppState extends State<SanmillApp> {
|
|||
debugShowCheckedModeBanner: false,
|
||||
home: Scaffold(
|
||||
body: DoubleBackToCloseApp(
|
||||
child: NavigationHomeScreen(),
|
||||
snackBar: SnackBar(
|
||||
content: Text(Resources.of().strings.tapBackAgainToLeave),
|
||||
),
|
||||
child: NavigationHomeScreen(),
|
||||
),
|
||||
),
|
||||
/*
|
||||
|
|
|
@ -16,63 +16,55 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import 'package:sanmill/common/config.dart';
|
||||
import 'package:sanmill/engine/engine.dart';
|
||||
|
||||
import 'position.dart';
|
||||
import 'types.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:sanmill/mill/position.dart';
|
||||
import 'package:sanmill/mill/types.dart';
|
||||
import 'package:sanmill/services/engine/engine.dart';
|
||||
import 'package:sanmill/shared/common/config.dart';
|
||||
|
||||
enum PlayerType { human, AI }
|
||||
Map<String, bool> isAi = {PieceColor.white: false, PieceColor.black: true};
|
||||
|
||||
// TODO: add constructor
|
||||
Game gameInstance = Game();
|
||||
|
||||
class Game {
|
||||
static Game? _instance;
|
||||
final String tag = "[game]";
|
||||
|
||||
static get instance {
|
||||
_instance ??= Game();
|
||||
return _instance;
|
||||
}
|
||||
|
||||
init() {
|
||||
void init() {
|
||||
_position = Position();
|
||||
_focusIndex = _blurIndex = invalidIndex;
|
||||
focusIndex = blurIndex = invalidIndex;
|
||||
}
|
||||
|
||||
start() {
|
||||
void start() {
|
||||
position.reset();
|
||||
|
||||
setWhoIsAi(engineType);
|
||||
}
|
||||
|
||||
newGame() {
|
||||
void newGame() {
|
||||
position.phase = Phase.ready;
|
||||
start();
|
||||
position.init();
|
||||
_focusIndex = _blurIndex = invalidIndex;
|
||||
focusIndex = blurIndex = invalidIndex;
|
||||
moveHistory = [""];
|
||||
sideToMove = PieceColor.white;
|
||||
}
|
||||
|
||||
String sideToMove = PieceColor.white;
|
||||
|
||||
bool? isAiToMove() {
|
||||
return isAi[sideToMove];
|
||||
bool get isAiToMove {
|
||||
assert(sideToMove == PieceColor.white || sideToMove == PieceColor.black);
|
||||
return isAi[sideToMove]!;
|
||||
}
|
||||
|
||||
List<String> moveHistory = [""];
|
||||
|
||||
Position _position = Position();
|
||||
get position => _position;
|
||||
Position get position => _position;
|
||||
|
||||
int _focusIndex = invalidIndex;
|
||||
int _blurIndex = invalidIndex;
|
||||
|
||||
get focusIndex => _focusIndex;
|
||||
set focusIndex(index) => _focusIndex = index;
|
||||
|
||||
get blurIndex => _blurIndex;
|
||||
set blurIndex(index) => _blurIndex = index;
|
||||
int focusIndex = invalidIndex;
|
||||
int blurIndex = invalidIndex;
|
||||
|
||||
Map<String, bool> isSearching = {
|
||||
PieceColor.white: false,
|
||||
|
@ -80,8 +72,10 @@ class Game {
|
|||
};
|
||||
|
||||
bool aiIsSearching() {
|
||||
print("$tag White is searching? ${isSearching[PieceColor.white]}\n"
|
||||
"$tag Black is searching? ${isSearching[PieceColor.black]}\n");
|
||||
debugPrint(
|
||||
"$tag White is searching? ${isSearching[PieceColor.white]}\n"
|
||||
"$tag Black is searching? ${isSearching[PieceColor.black]}\n",
|
||||
);
|
||||
|
||||
return isSearching[PieceColor.white] == true ||
|
||||
isSearching[PieceColor.black] == true;
|
||||
|
@ -110,13 +104,15 @@ class Game {
|
|||
break;
|
||||
}
|
||||
|
||||
print("$tag White is AI? ${isAi[PieceColor.white]}\n"
|
||||
"$tag Black is AI? ${isAi[PieceColor.black]}\n");
|
||||
debugPrint(
|
||||
"$tag White is AI? ${isAi[PieceColor.white]}\n"
|
||||
"$tag Black is AI? ${isAi[PieceColor.black]}\n",
|
||||
);
|
||||
}
|
||||
|
||||
select(int pos) {
|
||||
_focusIndex = pos;
|
||||
_blurIndex = invalidIndex;
|
||||
void select(int pos) {
|
||||
focusIndex = pos;
|
||||
blurIndex = invalidIndex;
|
||||
}
|
||||
|
||||
bool doMove(String move) {
|
||||
|
@ -124,7 +120,7 @@ class Game {
|
|||
start();
|
||||
}
|
||||
|
||||
print("$tag AI do move: $move");
|
||||
debugPrint("$tag AI do move: $move");
|
||||
|
||||
if (!position.doMove(move)) {
|
||||
return false;
|
||||
|
@ -132,50 +128,31 @@ class Game {
|
|||
|
||||
moveHistory.add(move);
|
||||
|
||||
sideToMove = position.sideToMove() ?? PieceColor.nobody;
|
||||
sideToMove = position.sideToMove;
|
||||
|
||||
printStat();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
printStat() {
|
||||
void printStat() {
|
||||
double whiteWinRate = 0;
|
||||
double blackWinRate = 0;
|
||||
double drawRate = 0;
|
||||
|
||||
int total = position.score[PieceColor.white] +
|
||||
position.score[PieceColor.black] +
|
||||
position.score[PieceColor.draw] ??
|
||||
0;
|
||||
final int total = position.score[PieceColor.white]! +
|
||||
position.score[PieceColor.black]! +
|
||||
position.score[PieceColor.draw]!;
|
||||
|
||||
if (total == 0) {
|
||||
whiteWinRate = 0;
|
||||
blackWinRate = 0;
|
||||
drawRate = 0;
|
||||
} else {
|
||||
whiteWinRate = position.score[PieceColor.white] * 100 / total ?? 0;
|
||||
blackWinRate = position.score[PieceColor.black] * 100 / total ?? 0;
|
||||
drawRate = position.score[PieceColor.draw] * 100 / total ?? 0;
|
||||
if (total != 0) {
|
||||
whiteWinRate = position.score[PieceColor.white]! * 100 / total;
|
||||
blackWinRate = position.score[PieceColor.black]! * 100 / total;
|
||||
drawRate = position.score[PieceColor.draw]! * 100 / total;
|
||||
}
|
||||
|
||||
String scoreInfo = "Score: " +
|
||||
position.score[PieceColor.white].toString() +
|
||||
" : " +
|
||||
position.score[PieceColor.black].toString() +
|
||||
" : " +
|
||||
position.score[PieceColor.draw].toString() +
|
||||
"\ttotal: " +
|
||||
total.toString() +
|
||||
"\n" +
|
||||
whiteWinRate.toString() +
|
||||
"% : " +
|
||||
blackWinRate.toString() +
|
||||
"% : " +
|
||||
drawRate.toString() +
|
||||
"%" +
|
||||
"\n";
|
||||
final String scoreInfo =
|
||||
"Score: ${position.score[PieceColor.white]} : ${position.score[PieceColor.black]} : ${position.score[PieceColor.draw]}\ttotal: $total\n$whiteWinRate% : $blackWinRate% : $drawRate%\n";
|
||||
|
||||
print("$tag $scoreInfo");
|
||||
debugPrint("$tag $scoreInfo");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/*
|
||||
/*
|
||||
This file is part of Sanmill.
|
||||
Copyright (C) 2019-2021 The Sanmill developers (see AUTHORS file)
|
||||
|
||||
|
@ -16,10 +16,12 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import 'position.dart';
|
||||
import 'rule.dart';
|
||||
import 'package:sanmill/mill/position.dart';
|
||||
import 'package:sanmill/mill/rule.dart';
|
||||
|
||||
class Mills {
|
||||
const Mills._();
|
||||
|
||||
static void adjacentSquaresInit() {
|
||||
// Note: Not follow order of MoveDirection array
|
||||
const adjacentSquares = [
|
||||
|
@ -31,7 +33,6 @@ class Mills {
|
|||
/* 5 */ [0, 0, 0, 0],
|
||||
/* 6 */ [0, 0, 0, 0],
|
||||
/* 7 */ [0, 0, 0, 0],
|
||||
|
||||
/* 8 */ [16, 9, 15, 0],
|
||||
/* 9 */ [10, 8, 0, 0],
|
||||
/* 10 */ [18, 11, 9, 0],
|
||||
|
@ -40,7 +41,6 @@ class Mills {
|
|||
/* 13 */ [14, 12, 0, 0],
|
||||
/* 14 */ [22, 15, 13, 0],
|
||||
/* 15 */ [8, 14, 0, 0],
|
||||
|
||||
/* 16 */ [8, 24, 17, 23],
|
||||
/* 17 */ [18, 16, 0, 0],
|
||||
/* 18 */ [10, 26, 19, 17],
|
||||
|
@ -49,7 +49,6 @@ class Mills {
|
|||
/* 21 */ [22, 20, 0, 0],
|
||||
/* 22 */ [14, 30, 23, 21],
|
||||
/* 23 */ [16, 22, 0, 0],
|
||||
|
||||
/* 24 */ [16, 25, 31, 0],
|
||||
/* 25 */ [26, 24, 0, 0],
|
||||
/* 26 */ [18, 27, 25, 0],
|
||||
|
@ -58,7 +57,6 @@ class Mills {
|
|||
/* 29 */ [30, 28, 0, 0],
|
||||
/* 30 */ [22, 31, 29, 0],
|
||||
/* 31 */ [24, 30, 0, 0],
|
||||
|
||||
/* 32 */ [0, 0, 0, 0],
|
||||
/* 33 */ [0, 0, 0, 0],
|
||||
/* 34 */ [0, 0, 0, 0],
|
||||
|
@ -78,7 +76,6 @@ class Mills {
|
|||
/* 5 */ [0, 0, 0, 0],
|
||||
/* 6 */ [0, 0, 0, 0],
|
||||
/* 7 */ [0, 0, 0, 0],
|
||||
|
||||
/* 8 */ [9, 15, 16, 0],
|
||||
/* 9 */ [17, 8, 10, 0],
|
||||
/* 10 */ [9, 11, 18, 0],
|
||||
|
@ -87,7 +84,6 @@ class Mills {
|
|||
/* 13 */ [21, 12, 14, 0],
|
||||
/* 14 */ [13, 15, 22, 0],
|
||||
/* 15 */ [23, 8, 14, 0],
|
||||
|
||||
/* 16 */ [17, 23, 8, 24],
|
||||
/* 17 */ [9, 25, 16, 18],
|
||||
/* 18 */ [17, 19, 10, 26],
|
||||
|
@ -96,7 +92,6 @@ class Mills {
|
|||
/* 21 */ [13, 29, 20, 22],
|
||||
/* 22 */ [21, 23, 14, 30],
|
||||
/* 23 */ [15, 31, 16, 22],
|
||||
|
||||
/* 24 */ [25, 31, 16, 0],
|
||||
/* 25 */ [17, 24, 26, 0],
|
||||
/* 26 */ [25, 27, 18, 0],
|
||||
|
@ -105,7 +100,6 @@ class Mills {
|
|||
/* 29 */ [21, 28, 30, 0],
|
||||
/* 30 */ [29, 31, 22, 0],
|
||||
/* 31 */ [23, 24, 30, 0],
|
||||
|
||||
/* 32 */ [0, 0, 0, 0],
|
||||
/* 33 */ [0, 0, 0, 0],
|
||||
/* 34 */ [0, 0, 0, 0],
|
||||
|
@ -165,7 +159,6 @@ class Mills {
|
|||
[0, 0],
|
||||
[0, 0]
|
||||
],
|
||||
|
||||
/* 8 */ [
|
||||
[16, 24],
|
||||
[9, 15],
|
||||
|
@ -206,7 +199,6 @@ class Mills {
|
|||
[13, 14],
|
||||
[8, 9]
|
||||
],
|
||||
|
||||
/* 16 */ [
|
||||
[8, 24],
|
||||
[17, 23],
|
||||
|
@ -247,7 +239,6 @@ class Mills {
|
|||
[21, 22],
|
||||
[16, 17]
|
||||
],
|
||||
|
||||
/* 24 */ [
|
||||
[8, 16],
|
||||
[25, 31],
|
||||
|
@ -288,7 +279,6 @@ class Mills {
|
|||
[29, 30],
|
||||
[24, 25]
|
||||
],
|
||||
|
||||
/* 32 */ [
|
||||
[0, 0],
|
||||
[0, 0],
|
||||
|
@ -372,7 +362,6 @@ class Mills {
|
|||
[0, 0],
|
||||
[0, 0]
|
||||
],
|
||||
|
||||
/* 8 */ [
|
||||
[16, 24],
|
||||
[9, 15],
|
||||
|
@ -413,7 +402,6 @@ class Mills {
|
|||
[13, 14],
|
||||
[8, 9]
|
||||
],
|
||||
|
||||
/* 16 */ [
|
||||
[8, 24],
|
||||
[17, 23],
|
||||
|
@ -454,7 +442,6 @@ class Mills {
|
|||
[21, 22],
|
||||
[16, 17]
|
||||
],
|
||||
|
||||
/* 24 */ [
|
||||
[8, 16],
|
||||
[25, 31],
|
||||
|
@ -495,7 +482,6 @@ class Mills {
|
|||
[29, 30],
|
||||
[24, 25]
|
||||
],
|
||||
|
||||
/* 32 */ [
|
||||
[0, 0],
|
||||
[0, 0],
|
||||
|
|
|
@ -16,16 +16,15 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import 'package:sanmill/engine/engine.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:sanmill/mill/game.dart';
|
||||
import 'package:sanmill/mill/mills.dart';
|
||||
import 'package:sanmill/mill/recorder.dart';
|
||||
import 'package:sanmill/mill/rule.dart';
|
||||
import 'package:sanmill/mill/types.dart';
|
||||
import 'package:sanmill/mill/zobrist.dart';
|
||||
import 'package:sanmill/services/audios.dart';
|
||||
|
||||
import 'mills.dart';
|
||||
import 'types.dart';
|
||||
import 'zobrist.dart';
|
||||
import 'package:sanmill/services/engine/engine.dart';
|
||||
|
||||
List<int> posKeyHistory = [];
|
||||
|
||||
|
@ -81,17 +80,25 @@ class Position {
|
|||
|
||||
String? record;
|
||||
|
||||
static late var millTable;
|
||||
static late var adjacentSquares;
|
||||
static late List<List<List<int>>> millTable;
|
||||
static late List<List<int>> adjacentSquares;
|
||||
|
||||
late Move move;
|
||||
|
||||
Position() {
|
||||
init();
|
||||
}
|
||||
|
||||
Position.clone(Position other) {
|
||||
_grid = [];
|
||||
other._grid.forEach((piece) => _grid.add(piece));
|
||||
for (final piece in other._grid) {
|
||||
_grid.add(piece);
|
||||
}
|
||||
|
||||
board = [];
|
||||
other.board.forEach((piece) => board.add(piece));
|
||||
for (final piece in other.board) {
|
||||
board.add(piece);
|
||||
}
|
||||
|
||||
recorder = other.recorder;
|
||||
|
||||
|
@ -124,11 +131,9 @@ class Position {
|
|||
|
||||
bool empty(int sq) => pieceOn(sq) == Piece.noPiece;
|
||||
|
||||
String sideToMove() => _sideToMove;
|
||||
String get sideToMove => _sideToMove;
|
||||
|
||||
String movedPiece(int move) {
|
||||
return pieceOn(fromSq(move));
|
||||
}
|
||||
String movedPiece(int move) => pieceOn(fromSq(move));
|
||||
|
||||
bool movePiece(int from, int to) {
|
||||
if (selectPiece(from) == 0) {
|
||||
|
@ -138,7 +143,7 @@ class Position {
|
|||
return false;
|
||||
}
|
||||
|
||||
init() {
|
||||
void init() {
|
||||
for (var i = 0; i < _grid.length; i++) {
|
||||
_grid[i] = Piece.noPiece;
|
||||
}
|
||||
|
@ -155,95 +160,83 @@ class Position {
|
|||
recorder = GameRecorder(lastPositionWithRemove: fen());
|
||||
}
|
||||
|
||||
Position() {
|
||||
init();
|
||||
}
|
||||
|
||||
/// fen() returns a FEN representation of the position.
|
||||
|
||||
String fen() {
|
||||
var ss = '';
|
||||
final buffer = StringBuffer();
|
||||
|
||||
// Piece placement data
|
||||
for (var file = 1; file <= fileNumber; file++) {
|
||||
for (var rank = 1; rank <= rankNumber; rank++) {
|
||||
final piece = pieceOnGrid(squareToIndex[makeSquare(file, rank)]!);
|
||||
ss += piece;
|
||||
buffer.write(piece);
|
||||
}
|
||||
|
||||
if (file == 3)
|
||||
ss += ' ';
|
||||
else
|
||||
ss += '/';
|
||||
if (file == 3) {
|
||||
buffer.write(' ');
|
||||
} else {
|
||||
buffer.write('/');
|
||||
}
|
||||
}
|
||||
|
||||
// Active color
|
||||
ss += _sideToMove == PieceColor.white ? "w" : "b";
|
||||
buffer.write(_sideToMove == PieceColor.white ? "w" : "b");
|
||||
|
||||
ss += " ";
|
||||
buffer.write(" ");
|
||||
|
||||
// Phrase
|
||||
switch (phase) {
|
||||
case Phase.none:
|
||||
ss += "n";
|
||||
buffer.write("n");
|
||||
break;
|
||||
case Phase.ready:
|
||||
ss += "r";
|
||||
buffer.write("r");
|
||||
break;
|
||||
case Phase.placing:
|
||||
ss += "p";
|
||||
buffer.write("p");
|
||||
break;
|
||||
case Phase.moving:
|
||||
ss += "m";
|
||||
buffer.write("m");
|
||||
break;
|
||||
case Phase.gameOver:
|
||||
ss += "o";
|
||||
buffer.write("o");
|
||||
break;
|
||||
default:
|
||||
ss += "?";
|
||||
buffer.write("?");
|
||||
break;
|
||||
}
|
||||
|
||||
ss += " ";
|
||||
buffer.write(" ");
|
||||
|
||||
// Action
|
||||
switch (action) {
|
||||
case Act.place:
|
||||
ss += "p";
|
||||
buffer.write("p");
|
||||
break;
|
||||
case Act.select:
|
||||
ss += "s";
|
||||
buffer.write("s");
|
||||
break;
|
||||
case Act.remove:
|
||||
ss += "r";
|
||||
buffer.write("r");
|
||||
break;
|
||||
default:
|
||||
ss += "?";
|
||||
buffer.write("?");
|
||||
break;
|
||||
}
|
||||
|
||||
ss += " ";
|
||||
buffer.write(" ");
|
||||
|
||||
ss += pieceOnBoardCount[PieceColor.white].toString() +
|
||||
" " +
|
||||
pieceInHandCount[PieceColor.white].toString() +
|
||||
" " +
|
||||
pieceOnBoardCount[PieceColor.black].toString() +
|
||||
" " +
|
||||
pieceInHandCount[PieceColor.black].toString() +
|
||||
" " +
|
||||
pieceToRemoveCount.toString() +
|
||||
" ";
|
||||
buffer.write(
|
||||
"${pieceOnBoardCount[PieceColor.white]} ${pieceInHandCount[PieceColor.white]} ${pieceOnBoardCount[PieceColor.black]} ${pieceInHandCount[PieceColor.black]} $pieceToRemoveCount ",
|
||||
);
|
||||
|
||||
int sideIsBlack = _sideToMove == PieceColor.black ? 1 : 0;
|
||||
final int sideIsBlack = _sideToMove == PieceColor.black ? 1 : 0;
|
||||
|
||||
ss += st.rule50.toString() +
|
||||
" " +
|
||||
(1 + (gamePly - sideIsBlack) ~/ 2).toString();
|
||||
buffer.write("${st.rule50} ${1 + (gamePly - sideIsBlack) ~/ 2}");
|
||||
|
||||
//print("FEN is $ss");
|
||||
//debugPrint("FEN is $ss");
|
||||
|
||||
return ss;
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
/// Position::legal() tests whether a pseudo-legal move is legal
|
||||
|
@ -251,16 +244,16 @@ class Position {
|
|||
bool legal(Move move) {
|
||||
if (!isOk(move.from) || !isOk(move.to)) return false;
|
||||
|
||||
String us = _sideToMove;
|
||||
final String us = _sideToMove;
|
||||
|
||||
if (move.from == move.to) {
|
||||
print("[position] Move $move.move from == to");
|
||||
debugPrint("[position] Move $move.move from == to");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (move.type == MoveType.remove) {
|
||||
if (movedPiece(move.to) != us) {
|
||||
print("[position] Move $move.to to != us");
|
||||
debugPrint("[position] Move $move.to to != us");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -311,11 +304,11 @@ class Position {
|
|||
|
||||
bool ret = false;
|
||||
|
||||
Move m = Move(move);
|
||||
final Move m = Move(move);
|
||||
|
||||
switch (m.type) {
|
||||
case MoveType.remove:
|
||||
ret = (removePiece(m.to) == 0);
|
||||
ret = removePiece(m.to) == 0;
|
||||
if (ret) {
|
||||
// Reset rule 50 counter
|
||||
st.rule50 = 0;
|
||||
|
@ -349,13 +342,15 @@ class Position {
|
|||
++st.pliesFromNull;
|
||||
|
||||
if (record != null && record!.length > "-(1,2)".length) {
|
||||
if (posKeyHistory.length == 0 ||
|
||||
(posKeyHistory.length > 0 &&
|
||||
if (posKeyHistory.isEmpty ||
|
||||
(posKeyHistory.isNotEmpty &&
|
||||
st.key != posKeyHistory[posKeyHistory.length - 1])) {
|
||||
posKeyHistory.add(st.key);
|
||||
if (rule.threefoldRepetitionRule && hasGameCycle()) {
|
||||
setGameOver(
|
||||
PieceColor.draw, GameOverReason.drawReasonThreefoldRepetition);
|
||||
PieceColor.draw,
|
||||
GameOverReason.drawReasonThreefoldRepetition,
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -376,7 +371,7 @@ class Position {
|
|||
}
|
||||
}
|
||||
|
||||
int size = ss.length;
|
||||
final int size = ss.length;
|
||||
|
||||
for (int i = size - 1; i >= 0; i--) {
|
||||
if (ss[i].move.type == MoveType.remove) {
|
||||
|
@ -394,11 +389,11 @@ class Position {
|
|||
|
||||
bool hasGameCycle() {
|
||||
int repetition = 0; // Note: Engine is global val
|
||||
for (var i in posKeyHistory) {
|
||||
for (final i in posKeyHistory) {
|
||||
if (st.key == i) {
|
||||
repetition++;
|
||||
if (repetition == 3) {
|
||||
print("[position] Has game cycle.");
|
||||
debugPrint("[position] Has game cycle.");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -464,7 +459,7 @@ class Position {
|
|||
|
||||
bool putPiece(int s) {
|
||||
var piece = Piece.noPiece;
|
||||
var us = _sideToMove;
|
||||
final us = _sideToMove;
|
||||
|
||||
if (phase == Phase.gameOver ||
|
||||
action != Act.place ||
|
||||
|
@ -478,7 +473,7 @@ class Position {
|
|||
}
|
||||
|
||||
if (phase == Phase.placing) {
|
||||
piece = sideToMove();
|
||||
piece = sideToMove;
|
||||
if (pieceInHandCount[us] != null) {
|
||||
pieceInHandCount[us] = pieceInHandCount[us]! - 1;
|
||||
}
|
||||
|
@ -490,17 +485,19 @@ class Position {
|
|||
_grid[squareToIndex[s]!] = piece;
|
||||
board[s] = piece;
|
||||
|
||||
record = "(" + fileOf(s).toString() + "," + rankOf(s).toString() + ")";
|
||||
record = "(${fileOf(s)},${rankOf(s)})";
|
||||
|
||||
updateKey(s);
|
||||
|
||||
currentSquare = s;
|
||||
|
||||
int n = millsCount(currentSquare);
|
||||
final int n = millsCount(currentSquare);
|
||||
|
||||
if (n == 0) {
|
||||
assert(pieceInHandCount[PieceColor.white]! >= 0 &&
|
||||
pieceInHandCount[PieceColor.black]! >= 0);
|
||||
assert(
|
||||
pieceInHandCount[PieceColor.white]! >= 0 &&
|
||||
pieceInHandCount[PieceColor.black]! >= 0,
|
||||
);
|
||||
|
||||
if (pieceInHandCount[PieceColor.white] == 0 &&
|
||||
pieceInHandCount[PieceColor.black] == 0) {
|
||||
|
@ -525,7 +522,7 @@ class Position {
|
|||
} else {
|
||||
changeSideToMove();
|
||||
}
|
||||
Game.instance.focusIndex = squareToIndex[s] ?? invalidIndex;
|
||||
gameInstance.focusIndex = squareToIndex[s] ?? invalidIndex;
|
||||
Audios.playTone(Audios.placeSoundId);
|
||||
} else {
|
||||
pieceToRemoveCount = rule.mayRemoveMultiple ? n : 1;
|
||||
|
@ -561,7 +558,7 @@ class Position {
|
|||
action = Act.remove;
|
||||
}
|
||||
|
||||
Game.instance.focusIndex = squareToIndex[s] ?? invalidIndex;
|
||||
gameInstance.focusIndex = squareToIndex[s] ?? invalidIndex;
|
||||
Audios.playTone(Audios.millSoundId);
|
||||
}
|
||||
} else if (phase == Phase.moving) {
|
||||
|
@ -570,8 +567,7 @@ class Position {
|
|||
}
|
||||
|
||||
// if illegal
|
||||
if (pieceOnBoardCount[sideToMove()]! > rule.flyPieceCount ||
|
||||
!rule.mayFly) {
|
||||
if (pieceOnBoardCount[sideToMove]! > rule.flyPieceCount || !rule.mayFly) {
|
||||
int md;
|
||||
|
||||
for (md = 0; md < moveDirectionNumber; md++) {
|
||||
|
@ -580,21 +576,15 @@ class Position {
|
|||
|
||||
// not in moveTable
|
||||
if (md == moveDirectionNumber) {
|
||||
print(
|
||||
"[position] putPiece: [$s] is not in [$currentSquare]'s move table.");
|
||||
debugPrint(
|
||||
"[position] putPiece: [$s] is not in [$currentSquare]'s move table.",
|
||||
);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
record = "(" +
|
||||
fileOf(currentSquare).toString() +
|
||||
"," +
|
||||
rankOf(currentSquare).toString() +
|
||||
")->(" +
|
||||
fileOf(s).toString() +
|
||||
"," +
|
||||
rankOf(s).toString() +
|
||||
")";
|
||||
record =
|
||||
"(${fileOf(currentSquare)},${rankOf(currentSquare)})->(${fileOf(s)},${rankOf(s)})";
|
||||
|
||||
st.rule50++;
|
||||
|
||||
|
@ -606,7 +596,7 @@ class Position {
|
|||
_grid[squareToIndex[currentSquare]!] = Piece.noPiece;
|
||||
|
||||
currentSquare = s;
|
||||
int n = millsCount(currentSquare);
|
||||
final int n = millsCount(currentSquare);
|
||||
|
||||
// midgame
|
||||
if (n == 0) {
|
||||
|
@ -616,14 +606,14 @@ class Position {
|
|||
if (checkIfGameIsOver()) {
|
||||
return true;
|
||||
} else {
|
||||
Game.instance.focusIndex = squareToIndex[s] ?? invalidIndex;
|
||||
gameInstance.focusIndex = squareToIndex[s] ?? invalidIndex;
|
||||
Audios.playTone(Audios.placeSoundId);
|
||||
}
|
||||
} else {
|
||||
pieceToRemoveCount = rule.mayRemoveMultiple ? n : 1;
|
||||
updateKeyMisc();
|
||||
action = Act.remove;
|
||||
Game.instance.focusIndex = squareToIndex[s] ?? invalidIndex;
|
||||
gameInstance.focusIndex = squareToIndex[s] ?? invalidIndex;
|
||||
Audios.playTone(Audios.millSoundId);
|
||||
}
|
||||
} else {
|
||||
|
@ -641,11 +631,11 @@ class Position {
|
|||
if (pieceToRemoveCount <= 0) return -1;
|
||||
|
||||
// if piece is not their
|
||||
if (!(PieceColor.opponent(sideToMove()) == board[s])) return -2;
|
||||
if (!(PieceColor.opponent(sideToMove) == board[s])) return -2;
|
||||
|
||||
if (!rule.mayRemoveFromMillsAlways &&
|
||||
potentialMillsCount(s, PieceColor.nobody) > 0 &&
|
||||
!isAllInMills(PieceColor.opponent(sideToMove()))) {
|
||||
!isAllInMills(PieceColor.opponent(sideToMove))) {
|
||||
return -3;
|
||||
}
|
||||
|
||||
|
@ -662,7 +652,7 @@ class Position {
|
|||
board[s] = _grid[squareToIndex[s]!] = Piece.noPiece;
|
||||
}
|
||||
|
||||
record = "-(" + fileOf(s).toString() + "," + rankOf(s).toString() + ")";
|
||||
record = "-(${fileOf(s)},${rankOf(s)})";
|
||||
st.rule50 = 0; // TODO: Need to move out?
|
||||
|
||||
if (pieceOnBoardCount[them] != null) {
|
||||
|
@ -671,7 +661,7 @@ class Position {
|
|||
|
||||
if (pieceOnBoardCount[them]! + pieceInHandCount[them]! <
|
||||
rule.piecesAtLeastCount) {
|
||||
setGameOver(sideToMove(), GameOverReason.loseReasonlessThanThree);
|
||||
setGameOver(sideToMove, GameOverReason.loseReasonlessThanThree);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -720,13 +710,13 @@ class Position {
|
|||
return -3;
|
||||
}
|
||||
|
||||
if (!(board[sq] == sideToMove())) {
|
||||
if (!(board[sq] == sideToMove)) {
|
||||
return -4;
|
||||
}
|
||||
|
||||
currentSquare = sq;
|
||||
action = Act.place;
|
||||
Game.instance.blurIndex = squareToIndex[sq];
|
||||
gameInstance.blurIndex = squareToIndex[sq]!;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -752,7 +742,7 @@ class Position {
|
|||
gameOverReason = reason;
|
||||
winner = w;
|
||||
|
||||
print("[position] Game over, $w win, because of $reason");
|
||||
debugPrint("[position] Game over, $w win, because of $reason");
|
||||
updateScore();
|
||||
}
|
||||
|
||||
|
@ -813,7 +803,9 @@ class Position {
|
|||
if (phase == Phase.moving && action == Act.select && isAllSurrounded()) {
|
||||
if (rule.isLoseButNotChangeSideWhenNoWay) {
|
||||
setGameOver(
|
||||
PieceColor.opponent(sideToMove()), GameOverReason.loseReasonNoWay);
|
||||
PieceColor.opponent(sideToMove),
|
||||
GameOverReason.loseReasonNoWay,
|
||||
);
|
||||
return true;
|
||||
} else {
|
||||
changeSideToMove(); // TODO: Need?
|
||||
|
@ -850,7 +842,7 @@ class Position {
|
|||
void changeSideToMove() {
|
||||
setSideToMove(PieceColor.opponent(_sideToMove));
|
||||
st.key ^= Zobrist.side;
|
||||
print("[position] $_sideToMove to move.");
|
||||
debugPrint("[position] $_sideToMove to move.");
|
||||
|
||||
/*
|
||||
if (phase == Phase.moving &&
|
||||
|
@ -858,18 +850,16 @@ class Position {
|
|||
isAllSurrounded() &&
|
||||
!rule.mayFly &&
|
||||
pieceOnBoardCount[sideToMove()]! >= rule.piecesAtLeastCount) {
|
||||
print("[position] $_sideToMove is no way to go.");
|
||||
debugPrint("[position] $_sideToMove is no way to go.");
|
||||
changeSideToMove();
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
int updateKey(int s) {
|
||||
String pieceType = colorOn(s);
|
||||
final String pieceType = colorOn(s);
|
||||
|
||||
st.key ^= Zobrist.psq[pieceColorIndex[pieceType]!][s];
|
||||
|
||||
return st.key;
|
||||
return st.key ^= Zobrist.psq[pieceColorIndex[pieceType]!][s];
|
||||
}
|
||||
|
||||
int revertKey(int s) {
|
||||
|
@ -879,7 +869,7 @@ class Position {
|
|||
int updateKeyMisc() {
|
||||
st.key = st.key << Zobrist.KEY_MISC_BIT >> Zobrist.KEY_MISC_BIT;
|
||||
|
||||
st.key |= (pieceToRemoveCount) << (32 - Zobrist.KEY_MISC_BIT);
|
||||
st.key |= pieceToRemoveCount << (32 - Zobrist.KEY_MISC_BIT);
|
||||
|
||||
return st.key;
|
||||
}
|
||||
|
@ -893,11 +883,12 @@ class Position {
|
|||
int potentialMillsCount(int to, String c, {int from = 0}) {
|
||||
int n = 0;
|
||||
String locbak = Piece.noPiece;
|
||||
String _c = c;
|
||||
|
||||
assert(0 <= from && from < sqNumber);
|
||||
|
||||
if (c == PieceColor.nobody) {
|
||||
c = colorOn(to);
|
||||
if (_c == PieceColor.nobody) {
|
||||
_c = colorOn(to);
|
||||
}
|
||||
|
||||
if (from != 0 && from >= sqBegin && from < sqEnd) {
|
||||
|
@ -906,7 +897,8 @@ class Position {
|
|||
}
|
||||
|
||||
for (int l = 0; l < lineDirectionNumber; l++) {
|
||||
if (c == board[millTable[to][l][0]] && c == board[millTable[to][l][1]]) {
|
||||
if (_c == board[millTable[to][l][0]] &&
|
||||
_c == board[millTable[to][l][1]]) {
|
||||
n++;
|
||||
}
|
||||
}
|
||||
|
@ -920,10 +912,10 @@ class Position {
|
|||
|
||||
int millsCount(int s) {
|
||||
int n = 0;
|
||||
List<int?> idx = [0, 0, 0];
|
||||
final List<int?> idx = [0, 0, 0];
|
||||
int min = 0;
|
||||
int? temp = 0;
|
||||
String m = colorOn(s);
|
||||
final String m = colorOn(s);
|
||||
|
||||
for (int i = 0; i < idx.length; i++) {
|
||||
idx[0] = s;
|
||||
|
@ -981,20 +973,18 @@ class Position {
|
|||
}
|
||||
|
||||
// Can fly
|
||||
if (pieceOnBoardCount[sideToMove()]! <= rule.flyPieceCount && rule.mayFly) {
|
||||
if (pieceOnBoardCount[sideToMove]! <= rule.flyPieceCount && rule.mayFly) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int? moveSquare;
|
||||
|
||||
for (int s = sqBegin; s < sqEnd; s++) {
|
||||
if (!(sideToMove() == colorOn(s))) {
|
||||
if (!(sideToMove == colorOn(s))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int d = moveDirectionBegin; d < moveDirectionNumber; d++) {
|
||||
moveSquare = adjacentSquares[s][d];
|
||||
if (moveSquare != 0 && board[moveSquare!] == Piece.noPiece) {
|
||||
final int moveSquare = adjacentSquares[s][d];
|
||||
if (moveSquare != 0 && board[moveSquare] == Piece.noPiece) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -1005,15 +995,15 @@ class Position {
|
|||
|
||||
bool isStarSquare(int s) {
|
||||
if (rule.hasDiagonalLines == true) {
|
||||
return (s == 17 || s == 19 || s == 21 || s == 23);
|
||||
return s == 17 || s == 19 || s == 21 || s == 23;
|
||||
}
|
||||
|
||||
return (s == 16 || s == 18 || s == 20 || s == 22);
|
||||
return s == 16 || s == 18 || s == 20 || s == 22;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
int getNPiecesInHand() {
|
||||
int get nPiecesInHand {
|
||||
pieceInHandCount[PieceColor.white] =
|
||||
rule.piecesCount - pieceOnBoardCount[PieceColor.white]!;
|
||||
pieceInHandCount[PieceColor.black] =
|
||||
|
@ -1054,7 +1044,7 @@ class Position {
|
|||
return -1;
|
||||
}
|
||||
|
||||
getNPiecesInHand();
|
||||
nPiecesInHand;
|
||||
pieceToRemoveCount = 0;
|
||||
|
||||
winner = PieceColor.nobody;
|
||||
|
@ -1071,7 +1061,7 @@ class Position {
|
|||
|
||||
for (int f = 1; f < fileExNumber; f++) {
|
||||
for (int r = 0; r < rankNumber; r++) {
|
||||
int s = f * rankNumber + r;
|
||||
final int s = f * rankNumber + r;
|
||||
if (board[s] == Piece.whiteStone) {
|
||||
if (pieceOnBoardCount[PieceColor.white] != null) {
|
||||
pieceOnBoardCount[PieceColor.white] =
|
||||
|
@ -1101,37 +1091,37 @@ class Position {
|
|||
String errString = "";
|
||||
|
||||
if (recorder == null) {
|
||||
print("[goto] recorder is null.");
|
||||
debugPrint("[goto] recorder is null.");
|
||||
return "null";
|
||||
}
|
||||
|
||||
var history = recorder!.getHistory();
|
||||
final history = recorder!.history;
|
||||
if (moveIndex < -1 || history.length <= moveIndex) {
|
||||
print("[goto] moveIndex is out of range.");
|
||||
debugPrint("[goto] moveIndex is out of range.");
|
||||
return "out-of-range";
|
||||
}
|
||||
|
||||
if (recorder!.cur == moveIndex) {
|
||||
print("[goto] cur is equal to moveIndex.");
|
||||
debugPrint("[goto] cur is equal to moveIndex.");
|
||||
return "equal";
|
||||
}
|
||||
|
||||
// Backup context
|
||||
var engineTypeBackup = Game.instance.engineType;
|
||||
final engineTypeBackup = gameInstance.engineType;
|
||||
|
||||
Game.instance.engineType = EngineType.humanVsHuman;
|
||||
Game.instance.setWhoIsAi(EngineType.humanVsHuman);
|
||||
gameInstance.engineType = EngineType.humanVsHuman;
|
||||
gameInstance.setWhoIsAi(EngineType.humanVsHuman);
|
||||
|
||||
var historyBack = history;
|
||||
final historyBack = history;
|
||||
|
||||
await Game.instance.newGame();
|
||||
gameInstance.newGame();
|
||||
|
||||
if (moveIndex == -1) {
|
||||
errString = "";
|
||||
}
|
||||
|
||||
for (var i = 0; i <= moveIndex; i++) {
|
||||
if (Game.instance.doMove(history[i].move) == false) {
|
||||
if (gameInstance.doMove(history[i].move!) == false) {
|
||||
errString = history[i].move!;
|
||||
break;
|
||||
}
|
||||
|
@ -1141,9 +1131,9 @@ class Position {
|
|||
}
|
||||
|
||||
// Restore context
|
||||
Game.instance.engineType = engineTypeBackup;
|
||||
Game.instance.setWhoIsAi(engineTypeBackup);
|
||||
recorder!.setHistory(historyBack);
|
||||
gameInstance.engineType = engineTypeBackup;
|
||||
gameInstance.setWhoIsAi(engineTypeBackup);
|
||||
recorder!.history = historyBack;
|
||||
recorder!.cur = moveIndex;
|
||||
|
||||
return errString;
|
||||
|
@ -1170,15 +1160,15 @@ class Position {
|
|||
}
|
||||
|
||||
Future<String> stepForwardAll() async {
|
||||
return _gotoHistory(recorder!.getHistory().length - 1);
|
||||
return _gotoHistory(recorder!.history.length - 1);
|
||||
}
|
||||
|
||||
String movesSinceLastRemove() {
|
||||
int? i = 0;
|
||||
String moves = "";
|
||||
final buffer = StringBuffer();
|
||||
int posAfterLastRemove = 0;
|
||||
|
||||
//print("recorder.movesCount = ${recorder.movesCount}");
|
||||
//debugPrint("recorder.movesCount = ${recorder.movesCount}");
|
||||
|
||||
for (i = recorder!.movesCount - 1; i! >= 0; i--) {
|
||||
//if (recorder.moveAt(i).type == MoveType.remove) break;
|
||||
|
@ -1189,28 +1179,29 @@ class Position {
|
|||
posAfterLastRemove = i + 1;
|
||||
}
|
||||
|
||||
//print("[movesSinceLastRemove] posAfterLastRemove = $posAfterLastRemove");
|
||||
//debugPrint("[movesSinceLastRemove] posAfterLastRemove = $posAfterLastRemove");
|
||||
|
||||
for (int i = posAfterLastRemove; i < recorder!.movesCount; i++) {
|
||||
moves += " ${recorder!.moveAt(i).move}";
|
||||
buffer.write(" ${recorder!.moveAt(i).move}");
|
||||
}
|
||||
|
||||
//print("moves = $moves");
|
||||
final String moves = buffer.toString();
|
||||
//debugPrint("moves = $moves");
|
||||
|
||||
var idx = moves.indexOf('-(');
|
||||
final idx = moves.indexOf('-(');
|
||||
if (idx != -1) {
|
||||
//print("moves[$idx] is -(");
|
||||
//debugPrint("moves[$idx] is -(");
|
||||
assert(false);
|
||||
}
|
||||
|
||||
return moves.length > 0 ? moves.substring(1) : '';
|
||||
return moves.isNotEmpty ? moves.substring(1) : '';
|
||||
}
|
||||
|
||||
get moveHistoryText => recorder!.buildMoveHistoryText();
|
||||
String get moveHistoryText => recorder!.buildMoveHistoryText();
|
||||
|
||||
get side => _sideToMove;
|
||||
String get side => _sideToMove;
|
||||
|
||||
get lastMove => recorder!.last;
|
||||
Move? get lastMove => recorder!.last;
|
||||
|
||||
get lastPositionWithRemove => recorder!.lastPositionWithRemove;
|
||||
String? get lastPositionWithRemove => recorder!.lastPositionWithRemove;
|
||||
}
|
||||
|
|
|
@ -16,34 +16,26 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import 'package:sanmill/common/config.dart';
|
||||
|
||||
import 'position.dart';
|
||||
import 'types.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:sanmill/mill/position.dart';
|
||||
import 'package:sanmill/mill/types.dart';
|
||||
import 'package:sanmill/shared/common/config.dart';
|
||||
|
||||
// TODO
|
||||
class GameRecorder {
|
||||
int cur = -1;
|
||||
String? lastPositionWithRemove = "";
|
||||
var _history = <Move>[];
|
||||
List<Move> history = <Move>[];
|
||||
final tag = "[GameRecorder]";
|
||||
|
||||
GameRecorder({this.cur = -1, this.lastPositionWithRemove});
|
||||
|
||||
List<Move> getHistory() {
|
||||
return _history;
|
||||
}
|
||||
|
||||
void setHistory(List<Move> newHistory) {
|
||||
_history = newHistory;
|
||||
}
|
||||
|
||||
String wmdNotationToMoveString(String wmd) {
|
||||
String move = "";
|
||||
|
||||
if (wmd.length == 3 && wmd[0] == "x") {
|
||||
if (wmdNotationToMove[wmd.substring(1, 3)] != null) {
|
||||
move = '-' + wmdNotationToMove[wmd.substring(1, 3)]!;
|
||||
move = '-${wmdNotationToMove[wmd.substring(1, 3)]!}';
|
||||
}
|
||||
} else if (wmd.length == 2) {
|
||||
if (wmdNotationToMove[wmd] != null) {
|
||||
|
@ -52,15 +44,14 @@ class GameRecorder {
|
|||
} else if (wmd.length == 5 && wmd[2] == '-') {
|
||||
if (wmdNotationToMove[(wmd.substring(0, 2))] != null &&
|
||||
wmdNotationToMove[(wmd.substring(3, 5))] != null) {
|
||||
move = wmdNotationToMove[(wmd.substring(0, 2))]! +
|
||||
'->' +
|
||||
wmdNotationToMove[(wmd.substring(3, 5))]!;
|
||||
move =
|
||||
'${wmdNotationToMove[(wmd.substring(0, 2))]!}->${wmdNotationToMove[(wmd.substring(3, 5))]!}';
|
||||
}
|
||||
} else if ((wmd.length == 8 && wmd[2] == '-' && wmd[5] == 'x') ||
|
||||
(wmd.length == 5 && wmd[2] == 'x')) {
|
||||
print("$tag Not support parsing format oo-ooxo notation.");
|
||||
debugPrint("$tag Not support parsing format oo-ooxo notation.");
|
||||
} else {
|
||||
print("$tag Parse notation $wmd failed.");
|
||||
debugPrint("$tag Parse notation $wmd failed.");
|
||||
}
|
||||
|
||||
return move;
|
||||
|
@ -69,60 +60,57 @@ class GameRecorder {
|
|||
String playOkNotationToMoveString(String playOk) {
|
||||
String move = "";
|
||||
|
||||
if (playOk.length == 0) {
|
||||
if (playOk.isEmpty) {
|
||||
return "";
|
||||
}
|
||||
|
||||
var iDash = playOk.indexOf('-');
|
||||
var iX = playOk.indexOf('x');
|
||||
final iDash = playOk.indexOf('-');
|
||||
final iX = playOk.indexOf('x');
|
||||
|
||||
if (iDash == -1 && iX == -1) {
|
||||
// 12
|
||||
var val = int.parse(playOk);
|
||||
final val = int.parse(playOk);
|
||||
if (val >= 1 && val <= 24) {
|
||||
move = playOkNotationToMove[playOk]!;
|
||||
return move;
|
||||
return playOkNotationToMove[playOk]!;
|
||||
} else {
|
||||
print("$tag Parse PlayOK notation $playOk failed.");
|
||||
debugPrint("$tag Parse PlayOK notation $playOk failed.");
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
if (iX == 0) {
|
||||
// x12
|
||||
var sub = playOk.substring(1);
|
||||
var val = int.parse(sub);
|
||||
final sub = playOk.substring(1);
|
||||
final val = int.parse(sub);
|
||||
if (val >= 1 && val <= 24) {
|
||||
move = "-" + playOkNotationToMove[sub]!;
|
||||
return move;
|
||||
return "-${playOkNotationToMove[sub]!}";
|
||||
} else {
|
||||
print("$tag Parse PlayOK notation $playOk failed.");
|
||||
debugPrint("$tag Parse PlayOK notation $playOk failed.");
|
||||
return "";
|
||||
}
|
||||
}
|
||||
if (iDash != -1 && iX == -1) {
|
||||
// 12-13
|
||||
var sub1 = playOk.substring(0, iDash);
|
||||
var val1 = int.parse(sub1);
|
||||
final sub1 = playOk.substring(0, iDash);
|
||||
final val1 = int.parse(sub1);
|
||||
if (val1 >= 1 && val1 <= 24) {
|
||||
move = playOkNotationToMove[sub1]!;
|
||||
} else {
|
||||
print("$tag Parse PlayOK notation $playOk failed.");
|
||||
debugPrint("$tag Parse PlayOK notation $playOk failed.");
|
||||
return "";
|
||||
}
|
||||
|
||||
var sub2 = playOk.substring(iDash + 1);
|
||||
var val2 = int.parse(sub2);
|
||||
final sub2 = playOk.substring(iDash + 1);
|
||||
final val2 = int.parse(sub2);
|
||||
if (val2 >= 1 && val2 <= 24) {
|
||||
move = move + "->" + playOkNotationToMove[sub2]!;
|
||||
return move;
|
||||
return "$move->${playOkNotationToMove[sub2]!}";
|
||||
} else {
|
||||
print("$tag Parse PlayOK notation $playOk failed.");
|
||||
debugPrint("$tag Parse PlayOK notation $playOk failed.");
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
print("$tag Not support parsing format oo-ooxo PlayOK notation.");
|
||||
debugPrint("$tag Not support parsing format oo-ooxo PlayOK notation.");
|
||||
return "";
|
||||
}
|
||||
|
||||
|
@ -141,7 +129,7 @@ class GameRecorder {
|
|||
return true;
|
||||
}
|
||||
|
||||
if (text.length > 0 && text[0] == '[') {
|
||||
if (text.isNotEmpty && text[0] == '[') {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -186,8 +174,8 @@ class GameRecorder {
|
|||
return importGoldToken(moveList);
|
||||
}
|
||||
|
||||
List<Move> newHistory = [];
|
||||
List<String> list = moveList
|
||||
final List<Move> newHistory = [];
|
||||
final List<String> list = moveList
|
||||
.toLowerCase()
|
||||
.replaceAll('\n', ' ')
|
||||
.replaceAll(',', ' ')
|
||||
|
@ -223,57 +211,57 @@ class GameRecorder {
|
|||
i = i.trim();
|
||||
|
||||
if (int.tryParse(i) != null) {
|
||||
i = i + '.';
|
||||
i = '$i.';
|
||||
}
|
||||
|
||||
if (i.length > 0 && !i.endsWith(".")) {
|
||||
if (i.isNotEmpty && !i.endsWith(".")) {
|
||||
if (i.length == 5 && i[2] == 'x') {
|
||||
// "a1xc3"
|
||||
String m1 = wmdNotationToMoveString(i.substring(0, 2));
|
||||
final String m1 = wmdNotationToMoveString(i.substring(0, 2));
|
||||
if (m1 != "") {
|
||||
newHistory.add(Move(m1));
|
||||
} else {
|
||||
print("Cannot import $i");
|
||||
debugPrint("Cannot import $i");
|
||||
return i;
|
||||
}
|
||||
String m2 = wmdNotationToMoveString(i.substring(2));
|
||||
final String m2 = wmdNotationToMoveString(i.substring(2));
|
||||
if (m2 != "") {
|
||||
newHistory.add(Move(m2));
|
||||
} else {
|
||||
print("Cannot import $i");
|
||||
debugPrint("Cannot import $i");
|
||||
return i;
|
||||
}
|
||||
} else if (i.length == 8 && i[2] == '-' && i[5] == 'x') {
|
||||
// "a1-b2xc3"
|
||||
String m1 = wmdNotationToMoveString(i.substring(0, 5));
|
||||
final String m1 = wmdNotationToMoveString(i.substring(0, 5));
|
||||
if (m1 != "") {
|
||||
newHistory.add(Move(m1));
|
||||
} else {
|
||||
print("Cannot import $i");
|
||||
debugPrint("Cannot import $i");
|
||||
return i;
|
||||
}
|
||||
String m2 = wmdNotationToMoveString(i.substring(5));
|
||||
final String m2 = wmdNotationToMoveString(i.substring(5));
|
||||
if (m2 != "") {
|
||||
newHistory.add(Move(m2));
|
||||
} else {
|
||||
print("Cannot import $i");
|
||||
debugPrint("Cannot import $i");
|
||||
return i;
|
||||
}
|
||||
} else {
|
||||
// no x
|
||||
String m = wmdNotationToMoveString(i);
|
||||
final String m = wmdNotationToMoveString(i);
|
||||
if (m != "") {
|
||||
newHistory.add(Move(m));
|
||||
} else {
|
||||
print("Cannot import $i");
|
||||
debugPrint("Cannot import $i");
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (newHistory.length > 0) {
|
||||
setHistory(newHistory);
|
||||
if (newHistory.isNotEmpty) {
|
||||
history = newHistory;
|
||||
}
|
||||
|
||||
return "";
|
||||
|
@ -284,9 +272,9 @@ class GameRecorder {
|
|||
}
|
||||
|
||||
String importPlayOk(String moveList) {
|
||||
List<Move> newHistory = [];
|
||||
final List<Move> newHistory = [];
|
||||
|
||||
List<String> list = moveList
|
||||
final List<String> list = moveList
|
||||
.replaceAll('\n', ' ')
|
||||
.replaceAll(' 1/2-1/2', '')
|
||||
.replaceAll(' 1-0', '')
|
||||
|
@ -297,40 +285,40 @@ class GameRecorder {
|
|||
for (var i in list) {
|
||||
i = i.trim();
|
||||
|
||||
if (i.length > 0 &&
|
||||
if (i.isNotEmpty &&
|
||||
!i.endsWith(".") &&
|
||||
!i.startsWith("[") &&
|
||||
!i.endsWith("]")) {
|
||||
var iX = i.indexOf('x');
|
||||
final iX = i.indexOf('x');
|
||||
if (iX == -1) {
|
||||
String m = playOkNotationToMoveString(i);
|
||||
final String m = playOkNotationToMoveString(i);
|
||||
if (m != "") {
|
||||
newHistory.add(Move(m));
|
||||
} else {
|
||||
print("Cannot import $i");
|
||||
debugPrint("Cannot import $i");
|
||||
return i;
|
||||
}
|
||||
} else if (iX != -1) {
|
||||
String m1 = playOkNotationToMoveString(i.substring(0, iX));
|
||||
final String m1 = playOkNotationToMoveString(i.substring(0, iX));
|
||||
if (m1 != "") {
|
||||
newHistory.add(Move(m1));
|
||||
} else {
|
||||
print("Cannot import $i");
|
||||
debugPrint("Cannot import $i");
|
||||
return i;
|
||||
}
|
||||
String m2 = playOkNotationToMoveString(i.substring(iX));
|
||||
final String m2 = playOkNotationToMoveString(i.substring(iX));
|
||||
if (m2 != "") {
|
||||
newHistory.add(Move(m2));
|
||||
} else {
|
||||
print("Cannot import $i");
|
||||
debugPrint("Cannot import $i");
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (newHistory.length > 0) {
|
||||
setHistory(newHistory);
|
||||
if (newHistory.isNotEmpty) {
|
||||
history = newHistory;
|
||||
}
|
||||
|
||||
return "";
|
||||
|
@ -355,16 +343,16 @@ class GameRecorder {
|
|||
}
|
||||
|
||||
void jumpToTail() {
|
||||
cur = _history.length - 1;
|
||||
cur = history.length - 1;
|
||||
}
|
||||
|
||||
void clear() {
|
||||
_history.clear();
|
||||
history.clear();
|
||||
cur = 0;
|
||||
}
|
||||
|
||||
bool isClean() {
|
||||
return cur == _history.length - 1;
|
||||
return cur == history.length - 1;
|
||||
}
|
||||
|
||||
void prune() {
|
||||
|
@ -372,19 +360,19 @@ class GameRecorder {
|
|||
return;
|
||||
}
|
||||
|
||||
_history.removeRange(cur + 1, _history.length);
|
||||
history.removeRange(cur + 1, history.length);
|
||||
}
|
||||
|
||||
void moveIn(Move move, Position position) {
|
||||
if (_history.length > 0) {
|
||||
if (_history[_history.length - 1].move == move.move) {
|
||||
if (history.isNotEmpty) {
|
||||
if (history[history.length - 1].move == move.move) {
|
||||
//assert(false);
|
||||
// TODO: WAR
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
_history.add(move);
|
||||
history.add(move);
|
||||
cur++;
|
||||
|
||||
if (move.type == MoveType.remove) {
|
||||
|
@ -393,22 +381,22 @@ class GameRecorder {
|
|||
}
|
||||
|
||||
Move? removeLast() {
|
||||
if (_history.isEmpty) return null;
|
||||
return _history.removeLast();
|
||||
if (history.isEmpty) return null;
|
||||
return history.removeLast();
|
||||
}
|
||||
|
||||
get last => _history.isEmpty ? null : _history.last;
|
||||
Move? get last => history.isEmpty ? null : history.last;
|
||||
|
||||
Move moveAt(int index) => _history[index];
|
||||
Move moveAt(int index) => history[index];
|
||||
|
||||
get movesCount => _history.length;
|
||||
int get movesCount => history.length;
|
||||
|
||||
get lastMove => movesCount == 0 ? null : moveAt(movesCount - 1);
|
||||
Move? get lastMove => movesCount == 0 ? null : moveAt(movesCount - 1);
|
||||
|
||||
get lastEffectiveMove => cur == -1 ? null : moveAt(cur);
|
||||
Move? get lastEffectiveMove => cur == -1 ? null : moveAt(cur);
|
||||
|
||||
String buildMoveHistoryText({cols = 2}) {
|
||||
if (_history.length == 0) {
|
||||
String buildMoveHistoryText({int cols = 2}) {
|
||||
if (history.isEmpty) {
|
||||
return '';
|
||||
}
|
||||
|
||||
|
@ -421,21 +409,21 @@ class GameRecorder {
|
|||
if (k % cols == 1) {
|
||||
num = "${(k + 1) ~/ 2}. ";
|
||||
if (k < 9 * cols) {
|
||||
num = " " + num + " ";
|
||||
num = " $num ";
|
||||
}
|
||||
} else {
|
||||
num = "";
|
||||
}
|
||||
if (i + 1 <= cur && _history[i + 1].type == MoveType.remove) {
|
||||
if (i + 1 <= cur && history[i + 1].type == MoveType.remove) {
|
||||
moveHistoryText +=
|
||||
'$num${_history[i].notation}${_history[i + 1].notation} ';
|
||||
'$num${history[i].notation}${history[i + 1].notation} ';
|
||||
i++;
|
||||
} else {
|
||||
moveHistoryText += '$num${_history[i].notation} ';
|
||||
moveHistoryText += '$num${history[i].notation} ';
|
||||
}
|
||||
k++;
|
||||
} else {
|
||||
moveHistoryText += '${i < 9 ? ' ' : ''}${i + 1}. ${_history[i].move} ';
|
||||
moveHistoryText += '${i < 9 ? ' ' : ''}${i + 1}. ${history[i].move} ';
|
||||
}
|
||||
|
||||
if (Config.standardNotationEnabled) {
|
||||
|
@ -449,8 +437,6 @@ class GameRecorder {
|
|||
moveHistoryText = "";
|
||||
}
|
||||
|
||||
moveHistoryText = moveHistoryText.replaceAll(' \n', '\n');
|
||||
|
||||
return moveHistoryText;
|
||||
return moveHistoryText.replaceAll(' \n', '\n');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ class Rule {
|
|||
int piecesCount = specialCountryAndRegion == "Iran" ? 12 : 9;
|
||||
int flyPieceCount = 3;
|
||||
int piecesAtLeastCount = 3;
|
||||
bool hasDiagonalLines = specialCountryAndRegion == "Iran" ? true : false;
|
||||
bool hasDiagonalLines = specialCountryAndRegion == "Iran";
|
||||
bool hasBannedLocations = false;
|
||||
bool mayMoveInPlacingPhase = false;
|
||||
bool isDefenderMoveFirst = false;
|
||||
|
|
|
@ -16,7 +16,9 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
abs(value) => value > 0 ? value : -value;
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
int abs(int value) => value > 0 ? value : -value;
|
||||
|
||||
class Move {
|
||||
static const invalidMove = -1;
|
||||
|
@ -48,7 +50,7 @@ class Move {
|
|||
// Used to restore fen step counter when undoing move
|
||||
String counterMarks = "";
|
||||
|
||||
parse() {
|
||||
void parse() {
|
||||
if (!legal(move)) {
|
||||
throw "Error: Invalid Move: $move";
|
||||
}
|
||||
|
@ -82,7 +84,7 @@ class Move {
|
|||
removed = Piece.noPiece;
|
||||
} else if (move == "draw") {
|
||||
// TODO
|
||||
print("[TODO] Computer request draw");
|
||||
debugPrint("[TODO] Computer request draw");
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
|
@ -99,8 +101,7 @@ class Move {
|
|||
/// Remove: -(1,2)
|
||||
/// Move: (3,1)->(2,1)
|
||||
|
||||
Move.set(String move) {
|
||||
this.move = move;
|
||||
Move.set(this.move) {
|
||||
parse();
|
||||
}
|
||||
|
||||
|
@ -111,7 +112,7 @@ class Move {
|
|||
|
||||
if (move == null || move.length > "(3,1)->(2,1)".length) return false;
|
||||
|
||||
String range = "0123456789(,)->";
|
||||
const String range = "0123456789(,)->";
|
||||
|
||||
if (!(move[0] == '(' || move[0] == '-')) {
|
||||
return false;
|
||||
|
@ -146,10 +147,16 @@ class PieceColor {
|
|||
static const draw = '=';
|
||||
|
||||
static String of(String piece) {
|
||||
if (white.contains(piece)) return white;
|
||||
if (black.contains(piece)) return black;
|
||||
if (ban.contains(piece)) return ban;
|
||||
return nobody;
|
||||
switch (piece) {
|
||||
case white:
|
||||
return white;
|
||||
case black:
|
||||
return black;
|
||||
case ban:
|
||||
return ban;
|
||||
default:
|
||||
return nobody;
|
||||
}
|
||||
}
|
||||
|
||||
static bool isSameColor(String p1, String p2) => of(p1) == of(p2);
|
||||
|
@ -190,6 +197,7 @@ enum GameOverReason {
|
|||
enum PieceType { none, whiteStone, blackStone, ban, count, stone }
|
||||
|
||||
class Piece {
|
||||
const Piece._();
|
||||
static const noPiece = PieceColor.none;
|
||||
static const whiteStone = PieceColor.white;
|
||||
static const blackStone = PieceColor.black;
|
||||
|
@ -264,17 +272,17 @@ int makeSquare(int file, int rank) {
|
|||
}
|
||||
|
||||
bool isOk(int sq) {
|
||||
bool ret = (sq == 0 || (sq >= sqBegin && sq < sqEnd));
|
||||
final bool ret = sq == 0 || (sq >= sqBegin && sq < sqEnd);
|
||||
|
||||
if (ret == false) {
|
||||
print("[types] $sq is not OK");
|
||||
debugPrint("[types] $sq is not OK");
|
||||
}
|
||||
|
||||
return ret; // TODO: SQ_NONE?
|
||||
}
|
||||
|
||||
int fileOf(int sq) {
|
||||
return (sq >> 3);
|
||||
return sq >> 3;
|
||||
}
|
||||
|
||||
int rankOf(int sq) {
|
||||
|
@ -282,13 +290,11 @@ int rankOf(int sq) {
|
|||
}
|
||||
|
||||
int fromSq(int move) {
|
||||
move = abs(move);
|
||||
return (move >> 8);
|
||||
return abs(move) >> 8;
|
||||
}
|
||||
|
||||
int toSq(int move) {
|
||||
move = abs(move);
|
||||
return (move & 0x00FF);
|
||||
return abs(move) & 0x00FF;
|
||||
}
|
||||
|
||||
int makeMove(int from, int to) {
|
||||
|
|
|
@ -17,8 +17,10 @@
|
|||
*/
|
||||
|
||||
class Zobrist {
|
||||
const Zobrist._();
|
||||
|
||||
static const int KEY_MISC_BIT = 2;
|
||||
static var psq = [
|
||||
static const List<List<int>> psq = [
|
||||
[
|
||||
0x4E421A,
|
||||
0x3962FF,
|
||||
|
@ -189,5 +191,5 @@ class Zobrist {
|
|||
]
|
||||
];
|
||||
|
||||
static int side = 0x201906;
|
||||
static const int side = 0x201906;
|
||||
}
|
||||
|
|
|
@ -22,90 +22,65 @@ import 'package:devicelocale/devicelocale.dart';
|
|||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:package_info_plus/package_info_plus.dart';
|
||||
import 'package:sanmill/common/constants.dart';
|
||||
import 'package:sanmill/generated/flutter_version.dart';
|
||||
import 'package:sanmill/generated/l10n.dart';
|
||||
import 'package:sanmill/style/app_theme.dart';
|
||||
import 'package:sanmill/widgets/settings_list_tile.dart';
|
||||
import 'package:sanmill/screens/license_page.dart';
|
||||
import 'package:sanmill/screens/oss_license_page.dart';
|
||||
import 'package:sanmill/shared/common/constants.dart';
|
||||
import 'package:sanmill/shared/list_item_divider.dart';
|
||||
import 'package:sanmill/shared/settings/settings_list_tile.dart';
|
||||
import 'package:sanmill/shared/theme/app_theme.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
import 'license_page.dart';
|
||||
import 'list_item_divider.dart';
|
||||
import 'oss_license_page.dart';
|
||||
|
||||
class AboutPage extends StatefulWidget {
|
||||
@override
|
||||
_AboutPageState createState() => _AboutPageState();
|
||||
}
|
||||
|
||||
class _AboutPageState extends State<AboutPage> {
|
||||
String _version = "";
|
||||
class AboutPage extends StatelessWidget {
|
||||
final String tag = "[about] ";
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_loadVersionInfo();
|
||||
super.initState();
|
||||
}
|
||||
|
||||
String getMode() {
|
||||
late String ret;
|
||||
String get mode {
|
||||
if (kDebugMode) {
|
||||
ret = "- debug";
|
||||
return "- debug";
|
||||
} else if (kProfileMode) {
|
||||
ret = "- profile";
|
||||
return "- profile";
|
||||
} else if (kReleaseMode) {
|
||||
ret = "";
|
||||
return "";
|
||||
} else {
|
||||
ret = "-test";
|
||||
return "-test";
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
String mode = getMode();
|
||||
final List<Widget> _children = [
|
||||
FutureBuilder<PackageInfo>(
|
||||
future: PackageInfo.fromPlatform(),
|
||||
builder: (_, data) {
|
||||
late final String _version;
|
||||
if (!data.hasData) {
|
||||
_version = '';
|
||||
} else {
|
||||
final packageInfo = data.data!;
|
||||
if (Platform.isWindows) {
|
||||
_version = packageInfo.version; // TODO
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: AppTheme.aboutPageBackgroundColor,
|
||||
appBar: AppBar(
|
||||
centerTitle: true,
|
||||
title: Text(S.of(context).about + " " + S.of(context).appName)),
|
||||
body: SingleChildScrollView(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
children: children(context, mode),
|
||||
),
|
||||
} else {
|
||||
_version = '${packageInfo.version} (${packageInfo.buildNumber})';
|
||||
}
|
||||
}
|
||||
return SettingsListTile(
|
||||
titleString: S.of(context).versionInfo,
|
||||
subtitleString: "${Constants.projectName} $_version $mode",
|
||||
onTap: () => _showVersionInfo(context, _version),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
List<Widget> children(BuildContext context, String mode) {
|
||||
return <Widget>[
|
||||
SettingsListTile(
|
||||
context: context,
|
||||
titleString: S.of(context).versionInfo,
|
||||
subtitleString: Constants.projectName + " $_version" + " " + mode,
|
||||
onTap: _showVersionInfo,
|
||||
),
|
||||
ListItemDivider(),
|
||||
SettingsListTile(
|
||||
context: context,
|
||||
titleString: S.of(context).feedback,
|
||||
onTap: _launchFeedback,
|
||||
),
|
||||
ListItemDivider(),
|
||||
SettingsListTile(
|
||||
context: context,
|
||||
titleString: S.of(context).eula,
|
||||
onTap: () {
|
||||
_launchEULA();
|
||||
},
|
||||
onTap: _launchEULA,
|
||||
),
|
||||
ListItemDivider(),
|
||||
SettingsListTile(
|
||||
context: context,
|
||||
titleString: S.of(context).license,
|
||||
onTap: () {
|
||||
Navigator.push(
|
||||
|
@ -116,76 +91,54 @@ class _AboutPageState extends State<AboutPage> {
|
|||
);
|
||||
},
|
||||
),
|
||||
ListItemDivider(),
|
||||
SettingsListTile(
|
||||
context: context,
|
||||
titleString: S.of(context).sourceCode,
|
||||
onTap: () {
|
||||
_launchSourceCode();
|
||||
},
|
||||
onTap: _launchSourceCode,
|
||||
),
|
||||
ListItemDivider(),
|
||||
SettingsListTile(
|
||||
context: context,
|
||||
titleString: S.of(context).privacyPolicy,
|
||||
onTap: () {
|
||||
_launchPrivacyPolicy();
|
||||
},
|
||||
onTap: _launchPrivacyPolicy,
|
||||
),
|
||||
ListItemDivider(),
|
||||
SettingsListTile(
|
||||
context: context,
|
||||
titleString: S.of(context).ossLicenses,
|
||||
onTap: () {
|
||||
_launchThirdPartyNotices();
|
||||
},
|
||||
onTap: () => _launchThirdPartyNotices(context),
|
||||
),
|
||||
ListItemDivider(),
|
||||
SettingsListTile(
|
||||
context: context,
|
||||
titleString: S.of(context).helpImproveTranslate,
|
||||
onTap: () {
|
||||
_launchHelpImproveTranslate();
|
||||
},
|
||||
onTap: _launchHelpImproveTranslate,
|
||||
),
|
||||
ListItemDivider(),
|
||||
SettingsListTile(
|
||||
context: context,
|
||||
titleString: S.of(context).thanks,
|
||||
onTap: () {
|
||||
_launchThanks();
|
||||
},
|
||||
onTap: _launchThanks,
|
||||
),
|
||||
ListItemDivider(),
|
||||
];
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: AppTheme.aboutPageBackgroundColor,
|
||||
appBar: AppBar(
|
||||
centerTitle: true,
|
||||
title: Text("${S.of(context).about} ${S.of(context).appName}"),
|
||||
),
|
||||
body: ListView.separated(
|
||||
itemBuilder: (_, index) => _children[index],
|
||||
separatorBuilder: (_, __) => const ListItemDivider(),
|
||||
itemCount: _children.length,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
_loadVersionInfo() async {
|
||||
final packageInfo = await PackageInfo.fromPlatform();
|
||||
|
||||
if (Platform.isWindows) {
|
||||
setState(() {
|
||||
_version = '${packageInfo.version}'; // TODO
|
||||
});
|
||||
} else {
|
||||
setState(() {
|
||||
_version = '${packageInfo.version} (${packageInfo.buildNumber})';
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
_launchURL(String url) async {
|
||||
Future<void> _launchURL(String url) async {
|
||||
await launch(url);
|
||||
}
|
||||
|
||||
_launchFeedback() async {
|
||||
Future<void> _launchFeedback() async {
|
||||
String? locale = "en_US";
|
||||
|
||||
if (!Platform.isWindows) {
|
||||
locale = await Devicelocale.currentLocale;
|
||||
}
|
||||
|
||||
print("$tag local = $locale");
|
||||
debugPrint("$tag local = $locale");
|
||||
if (locale != null && locale.startsWith("zh_")) {
|
||||
_launchURL(Constants.giteeIssuesURL);
|
||||
} else {
|
||||
|
@ -193,14 +146,14 @@ class _AboutPageState extends State<AboutPage> {
|
|||
}
|
||||
}
|
||||
|
||||
_launchEULA() async {
|
||||
Future<void> _launchEULA() async {
|
||||
String? locale = "en_US";
|
||||
|
||||
if (!Platform.isWindows) {
|
||||
locale = await Devicelocale.currentLocale;
|
||||
}
|
||||
|
||||
print("$tag local = $locale");
|
||||
debugPrint("$tag local = $locale");
|
||||
if (locale != null && locale.startsWith("zh_")) {
|
||||
_launchURL(Constants.giteeEulaURL);
|
||||
} else {
|
||||
|
@ -208,14 +161,14 @@ class _AboutPageState extends State<AboutPage> {
|
|||
}
|
||||
}
|
||||
|
||||
_launchSourceCode() async {
|
||||
Future<void> _launchSourceCode() async {
|
||||
String? locale = "en_US";
|
||||
|
||||
if (!Platform.isWindows) {
|
||||
locale = await Devicelocale.currentLocale;
|
||||
}
|
||||
|
||||
print("$tag local = $locale");
|
||||
debugPrint("$tag local = $locale");
|
||||
if (locale != null && locale.startsWith("zh_")) {
|
||||
_launchURL(Constants.giteeSourceCodeURL);
|
||||
} else {
|
||||
|
@ -223,12 +176,13 @@ class _AboutPageState extends State<AboutPage> {
|
|||
}
|
||||
}
|
||||
|
||||
_launchThirdPartyNotices() async {
|
||||
Future<void> _launchThirdPartyNotices(BuildContext context) async {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => OssLicensesPage(),
|
||||
));
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => OssLicensesPage(),
|
||||
),
|
||||
);
|
||||
/*
|
||||
String? locale = "en_US";
|
||||
|
||||
|
@ -236,7 +190,7 @@ class _AboutPageState extends State<AboutPage> {
|
|||
locale = await Devicelocale.currentLocale;
|
||||
}
|
||||
|
||||
print("$tag local = $locale");
|
||||
debugPrint("$tag local = $locale");
|
||||
if (locale != null && locale.startsWith("zh_")) {
|
||||
_launchURL(Constants.giteeThirdPartyNoticesURL);
|
||||
} else {
|
||||
|
@ -245,14 +199,14 @@ class _AboutPageState extends State<AboutPage> {
|
|||
*/
|
||||
}
|
||||
|
||||
_launchPrivacyPolicy() async {
|
||||
Future<void> _launchPrivacyPolicy() async {
|
||||
String? locale = "en_US";
|
||||
|
||||
if (!Platform.isWindows) {
|
||||
locale = await Devicelocale.currentLocale;
|
||||
}
|
||||
|
||||
print("$tag local = $locale");
|
||||
debugPrint("$tag local = $locale");
|
||||
if (locale != null && locale.startsWith("zh_")) {
|
||||
_launchURL(Constants.giteePrivacyPolicyURL);
|
||||
} else {
|
||||
|
@ -260,14 +214,14 @@ class _AboutPageState extends State<AboutPage> {
|
|||
}
|
||||
}
|
||||
|
||||
_launchHelpImproveTranslate() async {
|
||||
Future<void> _launchHelpImproveTranslate() async {
|
||||
String? locale = "en_US";
|
||||
|
||||
if (!Platform.isWindows) {
|
||||
locale = await Devicelocale.currentLocale;
|
||||
}
|
||||
|
||||
print("$tag local = $locale");
|
||||
debugPrint("$tag local = $locale");
|
||||
if (locale != null && locale.startsWith("zh_")) {
|
||||
_launchURL(Constants.giteeHelpImproveTranslateURL);
|
||||
} else {
|
||||
|
@ -275,14 +229,14 @@ class _AboutPageState extends State<AboutPage> {
|
|||
}
|
||||
}
|
||||
|
||||
_launchThanks() async {
|
||||
Future<void> _launchThanks() async {
|
||||
String? locale = "en_US";
|
||||
|
||||
if (!Platform.isWindows) {
|
||||
locale = await Devicelocale.currentLocale;
|
||||
}
|
||||
|
||||
print("$tag local = $locale");
|
||||
debugPrint("$tag local = $locale");
|
||||
if (locale != null && locale.startsWith("zh_")) {
|
||||
_launchURL(Constants.giteeThanksURL);
|
||||
} else {
|
||||
|
@ -290,27 +244,39 @@ class _AboutPageState extends State<AboutPage> {
|
|||
}
|
||||
}
|
||||
|
||||
_showVersionInfo() {
|
||||
void _showVersionInfo(BuildContext context, String version) {
|
||||
showDialog(
|
||||
context: context,
|
||||
barrierDismissible: true,
|
||||
builder: (context) => versionDialog(context),
|
||||
builder: (_) => _VersionDialog(
|
||||
version: version,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
AlertDialog versionDialog(BuildContext context) {
|
||||
class _VersionDialog extends StatelessWidget {
|
||||
const _VersionDialog({
|
||||
Key? key,
|
||||
required this.version,
|
||||
}) : super(key: key);
|
||||
|
||||
final String version;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AlertDialog(
|
||||
title: Text(
|
||||
S.of(context).appName,
|
||||
style: TextStyle(color: AppTheme.dialogTitleColor),
|
||||
style: const TextStyle(color: AppTheme.dialogTitleColor),
|
||||
),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Text(S.of(context).version + ": $_version"),
|
||||
SizedBox(height: AppTheme.sizedBoxHeight),
|
||||
SizedBox(height: AppTheme.sizedBoxHeight),
|
||||
Text("${S.of(context).version}: $version"),
|
||||
const SizedBox(height: AppTheme.sizedBoxHeight),
|
||||
const SizedBox(height: AppTheme.sizedBoxHeight),
|
||||
Text(
|
||||
S.of(context).copyright,
|
||||
style: TextStyle(
|
||||
|
@ -322,45 +288,49 @@ class _AboutPageState extends State<AboutPage> {
|
|||
actions: <Widget>[
|
||||
TextButton(
|
||||
child: Text(S.of(context).more),
|
||||
onPressed: () => _showFlutterVersionInfo(),
|
||||
onPressed: () => _showFlutterVersionInfo(context),
|
||||
),
|
||||
TextButton(
|
||||
child: Text(S.of(context).ok),
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
onPressed: () => Navigator.pop(context),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
_showFlutterVersionInfo() {
|
||||
Navigator.of(context).pop();
|
||||
void _showFlutterVersionInfo(BuildContext context) {
|
||||
Navigator.pop(context);
|
||||
|
||||
showDialog(
|
||||
context: context,
|
||||
barrierDismissible: true,
|
||||
builder: (context) => flutterVersionDialog(context),
|
||||
builder: (context) => _flutterVersionDialog(context),
|
||||
);
|
||||
}
|
||||
|
||||
AlertDialog flutterVersionDialog(BuildContext context) {
|
||||
AlertDialog _flutterVersionDialog(BuildContext context) {
|
||||
return AlertDialog(
|
||||
title: Text(
|
||||
S.of(context).more,
|
||||
style: TextStyle(color: AppTheme.dialogTitleColor),
|
||||
style: const TextStyle(color: AppTheme.dialogTitleColor),
|
||||
),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
"${flutterVersion.toString().replaceAll('{', '').replaceAll('}', '').replaceAll(', ', '\n')}",
|
||||
flutterVersion
|
||||
.toString()
|
||||
.replaceAll('{', '')
|
||||
.replaceAll('}', '')
|
||||
.replaceAll(', ', '\n'),
|
||||
),
|
||||
],
|
||||
),
|
||||
actions: <Widget>[
|
||||
TextButton(
|
||||
child: Text(S.of(context).ok),
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
onPressed: () => Navigator.pop(context),
|
||||
),
|
||||
],
|
||||
);
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
This file is part of Sanmill.
|
||||
Copyright (C) 2019-2021 The Sanmill developers (see AUTHORS file)
|
||||
|
||||
Sanmill is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Sanmill is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart' show rootBundle;
|
||||
import 'package:sanmill/generated/l10n.dart';
|
||||
import 'package:sanmill/shared/common/constants.dart';
|
||||
|
||||
class EnvironmentVariablesPage extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return FutureBuilder<String>(
|
||||
future: rootBundle.loadString(Constants.environmentVariablesFilename),
|
||||
builder: (context, data) {
|
||||
late final String _data;
|
||||
if (!data.hasData) {
|
||||
_data = 'Nothing to show';
|
||||
} else {
|
||||
_data = data.data!;
|
||||
}
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(S.of(context).environmentVariables),
|
||||
centerTitle: true,
|
||||
),
|
||||
body: SingleChildScrollView(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Text(
|
||||
_data,
|
||||
style: const TextStyle(fontFamily: 'Monospace', fontSize: 12),
|
||||
textAlign: TextAlign.left,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
|
@ -16,22 +16,16 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:sanmill/common/config.dart';
|
||||
import 'package:sanmill/generated/l10n.dart';
|
||||
import 'package:sanmill/l10n/resources.dart';
|
||||
import 'package:sanmill/mill/game.dart';
|
||||
import 'package:sanmill/mill/types.dart';
|
||||
import 'package:sanmill/painting/board_painter.dart';
|
||||
import 'package:sanmill/painting/pieces_painter.dart';
|
||||
import 'package:sanmill/style/app_theme.dart';
|
||||
part of 'package:sanmill/screens/game_page/game_page.dart';
|
||||
|
||||
typedef BoardTapCallback = dynamic Function(int index);
|
||||
|
||||
class Board extends StatelessWidget {
|
||||
final double width;
|
||||
final double height;
|
||||
final Function(BuildContext, int) onBoardTap;
|
||||
final animationValue;
|
||||
List<String> squareDesc = [];
|
||||
final BoardTapCallback onBoardTap;
|
||||
final double animationValue;
|
||||
final List<String> squareDesc = [];
|
||||
final String tag = "[board]";
|
||||
|
||||
Board({
|
||||
|
@ -42,44 +36,39 @@ class Board extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var padding = AppTheme.boardPadding;
|
||||
final padding = AppTheme.boardPadding;
|
||||
|
||||
buildSquareDescription(context);
|
||||
|
||||
var container = Container(
|
||||
margin: EdgeInsets.symmetric(
|
||||
vertical: 0,
|
||||
horizontal: 0,
|
||||
final grid = GridView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: 7,
|
||||
),
|
||||
child: GridView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: 7,
|
||||
),
|
||||
children: List.generate(7 * 7, (index) {
|
||||
return Center(
|
||||
child: Text(
|
||||
squareDesc[index],
|
||||
style: TextStyle(
|
||||
fontSize: Config.fontSize,
|
||||
color: Config.developerMode ? Colors.red : Colors.transparent,
|
||||
),
|
||||
children: List.generate(
|
||||
7 * 7,
|
||||
(index) => Center(
|
||||
child: Text(
|
||||
squareDesc[index],
|
||||
style: TextStyle(
|
||||
fontSize: Config.fontSize,
|
||||
color: Config.developerMode ? Colors.red : Colors.transparent,
|
||||
),
|
||||
);
|
||||
}),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
var customPaint = CustomPaint(
|
||||
final customPaint = CustomPaint(
|
||||
painter: BoardPainter(width: width),
|
||||
foregroundPainter: PiecesPainter(
|
||||
width: width,
|
||||
position: Game.instance.position,
|
||||
focusIndex: Game.instance.focusIndex,
|
||||
blurIndex: Game.instance.blurIndex,
|
||||
position: gameInstance.position,
|
||||
focusIndex: gameInstance.focusIndex,
|
||||
blurIndex: gameInstance.blurIndex,
|
||||
animationValue: animationValue,
|
||||
),
|
||||
child: container,
|
||||
child: grid,
|
||||
);
|
||||
|
||||
final boardContainer = Container(
|
||||
|
@ -101,37 +90,37 @@ class Board extends StatelessWidget {
|
|||
*/
|
||||
child: boardContainer,
|
||||
onTapUp: (d) {
|
||||
final gridWidth = (width - padding * 2);
|
||||
final gridWidth = width - padding * 2;
|
||||
final squareWidth = gridWidth / 7;
|
||||
final dx = d.localPosition.dx;
|
||||
final dy = d.localPosition.dy;
|
||||
|
||||
final column = (dx - padding) ~/ squareWidth;
|
||||
if (column < 0 || column > 6) {
|
||||
print("$tag Tap on column $column (ignored).");
|
||||
debugPrint("$tag Tap on column $column (ignored).");
|
||||
return;
|
||||
}
|
||||
|
||||
final row = (dy - padding) ~/ squareWidth;
|
||||
if (row < 0 || row > 6) {
|
||||
print("$tag Tap on row $row (ignored).");
|
||||
debugPrint("$tag Tap on row $row (ignored).");
|
||||
return;
|
||||
}
|
||||
|
||||
final index = row * 7 + column;
|
||||
|
||||
print("$tag Tap on ($row, $column) <$index>");
|
||||
debugPrint("$tag Tap on ($row, $column) <$index>");
|
||||
|
||||
onBoardTap(context, index);
|
||||
onBoardTap(index);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void buildSquareDescription(BuildContext context) {
|
||||
List<String> coordinates = [];
|
||||
List<String> pieceDesc = [];
|
||||
final List<String> coordinates = [];
|
||||
final List<String> pieceDesc = [];
|
||||
|
||||
var map = [
|
||||
const map = [
|
||||
/* 1 */
|
||||
1,
|
||||
8,
|
||||
|
@ -190,7 +179,7 @@ class Board extends StatelessWidget {
|
|||
49
|
||||
];
|
||||
|
||||
var checkPoints = [
|
||||
const checkPoints = [
|
||||
/* 1 */
|
||||
1,
|
||||
0,
|
||||
|
@ -249,17 +238,18 @@ class Board extends StatelessWidget {
|
|||
1
|
||||
];
|
||||
|
||||
bool ltr = getBidirectionality(context) == Bidirectionality.leftToRight;
|
||||
final bool ltr =
|
||||
getBidirectionality(context) == Bidirectionality.leftToRight;
|
||||
|
||||
if (ltr) {
|
||||
for (var file in ['a', 'b', 'c', 'd', 'e', 'f', 'g']) {
|
||||
for (var rank in ['7', '6', '5', '4', '3', '2', '1']) {
|
||||
for (final file in ['a', 'b', 'c', 'd', 'e', 'f', 'g']) {
|
||||
for (final rank in ['7', '6', '5', '4', '3', '2', '1']) {
|
||||
coordinates.add("$file$rank");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (var file in ['g', 'f', 'e', 'd', 'c', 'b', 'a']) {
|
||||
for (var rank in ['7', '6', '5', '4', '3', '2', '1']) {
|
||||
for (final file in ['g', 'f', 'e', 'd', 'c', 'b', 'a']) {
|
||||
for (final rank in ['7', '6', '5', '4', '3', '2', '1']) {
|
||||
coordinates.add("$file$rank");
|
||||
}
|
||||
}
|
||||
|
@ -268,25 +258,38 @@ class Board extends StatelessWidget {
|
|||
for (var i = 0; i < 7 * 7; i++) {
|
||||
if (checkPoints[i] == 0) {
|
||||
pieceDesc.add(S.of(context).noPoint);
|
||||
} else if (Game.instance.position.pieceOnGrid(i) == PieceColor.white) {
|
||||
pieceDesc.add(S.of(context).whitePiece);
|
||||
} else if (Game.instance.position.pieceOnGrid(i) == PieceColor.black) {
|
||||
pieceDesc.add(S.of(context).blackPiece);
|
||||
} else if (Game.instance.position.pieceOnGrid(i) == PieceColor.ban) {
|
||||
pieceDesc.add(S.of(context).banPoint);
|
||||
} else if (Game.instance.position.pieceOnGrid(i) == PieceColor.none) {
|
||||
pieceDesc.add(S.of(context).emptyPoint);
|
||||
} else {
|
||||
switch (gameInstance.position.pieceOnGrid(i)) {
|
||||
case PieceColor.white:
|
||||
pieceDesc.add(S.of(context).whitePiece);
|
||||
|
||||
break;
|
||||
case PieceColor.black:
|
||||
pieceDesc.add(S.of(context).blackPiece);
|
||||
|
||||
break;
|
||||
case PieceColor.ban:
|
||||
pieceDesc.add(S.of(context).banPoint);
|
||||
|
||||
break;
|
||||
case PieceColor.none:
|
||||
pieceDesc.add(S.of(context).emptyPoint);
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
squareDesc.clear();
|
||||
|
||||
for (var i = 0; i < 7 * 7; i++) {
|
||||
var desc = pieceDesc[map[i] - 1];
|
||||
final desc = pieceDesc[map[i] - 1];
|
||||
if (desc == S.of(context).emptyPoint) {
|
||||
squareDesc.add(coordinates[i] + ": " + desc);
|
||||
squareDesc.add("${coordinates[i]}: $desc");
|
||||
} else {
|
||||
squareDesc.add(desc + ": " + coordinates[i]);
|
||||
squareDesc.add("$desc: ${coordinates[i]}");
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
This file is part of Sanmill.
|
||||
Copyright (C) 2019-2021 The Sanmill developers (see AUTHORS file)
|
||||
|
||||
Sanmill is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Sanmill is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
part of 'package:sanmill/screens/game_page/game_page.dart';
|
||||
|
||||
class GamePageToolBar extends StatelessWidget {
|
||||
final List<Widget> children;
|
||||
const GamePageToolBar({
|
||||
Key? key,
|
||||
required this.children,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
color: Color(Config.navigationToolbarBackgroundColor),
|
||||
),
|
||||
margin: const EdgeInsets.symmetric(vertical: 0.5),
|
||||
padding: const EdgeInsets.symmetric(vertical: 2),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
textDirection: TextDirection.ltr,
|
||||
children: children,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,556 @@
|
|||
/*
|
||||
This file is part of Sanmill.
|
||||
Copyright (C) 2019-2021 The Sanmill developers (see AUTHORS file)
|
||||
|
||||
Sanmill is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Sanmill is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:sanmill/generated/l10n.dart';
|
||||
import 'package:sanmill/screens/env_page.dart';
|
||||
import 'package:sanmill/shared/common/config.dart';
|
||||
import 'package:sanmill/shared/common/settings.dart';
|
||||
import 'package:sanmill/shared/dialog.dart';
|
||||
import 'package:sanmill/shared/settings/settings_card.dart';
|
||||
import 'package:sanmill/shared/settings/settings_list_tile.dart';
|
||||
import 'package:sanmill/shared/settings/settings_switch_list_tile.dart';
|
||||
import 'package:sanmill/shared/theme/app_theme.dart';
|
||||
|
||||
class Developer {
|
||||
const Developer._();
|
||||
static bool developerModeEnabled = false;
|
||||
}
|
||||
|
||||
class GameSettingsPage extends StatefulWidget {
|
||||
@override
|
||||
_GameSettingsPageState createState() => _GameSettingsPageState();
|
||||
}
|
||||
|
||||
class _GameSettingsPageState extends State<GameSettingsPage> {
|
||||
Color pickerColor = const Color(0xFF808080);
|
||||
Color currentColor = const Color(0xFF808080);
|
||||
|
||||
late StreamController<int> _events;
|
||||
|
||||
List<String> algorithmNames = ['Alpha-Beta', 'PVS', 'MTD(f)'];
|
||||
|
||||
final String tag = "[game_settings_page]";
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_events = StreamController<int>.broadcast();
|
||||
_events.add(10);
|
||||
}
|
||||
|
||||
Future<void> _restore() async {
|
||||
final settings = await Settings.instance();
|
||||
await settings.restore();
|
||||
}
|
||||
|
||||
SliderTheme _skillLevelSliderTheme(BuildContext context, Function setState) {
|
||||
return SliderTheme(
|
||||
data: AppTheme.sliderThemeData,
|
||||
child: Semantics(
|
||||
label: S.of(context).skillLevel,
|
||||
child: Slider(
|
||||
value: Config.skillLevel.toDouble(),
|
||||
min: 1,
|
||||
max: 30,
|
||||
divisions: 29,
|
||||
label: Config.skillLevel.toString(),
|
||||
onChanged: (value) => setState(() {
|
||||
debugPrint("[config] Slider value: $value");
|
||||
Config.skillLevel = value.toInt();
|
||||
Config.save();
|
||||
}),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
SliderTheme _moveTimeSliderTheme(BuildContext context, Function setState) {
|
||||
return SliderTheme(
|
||||
data: AppTheme.sliderThemeData,
|
||||
child: Semantics(
|
||||
label: S.of(context).moveTime,
|
||||
child: Slider(
|
||||
value: Config.moveTime.toDouble(),
|
||||
max: 60,
|
||||
divisions: 60,
|
||||
label: Config.moveTime.toString(),
|
||||
onChanged: (value) => setState(() {
|
||||
debugPrint("[config] Slider value: $value");
|
||||
Config.moveTime = value.toInt();
|
||||
Config.save();
|
||||
}),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Restore
|
||||
|
||||
Future<void> restoreFactoryDefaultSettings() async {
|
||||
Future<void> confirm() async {
|
||||
Navigator.pop(context);
|
||||
if (Platform.isAndroid) {
|
||||
showCountdownDialog(context, 10, _events, _restore);
|
||||
} else {
|
||||
_restore();
|
||||
ScaffoldMessenger.of(context).clearSnackBars();
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(S.of(context).exitAppManually)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void cancel() => Navigator.pop(context);
|
||||
|
||||
var prompt = "";
|
||||
|
||||
if (Platform.isAndroid) {
|
||||
prompt = S.of(context).exitApp;
|
||||
}
|
||||
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertDialog(
|
||||
title: Text(
|
||||
S.of(context).restore,
|
||||
style: TextStyle(
|
||||
color: AppTheme.dialogTitleColor,
|
||||
fontSize: Config.fontSize + 4,
|
||||
),
|
||||
),
|
||||
content: SingleChildScrollView(
|
||||
child: Text(
|
||||
"${S.of(context).restoreDefaultSettings}?\n$prompt",
|
||||
style: TextStyle(
|
||||
fontSize: Config.fontSize,
|
||||
),
|
||||
),
|
||||
),
|
||||
actions: <Widget>[
|
||||
TextButton(
|
||||
onPressed: confirm,
|
||||
child: Text(
|
||||
S.of(context).ok,
|
||||
style: TextStyle(
|
||||
fontSize: Config.fontSize,
|
||||
),
|
||||
),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: cancel,
|
||||
child: Text(
|
||||
S.of(context).cancel,
|
||||
style: TextStyle(
|
||||
fontSize: Config.fontSize,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: AppTheme.lightBackgroundColor,
|
||||
appBar: AppBar(
|
||||
centerTitle: true,
|
||||
title: Text(S.of(context).preferences),
|
||||
),
|
||||
body: SingleChildScrollView(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: children(context),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
List<Widget> children(BuildContext context) {
|
||||
return <Widget>[
|
||||
Text(S.of(context).whoMovesFirst, style: AppTheme.settingsHeaderStyle),
|
||||
SettingsCard(
|
||||
children: <Widget>[
|
||||
SettingsSwitchListTile(
|
||||
value: !Config.aiMovesFirst,
|
||||
onChanged: setWhoMovesFirst,
|
||||
titleString:
|
||||
Config.aiMovesFirst ? S.of(context).ai : S.of(context).human,
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: AppTheme.sizedBoxHeight),
|
||||
Text(S.of(context).difficulty, style: AppTheme.settingsHeaderStyle),
|
||||
SettingsCard(
|
||||
children: <Widget>[
|
||||
SettingsListTile(
|
||||
titleString: S.of(context).skillLevel,
|
||||
//trailingString: "L" + Config.skillLevel.toString(),
|
||||
onTap: setSkillLevel,
|
||||
),
|
||||
SettingsListTile(
|
||||
titleString: S.of(context).moveTime,
|
||||
onTap: setMoveTime,
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: AppTheme.sizedBoxHeight),
|
||||
Text(S.of(context).aisPlayStyle, style: AppTheme.settingsHeaderStyle),
|
||||
SettingsCard(
|
||||
children: <Widget>[
|
||||
SettingsListTile(
|
||||
titleString: S.of(context).algorithm,
|
||||
trailingString: algorithmNames[Config.algorithm],
|
||||
onTap: setAlgorithm,
|
||||
),
|
||||
SettingsSwitchListTile(
|
||||
value: Config.drawOnHumanExperience,
|
||||
onChanged: setDrawOnHumanExperience,
|
||||
titleString: S.of(context).drawOnHumanExperience,
|
||||
),
|
||||
SettingsSwitchListTile(
|
||||
value: Config.considerMobility,
|
||||
onChanged: setConsiderMobility,
|
||||
titleString: S.of(context).considerMobility,
|
||||
),
|
||||
SettingsSwitchListTile(
|
||||
value: Config.aiIsLazy,
|
||||
onChanged: setAiIsLazy,
|
||||
titleString: S.of(context).passive,
|
||||
),
|
||||
SettingsSwitchListTile(
|
||||
value: Config.shufflingEnabled,
|
||||
onChanged: setShufflingEnabled,
|
||||
titleString: S.of(context).shufflingEnabled,
|
||||
),
|
||||
],
|
||||
),
|
||||
if (!Platform.isWindows) const SizedBox(height: AppTheme.sizedBoxHeight),
|
||||
if (!Platform.isWindows)
|
||||
Text(S.of(context).playSounds, style: AppTheme.settingsHeaderStyle),
|
||||
if (!Platform.isWindows)
|
||||
SettingsCard(
|
||||
children: <Widget>[
|
||||
SettingsSwitchListTile(
|
||||
value: Config.toneEnabled,
|
||||
onChanged: setTone,
|
||||
titleString: S.of(context).playSoundsInTheGame,
|
||||
),
|
||||
SettingsSwitchListTile(
|
||||
value: Config.keepMuteWhenTakingBack,
|
||||
onChanged: setKeepMuteWhenTakingBack,
|
||||
titleString: S.of(context).keepMuteWhenTakingBack,
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: AppTheme.sizedBoxHeight),
|
||||
Text(S.of(context).accessibility, style: AppTheme.settingsHeaderStyle),
|
||||
SettingsCard(
|
||||
children: <Widget>[
|
||||
SettingsSwitchListTile(
|
||||
value: Config.screenReaderSupport,
|
||||
onChanged: setScreenReaderSupport,
|
||||
titleString: S.of(context).screenReaderSupport,
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: AppTheme.sizedBoxHeight),
|
||||
Text(S.of(context).restore, style: AppTheme.settingsHeaderStyle),
|
||||
SettingsCard(
|
||||
children: <Widget>[
|
||||
SettingsListTile(
|
||||
titleString: S.of(context).restoreDefaultSettings,
|
||||
onTap: restoreFactoryDefaultSettings,
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: AppTheme.sizedBoxHeight),
|
||||
if (Developer.developerModeEnabled)
|
||||
Text(
|
||||
S.of(context).forDevelopers,
|
||||
style: AppTheme.settingsHeaderStyle,
|
||||
),
|
||||
if (Developer.developerModeEnabled)
|
||||
SettingsCard(
|
||||
children: <Widget>[
|
||||
SettingsSwitchListTile(
|
||||
value: Config.developerMode,
|
||||
onChanged: setDeveloperMode,
|
||||
titleString: S.of(context).developerMode,
|
||||
),
|
||||
SettingsSwitchListTile(
|
||||
value: Config.experimentsEnabled,
|
||||
onChanged: setExperimentsEnabled,
|
||||
titleString: S.of(context).experiments,
|
||||
),
|
||||
SettingsSwitchListTile(
|
||||
value: Config.isAutoRestart,
|
||||
onChanged: setIsAutoRestart,
|
||||
titleString: S.of(context).isAutoRestart,
|
||||
),
|
||||
SettingsListTile(
|
||||
titleString: S.of(context).environmentVariables,
|
||||
onTap: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => EnvironmentVariablesPage(),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
Future<void> setSkillLevel() async {
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
builder: (_) => StatefulBuilder(
|
||||
builder: _skillLevelSliderTheme,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> setMoveTime() async {
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
builder: (_) => StatefulBuilder(
|
||||
builder: _moveTimeSliderTheme,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> setWhoMovesFirst(bool value) async {
|
||||
setState(() => Config.aiMovesFirst = !value);
|
||||
|
||||
debugPrint("[config] aiMovesFirst: ${Config.aiMovesFirst}");
|
||||
|
||||
Config.save();
|
||||
}
|
||||
|
||||
Future<void> setAiIsLazy(bool value) async {
|
||||
setState(() => Config.aiIsLazy = value);
|
||||
|
||||
debugPrint("[config] aiMovesFirst: $value");
|
||||
|
||||
Config.save();
|
||||
}
|
||||
|
||||
void setAlgorithm() {
|
||||
Future<void> callback(int? algorithm) async {
|
||||
debugPrint("[config] algorithm = $algorithm");
|
||||
|
||||
Navigator.pop(context);
|
||||
|
||||
setState(() => Config.algorithm = algorithm ?? 2);
|
||||
|
||||
debugPrint("[config] Config.algorithm: ${Config.algorithm}");
|
||||
|
||||
Config.save();
|
||||
}
|
||||
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
builder: (BuildContext context) => Semantics(
|
||||
label: S.of(context).algorithm,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
RadioListTile(
|
||||
activeColor: AppTheme.switchListTileActiveColor,
|
||||
title: const Text('Alpha-Beta'),
|
||||
groupValue: Config.algorithm,
|
||||
value: 0,
|
||||
onChanged: callback,
|
||||
),
|
||||
RadioListTile(
|
||||
activeColor: AppTheme.switchListTileActiveColor,
|
||||
title: const Text('PVS'),
|
||||
groupValue: Config.algorithm,
|
||||
value: 1,
|
||||
onChanged: callback,
|
||||
),
|
||||
RadioListTile(
|
||||
activeColor: AppTheme.switchListTileActiveColor,
|
||||
title: const Text('MTD(f)'),
|
||||
groupValue: Config.algorithm,
|
||||
value: 2,
|
||||
onChanged: callback,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> setDrawOnHumanExperience(bool value) async {
|
||||
setState(() => Config.drawOnHumanExperience = value);
|
||||
|
||||
debugPrint("[config] drawOnHumanExperience: $value");
|
||||
|
||||
Config.save();
|
||||
}
|
||||
|
||||
Future<void> setConsiderMobility(bool value) async {
|
||||
setState(() => Config.considerMobility = value);
|
||||
|
||||
debugPrint("[config] considerMobility: $value");
|
||||
|
||||
Config.save();
|
||||
}
|
||||
|
||||
Future<void> setIsAutoRestart(bool value) async {
|
||||
setState(() => Config.isAutoRestart = value);
|
||||
|
||||
debugPrint("[config] isAutoRestart: $value");
|
||||
|
||||
Config.save();
|
||||
}
|
||||
|
||||
Future<void> setIsAutoChangeFirstMove(bool value) async {
|
||||
setState(() => Config.isAutoChangeFirstMove = value);
|
||||
|
||||
debugPrint("[config] isAutoChangeFirstMove: $value");
|
||||
|
||||
Config.save();
|
||||
}
|
||||
|
||||
Future<void> setResignIfMostLose(bool value) async {
|
||||
setState(() => Config.resignIfMostLose = value);
|
||||
|
||||
debugPrint("[config] resignIfMostLose: $value");
|
||||
|
||||
Config.save();
|
||||
}
|
||||
|
||||
Future<void> setShufflingEnabled(bool value) async {
|
||||
setState(() => Config.shufflingEnabled = value);
|
||||
|
||||
debugPrint("[config] shufflingEnabled: $value");
|
||||
|
||||
Config.save();
|
||||
}
|
||||
|
||||
Future<void> setLearnEndgame(bool value) async {
|
||||
setState(() => Config.learnEndgame = value);
|
||||
|
||||
debugPrint("[config] learnEndgame: $value");
|
||||
|
||||
Config.save();
|
||||
}
|
||||
|
||||
Future<void> setOpeningBook(bool value) async {
|
||||
setState(() => Config.openingBook = value);
|
||||
|
||||
debugPrint("[config] openingBook: $value");
|
||||
|
||||
Config.save();
|
||||
}
|
||||
|
||||
Future<void> setTone(bool value) async {
|
||||
setState(() => Config.toneEnabled = value);
|
||||
|
||||
debugPrint("[config] toneEnabled: $value");
|
||||
|
||||
Config.save();
|
||||
}
|
||||
|
||||
Future<void> setKeepMuteWhenTakingBack(bool value) async {
|
||||
setState(() => Config.keepMuteWhenTakingBack = value);
|
||||
|
||||
debugPrint("[config] keepMuteWhenTakingBack: $value");
|
||||
|
||||
Config.save();
|
||||
}
|
||||
|
||||
Future<void> setScreenReaderSupport(bool value) async {
|
||||
setState(() => Config.screenReaderSupport = value);
|
||||
|
||||
debugPrint("[config] screenReaderSupport: $value");
|
||||
|
||||
Config.save();
|
||||
}
|
||||
|
||||
Future<void> setDeveloperMode(bool value) async {
|
||||
setState(() => Config.developerMode = value);
|
||||
|
||||
debugPrint("[config] developerMode: $value");
|
||||
|
||||
Config.save();
|
||||
}
|
||||
|
||||
Future<void> setExperimentsEnabled(bool value) async {
|
||||
setState(() => Config.experimentsEnabled = value);
|
||||
|
||||
debugPrint("[config] experimentsEnabled: $value");
|
||||
|
||||
Config.save();
|
||||
}
|
||||
|
||||
// Display
|
||||
|
||||
Future<void> setLanguage(String value) async {
|
||||
setState(() => Config.languageCode = value);
|
||||
|
||||
debugPrint("[config] languageCode: $value");
|
||||
|
||||
Config.save();
|
||||
}
|
||||
|
||||
Future<void> setIsPieceCountInHandShown(bool value) async {
|
||||
setState(() => Config.isPieceCountInHandShown = value);
|
||||
|
||||
debugPrint("[config] isPieceCountInHandShown: $value");
|
||||
|
||||
Config.save();
|
||||
}
|
||||
|
||||
Future<void> setIsNotationsShown(bool value) async {
|
||||
setState(() => Config.isNotationsShown = value);
|
||||
|
||||
debugPrint("[config] isNotationsShown: $value");
|
||||
|
||||
Config.save();
|
||||
}
|
||||
|
||||
Future<void> setIsHistoryNavigationToolbarShown(bool value) async {
|
||||
setState(() => Config.isHistoryNavigationToolbarShown = value);
|
||||
|
||||
debugPrint("[config] isHistoryNavigationToolbarShown: $value");
|
||||
|
||||
Config.save();
|
||||
}
|
||||
|
||||
Future<void> setStandardNotationEnabled(bool value) async {
|
||||
setState(() => Config.standardNotationEnabled = value);
|
||||
|
||||
debugPrint("[config] standardNotationEnabled: $value");
|
||||
|
||||
Config.save();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:sanmill/generated/l10n.dart';
|
||||
import 'package:sanmill/shared/common/config.dart';
|
||||
import 'package:sanmill/shared/theme/app_theme.dart';
|
||||
|
||||
class HelpScreen extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
elevation: 0.0,
|
||||
backgroundColor: Color(Config.darkBackgroundColor),
|
||||
centerTitle: true,
|
||||
title: Text(
|
||||
S.of(context).howToPlay,
|
||||
style: TextStyle(
|
||||
color: AppTheme.helpTextColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
backgroundColor: Color(Config.darkBackgroundColor),
|
||||
body: SingleChildScrollView(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Text(
|
||||
S.of(context).helpContent,
|
||||
style: TextStyle(
|
||||
fontSize: Config.fontSize,
|
||||
color: AppTheme.helpTextColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,261 @@
|
|||
/*
|
||||
This file is part of Sanmill.
|
||||
Copyright (C) 2019-2021 The Sanmill developers (see AUTHORS file)
|
||||
|
||||
Sanmill is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Sanmill is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
part of 'package:sanmill/screens/navigation_home_screen.dart';
|
||||
|
||||
enum DrawerIndex {
|
||||
humanVsAi,
|
||||
humanVsHuman,
|
||||
aiVsAi,
|
||||
preferences,
|
||||
ruleSettings,
|
||||
personalization,
|
||||
feedback,
|
||||
Help,
|
||||
About
|
||||
}
|
||||
|
||||
class DrawerListItem {
|
||||
const DrawerListItem({
|
||||
required this.index,
|
||||
required this.title,
|
||||
required this.icon,
|
||||
});
|
||||
|
||||
final DrawerIndex index;
|
||||
final String title;
|
||||
final Icon icon;
|
||||
}
|
||||
|
||||
class HomeDrawer extends StatelessWidget {
|
||||
const HomeDrawer({
|
||||
Key? key,
|
||||
required this.screenIndex,
|
||||
required this.iconAnimationController,
|
||||
required this.callBackIndex,
|
||||
required this.items,
|
||||
}) : super(key: key);
|
||||
|
||||
final AnimationController iconAnimationController;
|
||||
final DrawerIndex screenIndex;
|
||||
final Function(DrawerIndex) callBackIndex;
|
||||
final List<DrawerListItem> items;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Material(
|
||||
color: Color(Config.drawerBackgroundColor),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: <Widget>[
|
||||
_DrawerHeader(
|
||||
iconAnimationController: iconAnimationController,
|
||||
),
|
||||
Divider(height: 1, color: AppTheme.drawerDividerColor),
|
||||
ListView.builder(
|
||||
padding: const EdgeInsets.only(top: 4.0),
|
||||
physics: const BouncingScrollPhysics(),
|
||||
shrinkWrap: true,
|
||||
itemCount: items.length,
|
||||
itemBuilder: _buildChildren,
|
||||
),
|
||||
//drawFooter,
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> navigationToScreen(DrawerIndex index) async {
|
||||
callBackIndex(index);
|
||||
}
|
||||
|
||||
Widget _buildChildren(BuildContext context, int index) {
|
||||
final listItem = items[index];
|
||||
final bool isSelected = screenIndex == listItem.index;
|
||||
|
||||
final bool ltr =
|
||||
getBidirectionality(context) == Bidirectionality.leftToRight;
|
||||
const double radius = 28.0;
|
||||
final animatedBuilder = AnimatedBuilder(
|
||||
animation: iconAnimationController,
|
||||
builder: (BuildContext context, Widget? child) {
|
||||
return Transform(
|
||||
transform: Matrix4.translationValues(
|
||||
(MediaQuery.of(context).size.width * 0.75 - 64) *
|
||||
(1.0 - iconAnimationController.value - 1.0),
|
||||
0.0,
|
||||
0.0,
|
||||
),
|
||||
child: child,
|
||||
);
|
||||
},
|
||||
child: Container(
|
||||
width: MediaQuery.of(context).size.width * 0.75 - 64,
|
||||
height: 46,
|
||||
decoration: BoxDecoration(
|
||||
color: Color(Config.drawerHighlightItemColor),
|
||||
borderRadius: BorderRadius.only(
|
||||
topLeft: Radius.circular(ltr ? 0 : radius),
|
||||
topRight: Radius.circular(ltr ? radius : 0),
|
||||
bottomLeft: Radius.circular(ltr ? 0 : radius),
|
||||
bottomRight: Radius.circular(ltr ? radius : 0),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
final listItemIcon = Icon(
|
||||
listItem.icon.icon,
|
||||
color: isSelected
|
||||
? Color(Config.drawerTextColor) // TODO: drawerHighlightTextColor
|
||||
: Color(Config.drawerTextColor),
|
||||
);
|
||||
|
||||
final child = Row(
|
||||
children: <Widget>[
|
||||
const SizedBox(height: 46.0, width: 6.0),
|
||||
const Padding(
|
||||
padding: EdgeInsets.all(4.0),
|
||||
),
|
||||
listItemIcon,
|
||||
const Padding(
|
||||
padding: EdgeInsets.all(4.0),
|
||||
),
|
||||
Text(
|
||||
listItem.title,
|
||||
style: TextStyle(
|
||||
fontWeight: isSelected ? FontWeight.w700 : FontWeight.w500,
|
||||
fontSize: Config.fontSize,
|
||||
color: isSelected
|
||||
? Color(
|
||||
Config.drawerTextColor,
|
||||
) // TODO: drawerHighlightTextColor
|
||||
: Color(Config.drawerTextColor),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
return InkWell(
|
||||
splashColor: AppTheme.drawerSplashColor,
|
||||
highlightColor: AppTheme.drawerHighlightColor,
|
||||
onTap: () => navigationToScreen(listItem.index),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8),
|
||||
child: isSelected
|
||||
? Stack(
|
||||
children: <Widget>[
|
||||
child,
|
||||
animatedBuilder,
|
||||
],
|
||||
)
|
||||
: child,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _DrawerHeader extends StatelessWidget {
|
||||
const _DrawerHeader({
|
||||
Key? key,
|
||||
required this.iconAnimationController,
|
||||
}) : super(key: key);
|
||||
|
||||
final AnimationController iconAnimationController;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
const String tag = "[home_drawer]";
|
||||
|
||||
final List<Color> animatedTextsColors = [
|
||||
Color(Config.drawerTextColor),
|
||||
Colors.black,
|
||||
Colors.blue,
|
||||
Colors.yellow,
|
||||
Colors.red,
|
||||
Color(Config.darkBackgroundColor),
|
||||
Color(Config.boardBackgroundColor),
|
||||
Color(Config.drawerHighlightItemColor),
|
||||
];
|
||||
|
||||
final rotationTransition = RotationTransition(
|
||||
turns: AlwaysStoppedAnimation<double>(
|
||||
Tween<double>(begin: 0.0, end: 24.0)
|
||||
.animate(
|
||||
CurvedAnimation(
|
||||
parent: iconAnimationController,
|
||||
curve: Curves.fastOutSlowIn,
|
||||
),
|
||||
)
|
||||
.value /
|
||||
360,
|
||||
),
|
||||
);
|
||||
|
||||
final scaleTransition = ScaleTransition(
|
||||
scale: AlwaysStoppedAnimation<double>(
|
||||
1.0 - (iconAnimationController.value) * 0.2,
|
||||
),
|
||||
child: rotationTransition,
|
||||
);
|
||||
|
||||
final animatedBuilder = AnimatedBuilder(
|
||||
animation: iconAnimationController,
|
||||
builder: (_, __) => scaleTransition,
|
||||
);
|
||||
|
||||
final animation = GestureDetector(
|
||||
child: AnimatedTextKit(
|
||||
animatedTexts: [
|
||||
ColorizeAnimatedText(
|
||||
S.of(context).appName,
|
||||
textStyle: TextStyle(
|
||||
fontSize: Config.fontSize + 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
colors: animatedTextsColors,
|
||||
speed: const Duration(seconds: 3),
|
||||
),
|
||||
],
|
||||
pause: const Duration(seconds: 3),
|
||||
repeatForever: true,
|
||||
stopPauseOnTap: true,
|
||||
onTap: () => debugPrint("$tag DoubleTap to enable developer mode."),
|
||||
),
|
||||
onDoubleTap: () {
|
||||
Developer.developerModeEnabled = true;
|
||||
debugPrint("$tag Developer mode enabled.");
|
||||
},
|
||||
);
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
// TODO: can animatedBuilder be removed? does not appear in the widget tree
|
||||
animatedBuilder,
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: isLargeScreen ? 30 : 8, left: 4),
|
||||
child: ExcludeSemantics(child: animation),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
This file is part of Sanmill.
|
||||
Copyright (C) 2019-2021 The Sanmill developers (see AUTHORS file)
|
||||
|
||||
Sanmill is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Sanmill is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart' show rootBundle;
|
||||
import 'package:sanmill/generated/l10n.dart';
|
||||
import 'package:sanmill/shared/common/constants.dart';
|
||||
|
||||
class LicenseAgreementPage extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return FutureBuilder<String>(
|
||||
future: rootBundle.loadString(Constants.gplLicenseFilename),
|
||||
builder: (context, data) {
|
||||
late final String _data;
|
||||
if (!data.hasData) {
|
||||
_data = 'Nothing to show';
|
||||
} else {
|
||||
_data = data.data!;
|
||||
}
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(S.of(context).license),
|
||||
centerTitle: true,
|
||||
),
|
||||
body: SingleChildScrollView(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Text(
|
||||
_data,
|
||||
style: const TextStyle(fontFamily: 'Monospace', fontSize: 12),
|
||||
textAlign: TextAlign.left,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
|
@ -19,26 +19,29 @@
|
|||
import 'dart:io';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:animated_text_kit/animated_text_kit.dart';
|
||||
import 'package:feedback/feedback.dart';
|
||||
import 'package:fluentui_system_icons/fluentui_system_icons.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_email_sender/flutter_email_sender.dart';
|
||||
import 'package:package_info_plus/package_info_plus.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:sanmill/common/config.dart';
|
||||
import 'package:sanmill/common/constants.dart';
|
||||
import 'package:sanmill/engine/engine.dart';
|
||||
import 'package:sanmill/generated/l10n.dart';
|
||||
import 'package:sanmill/l10n/resources.dart';
|
||||
import 'package:sanmill/mill/game.dart';
|
||||
import 'package:sanmill/style/app_theme.dart';
|
||||
import 'package:sanmill/style/colors.dart';
|
||||
import 'package:sanmill/widgets/about_page.dart';
|
||||
import 'package:sanmill/widgets/drawer_user_controller.dart';
|
||||
import 'package:sanmill/widgets/help_screen.dart';
|
||||
import 'package:sanmill/widgets/home_drawer.dart';
|
||||
import 'package:sanmill/screens/about_page.dart';
|
||||
import 'package:sanmill/screens/game_page/game_page.dart';
|
||||
import 'package:sanmill/screens/game_settings_page.dart';
|
||||
import 'package:sanmill/screens/help_screen.dart';
|
||||
import 'package:sanmill/screens/personalization_settings_page.dart';
|
||||
import 'package:sanmill/screens/rule_settings_page.dart';
|
||||
import 'package:sanmill/services/engine/engine.dart';
|
||||
import 'package:sanmill/shared/common/config.dart';
|
||||
import 'package:sanmill/shared/common/constants.dart';
|
||||
import 'package:sanmill/shared/theme/app_theme.dart';
|
||||
|
||||
import 'game_page.dart';
|
||||
import 'game_settings_page.dart';
|
||||
import 'personalization_settings_page.dart';
|
||||
import 'rule_settings_page.dart';
|
||||
part 'package:sanmill/screens/home_drawer.dart';
|
||||
part 'package:sanmill/shared/drawer_controller.dart';
|
||||
|
||||
class NavigationHomeScreen extends StatefulWidget {
|
||||
@override
|
||||
|
@ -46,48 +49,39 @@ class NavigationHomeScreen extends StatefulWidget {
|
|||
}
|
||||
|
||||
class _NavigationHomeScreenState extends State<NavigationHomeScreen> {
|
||||
Widget? screenView;
|
||||
DrawerIndex? drawerIndex;
|
||||
late Widget screenView;
|
||||
late DrawerIndex drawerIndex;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
drawerIndex = DrawerIndex.humanVsAi;
|
||||
screenView = GamePage(EngineType.humanVsAi);
|
||||
screenView = const GamePage(EngineType.humanVsAi);
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
color: UIColors.nearlyWhite,
|
||||
child: SafeArea(
|
||||
top: false,
|
||||
bottom: false,
|
||||
child: Scaffold(
|
||||
backgroundColor: AppTheme.navigationHomeScreenBackgroundColor,
|
||||
body: DrawerUserController(
|
||||
screenIndex: drawerIndex,
|
||||
drawerWidth: MediaQuery.of(context).size.width * 0.75,
|
||||
onDrawerCall: (DrawerIndex index) {
|
||||
// callback from drawer for replace screen
|
||||
// as user need with passing DrawerIndex (Enum index)
|
||||
changeIndex(index);
|
||||
},
|
||||
// we replace screen view as
|
||||
// we need on navigate starting screens
|
||||
screenView: screenView,
|
||||
),
|
||||
),
|
||||
return Material(
|
||||
color: AppTheme.navigationHomeScreenBackgroundColor,
|
||||
child: DrawerController(
|
||||
screenIndex: drawerIndex,
|
||||
drawerWidth: MediaQuery.of(context).size.width * 0.75,
|
||||
onDrawerCall: changeIndex,
|
||||
// we replace screen view as
|
||||
// we need on navigate starting screens
|
||||
screenView: screenView,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// callback from drawer for replace screen
|
||||
/// as user need with passing DrawerIndex (Enum index)
|
||||
void changeIndex(DrawerIndex index) {
|
||||
if (drawerIndex == index && drawerIndex != DrawerIndex.feedback) {
|
||||
return;
|
||||
}
|
||||
|
||||
var drawerMap = {
|
||||
final drawerMap = {
|
||||
DrawerIndex.humanVsAi: EngineType.humanVsAi,
|
||||
DrawerIndex.humanVsHuman: EngineType.humanVsHuman,
|
||||
DrawerIndex.aiVsAi: EngineType.aiVsAi,
|
||||
|
@ -95,28 +89,21 @@ class _NavigationHomeScreenState extends State<NavigationHomeScreen> {
|
|||
|
||||
drawerIndex = index;
|
||||
|
||||
var engineType = drawerMap[drawerIndex!];
|
||||
if (engineType != null) {
|
||||
setState(() {
|
||||
Game.instance.setWhoIsAi(engineType);
|
||||
// TODO: use switch case
|
||||
final engineType = drawerMap[drawerIndex];
|
||||
setState(() {
|
||||
if (engineType != null) {
|
||||
gameInstance.setWhoIsAi(engineType);
|
||||
screenView = GamePage(engineType);
|
||||
});
|
||||
} else if (drawerIndex == DrawerIndex.preferences) {
|
||||
setState(() {
|
||||
} else if (drawerIndex == DrawerIndex.preferences) {
|
||||
screenView = GameSettingsPage();
|
||||
});
|
||||
} else if (drawerIndex == DrawerIndex.ruleSettings) {
|
||||
setState(() {
|
||||
} else if (drawerIndex == DrawerIndex.ruleSettings) {
|
||||
screenView = RuleSettingsPage();
|
||||
});
|
||||
} else if (drawerIndex == DrawerIndex.personalization) {
|
||||
setState(() {
|
||||
} else if (drawerIndex == DrawerIndex.personalization) {
|
||||
screenView = PersonalizationSettingsPage();
|
||||
});
|
||||
} else if (drawerIndex == DrawerIndex.feedback && !Config.developerMode) {
|
||||
setState(() {
|
||||
} else if (drawerIndex == DrawerIndex.feedback && !Config.developerMode) {
|
||||
if (Platform.isWindows) {
|
||||
print("flutter_email_sender does not support Windows.");
|
||||
debugPrint("flutter_email_sender does not support Windows.");
|
||||
//_launchFeedback();
|
||||
} else {
|
||||
BetterFeedback.of(context).show((feedback) async {
|
||||
|
@ -124,33 +111,28 @@ class _NavigationHomeScreenState extends State<NavigationHomeScreen> {
|
|||
final screenshotFilePath =
|
||||
await writeImageToStorage(feedback.screenshot);
|
||||
final packageInfo = await PackageInfo.fromPlatform();
|
||||
var _version =
|
||||
final _version =
|
||||
'${packageInfo.version} (${packageInfo.buildNumber})';
|
||||
|
||||
final Email email = Email(
|
||||
body: feedback.text,
|
||||
subject: Constants.feedbackSubjectPrefix +
|
||||
"$_version" +
|
||||
_version +
|
||||
Constants.feedbackSubjectSuffix,
|
||||
recipients: [Constants.recipients],
|
||||
attachmentPaths: [screenshotFilePath],
|
||||
isHTML: false,
|
||||
);
|
||||
await FlutterEmailSender.send(email);
|
||||
});
|
||||
}
|
||||
});
|
||||
} else if (drawerIndex == DrawerIndex.Help && !Config.developerMode) {
|
||||
setState(() {
|
||||
} else if (drawerIndex == DrawerIndex.Help && !Config.developerMode) {
|
||||
screenView = HelpScreen();
|
||||
});
|
||||
} else if (drawerIndex == DrawerIndex.About && !Config.developerMode) {
|
||||
setState(() {
|
||||
} else if (drawerIndex == DrawerIndex.About && !Config.developerMode) {
|
||||
screenView = AboutPage();
|
||||
});
|
||||
} else {
|
||||
//do in your way......
|
||||
}
|
||||
} else {
|
||||
//do in your way......
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Future<String> writeImageToStorage(Uint8List feedbackScreenshot) async {
|
|
@ -0,0 +1,176 @@
|
|||
/*
|
||||
This file is part of Sanmill.
|
||||
Copyright (C) 2019-2021 The Sanmill developers (see AUTHORS file)
|
||||
|
||||
Sanmill is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Sanmill is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import 'package:fluentui_system_icons/fluentui_system_icons.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:sanmill/generated/l10n.dart';
|
||||
import 'package:sanmill/generated/oss_licenses.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
// TODO: use flutters build in viewLicense function
|
||||
class FlutterLicense extends LicenseEntry {
|
||||
@override
|
||||
final List<String> packages;
|
||||
@override
|
||||
final List<LicenseParagraph> paragraphs;
|
||||
|
||||
FlutterLicense(this.packages, this.paragraphs);
|
||||
}
|
||||
|
||||
/// display all used packages and their license
|
||||
class OssLicensesPage extends StatelessWidget {
|
||||
static Future<List<String>> loadLicenses() async {
|
||||
Stream<LicenseEntry> licenses() async* {
|
||||
yield FlutterLicense(
|
||||
['Sound Effects'],
|
||||
[
|
||||
const LicenseParagraph(
|
||||
'CC-0\nhttps://freesound.org/people/unfa/sounds/243749/',
|
||||
0,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
LicenseRegistry.addLicense(licenses);
|
||||
|
||||
// merging non-dart based dependency list using LicenseRegistry.
|
||||
final ossKeys = ossLicenses.keys.toList();
|
||||
final lm = <String, List<String>>{};
|
||||
await for (final l in LicenseRegistry.licenses) {
|
||||
for (final p in l.packages) {
|
||||
if (!ossKeys.contains(p)) {
|
||||
final lp = lm.putIfAbsent(p, () => []);
|
||||
lp.addAll(l.paragraphs.map((p) => p.text));
|
||||
ossKeys.add(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (final key in lm.keys) {
|
||||
ossLicenses[key] = {'license': lm[key]!.join('\n')};
|
||||
}
|
||||
return ossKeys..sort();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(S.of(context).ossLicenses),
|
||||
),
|
||||
body: FutureBuilder<List<String>>(
|
||||
future: loadLicenses(),
|
||||
builder: (context, snapshot) => ListView.separated(
|
||||
itemCount: snapshot.data?.length ?? 0,
|
||||
itemBuilder: (context, index) {
|
||||
final key = snapshot.data![index];
|
||||
final ossl = ossLicenses[key] as Map<String, dynamic>;
|
||||
final version = ossl['version'];
|
||||
final desc = ossl['description'] as String?;
|
||||
return ListTile(
|
||||
title: Text('$key $version'),
|
||||
subtitle: desc != null ? Text(desc) : null,
|
||||
trailing: const Icon(FluentIcons.chevron_right_24_regular),
|
||||
onTap: () => Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (_) => MiscOssLicenseSingle(name: key, json: ossl),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
separatorBuilder: (context, index) => const Divider(),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
class MiscOssLicenseSingle extends StatelessWidget {
|
||||
final String name;
|
||||
final Map<String, dynamic> json;
|
||||
|
||||
const MiscOssLicenseSingle({
|
||||
required this.name,
|
||||
required this.json,
|
||||
});
|
||||
|
||||
String get version => json['version'] as String? ?? "";
|
||||
String? get description => json['description'] as String?;
|
||||
String get licenseText => json['license'] as String;
|
||||
String? get homepage => json['homepage'] as String?;
|
||||
|
||||
String get _bodyText => licenseText.split('\n').map((line) {
|
||||
if (line.startsWith('//')) line = line.substring(2);
|
||||
return line.trim();
|
||||
}).join('\n');
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => Scaffold(
|
||||
appBar: AppBar(title: Text('$name $version')),
|
||||
backgroundColor: Theme.of(context).canvasColor,
|
||||
body: ListView(
|
||||
children: <Widget>[
|
||||
if (description != null)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
top: 12.0,
|
||||
left: 12.0,
|
||||
right: 12.0,
|
||||
),
|
||||
child: Text(
|
||||
description!,
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyText2!
|
||||
.copyWith(fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
if (homepage != null)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
top: 12.0,
|
||||
left: 12.0,
|
||||
right: 12.0,
|
||||
),
|
||||
child: InkWell(
|
||||
child: Text(
|
||||
homepage!,
|
||||
style: const TextStyle(
|
||||
color: Colors.blue,
|
||||
decoration: TextDecoration.underline,
|
||||
),
|
||||
),
|
||||
onTap: () => launch(homepage!),
|
||||
),
|
||||
),
|
||||
if (description != null || homepage != null) const Divider(),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
top: 12.0,
|
||||
left: 12.0,
|
||||
right: 12.0,
|
||||
),
|
||||
child: Text(
|
||||
_bodyText,
|
||||
style: Theme.of(context).textTheme.bodyText2,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
|
@ -18,16 +18,14 @@
|
|||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_colorpicker/flutter_colorpicker.dart';
|
||||
import 'package:sanmill/common/config.dart';
|
||||
import 'package:sanmill/common/constants.dart';
|
||||
import 'package:sanmill/generated/l10n.dart';
|
||||
import 'package:sanmill/l10n/resources.dart';
|
||||
import 'package:sanmill/style/app_theme.dart';
|
||||
import 'package:sanmill/widgets/settings_card.dart';
|
||||
import 'package:sanmill/widgets/settings_list_tile.dart';
|
||||
import 'package:sanmill/widgets/settings_switch_list_tile.dart';
|
||||
|
||||
import 'list_item_divider.dart';
|
||||
import 'package:sanmill/shared/common/config.dart';
|
||||
import 'package:sanmill/shared/common/constants.dart';
|
||||
import 'package:sanmill/shared/settings/settings_card.dart';
|
||||
import 'package:sanmill/shared/settings/settings_list_tile.dart';
|
||||
import 'package:sanmill/shared/settings/settings_switch_list_tile.dart';
|
||||
import 'package:sanmill/shared/theme/app_theme.dart';
|
||||
|
||||
class PersonalizationSettingsPage extends StatefulWidget {
|
||||
@override
|
||||
|
@ -38,21 +36,16 @@ class PersonalizationSettingsPage extends StatefulWidget {
|
|||
class _PersonalizationSettingsPageState
|
||||
extends State<PersonalizationSettingsPage> {
|
||||
// create some values
|
||||
Color pickerColor = Color(0xFF808080);
|
||||
Color currentColor = Color(0xFF808080);
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
}
|
||||
Color pickerColor = const Color(0xFF808080);
|
||||
Color currentColor = const Color(0xFF808080);
|
||||
|
||||
// ValueChanged<Color> callback
|
||||
void changeColor(Color color) {
|
||||
setState(() => pickerColor = color);
|
||||
}
|
||||
|
||||
showColorDialog(String colorString) async {
|
||||
Map<String, int> colorStrToVal = {
|
||||
Future<void> showColorDialog(String colorString) async {
|
||||
final Map<String, int> colorStrToVal = {
|
||||
S.of(context).boardColor: Config.boardBackgroundColor,
|
||||
S.of(context).backgroundColor: Config.darkBackgroundColor,
|
||||
S.of(context).lineColor: Config.boardLineColor,
|
||||
|
@ -73,9 +66,9 @@ class _PersonalizationSettingsPageState
|
|||
Config.navigationToolbarIconColor,
|
||||
};
|
||||
|
||||
AlertDialog alert = AlertDialog(
|
||||
final AlertDialog alert = AlertDialog(
|
||||
title: Text(
|
||||
S.of(context).pick + " " + colorString,
|
||||
"${S.of(context).pick} $colorString",
|
||||
style: TextStyle(
|
||||
fontSize: Config.fontSize + 4,
|
||||
),
|
||||
|
@ -84,7 +77,6 @@ class _PersonalizationSettingsPageState
|
|||
child: ColorPicker(
|
||||
pickerColor: Color(colorStrToVal[colorString]!),
|
||||
onColorChanged: changeColor,
|
||||
showLabel: true,
|
||||
),
|
||||
),
|
||||
actions: <Widget>[
|
||||
|
@ -98,7 +90,7 @@ class _PersonalizationSettingsPageState
|
|||
onPressed: () {
|
||||
setState(() => currentColor = pickerColor);
|
||||
|
||||
print("[config] pickerColor.value: ${pickerColor.value}");
|
||||
debugPrint("[config] pickerColor.value: ${pickerColor.value}");
|
||||
|
||||
if (colorString == S.of(context).boardColor) {
|
||||
Config.boardBackgroundColor = pickerColor.value;
|
||||
|
@ -136,7 +128,7 @@ class _PersonalizationSettingsPageState
|
|||
}
|
||||
|
||||
Config.save();
|
||||
Navigator.of(context).pop();
|
||||
Navigator.pop(context);
|
||||
},
|
||||
),
|
||||
TextButton(
|
||||
|
@ -147,7 +139,7 @@ class _PersonalizationSettingsPageState
|
|||
),
|
||||
),
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
Navigator.pop(context);
|
||||
},
|
||||
),
|
||||
],
|
||||
|
@ -162,20 +154,22 @@ class _PersonalizationSettingsPageState
|
|||
);
|
||||
}
|
||||
|
||||
SliderTheme _boardBorderLineWidthSliderTheme(context, setState) {
|
||||
SliderTheme _boardBorderLineWidthSliderTheme(
|
||||
BuildContext context,
|
||||
Function setState,
|
||||
) {
|
||||
return SliderTheme(
|
||||
data: AppTheme.sliderThemeData,
|
||||
child: Semantics(
|
||||
label: S.of(context).boardBorderLineWidth,
|
||||
child: Slider(
|
||||
value: Config.boardBorderLineWidth.toDouble(),
|
||||
min: 0.0,
|
||||
value: Config.boardBorderLineWidth,
|
||||
max: 20.0,
|
||||
divisions: 200,
|
||||
label: Config.boardBorderLineWidth.toStringAsFixed(1),
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
print("[config] BoardBorderLineWidth value: $value");
|
||||
debugPrint("[config] BoardBorderLineWidth value: $value");
|
||||
Config.boardBorderLineWidth = value;
|
||||
Config.save();
|
||||
});
|
||||
|
@ -185,31 +179,31 @@ class _PersonalizationSettingsPageState
|
|||
);
|
||||
}
|
||||
|
||||
setBoardBorderLineWidth() async {
|
||||
Future<void> setBoardBorderLineWidth() async {
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
builder: (BuildContext context) => StatefulBuilder(
|
||||
builder: (context, setState) {
|
||||
return _boardBorderLineWidthSliderTheme(context, setState);
|
||||
},
|
||||
builder: (_) => StatefulBuilder(
|
||||
builder: _boardBorderLineWidthSliderTheme,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
SliderTheme _boardInnerLineWidthSliderTheme(context, setState) {
|
||||
SliderTheme _boardInnerLineWidthSliderTheme(
|
||||
BuildContext context,
|
||||
Function setState,
|
||||
) {
|
||||
return SliderTheme(
|
||||
data: AppTheme.sliderThemeData,
|
||||
child: Semantics(
|
||||
label: S.of(context).boardInnerLineWidth,
|
||||
child: Slider(
|
||||
value: Config.boardInnerLineWidth.toDouble(),
|
||||
min: 0.0,
|
||||
value: Config.boardInnerLineWidth,
|
||||
max: 20.0,
|
||||
divisions: 200,
|
||||
label: Config.boardInnerLineWidth.toStringAsFixed(1),
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
print("[config] BoardInnerLineWidth value: $value");
|
||||
debugPrint("[config] BoardInnerLineWidth value: $value");
|
||||
Config.boardInnerLineWidth = value;
|
||||
Config.save();
|
||||
});
|
||||
|
@ -219,26 +213,24 @@ class _PersonalizationSettingsPageState
|
|||
);
|
||||
}
|
||||
|
||||
setBoardInnerLineWidth() async {
|
||||
Future<void> setBoardInnerLineWidth() async {
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
builder: (BuildContext context) => StatefulBuilder(
|
||||
builder: (context, setState) {
|
||||
return _boardInnerLineWidthSliderTheme(context, setState);
|
||||
},
|
||||
builder: (_) => StatefulBuilder(
|
||||
builder: _boardInnerLineWidthSliderTheme,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
setPointStyle() {
|
||||
callback(int? pointStyle) async {
|
||||
Navigator.of(context).pop();
|
||||
void setPointStyle() {
|
||||
Future<void> callback(int? pointStyle) async {
|
||||
Navigator.pop(context);
|
||||
|
||||
setState(() {
|
||||
Config.pointStyle = pointStyle ?? 0;
|
||||
});
|
||||
setState(
|
||||
() => Config.pointStyle = pointStyle ?? 0,
|
||||
);
|
||||
|
||||
print("[config] pointStyle: $pointStyle");
|
||||
debugPrint("[config] pointStyle: $pointStyle");
|
||||
|
||||
Config.save();
|
||||
}
|
||||
|
@ -257,7 +249,6 @@ class _PersonalizationSettingsPageState
|
|||
value: 0,
|
||||
onChanged: callback,
|
||||
),
|
||||
ListItemDivider(),
|
||||
RadioListTile(
|
||||
activeColor: AppTheme.switchListTileActiveColor,
|
||||
title: Text(S.of(context).solid),
|
||||
|
@ -265,16 +256,14 @@ class _PersonalizationSettingsPageState
|
|||
value: 1,
|
||||
onChanged: callback,
|
||||
),
|
||||
ListItemDivider(),
|
||||
/*
|
||||
RadioListTile(
|
||||
activeColor: AppTheme.switchListTileActiveColor,
|
||||
title: Text(S.of(context).hollow),
|
||||
title: const Text(S.of(context).hollow),
|
||||
groupValue: Config.pointStyle,
|
||||
value: 2,
|
||||
onChanged: callback,
|
||||
),
|
||||
ListItemDivider(),
|
||||
*/
|
||||
],
|
||||
),
|
||||
|
@ -282,20 +271,19 @@ class _PersonalizationSettingsPageState
|
|||
);
|
||||
}
|
||||
|
||||
SliderTheme _pointWidthSliderTheme(context, setState) {
|
||||
SliderTheme _pointWidthSliderTheme(BuildContext context, Function setState) {
|
||||
return SliderTheme(
|
||||
data: AppTheme.sliderThemeData,
|
||||
child: Semantics(
|
||||
label: S.of(context).pointWidth,
|
||||
child: Slider(
|
||||
value: Config.pointWidth.toDouble(),
|
||||
min: 0,
|
||||
value: Config.pointWidth,
|
||||
max: 30.0,
|
||||
divisions: 30,
|
||||
label: Config.pointWidth.toStringAsFixed(1),
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
print("[config] pointWidth value: $value");
|
||||
debugPrint("[config] pointWidth value: $value");
|
||||
Config.pointWidth = value;
|
||||
Config.save();
|
||||
});
|
||||
|
@ -305,31 +293,28 @@ class _PersonalizationSettingsPageState
|
|||
);
|
||||
}
|
||||
|
||||
setPointWidth() async {
|
||||
Future<void> setPointWidth() async {
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
builder: (BuildContext context) => StatefulBuilder(
|
||||
builder: (context, setState) {
|
||||
return _pointWidthSliderTheme(context, setState);
|
||||
},
|
||||
builder: (_) => StatefulBuilder(
|
||||
builder: _pointWidthSliderTheme,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
SliderTheme _pieceWidthSliderTheme(context, setState) {
|
||||
SliderTheme _pieceWidthSliderTheme(BuildContext context, Function setState) {
|
||||
return SliderTheme(
|
||||
data: AppTheme.sliderThemeData,
|
||||
child: Semantics(
|
||||
label: S.of(context).pieceWidth,
|
||||
child: Slider(
|
||||
value: Config.pieceWidth.toDouble(),
|
||||
value: Config.pieceWidth,
|
||||
min: 0.5,
|
||||
max: 1.0,
|
||||
divisions: 50,
|
||||
label: Config.pieceWidth.toStringAsFixed(1),
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
print("[config] pieceWidth value: $value");
|
||||
debugPrint("[config] pieceWidth value: $value");
|
||||
Config.pieceWidth = value;
|
||||
Config.save();
|
||||
});
|
||||
|
@ -339,31 +324,29 @@ class _PersonalizationSettingsPageState
|
|||
);
|
||||
}
|
||||
|
||||
setPieceWidth() async {
|
||||
Future<void> setPieceWidth() async {
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
builder: (BuildContext context) => StatefulBuilder(
|
||||
builder: (context, setState) {
|
||||
return _pieceWidthSliderTheme(context, setState);
|
||||
},
|
||||
builder: (_) => StatefulBuilder(
|
||||
builder: _pieceWidthSliderTheme,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
SliderTheme _fontSizeSliderTheme(context, setState) {
|
||||
SliderTheme _fontSizeSliderTheme(BuildContext context, Function setState) {
|
||||
return SliderTheme(
|
||||
data: AppTheme.sliderThemeData,
|
||||
child: Semantics(
|
||||
label: S.of(context).fontSize,
|
||||
child: Slider(
|
||||
value: Config.fontSize.toDouble(),
|
||||
value: Config.fontSize,
|
||||
min: 16,
|
||||
max: 32,
|
||||
divisions: 16,
|
||||
label: Config.fontSize.toStringAsFixed(1),
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
print("[config] fontSize value: $value");
|
||||
debugPrint("[config] fontSize value: $value");
|
||||
Config.fontSize = value;
|
||||
Config.save();
|
||||
});
|
||||
|
@ -373,31 +356,28 @@ class _PersonalizationSettingsPageState
|
|||
);
|
||||
}
|
||||
|
||||
setFontSize() async {
|
||||
Future<void> setFontSize() async {
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
builder: (BuildContext context) => StatefulBuilder(
|
||||
builder: (context, setState) {
|
||||
return _fontSizeSliderTheme(context, setState);
|
||||
},
|
||||
builder: (_) => StatefulBuilder(
|
||||
builder: _fontSizeSliderTheme,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
SliderTheme _boardTopSliderTheme(context, setState) {
|
||||
SliderTheme _boardTopSliderTheme(BuildContext context, Function setState) {
|
||||
return SliderTheme(
|
||||
data: AppTheme.sliderThemeData,
|
||||
child: Semantics(
|
||||
label: S.of(context).boardTop,
|
||||
child: Slider(
|
||||
value: Config.boardTop.toDouble(),
|
||||
min: 0.0,
|
||||
value: Config.boardTop,
|
||||
max: 288.0,
|
||||
divisions: 288,
|
||||
label: Config.boardTop.toStringAsFixed(1),
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
print("[config] BoardTop value: $value");
|
||||
debugPrint("[config] BoardTop value: $value");
|
||||
Config.boardTop = value;
|
||||
Config.save();
|
||||
});
|
||||
|
@ -407,31 +387,31 @@ class _PersonalizationSettingsPageState
|
|||
);
|
||||
}
|
||||
|
||||
setBoardTop() async {
|
||||
Future<void> setBoardTop() async {
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
builder: (BuildContext context) => StatefulBuilder(
|
||||
builder: (context, setState) {
|
||||
return _boardTopSliderTheme(context, setState);
|
||||
},
|
||||
builder: (_) => StatefulBuilder(
|
||||
builder: _boardTopSliderTheme,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
SliderTheme _animationDurationSliderTheme(context, setState) {
|
||||
SliderTheme _animationDurationSliderTheme(
|
||||
BuildContext context,
|
||||
Function setState,
|
||||
) {
|
||||
return SliderTheme(
|
||||
data: AppTheme.sliderThemeData,
|
||||
child: Semantics(
|
||||
label: S.of(context).animationDuration,
|
||||
child: Slider(
|
||||
value: Config.animationDuration.toDouble(),
|
||||
min: 0.0,
|
||||
value: Config.animationDuration,
|
||||
max: 5.0,
|
||||
divisions: 50,
|
||||
label: Config.animationDuration.toStringAsFixed(1),
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
print("[config] AnimationDuration value: $value");
|
||||
debugPrint("[config] AnimationDuration value: $value");
|
||||
Config.animationDuration = value;
|
||||
Config.save();
|
||||
});
|
||||
|
@ -441,13 +421,11 @@ class _PersonalizationSettingsPageState
|
|||
);
|
||||
}
|
||||
|
||||
setAnimationDuration() async {
|
||||
Future<void> setAnimationDuration() async {
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
builder: (BuildContext context) => StatefulBuilder(
|
||||
builder: (context, setState) {
|
||||
return _animationDurationSliderTheme(context, setState);
|
||||
},
|
||||
builder: (_) => StatefulBuilder(
|
||||
builder: _animationDurationSliderTheme,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -471,17 +449,17 @@ class _PersonalizationSettingsPageState
|
|||
}
|
||||
|
||||
List<Widget> children(BuildContext context) {
|
||||
langCallback(var langCode) async {
|
||||
print("[config] languageCode = $langCode");
|
||||
Future<void> langCallback([String? langCode]) async {
|
||||
debugPrint("[config] languageCode = $langCode");
|
||||
|
||||
Navigator.of(context).pop();
|
||||
Navigator.pop(context);
|
||||
|
||||
setState(() {
|
||||
Config.languageCode = langCode ?? Constants.defaultLanguageCodeName;
|
||||
S.load(Locale(Resources.of().languageCode));
|
||||
});
|
||||
|
||||
print("[config] Config.languageCode: ${Config.languageCode}");
|
||||
debugPrint("[config] Config.languageCode: ${Config.languageCode}");
|
||||
|
||||
Config.save();
|
||||
}
|
||||
|
@ -489,203 +467,147 @@ class _PersonalizationSettingsPageState
|
|||
return <Widget>[
|
||||
Text(S.of(context).display, style: AppTheme.settingsHeaderStyle),
|
||||
SettingsCard(
|
||||
context: context,
|
||||
children: <Widget>[
|
||||
SettingsListTile(
|
||||
context: context,
|
||||
titleString: S.of(context).language,
|
||||
trailingString:
|
||||
Config.languageCode == Constants.defaultLanguageCodeName
|
||||
? ""
|
||||
: languageCodeToStrings[Config.languageCode.toString()]!
|
||||
.languageName,
|
||||
Config.languageCode != Constants.defaultLanguageCodeName
|
||||
? languageCodeToStrings[Config.languageCode]!.languageName
|
||||
: "",
|
||||
onTap: () => setLanguage(context, langCallback),
|
||||
),
|
||||
ListItemDivider(),
|
||||
SettingsSwitchListTile(
|
||||
context: context,
|
||||
value: Config.isPieceCountInHandShown,
|
||||
onChanged: setIsPieceCountInHandShown,
|
||||
titleString: S.of(context).isPieceCountInHandShown,
|
||||
),
|
||||
ListItemDivider(),
|
||||
SettingsSwitchListTile(
|
||||
context: context,
|
||||
value: Config.isNotationsShown,
|
||||
onChanged: setIsNotationsShown,
|
||||
titleString: S.of(context).isNotationsShown,
|
||||
),
|
||||
ListItemDivider(),
|
||||
SettingsSwitchListTile(
|
||||
context: context,
|
||||
value: Config.isHistoryNavigationToolbarShown,
|
||||
onChanged: setIsHistoryNavigationToolbarShown,
|
||||
titleString: S.of(context).isHistoryNavigationToolbarShown,
|
||||
),
|
||||
ListItemDivider(),
|
||||
SettingsListTile(
|
||||
context: context,
|
||||
titleString: S.of(context).boardBorderLineWidth,
|
||||
onTap: setBoardBorderLineWidth),
|
||||
ListItemDivider(),
|
||||
titleString: S.of(context).boardBorderLineWidth,
|
||||
onTap: setBoardBorderLineWidth,
|
||||
),
|
||||
SettingsListTile(
|
||||
context: context,
|
||||
titleString: S.of(context).boardInnerLineWidth,
|
||||
onTap: setBoardInnerLineWidth,
|
||||
),
|
||||
ListItemDivider(),
|
||||
SettingsListTile(
|
||||
context: context,
|
||||
titleString: S.of(context).pointStyle,
|
||||
onTap: setPointStyle,
|
||||
),
|
||||
ListItemDivider(),
|
||||
SettingsListTile(
|
||||
context: context,
|
||||
titleString: S.of(context).pointWidth,
|
||||
onTap: setPointWidth,
|
||||
),
|
||||
ListItemDivider(),
|
||||
SettingsListTile(
|
||||
context: context,
|
||||
titleString: S.of(context).pieceWidth,
|
||||
onTap: setPieceWidth,
|
||||
),
|
||||
ListItemDivider(),
|
||||
SettingsListTile(
|
||||
context: context,
|
||||
titleString: S.of(context).fontSize,
|
||||
onTap: setFontSize,
|
||||
),
|
||||
ListItemDivider(),
|
||||
SettingsListTile(
|
||||
context: context,
|
||||
titleString: S.of(context).boardTop,
|
||||
onTap: setBoardTop,
|
||||
),
|
||||
ListItemDivider(),
|
||||
SettingsListTile(
|
||||
context: context,
|
||||
titleString: S.of(context).animationDuration,
|
||||
onTap: setAnimationDuration,
|
||||
),
|
||||
ListItemDivider(),
|
||||
SettingsSwitchListTile(
|
||||
context: context,
|
||||
value: Config.standardNotationEnabled,
|
||||
onChanged: setStandardNotationEnabled,
|
||||
titleString: S.of(context).standardNotation,
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(height: AppTheme.sizedBoxHeight),
|
||||
const SizedBox(height: AppTheme.sizedBoxHeight),
|
||||
Text(S.of(context).color, style: AppTheme.settingsHeaderStyle),
|
||||
SettingsCard(
|
||||
context: context,
|
||||
children: <Widget>[
|
||||
SettingsListTile(
|
||||
context: context,
|
||||
titleString: S.of(context).boardColor,
|
||||
trailingColor: Config.boardBackgroundColor,
|
||||
onTap: () => showColorDialog(S.of(context).boardColor),
|
||||
),
|
||||
ListItemDivider(),
|
||||
SettingsListTile(
|
||||
context: context,
|
||||
titleString: S.of(context).backgroundColor,
|
||||
trailingColor: Config.darkBackgroundColor,
|
||||
onTap: () => showColorDialog(S.of(context).backgroundColor),
|
||||
),
|
||||
ListItemDivider(),
|
||||
SettingsListTile(
|
||||
context: context,
|
||||
titleString: S.of(context).lineColor,
|
||||
trailingColor: Config.boardLineColor,
|
||||
onTap: () => showColorDialog(S.of(context).lineColor),
|
||||
),
|
||||
ListItemDivider(),
|
||||
SettingsListTile(
|
||||
context: context,
|
||||
titleString: S.of(context).whitePieceColor,
|
||||
trailingColor: Config.whitePieceColor,
|
||||
onTap: () => showColorDialog(S.of(context).whitePieceColor),
|
||||
),
|
||||
ListItemDivider(),
|
||||
SettingsListTile(
|
||||
context: context,
|
||||
titleString: S.of(context).blackPieceColor,
|
||||
trailingColor: Config.blackPieceColor,
|
||||
onTap: () => showColorDialog(S.of(context).blackPieceColor),
|
||||
),
|
||||
ListItemDivider(),
|
||||
SettingsListTile(
|
||||
context: context,
|
||||
titleString: S.of(context).pieceHighlightColor,
|
||||
trailingColor: Config.pieceHighlightColor,
|
||||
onTap: () => showColorDialog(S.of(context).pieceHighlightColor),
|
||||
),
|
||||
ListItemDivider(),
|
||||
SettingsListTile(
|
||||
context: context,
|
||||
titleString: S.of(context).messageColor,
|
||||
trailingColor: Config.messageColor,
|
||||
onTap: () => showColorDialog(S.of(context).messageColor),
|
||||
),
|
||||
ListItemDivider(),
|
||||
SettingsListTile(
|
||||
context: context,
|
||||
titleString: S.of(context).drawerColor,
|
||||
trailingColor: Config.drawerColor,
|
||||
onTap: () => showColorDialog(S.of(context).drawerColor),
|
||||
),
|
||||
ListItemDivider(),
|
||||
SettingsListTile(
|
||||
context: context,
|
||||
titleString: S.of(context).drawerBackgroundColor,
|
||||
trailingColor: Config.drawerBackgroundColor,
|
||||
onTap: () => showColorDialog(S.of(context).drawerBackgroundColor),
|
||||
),
|
||||
ListItemDivider(),
|
||||
SettingsListTile(
|
||||
context: context,
|
||||
titleString: S.of(context).drawerTextColor,
|
||||
trailingColor: Config.drawerTextColor,
|
||||
onTap: () => showColorDialog(S.of(context).drawerTextColor),
|
||||
),
|
||||
ListItemDivider(),
|
||||
SettingsListTile(
|
||||
context: context,
|
||||
titleString: S.of(context).drawerHighlightItemColor,
|
||||
trailingColor: Config.drawerHighlightItemColor,
|
||||
onTap: () =>
|
||||
showColorDialog(S.of(context).drawerHighlightItemColor),
|
||||
),
|
||||
ListItemDivider(),
|
||||
SettingsListTile(
|
||||
context: context,
|
||||
titleString: S.of(context).mainToolbarBackgroundColor,
|
||||
trailingColor: Config.mainToolbarBackgroundColor,
|
||||
onTap: () =>
|
||||
showColorDialog(S.of(context).mainToolbarBackgroundColor),
|
||||
),
|
||||
ListItemDivider(),
|
||||
SettingsListTile(
|
||||
context: context,
|
||||
titleString: S.of(context).mainToolbarIconColor,
|
||||
trailingColor: Config.mainToolbarIconColor,
|
||||
onTap: () => showColorDialog(S.of(context).mainToolbarIconColor),
|
||||
),
|
||||
ListItemDivider(),
|
||||
SettingsListTile(
|
||||
context: context,
|
||||
titleString: S.of(context).navigationToolbarBackgroundColor,
|
||||
trailingColor: Config.navigationToolbarBackgroundColor,
|
||||
onTap: () =>
|
||||
showColorDialog(S.of(context).navigationToolbarBackgroundColor),
|
||||
),
|
||||
ListItemDivider(),
|
||||
SettingsListTile(
|
||||
context: context,
|
||||
titleString: S.of(context).navigationToolbarIconColor,
|
||||
trailingColor: Config.navigationToolbarIconColor,
|
||||
onTap: () =>
|
||||
|
@ -698,36 +620,28 @@ class _PersonalizationSettingsPageState
|
|||
|
||||
// Display
|
||||
|
||||
setIsPieceCountInHandShown(bool value) async {
|
||||
setState(() {
|
||||
Config.isPieceCountInHandShown = value;
|
||||
});
|
||||
Future<void> setIsPieceCountInHandShown(bool value) async {
|
||||
setState(() => Config.isPieceCountInHandShown = value);
|
||||
|
||||
Config.save();
|
||||
}
|
||||
|
||||
setIsNotationsShown(bool value) async {
|
||||
setState(() {
|
||||
Config.isNotationsShown = value;
|
||||
});
|
||||
Future<void> setIsNotationsShown(bool value) async {
|
||||
setState(() => Config.isNotationsShown = value);
|
||||
|
||||
Config.save();
|
||||
}
|
||||
|
||||
setIsHistoryNavigationToolbarShown(bool value) async {
|
||||
setState(() {
|
||||
Config.isHistoryNavigationToolbarShown = value;
|
||||
});
|
||||
Future<void> setIsHistoryNavigationToolbarShown(bool value) async {
|
||||
setState(() => Config.isHistoryNavigationToolbarShown = value);
|
||||
|
||||
Config.save();
|
||||
}
|
||||
|
||||
setStandardNotationEnabled(bool value) async {
|
||||
setState(() {
|
||||
Config.standardNotationEnabled = value;
|
||||
});
|
||||
Future<void> setStandardNotationEnabled(bool value) async {
|
||||
setState(() => Config.standardNotationEnabled = value);
|
||||
|
||||
print("[config] standardNotationEnabled: $value");
|
||||
debugPrint("[config] standardNotationEnabled: $value");
|
||||
|
||||
Config.save();
|
||||
}
|
|
@ -17,17 +17,15 @@
|
|||
*/
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:sanmill/common/config.dart';
|
||||
import 'package:sanmill/generated/l10n.dart';
|
||||
import 'package:sanmill/l10n/resources.dart';
|
||||
import 'package:sanmill/mill/rule.dart';
|
||||
import 'package:sanmill/style/app_theme.dart';
|
||||
import 'package:sanmill/widgets/settings_card.dart';
|
||||
import 'package:sanmill/widgets/settings_list_tile.dart';
|
||||
import 'package:sanmill/widgets/settings_switch_list_tile.dart';
|
||||
|
||||
import 'list_item_divider.dart';
|
||||
import 'snack_bar.dart';
|
||||
import 'package:sanmill/shared/common/config.dart';
|
||||
import 'package:sanmill/shared/settings/settings_card.dart';
|
||||
import 'package:sanmill/shared/settings/settings_list_tile.dart';
|
||||
import 'package:sanmill/shared/settings/settings_switch_list_tile.dart';
|
||||
import 'package:sanmill/shared/snack_bar.dart';
|
||||
import 'package:sanmill/shared/theme/app_theme.dart';
|
||||
|
||||
class RuleSettingsPage extends StatefulWidget {
|
||||
@override
|
||||
|
@ -35,11 +33,6 @@ class RuleSettingsPage extends StatefulWidget {
|
|||
}
|
||||
|
||||
class _RuleSettingsPageState extends State<RuleSettingsPage> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
|
@ -60,74 +53,57 @@ class _RuleSettingsPageState extends State<RuleSettingsPage> {
|
|||
return <Widget>[
|
||||
Text(S.of(context).general, style: AppTheme.settingsHeaderStyle),
|
||||
SettingsCard(
|
||||
context: context,
|
||||
children: <Widget>[
|
||||
SettingsListTile(
|
||||
context: context,
|
||||
titleString: S.of(context).piecesCount,
|
||||
subtitleString: S.of(context).piecesCount_Detail,
|
||||
trailingString: Config.piecesCount.toString(),
|
||||
onTap: setNTotalPiecesEachSide,
|
||||
),
|
||||
ListItemDivider(),
|
||||
SettingsSwitchListTile(
|
||||
context: context,
|
||||
value: Config.hasDiagonalLines,
|
||||
onChanged: setHasDiagonalLines,
|
||||
titleString: S.of(context).hasDiagonalLines,
|
||||
subtitleString: S.of(context).hasDiagonalLines_Detail,
|
||||
),
|
||||
ListItemDivider(),
|
||||
SettingsListTile(
|
||||
context: context,
|
||||
titleString: S.of(context).nMoveRule,
|
||||
subtitleString: S.of(context).nMoveRule_Detail,
|
||||
trailingString: Config.nMoveRule.toString(),
|
||||
onTap: setNMoveRule,
|
||||
),
|
||||
ListItemDivider(),
|
||||
SettingsListTile(
|
||||
context: context,
|
||||
titleString: S.of(context).endgameNMoveRule,
|
||||
subtitleString: S.of(context).endgameNMoveRule_Detail,
|
||||
trailingString: Config.endgameNMoveRule.toString(),
|
||||
onTap: setEndgameNMoveRule,
|
||||
),
|
||||
ListItemDivider(),
|
||||
SettingsSwitchListTile(
|
||||
context: context,
|
||||
value: Config.threefoldRepetitionRule,
|
||||
onChanged: setThreefoldRepetitionRule,
|
||||
titleString: S.of(context).threefoldRepetitionRule,
|
||||
subtitleString: S.of(context).threefoldRepetitionRule_Detail,
|
||||
),
|
||||
ListItemDivider(),
|
||||
],
|
||||
),
|
||||
SizedBox(height: AppTheme.sizedBoxHeight),
|
||||
const SizedBox(height: AppTheme.sizedBoxHeight),
|
||||
Text(S.of(context).placing, style: AppTheme.settingsHeaderStyle),
|
||||
SettingsCard(
|
||||
context: context,
|
||||
children: <Widget>[
|
||||
SettingsSwitchListTile(
|
||||
context: context,
|
||||
value: Config.hasBannedLocations,
|
||||
onChanged: setHasBannedLocations,
|
||||
titleString: S.of(context).hasBannedLocations,
|
||||
subtitleString: S.of(context).hasBannedLocations_Detail,
|
||||
),
|
||||
ListItemDivider(),
|
||||
SettingsSwitchListTile(
|
||||
context: context,
|
||||
value: Config.isWhiteLoseButNotDrawWhenBoardFull,
|
||||
onChanged: setIsWhiteLoseButNotDrawWhenBoardFull,
|
||||
titleString: S.of(context).isWhiteLoseButNotDrawWhenBoardFull,
|
||||
subtitleString:
|
||||
S.of(context).isWhiteLoseButNotDrawWhenBoardFull_Detail,
|
||||
),
|
||||
ListItemDivider(),
|
||||
SettingsSwitchListTile(
|
||||
context: context,
|
||||
value: Config.mayOnlyRemoveUnplacedPieceInPlacingPhase,
|
||||
onChanged: setMayOnlyRemoveUnplacedPieceInPlacingPhase,
|
||||
titleString: S.of(context).removeUnplacedPiece,
|
||||
|
@ -135,31 +111,25 @@ class _RuleSettingsPageState extends State<RuleSettingsPage> {
|
|||
),
|
||||
],
|
||||
),
|
||||
SizedBox(height: AppTheme.sizedBoxHeight),
|
||||
const SizedBox(height: AppTheme.sizedBoxHeight),
|
||||
Text(S.of(context).moving, style: AppTheme.settingsHeaderStyle),
|
||||
SettingsCard(
|
||||
context: context,
|
||||
children: <Widget>[
|
||||
Config.experimentsEnabled
|
||||
? SettingsSwitchListTile(
|
||||
context: context,
|
||||
value: Config.mayMoveInPlacingPhase,
|
||||
onChanged: setMayMoveInPlacingPhase,
|
||||
titleString: S.of(context).mayMoveInPlacingPhase,
|
||||
subtitleString: S.of(context).mayMoveInPlacingPhase_Detail,
|
||||
)
|
||||
: SizedBox(height: 1),
|
||||
Config.experimentsEnabled ? ListItemDivider() : SizedBox(height: 1),
|
||||
if (Config.experimentsEnabled)
|
||||
SettingsSwitchListTile(
|
||||
value: Config.mayMoveInPlacingPhase,
|
||||
onChanged: setMayMoveInPlacingPhase,
|
||||
titleString: S.of(context).mayMoveInPlacingPhase,
|
||||
subtitleString: S.of(context).mayMoveInPlacingPhase_Detail,
|
||||
)
|
||||
else
|
||||
SettingsSwitchListTile(
|
||||
value: Config.isDefenderMoveFirst,
|
||||
onChanged: setIsDefenderMoveFirst,
|
||||
titleString: S.of(context).isDefenderMoveFirst,
|
||||
subtitleString: S.of(context).isDefenderMoveFirst_Detail,
|
||||
),
|
||||
SettingsSwitchListTile(
|
||||
context: context,
|
||||
value: Config.isDefenderMoveFirst,
|
||||
onChanged: setIsDefenderMoveFirst,
|
||||
titleString: S.of(context).isDefenderMoveFirst,
|
||||
subtitleString: S.of(context).isDefenderMoveFirst_Detail,
|
||||
),
|
||||
ListItemDivider(),
|
||||
SettingsSwitchListTile(
|
||||
context: context,
|
||||
value: Config.isLoseButNotChangeSideWhenNoWay,
|
||||
onChanged: setIsLoseButNotChangeSideWhenNoWay,
|
||||
titleString: S.of(context).isLoseButNotChangeSideWhenNoWay,
|
||||
|
@ -168,21 +138,17 @@ class _RuleSettingsPageState extends State<RuleSettingsPage> {
|
|||
),
|
||||
],
|
||||
),
|
||||
SizedBox(height: AppTheme.sizedBoxHeight),
|
||||
const SizedBox(height: AppTheme.sizedBoxHeight),
|
||||
Text(S.of(context).mayFly, style: AppTheme.settingsHeaderStyle),
|
||||
SettingsCard(
|
||||
context: context,
|
||||
children: <Widget>[
|
||||
SettingsSwitchListTile(
|
||||
context: context,
|
||||
value: Config.mayFly,
|
||||
onChanged: setAllowFlyingAllowed,
|
||||
titleString: S.of(context).mayFly,
|
||||
subtitleString: S.of(context).mayFly_Detail,
|
||||
),
|
||||
ListItemDivider(),
|
||||
SettingsListTile(
|
||||
context: context,
|
||||
titleString: S.of(context).flyPieceCount,
|
||||
subtitleString: S.of(context).flyPieceCount_Detail,
|
||||
trailingString: Config.flyPieceCount.toString(),
|
||||
|
@ -190,27 +156,22 @@ class _RuleSettingsPageState extends State<RuleSettingsPage> {
|
|||
),
|
||||
],
|
||||
),
|
||||
SizedBox(height: AppTheme.sizedBoxHeight),
|
||||
const SizedBox(height: AppTheme.sizedBoxHeight),
|
||||
Text(S.of(context).removing, style: AppTheme.settingsHeaderStyle),
|
||||
SettingsCard(
|
||||
context: context,
|
||||
children: <Widget>[
|
||||
SettingsSwitchListTile(
|
||||
context: context,
|
||||
value: Config.mayRemoveFromMillsAlways,
|
||||
onChanged: setAllowRemovePieceInMill,
|
||||
titleString: S.of(context).mayRemoveFromMillsAlways,
|
||||
subtitleString: S.of(context).mayRemoveFromMillsAlways_Detail,
|
||||
),
|
||||
ListItemDivider(),
|
||||
SettingsSwitchListTile(
|
||||
context: context,
|
||||
value: Config.mayRemoveMultiple,
|
||||
onChanged: setAllowRemoveMultiPiecesWhenCloseMultiMill,
|
||||
titleString: S.of(context).mayRemoveMultiple,
|
||||
subtitleString: S.of(context).mayRemoveMultiple_Detail,
|
||||
),
|
||||
ListItemDivider(),
|
||||
],
|
||||
),
|
||||
];
|
||||
|
@ -218,18 +179,18 @@ class _RuleSettingsPageState extends State<RuleSettingsPage> {
|
|||
|
||||
// General
|
||||
|
||||
setNTotalPiecesEachSide() {
|
||||
callback(int? piecesCount) async {
|
||||
print("[config] piecesCount = $piecesCount");
|
||||
void setNTotalPiecesEachSide() {
|
||||
Future<void> callback(int? piecesCount) async {
|
||||
debugPrint("[config] piecesCount = $piecesCount");
|
||||
|
||||
Navigator.of(context).pop();
|
||||
Navigator.pop(context);
|
||||
|
||||
setState(() {
|
||||
rule.piecesCount = Config.piecesCount =
|
||||
piecesCount ?? (specialCountryAndRegion == "Iran" ? 12 : 9);
|
||||
});
|
||||
setState(
|
||||
() => rule.piecesCount = Config.piecesCount =
|
||||
piecesCount ?? (specialCountryAndRegion == "Iran" ? 12 : 9),
|
||||
);
|
||||
|
||||
print("[config] rule.piecesCount: ${rule.piecesCount}");
|
||||
debugPrint("[config] rule.piecesCount: ${rule.piecesCount}");
|
||||
|
||||
Config.save();
|
||||
}
|
||||
|
@ -243,53 +204,47 @@ class _RuleSettingsPageState extends State<RuleSettingsPage> {
|
|||
children: <Widget>[
|
||||
RadioListTile(
|
||||
activeColor: AppTheme.switchListTileActiveColor,
|
||||
title: Text('9'),
|
||||
title: const Text('9'),
|
||||
groupValue: Config.piecesCount,
|
||||
value: 9,
|
||||
onChanged: callback,
|
||||
),
|
||||
ListItemDivider(),
|
||||
RadioListTile(
|
||||
activeColor: AppTheme.switchListTileActiveColor,
|
||||
title: Text('10'),
|
||||
title: const Text('10'),
|
||||
groupValue: Config.piecesCount,
|
||||
value: 10,
|
||||
onChanged: callback,
|
||||
),
|
||||
ListItemDivider(),
|
||||
RadioListTile(
|
||||
activeColor: AppTheme.switchListTileActiveColor,
|
||||
title: Text('11'),
|
||||
title: const Text('11'),
|
||||
groupValue: Config.piecesCount,
|
||||
value: 11,
|
||||
onChanged: callback,
|
||||
),
|
||||
ListItemDivider(),
|
||||
RadioListTile(
|
||||
activeColor: AppTheme.switchListTileActiveColor,
|
||||
title: Text('12'),
|
||||
title: const Text('12'),
|
||||
groupValue: Config.piecesCount,
|
||||
value: 12,
|
||||
onChanged: callback,
|
||||
),
|
||||
ListItemDivider(),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
setNMoveRule() {
|
||||
callback(int? nMoveRule) async {
|
||||
print("[config] nMoveRule = $nMoveRule");
|
||||
void setNMoveRule() {
|
||||
Future<void> callback(int? nMoveRule) async {
|
||||
debugPrint("[config] nMoveRule = $nMoveRule");
|
||||
|
||||
Navigator.of(context).pop();
|
||||
Navigator.pop(context);
|
||||
|
||||
setState(() {
|
||||
rule.nMoveRule = Config.nMoveRule = nMoveRule ?? 100;
|
||||
});
|
||||
setState(() => rule.nMoveRule = Config.nMoveRule = nMoveRule ?? 100);
|
||||
|
||||
print("[config] rule.nMoveRule: ${rule.nMoveRule}");
|
||||
debugPrint("[config] rule.nMoveRule: ${rule.nMoveRule}");
|
||||
|
||||
Config.save();
|
||||
}
|
||||
|
@ -303,62 +258,57 @@ class _RuleSettingsPageState extends State<RuleSettingsPage> {
|
|||
children: <Widget>[
|
||||
RadioListTile(
|
||||
activeColor: AppTheme.switchListTileActiveColor,
|
||||
title: Text('30'),
|
||||
title: const Text('30'),
|
||||
groupValue: Config.nMoveRule,
|
||||
value: 30,
|
||||
onChanged: callback,
|
||||
),
|
||||
ListItemDivider(),
|
||||
RadioListTile(
|
||||
activeColor: AppTheme.switchListTileActiveColor,
|
||||
title: Text('50'),
|
||||
title: const Text('50'),
|
||||
groupValue: Config.nMoveRule,
|
||||
value: 50,
|
||||
onChanged: callback,
|
||||
),
|
||||
ListItemDivider(),
|
||||
RadioListTile(
|
||||
activeColor: AppTheme.switchListTileActiveColor,
|
||||
title: Text('60'),
|
||||
title: const Text('60'),
|
||||
groupValue: Config.nMoveRule,
|
||||
value: 60,
|
||||
onChanged: callback,
|
||||
),
|
||||
ListItemDivider(),
|
||||
RadioListTile(
|
||||
activeColor: AppTheme.switchListTileActiveColor,
|
||||
title: Text('100'),
|
||||
title: const Text('100'),
|
||||
groupValue: Config.nMoveRule,
|
||||
value: 100,
|
||||
onChanged: callback,
|
||||
),
|
||||
ListItemDivider(),
|
||||
RadioListTile(
|
||||
activeColor: AppTheme.switchListTileActiveColor,
|
||||
title: Text('200'),
|
||||
title: const Text('200'),
|
||||
groupValue: Config.nMoveRule,
|
||||
value: 200,
|
||||
onChanged: callback,
|
||||
),
|
||||
ListItemDivider(),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
setEndgameNMoveRule() {
|
||||
callback(int? endgameNMoveRule) async {
|
||||
print("[config] endgameNMoveRule = $endgameNMoveRule");
|
||||
void setEndgameNMoveRule() {
|
||||
Future<void> callback(int? endgameNMoveRule) async {
|
||||
debugPrint("[config] endgameNMoveRule = $endgameNMoveRule");
|
||||
|
||||
Navigator.of(context).pop();
|
||||
Navigator.pop(context);
|
||||
|
||||
setState(() {
|
||||
rule.endgameNMoveRule =
|
||||
Config.endgameNMoveRule = endgameNMoveRule ?? 100;
|
||||
});
|
||||
setState(
|
||||
() => rule.endgameNMoveRule =
|
||||
Config.endgameNMoveRule = endgameNMoveRule ?? 100,
|
||||
);
|
||||
|
||||
print("[config] rule.endgameNMoveRule: ${rule.endgameNMoveRule}");
|
||||
debugPrint("[config] rule.endgameNMoveRule: ${rule.endgameNMoveRule}");
|
||||
|
||||
Config.save();
|
||||
}
|
||||
|
@ -372,85 +322,77 @@ class _RuleSettingsPageState extends State<RuleSettingsPage> {
|
|||
children: <Widget>[
|
||||
RadioListTile(
|
||||
activeColor: AppTheme.switchListTileActiveColor,
|
||||
title: Text('5'),
|
||||
title: const Text('5'),
|
||||
groupValue: Config.endgameNMoveRule,
|
||||
value: 5,
|
||||
onChanged: callback,
|
||||
),
|
||||
ListItemDivider(),
|
||||
RadioListTile(
|
||||
activeColor: AppTheme.switchListTileActiveColor,
|
||||
title: Text('10'),
|
||||
title: const Text('10'),
|
||||
groupValue: Config.endgameNMoveRule,
|
||||
value: 10,
|
||||
onChanged: callback,
|
||||
),
|
||||
ListItemDivider(),
|
||||
RadioListTile(
|
||||
activeColor: AppTheme.switchListTileActiveColor,
|
||||
title: Text('20'),
|
||||
title: const Text('20'),
|
||||
groupValue: Config.endgameNMoveRule,
|
||||
value: 20,
|
||||
onChanged: callback,
|
||||
),
|
||||
ListItemDivider(),
|
||||
RadioListTile(
|
||||
activeColor: AppTheme.switchListTileActiveColor,
|
||||
title: Text('30'),
|
||||
title: const Text('30'),
|
||||
groupValue: Config.endgameNMoveRule,
|
||||
value: 30,
|
||||
onChanged: callback,
|
||||
),
|
||||
ListItemDivider(),
|
||||
RadioListTile(
|
||||
activeColor: AppTheme.switchListTileActiveColor,
|
||||
title: Text('50'),
|
||||
title: const Text('50'),
|
||||
groupValue: Config.endgameNMoveRule,
|
||||
value: 50,
|
||||
onChanged: callback,
|
||||
),
|
||||
ListItemDivider(),
|
||||
RadioListTile(
|
||||
activeColor: AppTheme.switchListTileActiveColor,
|
||||
title: Text('60'),
|
||||
title: const Text('60'),
|
||||
groupValue: Config.endgameNMoveRule,
|
||||
value: 60,
|
||||
onChanged: callback,
|
||||
),
|
||||
ListItemDivider(),
|
||||
RadioListTile(
|
||||
activeColor: AppTheme.switchListTileActiveColor,
|
||||
title: Text('100'),
|
||||
title: const Text('100'),
|
||||
groupValue: Config.endgameNMoveRule,
|
||||
value: 100,
|
||||
onChanged: callback,
|
||||
),
|
||||
ListItemDivider(),
|
||||
RadioListTile(
|
||||
activeColor: AppTheme.switchListTileActiveColor,
|
||||
title: Text('200'),
|
||||
title: const Text('200'),
|
||||
groupValue: Config.endgameNMoveRule,
|
||||
value: 200,
|
||||
onChanged: callback,
|
||||
),
|
||||
ListItemDivider(),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
setFlyPieceCount() {
|
||||
callback(int? flyPieceCount) async {
|
||||
print("[config] flyPieceCount = $flyPieceCount");
|
||||
void setFlyPieceCount() {
|
||||
Future<void> callback(int? flyPieceCount) async {
|
||||
debugPrint("[config] flyPieceCount = $flyPieceCount");
|
||||
|
||||
Navigator.of(context).pop();
|
||||
Navigator.pop(context);
|
||||
|
||||
setState(() {
|
||||
rule.flyPieceCount = Config.flyPieceCount = flyPieceCount ?? 3;
|
||||
});
|
||||
setState(
|
||||
() => rule.flyPieceCount = Config.flyPieceCount = flyPieceCount ?? 3,
|
||||
);
|
||||
|
||||
print("[config] rule.flyPieceCount: ${rule.flyPieceCount}");
|
||||
debugPrint("[config] rule.flyPieceCount: ${rule.flyPieceCount}");
|
||||
|
||||
Config.save();
|
||||
}
|
||||
|
@ -464,98 +406,93 @@ class _RuleSettingsPageState extends State<RuleSettingsPage> {
|
|||
children: <Widget>[
|
||||
RadioListTile(
|
||||
activeColor: AppTheme.switchListTileActiveColor,
|
||||
title: Text('3'),
|
||||
title: const Text('3'),
|
||||
groupValue: Config.flyPieceCount,
|
||||
value: 3,
|
||||
onChanged: callback,
|
||||
),
|
||||
ListItemDivider(),
|
||||
RadioListTile(
|
||||
activeColor: AppTheme.switchListTileActiveColor,
|
||||
title: Text('4'),
|
||||
title: const Text('4'),
|
||||
groupValue: Config.flyPieceCount,
|
||||
value: 4,
|
||||
onChanged: callback,
|
||||
),
|
||||
ListItemDivider(),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
setHasDiagonalLines(bool value) async {
|
||||
setState(() {
|
||||
rule.hasDiagonalLines = Config.hasDiagonalLines = value;
|
||||
});
|
||||
Future<void> setHasDiagonalLines(bool value) async {
|
||||
setState(() => rule.hasDiagonalLines = Config.hasDiagonalLines = value);
|
||||
|
||||
print("[config] rule.hasDiagonalLines: $value");
|
||||
debugPrint("[config] rule.hasDiagonalLines: $value");
|
||||
|
||||
Config.save();
|
||||
}
|
||||
|
||||
setAllowFlyingAllowed(bool value) async {
|
||||
setState(() {
|
||||
rule.mayFly = Config.mayFly = value;
|
||||
});
|
||||
Future<void> setAllowFlyingAllowed(bool value) async {
|
||||
setState(() => rule.mayFly = Config.mayFly = value);
|
||||
|
||||
print("[config] rule.mayFly: $value");
|
||||
debugPrint("[config] rule.mayFly: $value");
|
||||
|
||||
Config.save();
|
||||
}
|
||||
|
||||
setThreefoldRepetitionRule(bool value) async {
|
||||
setState(() {
|
||||
rule.threefoldRepetitionRule = Config.threefoldRepetitionRule = value;
|
||||
});
|
||||
Future<void> setThreefoldRepetitionRule(bool value) async {
|
||||
setState(
|
||||
() =>
|
||||
rule.threefoldRepetitionRule = Config.threefoldRepetitionRule = value,
|
||||
);
|
||||
|
||||
print("[config] rule.threefoldRepetitionRule: $value");
|
||||
debugPrint("[config] rule.threefoldRepetitionRule: $value");
|
||||
|
||||
Config.save();
|
||||
}
|
||||
|
||||
// Placing
|
||||
|
||||
setHasBannedLocations(bool value) async {
|
||||
setState(() {
|
||||
rule.hasBannedLocations = Config.hasBannedLocations = value;
|
||||
});
|
||||
Future<void> setHasBannedLocations(bool value) async {
|
||||
setState(() => rule.hasBannedLocations = Config.hasBannedLocations = value);
|
||||
|
||||
print("[config] rule.hasBannedLocations: $value");
|
||||
debugPrint("[config] rule.hasBannedLocations: $value");
|
||||
|
||||
Config.save();
|
||||
}
|
||||
|
||||
setIsWhiteLoseButNotDrawWhenBoardFull(bool value) async {
|
||||
setState(() {
|
||||
rule.isWhiteLoseButNotDrawWhenBoardFull =
|
||||
Config.isWhiteLoseButNotDrawWhenBoardFull = value;
|
||||
});
|
||||
Future<void> setIsWhiteLoseButNotDrawWhenBoardFull(bool value) async {
|
||||
setState(
|
||||
() => rule.isWhiteLoseButNotDrawWhenBoardFull =
|
||||
Config.isWhiteLoseButNotDrawWhenBoardFull = value,
|
||||
);
|
||||
|
||||
print("[config] rule.isWhiteLoseButNotDrawWhenBoardFull: $value");
|
||||
debugPrint("[config] rule.isWhiteLoseButNotDrawWhenBoardFull: $value");
|
||||
|
||||
Config.save();
|
||||
}
|
||||
|
||||
setMayOnlyRemoveUnplacedPieceInPlacingPhase(bool value) async {
|
||||
setState(() {
|
||||
rule.mayOnlyRemoveUnplacedPieceInPlacingPhase =
|
||||
Config.mayOnlyRemoveUnplacedPieceInPlacingPhase = value;
|
||||
});
|
||||
Future<void> setMayOnlyRemoveUnplacedPieceInPlacingPhase(bool value) async {
|
||||
setState(
|
||||
() => rule.mayOnlyRemoveUnplacedPieceInPlacingPhase =
|
||||
Config.mayOnlyRemoveUnplacedPieceInPlacingPhase = value,
|
||||
);
|
||||
|
||||
print("[config] rule.mayOnlyRemoveUnplacedPieceInPlacingPhase: $value");
|
||||
debugPrint(
|
||||
"[config] rule.mayOnlyRemoveUnplacedPieceInPlacingPhase: $value",
|
||||
);
|
||||
|
||||
Config.save();
|
||||
}
|
||||
|
||||
// Moving
|
||||
|
||||
setMayMoveInPlacingPhase(bool value) async {
|
||||
setState(() {
|
||||
rule.mayMoveInPlacingPhase = Config.mayMoveInPlacingPhase = value;
|
||||
});
|
||||
Future<void> setMayMoveInPlacingPhase(bool value) async {
|
||||
setState(
|
||||
() => rule.mayMoveInPlacingPhase = Config.mayMoveInPlacingPhase = value,
|
||||
);
|
||||
|
||||
print("[config] rule.mayMoveInPlacingPhase: $value");
|
||||
debugPrint("[config] rule.mayMoveInPlacingPhase: $value");
|
||||
|
||||
Config.save();
|
||||
|
||||
|
@ -565,57 +502,56 @@ class _RuleSettingsPageState extends State<RuleSettingsPage> {
|
|||
}
|
||||
}
|
||||
|
||||
setIsDefenderMoveFirst(bool value) async {
|
||||
setState(() {
|
||||
rule.isDefenderMoveFirst = Config.isDefenderMoveFirst = value;
|
||||
});
|
||||
Future<void> setIsDefenderMoveFirst(bool value) async {
|
||||
setState(
|
||||
() => rule.isDefenderMoveFirst = Config.isDefenderMoveFirst = value,
|
||||
);
|
||||
|
||||
print("[config] rule.isDefenderMoveFirst: $value");
|
||||
debugPrint("[config] rule.isDefenderMoveFirst: $value");
|
||||
|
||||
Config.save();
|
||||
}
|
||||
|
||||
setIsLoseButNotChangeSideWhenNoWay(bool value) async {
|
||||
setState(() {
|
||||
rule.isLoseButNotChangeSideWhenNoWay =
|
||||
Config.isLoseButNotChangeSideWhenNoWay = value;
|
||||
});
|
||||
Future<void> setIsLoseButNotChangeSideWhenNoWay(bool value) async {
|
||||
setState(
|
||||
() => rule.isLoseButNotChangeSideWhenNoWay =
|
||||
Config.isLoseButNotChangeSideWhenNoWay = value,
|
||||
);
|
||||
|
||||
print("[config] rule.isLoseButNotChangeSideWhenNoWay: $value");
|
||||
debugPrint("[config] rule.isLoseButNotChangeSideWhenNoWay: $value");
|
||||
|
||||
Config.save();
|
||||
}
|
||||
|
||||
// Removing
|
||||
|
||||
setAllowRemovePieceInMill(bool value) async {
|
||||
setState(() {
|
||||
rule.mayRemoveFromMillsAlways = Config.mayRemoveFromMillsAlways = value;
|
||||
});
|
||||
Future<void> setAllowRemovePieceInMill(bool value) async {
|
||||
setState(
|
||||
() => rule.mayRemoveFromMillsAlways =
|
||||
Config.mayRemoveFromMillsAlways = value,
|
||||
);
|
||||
|
||||
print("[config] rule.mayRemoveFromMillsAlways: $value");
|
||||
debugPrint("[config] rule.mayRemoveFromMillsAlways: $value");
|
||||
|
||||
Config.save();
|
||||
}
|
||||
|
||||
setAllowRemoveMultiPiecesWhenCloseMultiMill(bool value) async {
|
||||
setState(() {
|
||||
rule.mayRemoveMultiple = Config.mayRemoveMultiple = value;
|
||||
});
|
||||
Future<void> setAllowRemoveMultiPiecesWhenCloseMultiMill(bool value) async {
|
||||
setState(
|
||||
() => rule.mayRemoveMultiple = Config.mayRemoveMultiple = value,
|
||||
);
|
||||
|
||||
print("[config] rule.mayRemoveMultiple: $value");
|
||||
debugPrint("[config] rule.mayRemoveMultiple: $value");
|
||||
|
||||
Config.save();
|
||||
}
|
||||
|
||||
// Unused
|
||||
|
||||
setNPiecesAtLeast(int value) async {
|
||||
setState(() {
|
||||
rule.piecesAtLeastCount = Config.piecesAtLeastCount = value;
|
||||
});
|
||||
Future<void> setNPiecesAtLeast(int value) async {
|
||||
setState(() => rule.piecesAtLeastCount = Config.piecesAtLeastCount = value);
|
||||
|
||||
print("[config] rule.piecesAtLeastCount: $value");
|
||||
debugPrint("[config] rule.piecesAtLeastCount: $value");
|
||||
|
||||
Config.save();
|
||||
}
|
|
@ -18,29 +18,33 @@
|
|||
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:sanmill/common/config.dart';
|
||||
import 'package:sanmill/shared/common/config.dart';
|
||||
import 'package:soundpool/soundpool.dart';
|
||||
import 'package:stack_trace/stack_trace.dart';
|
||||
|
||||
class Audios {
|
||||
const Audios._();
|
||||
//static AudioPlayer? _player;
|
||||
static Soundpool? _soundpool;
|
||||
// TODO: use enum for the sounds
|
||||
static int? _alarmSoundStreamId;
|
||||
static var drawSoundId;
|
||||
static var flySoundId;
|
||||
static var goSoundId;
|
||||
static var illegalSoundId;
|
||||
static var loseSoundId;
|
||||
static var millSoundId;
|
||||
static var placeSoundId;
|
||||
static var removeSoundId;
|
||||
static var selectSoundId;
|
||||
static var winSoundId;
|
||||
static var isTemporaryMute = false;
|
||||
static int? drawSoundId;
|
||||
static int? flySoundId;
|
||||
static int? goSoundId;
|
||||
static int? illegalSoundId;
|
||||
static int? loseSoundId;
|
||||
static int? millSoundId;
|
||||
static int? placeSoundId;
|
||||
static int? removeSoundId;
|
||||
static int? selectSoundId;
|
||||
static int? winSoundId;
|
||||
static bool isTemporaryMute = false;
|
||||
|
||||
static Future<void> loadSounds() async {
|
||||
if (Platform.isWindows) {
|
||||
debugPrint("[audio] Audio Player does not support Windows.");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -50,7 +54,7 @@ class Audios {
|
|||
if (Config.developerMode) {
|
||||
assert(false);
|
||||
}
|
||||
print("[audio] Error: _soundpool is null.");
|
||||
debugPrint("[audio] Error: _soundpool is null.");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -60,7 +64,7 @@ class Audios {
|
|||
if (Config.developerMode) {
|
||||
assert(false);
|
||||
}
|
||||
print("[audio] Error: drawSoundId is null.");
|
||||
debugPrint("[audio] Error: drawSoundId is null.");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -70,7 +74,7 @@ class Audios {
|
|||
if (Config.developerMode) {
|
||||
assert(false);
|
||||
}
|
||||
print("[audio] Error: flySoundId is null.");
|
||||
debugPrint("[audio] Error: flySoundId is null.");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -80,7 +84,7 @@ class Audios {
|
|||
if (Config.developerMode) {
|
||||
assert(false);
|
||||
}
|
||||
print("[audio] Error: goSoundId is null.");
|
||||
debugPrint("[audio] Error: goSoundId is null.");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -90,7 +94,7 @@ class Audios {
|
|||
if (Config.developerMode) {
|
||||
assert(false);
|
||||
}
|
||||
print("[audio] Error: illegalSoundId is null.");
|
||||
debugPrint("[audio] Error: illegalSoundId is null.");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -100,7 +104,7 @@ class Audios {
|
|||
if (Config.developerMode) {
|
||||
assert(false);
|
||||
}
|
||||
print("[audio] Error: loseSoundId is null.");
|
||||
debugPrint("[audio] Error: loseSoundId is null.");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -110,7 +114,7 @@ class Audios {
|
|||
if (Config.developerMode) {
|
||||
assert(false);
|
||||
}
|
||||
print("[audio] Error: millSoundId is null.");
|
||||
debugPrint("[audio] Error: millSoundId is null.");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -120,7 +124,7 @@ class Audios {
|
|||
if (Config.developerMode) {
|
||||
assert(false);
|
||||
}
|
||||
print("[audio] Error: placeSoundId is null.");
|
||||
debugPrint("[audio] Error: placeSoundId is null.");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -130,7 +134,7 @@ class Audios {
|
|||
if (Config.developerMode) {
|
||||
assert(false);
|
||||
}
|
||||
print("[audio] Error: removeSoundId is null.");
|
||||
debugPrint("[audio] Error: removeSoundId is null.");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -140,7 +144,7 @@ class Audios {
|
|||
if (Config.developerMode) {
|
||||
assert(false);
|
||||
}
|
||||
print("[audio] Error: selectSoundId is null.");
|
||||
debugPrint("[audio] Error: selectSoundId is null.");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -150,12 +154,12 @@ class Audios {
|
|||
if (Config.developerMode) {
|
||||
assert(false);
|
||||
}
|
||||
print("[audio] Error: winSoundId is null.");
|
||||
debugPrint("[audio] Error: winSoundId is null.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static Future<void> _playSound(var soundId) async {
|
||||
static Future<void> _playSound(int? soundId) async {
|
||||
if (Platform.isWindows) {
|
||||
return;
|
||||
}
|
||||
|
@ -164,11 +168,11 @@ class Audios {
|
|||
if (Config.developerMode) {
|
||||
assert(false);
|
||||
}
|
||||
print("[audio] Error: soundId is null.");
|
||||
debugPrint("[audio] Error: soundId is null.");
|
||||
return;
|
||||
}
|
||||
|
||||
_alarmSoundStreamId = await _soundpool!.play(await soundId);
|
||||
_alarmSoundStreamId = await _soundpool!.play(soundId);
|
||||
}
|
||||
|
||||
static Future<void> _stopSound() async {
|
||||
|
@ -189,7 +193,7 @@ class Audios {
|
|||
_soundpool!.dispose();
|
||||
}
|
||||
|
||||
static playTone(var soundId) async {
|
||||
static Future<void> playTone(int? soundId) async {
|
||||
Chain.capture(() async {
|
||||
if (!Config.toneEnabled ||
|
||||
isTemporaryMute ||
|
||||
|
@ -198,7 +202,7 @@ class Audios {
|
|||
}
|
||||
|
||||
if (Platform.isWindows) {
|
||||
print("audio players is not support Windows.");
|
||||
debugPrint("audio players is not support Windows.");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -212,7 +216,7 @@ class Audios {
|
|||
_playSound(soundId);
|
||||
} catch (e) {
|
||||
// Fallback for all errors
|
||||
print(e);
|
||||
debugPrint(e.toString());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -20,41 +20,47 @@ import 'dart:async';
|
|||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:sanmill/common/config.dart';
|
||||
import 'package:sanmill/mill/position.dart';
|
||||
import 'package:sanmill/mill/types.dart';
|
||||
|
||||
import 'engine.dart';
|
||||
import 'package:sanmill/services/engine/engine.dart';
|
||||
import 'package:sanmill/shared/common/config.dart';
|
||||
|
||||
class NativeEngine extends Engine {
|
||||
static const platform = const MethodChannel('com.calcitem.sanmill/engine');
|
||||
static const platform = MethodChannel('com.calcitem.sanmill/engine');
|
||||
bool isActive = false;
|
||||
|
||||
@override
|
||||
Future<void> startup() async {
|
||||
await platform.invokeMethod('startup');
|
||||
await waitResponse(['uciok'], sleep: 100, times: 0);
|
||||
await waitResponse(['uciok']);
|
||||
}
|
||||
|
||||
Future<void> send(String command) async {
|
||||
print("[engine] send: $command");
|
||||
debugPrint("[engine] send: $command");
|
||||
await platform.invokeMethod('send', command);
|
||||
}
|
||||
|
||||
Future<String?> read() async {
|
||||
return await platform.invokeMethod('read');
|
||||
return platform.invokeMethod('read');
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> shutdown() async {
|
||||
isActive = false;
|
||||
await platform.invokeMethod('shutdown');
|
||||
}
|
||||
|
||||
Future<bool?> isReady() async {
|
||||
return await platform.invokeMethod('isReady');
|
||||
return platform.invokeMethod('isReady');
|
||||
}
|
||||
|
||||
FutureOr<bool> isThinking() async {
|
||||
return await platform.invokeMethod('isThinking');
|
||||
final _isThinking = await platform.invokeMethod<bool>('isThinking');
|
||||
if (_isThinking is bool) {
|
||||
return _isThinking;
|
||||
} else {
|
||||
throw 'Invalid platform response. Expected a value of type bool';
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -68,12 +74,12 @@ class NativeEngine extends Engine {
|
|||
await send('go');
|
||||
isActive = true;
|
||||
} else {
|
||||
print("[engine] Move now");
|
||||
debugPrint("[engine] Move now");
|
||||
}
|
||||
|
||||
final response = await waitResponse(['bestmove', 'nobestmove']);
|
||||
|
||||
print("[engine] response: $response");
|
||||
debugPrint("[engine] response: $response");
|
||||
|
||||
if (response.startsWith('bestmove')) {
|
||||
var best = response.substring('bestmove'.length + 1);
|
||||
|
@ -91,8 +97,11 @@ class NativeEngine extends Engine {
|
|||
return EngineResponse('timeout');
|
||||
}
|
||||
|
||||
Future<String> waitResponse(List<String> prefixes,
|
||||
{sleep = 100, times = 0}) async {
|
||||
Future<String> waitResponse(
|
||||
List<String> prefixes, {
|
||||
int sleep = 100,
|
||||
int times = 0,
|
||||
}) async {
|
||||
var timeLimit = Config.developerMode ? 100 : 6000;
|
||||
|
||||
if (Config.moveTime > 0) {
|
||||
|
@ -101,9 +110,9 @@ class NativeEngine extends Engine {
|
|||
}
|
||||
|
||||
if (times > timeLimit) {
|
||||
print("[engine] Timeout. sleep = $sleep, times = $times");
|
||||
debugPrint("[engine] Timeout. sleep = $sleep, times = $times");
|
||||
if (Config.developerMode && isActive) {
|
||||
throw ("Exception: waitResponse timeout.");
|
||||
throw "Exception: waitResponse timeout.";
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
@ -111,11 +120,11 @@ class NativeEngine extends Engine {
|
|||
final response = await read();
|
||||
|
||||
if (response != null) {
|
||||
for (var prefix in prefixes) {
|
||||
for (final prefix in prefixes) {
|
||||
if (response.startsWith(prefix)) {
|
||||
return response;
|
||||
} else {
|
||||
print("[engine] Unexpected engine response: $response");
|
||||
debugPrint("[engine] Unexpected engine response: $response");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -128,54 +137,89 @@ class NativeEngine extends Engine {
|
|||
|
||||
Future<void> stopSearching() async {
|
||||
isActive = false;
|
||||
print("[engine] Stop current thinking...");
|
||||
debugPrint("[engine] Stop current thinking...");
|
||||
await send('stop');
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> setOptions(BuildContext context) async {
|
||||
if (Config.settingsLoaded == false) {
|
||||
print("[engine] Settings is not loaded yet, now load settings...");
|
||||
debugPrint("[engine] Settings is not loaded yet, now load settings...");
|
||||
await Config.loadSettings();
|
||||
}
|
||||
|
||||
await send('setoption name DeveloperMode value ${Config.developerMode}');
|
||||
await send('setoption name Algorithm value ${Config.algorithm}');
|
||||
await send(
|
||||
'setoption name DrawOnHumanExperience value ${Config.drawOnHumanExperience}');
|
||||
'setoption name DeveloperMode value ${Config.developerMode}',
|
||||
);
|
||||
await send(
|
||||
'setoption name ConsiderMobility value ${Config.considerMobility}');
|
||||
await send('setoption name SkillLevel value ${Config.skillLevel}');
|
||||
await send('setoption name MoveTime value ${Config.moveTime}');
|
||||
await send('setoption name AiIsLazy value ${Config.aiIsLazy}');
|
||||
await send('setoption name Shuffling value ${Config.shufflingEnabled}');
|
||||
await send('setoption name PiecesCount value ${Config.piecesCount}');
|
||||
await send('setoption name FlyPieceCount value ${Config.flyPieceCount}');
|
||||
'setoption name Algorithm value ${Config.algorithm}',
|
||||
);
|
||||
await send(
|
||||
'setoption name PiecesAtLeastCount value ${Config.piecesAtLeastCount}');
|
||||
'setoption name DrawOnHumanExperience value ${Config.drawOnHumanExperience}',
|
||||
);
|
||||
await send(
|
||||
'setoption name HasDiagonalLines value ${Config.hasDiagonalLines}');
|
||||
'setoption name ConsiderMobility value ${Config.considerMobility}',
|
||||
);
|
||||
await send(
|
||||
'setoption name HasBannedLocations value ${Config.hasBannedLocations}');
|
||||
'setoption name SkillLevel value ${Config.skillLevel}',
|
||||
);
|
||||
await send(
|
||||
'setoption name MayMoveInPlacingPhase value ${Config.mayMoveInPlacingPhase}');
|
||||
'setoption name MoveTime value ${Config.moveTime}',
|
||||
);
|
||||
await send(
|
||||
'setoption name IsDefenderMoveFirst value ${Config.isDefenderMoveFirst}');
|
||||
'setoption name AiIsLazy value ${Config.aiIsLazy}',
|
||||
);
|
||||
await send(
|
||||
'setoption name MayRemoveMultiple value ${Config.mayRemoveMultiple}');
|
||||
'setoption name Shuffling value ${Config.shufflingEnabled}',
|
||||
);
|
||||
await send(
|
||||
'setoption name MayRemoveFromMillsAlways value ${Config.mayRemoveFromMillsAlways}');
|
||||
'setoption name PiecesCount value ${Config.piecesCount}',
|
||||
);
|
||||
await send(
|
||||
'setoption name MayOnlyRemoveUnplacedPieceInPlacingPhase value ${Config.mayOnlyRemoveUnplacedPieceInPlacingPhase}');
|
||||
'setoption name FlyPieceCount value ${Config.flyPieceCount}',
|
||||
);
|
||||
await send(
|
||||
'setoption name IsWhiteLoseButNotDrawWhenBoardFull value ${Config.isWhiteLoseButNotDrawWhenBoardFull}');
|
||||
'setoption name PiecesAtLeastCount value ${Config.piecesAtLeastCount}',
|
||||
);
|
||||
await send(
|
||||
'setoption name IsLoseButNotChangeSideWhenNoWay value ${Config.isLoseButNotChangeSideWhenNoWay}');
|
||||
await send('setoption name MayFly value ${Config.mayFly}');
|
||||
await send('setoption name NMoveRule value ${Config.nMoveRule}');
|
||||
'setoption name HasDiagonalLines value ${Config.hasDiagonalLines}',
|
||||
);
|
||||
await send(
|
||||
'setoption name EndgameNMoveRule value ${Config.endgameNMoveRule}');
|
||||
'setoption name HasBannedLocations value ${Config.hasBannedLocations}',
|
||||
);
|
||||
await send(
|
||||
'setoption name ThreefoldRepetitionRule value ${Config.threefoldRepetitionRule}');
|
||||
'setoption name MayMoveInPlacingPhase value ${Config.mayMoveInPlacingPhase}',
|
||||
);
|
||||
await send(
|
||||
'setoption name IsDefenderMoveFirst value ${Config.isDefenderMoveFirst}',
|
||||
);
|
||||
await send(
|
||||
'setoption name MayRemoveMultiple value ${Config.mayRemoveMultiple}',
|
||||
);
|
||||
await send(
|
||||
'setoption name MayRemoveFromMillsAlways value ${Config.mayRemoveFromMillsAlways}',
|
||||
);
|
||||
await send(
|
||||
'setoption name MayOnlyRemoveUnplacedPieceInPlacingPhase value ${Config.mayOnlyRemoveUnplacedPieceInPlacingPhase}',
|
||||
);
|
||||
await send(
|
||||
'setoption name IsWhiteLoseButNotDrawWhenBoardFull value ${Config.isWhiteLoseButNotDrawWhenBoardFull}',
|
||||
);
|
||||
await send(
|
||||
'setoption name IsLoseButNotChangeSideWhenNoWay value ${Config.isLoseButNotChangeSideWhenNoWay}',
|
||||
);
|
||||
await send(
|
||||
'setoption name MayFly value ${Config.mayFly}',
|
||||
);
|
||||
await send(
|
||||
'setoption name NMoveRule value ${Config.nMoveRule}',
|
||||
);
|
||||
await send(
|
||||
'setoption name EndgameNMoveRule value ${Config.endgameNMoveRule}',
|
||||
);
|
||||
await send(
|
||||
'setoption name ThreefoldRepetitionRule value ${Config.threefoldRepetitionRule}',
|
||||
);
|
||||
}
|
||||
|
||||
String getPositionFen(Position position) {
|
|
@ -16,14 +16,16 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import 'package:sanmill/common/constants.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:sanmill/l10n/resources.dart';
|
||||
import 'package:sanmill/mill/rule.dart';
|
||||
import 'package:sanmill/style/app_theme.dart';
|
||||
import 'package:sanmill/shared/common/constants.dart';
|
||||
import 'package:sanmill/shared/theme/app_theme.dart';
|
||||
|
||||
import 'settings.dart';
|
||||
|
||||
class Config {
|
||||
const Config._();
|
||||
static bool settingsLoaded = false;
|
||||
|
||||
static bool isPrivacyPolicyAccepted = false;
|
||||
|
@ -60,7 +62,7 @@ class Config {
|
|||
static double pointWidth = 10.0;
|
||||
static double pieceWidth = 0.9;
|
||||
static double fontSize = 16.0;
|
||||
static double boardTop = isLargeScreen() ? 75.0 : 36.0;
|
||||
static double boardTop = isLargeScreen ? 75.0 : 36.0;
|
||||
static double animationDuration = 0.0;
|
||||
|
||||
// Color
|
||||
|
@ -87,8 +89,7 @@ class Config {
|
|||
static int piecesCount = specialCountryAndRegion == "Iran" ? 12 : 9;
|
||||
static int flyPieceCount = 3;
|
||||
static int piecesAtLeastCount = 3;
|
||||
static bool hasDiagonalLines =
|
||||
specialCountryAndRegion == "Iran" ? true : false;
|
||||
static bool hasDiagonalLines = specialCountryAndRegion == "Iran";
|
||||
static bool hasBannedLocations = false;
|
||||
static bool mayMoveInPlacingPhase = false;
|
||||
static bool isDefenderMoveFirst = false;
|
||||
|
@ -102,124 +103,136 @@ class Config {
|
|||
static int endgameNMoveRule = 100;
|
||||
static bool threefoldRepetitionRule = true;
|
||||
|
||||
// TODO: use jsonSerializable
|
||||
static Future<void> loadSettings() async {
|
||||
print("[config] Loading settings...");
|
||||
debugPrint("[config] Loading settings...");
|
||||
|
||||
final settings = await Settings.instance();
|
||||
|
||||
Config.isPrivacyPolicyAccepted =
|
||||
settings['IsPrivacyPolicyAccepted'] ?? false;
|
||||
settings['IsPrivacyPolicyAccepted'] as bool? ?? false;
|
||||
|
||||
// Preferences
|
||||
Config.toneEnabled = settings['ToneEnabled'] ?? true;
|
||||
Config.keepMuteWhenTakingBack = settings['KeepMuteWhenTakingBack'] ?? true;
|
||||
Config.screenReaderSupport = settings['ScreenReaderSupport'] ?? false;
|
||||
Config.aiMovesFirst = settings['AiMovesFirst'] ?? false;
|
||||
Config.aiIsLazy = settings['AiIsLazy'] ?? false;
|
||||
Config.skillLevel = settings['SkillLevel'] ?? 1;
|
||||
Config.moveTime = settings['MoveTime'] ?? 1;
|
||||
Config.isAutoRestart = settings['IsAutoRestart'] ?? false;
|
||||
Config.isAutoChangeFirstMove = settings['IsAutoChangeFirstMove'] ?? false;
|
||||
Config.resignIfMostLose = settings['ResignIfMostLose'] ?? false;
|
||||
Config.shufflingEnabled = settings['ShufflingEnabled'] ?? true;
|
||||
Config.learnEndgame = settings['LearnEndgame'] ?? false;
|
||||
Config.openingBook = settings['OpeningBook'] ?? false;
|
||||
Config.algorithm = settings['Algorithm'] ?? 2;
|
||||
Config.drawOnHumanExperience = settings['DrawOnHumanExperience'] ?? true;
|
||||
Config.considerMobility = settings['ConsiderMobility'] ?? true;
|
||||
Config.developerMode = settings['DeveloperMode'] ?? false;
|
||||
Config.experimentsEnabled = settings['ExperimentsEnabled'] ?? false;
|
||||
Config.toneEnabled = settings['ToneEnabled'] as bool? ?? true;
|
||||
Config.keepMuteWhenTakingBack =
|
||||
settings['KeepMuteWhenTakingBack'] as bool? ?? true;
|
||||
Config.screenReaderSupport =
|
||||
settings['ScreenReaderSupport'] as bool? ?? false;
|
||||
Config.aiMovesFirst = settings['AiMovesFirst'] as bool? ?? false;
|
||||
Config.aiIsLazy = settings['AiIsLazy'] as bool? ?? false;
|
||||
Config.skillLevel = settings['SkillLevel'] as int? ?? 1;
|
||||
Config.moveTime = settings['MoveTime'] as int? ?? 1;
|
||||
Config.isAutoRestart = settings['IsAutoRestart'] as bool? ?? false;
|
||||
Config.isAutoChangeFirstMove =
|
||||
settings['IsAutoChangeFirstMove'] as bool? ?? false;
|
||||
Config.resignIfMostLose = settings['ResignIfMostLose'] as bool? ?? false;
|
||||
Config.shufflingEnabled = settings['ShufflingEnabled'] as bool? ?? true;
|
||||
Config.learnEndgame = settings['LearnEndgame'] as bool? ?? false;
|
||||
Config.openingBook = settings['OpeningBook'] as bool? ?? false;
|
||||
Config.algorithm = settings['Algorithm'] as int? ?? 2;
|
||||
Config.drawOnHumanExperience =
|
||||
settings['DrawOnHumanExperience'] as bool? ?? true;
|
||||
Config.considerMobility = settings['ConsiderMobility'] as bool? ?? true;
|
||||
Config.developerMode = settings['DeveloperMode'] as bool? ?? false;
|
||||
Config.experimentsEnabled =
|
||||
settings['ExperimentsEnabled'] as bool? ?? false;
|
||||
|
||||
// Display
|
||||
Config.languageCode =
|
||||
settings['LanguageCode'] ?? Constants.defaultLanguageCodeName;
|
||||
Config.languageCode = settings['LanguageCode'] as String? ??
|
||||
Constants.defaultLanguageCodeName;
|
||||
Config.standardNotationEnabled =
|
||||
settings['StandardNotationEnabled'] ?? true;
|
||||
settings['StandardNotationEnabled'] as bool? ?? true;
|
||||
Config.isPieceCountInHandShown =
|
||||
settings['IsPieceCountInHandShown'] ?? true;
|
||||
Config.isNotationsShown = settings['IsNotationsShown'] ?? false;
|
||||
settings['IsPieceCountInHandShown'] as bool? ?? true;
|
||||
Config.isNotationsShown = settings['IsNotationsShown'] as bool? ?? false;
|
||||
Config.isHistoryNavigationToolbarShown =
|
||||
settings['IsHistoryNavigationToolbarShown'] ?? false;
|
||||
Config.boardBorderLineWidth = settings['BoardBorderLineWidth'] ?? 2;
|
||||
Config.boardInnerLineWidth = settings['BoardInnerLineWidth'] ?? 2;
|
||||
Config.pointStyle = settings['PointStyle'] ?? 0;
|
||||
Config.pointWidth = settings['PointWidth'] ?? 10.0;
|
||||
Config.pieceWidth = settings['PieceWidth'] ?? 0.9;
|
||||
Config.fontSize = settings['FontSize'] ?? 16.0;
|
||||
Config.boardTop = settings['BoardTop'] ?? (isLargeScreen() ? 75 : 36);
|
||||
Config.animationDuration = settings['AnimationDuration'] ?? 0;
|
||||
settings['IsHistoryNavigationToolbarShown'] as bool? ?? false;
|
||||
Config.boardBorderLineWidth =
|
||||
settings['BoardBorderLineWidth'] as double? ?? 2.0;
|
||||
Config.boardInnerLineWidth =
|
||||
settings['BoardInnerLineWidth'] as double? ?? 2.0;
|
||||
Config.pointStyle = settings['PointStyle'] as int? ?? 0;
|
||||
Config.pointWidth = settings['PointWidth'] as double? ?? 10.0;
|
||||
Config.pieceWidth = settings['PieceWidth'] as double? ?? 0.9;
|
||||
Config.fontSize = settings['FontSize'] as double? ?? 16.0;
|
||||
Config.boardTop =
|
||||
settings['BoardTop'] as double? ?? (isLargeScreen ? 75.0 : 36.0);
|
||||
Config.animationDuration = settings['AnimationDuration'] as double? ?? 0.0;
|
||||
|
||||
// Color
|
||||
Config.boardLineColor =
|
||||
settings['BoardLineColor'] ?? AppTheme.boardLineColor.value;
|
||||
Config.darkBackgroundColor =
|
||||
settings['DarkBackgroundColor'] ?? AppTheme.darkBackgroundColor.value;
|
||||
Config.boardBackgroundColor =
|
||||
settings['BoardBackgroundColor'] ?? AppTheme.boardBackgroundColor.value;
|
||||
settings['BoardLineColor'] as int? ?? AppTheme.boardLineColor.value;
|
||||
Config.darkBackgroundColor = settings['DarkBackgroundColor'] as int? ??
|
||||
AppTheme.darkBackgroundColor.value;
|
||||
Config.boardBackgroundColor = settings['BoardBackgroundColor'] as int? ??
|
||||
AppTheme.boardBackgroundColor.value;
|
||||
Config.whitePieceColor =
|
||||
settings['WhitePieceColor'] ?? AppTheme.whitePieceColor.value;
|
||||
settings['WhitePieceColor'] as int? ?? AppTheme.whitePieceColor.value;
|
||||
Config.blackPieceColor =
|
||||
settings['BlackPieceColor'] ?? AppTheme.blackPieceColor.value;
|
||||
Config.pieceHighlightColor =
|
||||
settings['PieceHighlightColor'] ?? AppTheme.pieceHighlightColor.value;
|
||||
settings['BlackPieceColor'] as int? ?? AppTheme.blackPieceColor.value;
|
||||
Config.pieceHighlightColor = settings['PieceHighlightColor'] as int? ??
|
||||
AppTheme.pieceHighlightColor.value;
|
||||
Config.messageColor =
|
||||
settings['MessageColor'] ?? AppTheme.messageColor.value;
|
||||
Config.drawerColor = settings['DrawerColor'] ?? AppTheme.drawerColor.value;
|
||||
Config.drawerBackgroundColor = settings['DrawerBackgroundColor'] ??
|
||||
settings['MessageColor'] as int? ?? AppTheme.messageColor.value;
|
||||
Config.drawerColor =
|
||||
settings['DrawerColor'] as int? ?? AppTheme.drawerColor.value;
|
||||
Config.drawerBackgroundColor = settings['DrawerBackgroundColor'] as int? ??
|
||||
AppTheme.drawerBackgroundColor.value;
|
||||
Config.drawerTextColor =
|
||||
settings['DrawerTextColor'] ?? AppTheme.drawerTextColor.value;
|
||||
Config.drawerHighlightItemColor = settings['DrawerHighlightItemColor'] ??
|
||||
AppTheme.drawerHighlightItemColor.value;
|
||||
settings['DrawerTextColor'] as int? ?? AppTheme.drawerTextColor.value;
|
||||
Config.drawerHighlightItemColor =
|
||||
settings['DrawerHighlightItemColor'] as int? ??
|
||||
AppTheme.drawerHighlightItemColor.value;
|
||||
Config.mainToolbarBackgroundColor =
|
||||
settings['MainToolbarBackgroundColor'] ??
|
||||
settings['MainToolbarBackgroundColor'] as int? ??
|
||||
AppTheme.mainToolbarBackgroundColor.value;
|
||||
Config.mainToolbarIconColor =
|
||||
settings['MainToolbarIconColor'] ?? AppTheme.mainToolbarIconColor.value;
|
||||
Config.mainToolbarIconColor = settings['MainToolbarIconColor'] as int? ??
|
||||
AppTheme.mainToolbarIconColor.value;
|
||||
Config.navigationToolbarBackgroundColor =
|
||||
settings['NavigationToolbarBackgroundColor'] ??
|
||||
settings['NavigationToolbarBackgroundColor'] as int? ??
|
||||
AppTheme.navigationToolbarBackgroundColor.value;
|
||||
Config.navigationToolbarIconColor =
|
||||
settings['NavigationToolbarIconColor'] ??
|
||||
settings['NavigationToolbarIconColor'] as int? ??
|
||||
AppTheme.navigationToolbarIconColor.value;
|
||||
|
||||
// Rules
|
||||
rule.piecesCount = Config.piecesCount =
|
||||
settings['PiecesCount'] ?? (specialCountryAndRegion == "Iran" ? 12 : 9);
|
||||
rule.flyPieceCount = Config.flyPieceCount = settings['FlyPieceCount'] ?? 3;
|
||||
rule.piecesCount = Config.piecesCount = settings['PiecesCount'] as int? ??
|
||||
(specialCountryAndRegion == "Iran" ? 12 : 9);
|
||||
rule.flyPieceCount =
|
||||
Config.flyPieceCount = settings['FlyPieceCount'] as int? ?? 3;
|
||||
rule.piecesAtLeastCount =
|
||||
Config.piecesAtLeastCount = settings['PiecesAtLeastCount'] ?? 3;
|
||||
Config.piecesAtLeastCount = settings['PiecesAtLeastCount'] as int? ?? 3;
|
||||
rule.hasDiagonalLines = Config.hasDiagonalLines =
|
||||
settings['HasDiagonalLines'] ??
|
||||
(specialCountryAndRegion == "Iran" ? true : false);
|
||||
rule.hasBannedLocations =
|
||||
Config.hasBannedLocations = settings['HasBannedLocations'] ?? false;
|
||||
settings['HasDiagonalLines'] as bool? ??
|
||||
(specialCountryAndRegion == "Iran");
|
||||
rule.hasBannedLocations = Config.hasBannedLocations =
|
||||
settings['HasBannedLocations'] as bool? ?? false;
|
||||
rule.mayMoveInPlacingPhase = Config.mayMoveInPlacingPhase =
|
||||
settings['MayMoveInPlacingPhase'] ?? false;
|
||||
rule.isDefenderMoveFirst =
|
||||
Config.isDefenderMoveFirst = settings['IsDefenderMoveFirst'] ?? false;
|
||||
rule.mayRemoveMultiple =
|
||||
Config.mayRemoveMultiple = settings['MayRemoveMultiple'] ?? false;
|
||||
settings['MayMoveInPlacingPhase'] as bool? ?? false;
|
||||
rule.isDefenderMoveFirst = Config.isDefenderMoveFirst =
|
||||
settings['IsDefenderMoveFirst'] as bool? ?? false;
|
||||
rule.mayRemoveMultiple = Config.mayRemoveMultiple =
|
||||
settings['MayRemoveMultiple'] as bool? ?? false;
|
||||
rule.mayRemoveFromMillsAlways = Config.mayRemoveFromMillsAlways =
|
||||
settings['MayRemoveFromMillsAlways'] ?? false;
|
||||
rule.mayOnlyRemoveUnplacedPieceInPlacingPhase =
|
||||
Config.mayOnlyRemoveUnplacedPieceInPlacingPhase =
|
||||
settings['MayOnlyRemoveUnplacedPieceInPlacingPhase'] ?? false;
|
||||
settings['MayRemoveFromMillsAlways'] as bool? ?? false;
|
||||
rule.mayOnlyRemoveUnplacedPieceInPlacingPhase = Config
|
||||
.mayOnlyRemoveUnplacedPieceInPlacingPhase =
|
||||
settings['MayOnlyRemoveUnplacedPieceInPlacingPhase'] as bool? ?? false;
|
||||
rule.isWhiteLoseButNotDrawWhenBoardFull =
|
||||
Config.isWhiteLoseButNotDrawWhenBoardFull =
|
||||
settings['IsWhiteLoseButNotDrawWhenBoardFull'] ?? true;
|
||||
settings['IsWhiteLoseButNotDrawWhenBoardFull'] as bool? ?? true;
|
||||
rule.isLoseButNotChangeSideWhenNoWay =
|
||||
Config.isLoseButNotChangeSideWhenNoWay =
|
||||
settings['IsLoseButNotChangeSideWhenNoWay'] ?? true;
|
||||
rule.mayFly = Config.mayFly = settings['MayFly'] ?? true;
|
||||
rule.nMoveRule = Config.nMoveRule = settings['NMoveRule'] ?? 100;
|
||||
settings['IsLoseButNotChangeSideWhenNoWay'] as bool? ?? true;
|
||||
rule.mayFly = Config.mayFly = settings['MayFly'] as bool? ?? true;
|
||||
rule.nMoveRule = Config.nMoveRule = settings['NMoveRule'] as int? ?? 100;
|
||||
rule.endgameNMoveRule =
|
||||
Config.endgameNMoveRule = settings['EndgameNMoveRule'] ?? 100;
|
||||
Config.endgameNMoveRule = settings['EndgameNMoveRule'] as int? ?? 100;
|
||||
rule.threefoldRepetitionRule = Config.threefoldRepetitionRule =
|
||||
settings['ThreefoldRepetitionRule'] ?? true;
|
||||
settings['ThreefoldRepetitionRule'] as bool? ?? true;
|
||||
|
||||
settingsLoaded = true;
|
||||
print("[config] Loading settings done!");
|
||||
debugPrint("[config] Loading settings done!");
|
||||
}
|
||||
|
||||
static Future<bool> save() async {
|
|
@ -19,6 +19,7 @@
|
|||
import 'dart:ui';
|
||||
|
||||
class Constants {
|
||||
const Constants._();
|
||||
static String appName = "Mill";
|
||||
static String authorAccount = "calcitem";
|
||||
static String projectName = "Sanmill";
|
||||
|
@ -53,8 +54,8 @@ class Constants {
|
|||
static String githubEulaURL = "$githubRepoWiKiURL/EULA";
|
||||
static String giteeEulaURL = "$giteeRepoWiKiURL/EULA_zh";
|
||||
|
||||
static String githubSourceCodeURL = "$githubRepoURL";
|
||||
static String giteeSourceCodeURL = "$giteeRepoURL";
|
||||
static String githubSourceCodeURL = githubRepoURL;
|
||||
static String giteeSourceCodeURL = giteeRepoURL;
|
||||
|
||||
static String githubThirdPartyNoticesURL =
|
||||
"$githubRepoWiKiURL/third-party_notices";
|
||||
|
@ -77,10 +78,6 @@ class Constants {
|
|||
static final windowAspectRatio = windowHeight / windowWidth;
|
||||
}
|
||||
|
||||
bool isSmallScreen() {
|
||||
return Constants.windowHeight <= 800;
|
||||
}
|
||||
bool get isSmallScreen => Constants.windowHeight <= 800;
|
||||
|
||||
bool isLargeScreen() {
|
||||
return !isSmallScreen();
|
||||
}
|
||||
bool get isLargeScreen => !isSmallScreen;
|
|
@ -19,8 +19,9 @@
|
|||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:sanmill/common/constants.dart';
|
||||
import 'package:sanmill/shared/common/constants.dart';
|
||||
|
||||
class Settings {
|
||||
static final settingsFileName = Constants.settingsFilename;
|
||||
|
@ -29,19 +30,20 @@ class Settings {
|
|||
late File _file;
|
||||
Map<String, dynamic>? _values = {};
|
||||
|
||||
static instance() async {
|
||||
// TODO: add constructor
|
||||
static Future<Settings> instance() async {
|
||||
if (_instance == null) {
|
||||
_instance = Settings();
|
||||
await _instance!._load(settingsFileName);
|
||||
print("[settings] $settingsFileName loaded.");
|
||||
debugPrint("[settings] $settingsFileName loaded.");
|
||||
}
|
||||
|
||||
return _instance;
|
||||
return _instance!;
|
||||
}
|
||||
|
||||
operator [](String key) => _values![key];
|
||||
dynamic operator [](String key) => _values![key];
|
||||
|
||||
operator []=(String key, dynamic value) => _values![key] = value;
|
||||
void operator []=(String key, dynamic value) => _values![key] = value;
|
||||
|
||||
Future<bool> commit() async {
|
||||
_file.create(recursive: true);
|
||||
|
@ -57,18 +59,18 @@ class Settings {
|
|||
Future<bool> _load(String fileName) async {
|
||||
// TODO: main() ExternalStorage
|
||||
// var docDir = await getExternalStorageDirectory();
|
||||
var docDir = await getApplicationDocumentsDirectory();
|
||||
final docDir = await getApplicationDocumentsDirectory();
|
||||
|
||||
_file = File('${docDir.path}/$fileName');
|
||||
|
||||
print("[settings] Loading $_file ...");
|
||||
debugPrint("[settings] Loading $_file ...");
|
||||
|
||||
try {
|
||||
final contents = await _file.readAsString();
|
||||
_values = jsonDecode(contents);
|
||||
print(_values);
|
||||
_values = jsonDecode(contents) as Map<String, dynamic>?;
|
||||
debugPrint(_values.toString());
|
||||
} catch (e) {
|
||||
print(e);
|
||||
debugPrint(e.toString());
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -76,13 +78,13 @@ class Settings {
|
|||
}
|
||||
|
||||
Future<void> restore() async {
|
||||
print("[settings] Restoring Settings...");
|
||||
debugPrint("[settings] Restoring Settings...");
|
||||
|
||||
if (_file.existsSync()) {
|
||||
_file.deleteSync();
|
||||
print("[settings] $_file deleted");
|
||||
debugPrint("[settings] $_file deleted");
|
||||
} else {
|
||||
print("[settings] $_file does not exist");
|
||||
debugPrint("[settings] $_file does not exist");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -23,32 +23,36 @@ import 'package:devicelocale/devicelocale.dart';
|
|||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:sanmill/common/config.dart';
|
||||
import 'package:sanmill/common/constants.dart';
|
||||
import 'package:sanmill/generated/l10n.dart';
|
||||
import 'package:sanmill/shared/common/config.dart';
|
||||
import 'package:sanmill/shared/common/constants.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
int _counter = 0;
|
||||
Timer? _timer;
|
||||
|
||||
void startTimer(var counter, var events) {
|
||||
void startTimer(int counter, StreamController<int> events) {
|
||||
_counter = counter;
|
||||
if (_timer != null) {
|
||||
_timer!.cancel();
|
||||
}
|
||||
_timer = Timer.periodic(Duration(seconds: 1), (timer) {
|
||||
_timer = Timer.periodic(const Duration(seconds: 1), (timer) {
|
||||
(_counter > 0) ? _counter-- : _timer!.cancel();
|
||||
events.add(_counter);
|
||||
});
|
||||
}
|
||||
|
||||
void showCountdownDialog(
|
||||
BuildContext ctx, var seconds, var events, void fun()) {
|
||||
var alert = AlertDialog(
|
||||
BuildContext ctx,
|
||||
int seconds,
|
||||
StreamController<int> events,
|
||||
void Function() fun,
|
||||
) {
|
||||
final alert = AlertDialog(
|
||||
content: StreamBuilder<int>(
|
||||
stream: events.stream,
|
||||
builder: (BuildContext context, AsyncSnapshot<int> snapshot) {
|
||||
print("Count down: " + snapshot.data.toString());
|
||||
debugPrint("Count down: ${snapshot.data}");
|
||||
|
||||
if (snapshot.data == 0) {
|
||||
fun();
|
||||
|
@ -57,32 +61,28 @@ void showCountdownDialog(
|
|||
} else {}
|
||||
}
|
||||
|
||||
return Container(
|
||||
return SizedBox(
|
||||
height: 128,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
snapshot.data != null ? '${snapshot.data.toString()}' : "10",
|
||||
style: TextStyle(fontSize: 64),
|
||||
),
|
||||
SizedBox(
|
||||
height: 20,
|
||||
snapshot.data != null ? snapshot.data.toString() : "10",
|
||||
style: const TextStyle(fontSize: 64),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
InkWell(
|
||||
onTap: () {
|
||||
Navigator.of(context).pop();
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: Container(
|
||||
child: Center(
|
||||
child: Text(
|
||||
child: Center(
|
||||
child: Text(
|
||||
S.of(ctx).cancel,
|
||||
style: TextStyle(
|
||||
color: Colors.black,
|
||||
fontSize: Config.fontSize,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
)),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
|
@ -105,16 +105,19 @@ void showCountdownDialog(
|
|||
class _LinkTextSpan extends TextSpan {
|
||||
_LinkTextSpan({TextStyle? style, required String url, String? text})
|
||||
: super(
|
||||
style: style,
|
||||
text: text ?? url,
|
||||
recognizer: TapGestureRecognizer()
|
||||
..onTap = () {
|
||||
launch(url, forceSafariVC: false);
|
||||
});
|
||||
style: style,
|
||||
text: text ?? url,
|
||||
recognizer: TapGestureRecognizer()
|
||||
..onTap = () {
|
||||
launch(url, forceSafariVC: false);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
showPrivacyDialog(
|
||||
BuildContext context, setPrivacyPolicyAccepted(bool value)) async {
|
||||
Future<void> showPrivacyDialog(
|
||||
BuildContext context,
|
||||
Function(bool value) setPrivacyPolicyAccepted,
|
||||
) async {
|
||||
String? locale = "en_US";
|
||||
late String eulaURL;
|
||||
late String privacyPolicyURL;
|
||||
|
@ -122,7 +125,7 @@ showPrivacyDialog(
|
|||
locale = await Devicelocale.currentLocale;
|
||||
}
|
||||
|
||||
print("[about] local = $locale");
|
||||
debugPrint("[about] local = $locale");
|
||||
if (locale != null && locale.startsWith("zh_")) {
|
||||
eulaURL = Constants.giteeEulaURL;
|
||||
privacyPolicyURL = Constants.giteePrivacyPolicyURL;
|
||||
|
@ -133,8 +136,8 @@ showPrivacyDialog(
|
|||
|
||||
final ThemeData themeData = Theme.of(context);
|
||||
final TextStyle? aboutTextStyle = themeData.textTheme.bodyText1;
|
||||
final TextStyle linkStyle =
|
||||
themeData.textTheme.bodyText1!.copyWith(color: themeData.accentColor);
|
||||
final TextStyle linkStyle = themeData.textTheme.bodyText1!
|
||||
.copyWith(color: themeData.colorScheme.secondary);
|
||||
|
||||
showDialog(
|
||||
context: context,
|
||||
|
@ -176,18 +179,17 @@ showPrivacyDialog(
|
|||
child: Text(S.of(context).accept),
|
||||
onPressed: () {
|
||||
setPrivacyPolicyAccepted(true);
|
||||
Navigator.of(context).pop();
|
||||
Navigator.pop(context);
|
||||
},
|
||||
),
|
||||
Platform.isAndroid
|
||||
? TextButton(
|
||||
child: Text(S.of(context).exit),
|
||||
onPressed: () {
|
||||
setPrivacyPolicyAccepted(false);
|
||||
SystemChannels.platform.invokeMethod('SystemNavigator.pop');
|
||||
},
|
||||
)
|
||||
: Container(height: 0.0, width: 0.0),
|
||||
if (Platform.isAndroid)
|
||||
TextButton(
|
||||
child: Text(S.of(context).exit),
|
||||
onPressed: () {
|
||||
setPrivacyPolicyAccepted(false);
|
||||
SystemChannels.platform.invokeMethod('SystemNavigator.pop');
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
|
@ -16,95 +16,96 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:sanmill/common/config.dart';
|
||||
import 'package:sanmill/generated/l10n.dart';
|
||||
import 'package:sanmill/l10n/resources.dart';
|
||||
import 'package:sanmill/style/app_theme.dart';
|
||||
import 'package:sanmill/widgets/home_drawer.dart';
|
||||
part of 'package:sanmill/screens/navigation_home_screen.dart';
|
||||
|
||||
class DrawerUserController extends StatefulWidget {
|
||||
const DrawerUserController({
|
||||
class DrawerController extends StatefulWidget {
|
||||
const DrawerController({
|
||||
Key? key,
|
||||
this.drawerWidth = AppTheme.drawerWidth,
|
||||
this.onDrawerCall,
|
||||
this.screenView,
|
||||
required this.onDrawerCall,
|
||||
required this.screenView,
|
||||
this.animatedIconData = AnimatedIcons.arrow_menu,
|
||||
this.menuView,
|
||||
this.drawerIsOpen,
|
||||
this.screenIndex,
|
||||
required this.screenIndex,
|
||||
}) : super(key: key);
|
||||
|
||||
final double drawerWidth;
|
||||
final Function(DrawerIndex)? onDrawerCall;
|
||||
final Widget? screenView;
|
||||
final Function(DrawerIndex) onDrawerCall;
|
||||
final Widget screenView;
|
||||
final Function(bool)? drawerIsOpen;
|
||||
final AnimatedIconData animatedIconData;
|
||||
final Widget? menuView;
|
||||
final DrawerIndex? screenIndex;
|
||||
final DrawerIndex screenIndex;
|
||||
|
||||
@override
|
||||
_DrawerUserControllerState createState() => _DrawerUserControllerState();
|
||||
_DrawerControllerState createState() => _DrawerControllerState();
|
||||
}
|
||||
|
||||
class _DrawerUserControllerState extends State<DrawerUserController>
|
||||
class _DrawerControllerState extends State<DrawerController>
|
||||
with TickerProviderStateMixin {
|
||||
late ScrollController scrollController;
|
||||
late AnimationController iconAnimationController;
|
||||
late AnimationController animationController;
|
||||
late final ScrollController scrollController;
|
||||
late final AnimationController iconAnimationController;
|
||||
late final AnimationController animationController;
|
||||
|
||||
double scrollOffset = 0.0;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
animationController = AnimationController(
|
||||
duration: const Duration(milliseconds: 2000), vsync: this);
|
||||
duration: const Duration(seconds: 2),
|
||||
vsync: this,
|
||||
);
|
||||
|
||||
iconAnimationController = AnimationController(
|
||||
vsync: this, duration: const Duration(milliseconds: 0));
|
||||
iconAnimationController =
|
||||
AnimationController(vsync: this, duration: Duration.zero);
|
||||
|
||||
iconAnimationController
|
||||
..animateTo(1.0,
|
||||
duration: const Duration(milliseconds: 0),
|
||||
curve: Curves.fastOutSlowIn);
|
||||
iconAnimationController.animateTo(
|
||||
1.0,
|
||||
duration: Duration.zero,
|
||||
curve: Curves.fastOutSlowIn,
|
||||
);
|
||||
|
||||
scrollController =
|
||||
ScrollController(initialScrollOffset: widget.drawerWidth);
|
||||
|
||||
scrollController
|
||||
..addListener(() {
|
||||
if (scrollController.offset <= 0) {
|
||||
if (scrollOffset != 1.0) {
|
||||
setState(() {
|
||||
scrollOffset = 1.0;
|
||||
try {
|
||||
widget.drawerIsOpen!(true);
|
||||
} catch (_) {}
|
||||
});
|
||||
}
|
||||
iconAnimationController.animateTo(0.0,
|
||||
duration: const Duration(milliseconds: 0),
|
||||
curve: Curves.fastOutSlowIn);
|
||||
} else if (scrollController.offset > 0 &&
|
||||
scrollController.offset < widget.drawerWidth.floor()) {
|
||||
iconAnimationController.animateTo(
|
||||
(scrollController.offset * 100 / (widget.drawerWidth)) / 100,
|
||||
duration: const Duration(milliseconds: 0),
|
||||
curve: Curves.fastOutSlowIn);
|
||||
} else {
|
||||
if (scrollOffset != 0.0) {
|
||||
setState(() {
|
||||
scrollOffset = 0.0;
|
||||
try {
|
||||
widget.drawerIsOpen!(false);
|
||||
} catch (_) {}
|
||||
});
|
||||
}
|
||||
iconAnimationController.animateTo(1.0,
|
||||
duration: const Duration(milliseconds: 0),
|
||||
curve: Curves.fastOutSlowIn);
|
||||
scrollController.addListener(() {
|
||||
if (scrollController.offset <= 0) {
|
||||
if (scrollOffset != 1.0) {
|
||||
setState(() {
|
||||
scrollOffset = 1.0;
|
||||
try {
|
||||
widget.drawerIsOpen!(true);
|
||||
} catch (_) {}
|
||||
});
|
||||
}
|
||||
});
|
||||
iconAnimationController.animateTo(
|
||||
0.0,
|
||||
duration: Duration.zero,
|
||||
curve: Curves.fastOutSlowIn,
|
||||
);
|
||||
} else if (scrollController.offset < widget.drawerWidth.floor()) {
|
||||
iconAnimationController.animateTo(
|
||||
(scrollController.offset * 100 / (widget.drawerWidth)) / 100,
|
||||
duration: Duration.zero,
|
||||
curve: Curves.fastOutSlowIn,
|
||||
);
|
||||
} else {
|
||||
if (scrollOffset != 0.0) {
|
||||
setState(() {
|
||||
scrollOffset = 0.0;
|
||||
try {
|
||||
widget.drawerIsOpen!(false);
|
||||
} catch (_) {}
|
||||
});
|
||||
}
|
||||
iconAnimationController.animateTo(
|
||||
1.0,
|
||||
duration: Duration.zero,
|
||||
curve: Curves.fastOutSlowIn,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
WidgetsBinding.instance!.addPostFrameCallback((_) => getInitState());
|
||||
super.initState();
|
||||
|
@ -119,22 +120,23 @@ class _DrawerUserControllerState extends State<DrawerUserController>
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
bool ltr = getBidirectionality(context) == Bidirectionality.leftToRight;
|
||||
final bool ltr =
|
||||
getBidirectionality(context) == Bidirectionality.leftToRight;
|
||||
|
||||
// this just menu and arrow icon animation
|
||||
var inkWell = InkWell(
|
||||
final inkWell = InkWell(
|
||||
borderRadius: BorderRadius.circular(AppBar().preferredSize.height),
|
||||
child: Center(
|
||||
// if you use your own menu view UI you add form initialization
|
||||
child: widget.menuView != null
|
||||
? widget.menuView
|
||||
: Semantics(
|
||||
label: S.of(context).mainMenu,
|
||||
child: AnimatedIcon(
|
||||
icon: widget.animatedIconData,
|
||||
color: AppTheme.drawerAnimationIconColor,
|
||||
progress: iconAnimationController),
|
||||
child: widget.menuView ??
|
||||
Semantics(
|
||||
label: S.of(context).mainMenu,
|
||||
child: AnimatedIcon(
|
||||
icon: widget.animatedIconData,
|
||||
color: AppTheme.drawerAnimationIconColor,
|
||||
progress: iconAnimationController,
|
||||
),
|
||||
),
|
||||
),
|
||||
onTap: () {
|
||||
FocusScope.of(context).requestFocus(FocusNode());
|
||||
|
@ -142,7 +144,55 @@ class _DrawerUserControllerState extends State<DrawerUserController>
|
|||
},
|
||||
);
|
||||
|
||||
var animatedBuilder = AnimatedBuilder(
|
||||
final List<DrawerListItem> drawerItems = [
|
||||
DrawerListItem(
|
||||
index: DrawerIndex.humanVsAi,
|
||||
title: S.of(context).humanVsAi,
|
||||
icon: const Icon(FluentIcons.person_24_regular),
|
||||
),
|
||||
DrawerListItem(
|
||||
index: DrawerIndex.humanVsHuman,
|
||||
title: S.of(context).humanVsHuman,
|
||||
icon: const Icon(FluentIcons.people_24_regular),
|
||||
),
|
||||
DrawerListItem(
|
||||
index: DrawerIndex.aiVsAi,
|
||||
title: S.of(context).aiVsAi,
|
||||
icon: const Icon(FluentIcons.bot_24_regular),
|
||||
),
|
||||
DrawerListItem(
|
||||
index: DrawerIndex.preferences,
|
||||
title: S.of(context).preferences,
|
||||
icon: const Icon(FluentIcons.options_24_regular),
|
||||
),
|
||||
DrawerListItem(
|
||||
index: DrawerIndex.ruleSettings,
|
||||
title: S.of(context).ruleSettings,
|
||||
icon: const Icon(FluentIcons.task_list_ltr_24_regular),
|
||||
),
|
||||
DrawerListItem(
|
||||
index: DrawerIndex.personalization,
|
||||
title: S.of(context).personalization,
|
||||
icon: const Icon(FluentIcons.design_ideas_24_regular),
|
||||
),
|
||||
DrawerListItem(
|
||||
index: DrawerIndex.feedback,
|
||||
title: S.of(context).feedback,
|
||||
icon: const Icon(FluentIcons.chat_warning_24_regular),
|
||||
),
|
||||
DrawerListItem(
|
||||
index: DrawerIndex.Help,
|
||||
title: S.of(context).help,
|
||||
icon: const Icon(FluentIcons.question_circle_24_regular),
|
||||
),
|
||||
DrawerListItem(
|
||||
index: DrawerIndex.About,
|
||||
title: S.of(context).about,
|
||||
icon: const Icon(FluentIcons.info_24_regular),
|
||||
),
|
||||
];
|
||||
|
||||
final animatedBuilder = AnimatedBuilder(
|
||||
animation: iconAnimationController,
|
||||
builder: (BuildContext context, Widget? child) {
|
||||
return Transform(
|
||||
|
@ -151,16 +201,15 @@ class _DrawerUserControllerState extends State<DrawerUserController>
|
|||
transform:
|
||||
Matrix4.translationValues(scrollController.offset, 0.0, 0.0),
|
||||
child: HomeDrawer(
|
||||
screenIndex: widget.screenIndex == null
|
||||
? DrawerIndex.humanVsAi
|
||||
: widget.screenIndex,
|
||||
screenIndex: widget.screenIndex,
|
||||
iconAnimationController: iconAnimationController,
|
||||
callBackIndex: (DrawerIndex? indexType) {
|
||||
callBackIndex: (DrawerIndex indexType) {
|
||||
onDrawerClick();
|
||||
try {
|
||||
widget.onDrawerCall!(indexType!);
|
||||
} catch (e) {}
|
||||
widget.onDrawerCall(indexType);
|
||||
} catch (_) {}
|
||||
},
|
||||
items: drawerItems,
|
||||
),
|
||||
);
|
||||
},
|
||||
|
@ -172,7 +221,7 @@ class _DrawerUserControllerState extends State<DrawerUserController>
|
|||
tapOffset = 10; // TODO: WAR
|
||||
}
|
||||
|
||||
var stack = Stack(
|
||||
final stack = Stack(
|
||||
children: <Widget>[
|
||||
// this IgnorePointer we use as touch(user Interface) widget.screen View,
|
||||
// for example scrolloffset == 1
|
||||
|
@ -186,16 +235,15 @@ class _DrawerUserControllerState extends State<DrawerUserController>
|
|||
// tap on a few home screen area and close the drawer
|
||||
if (scrollOffset == 1.0)
|
||||
InkWell(
|
||||
onTap: () {
|
||||
onDrawerClick();
|
||||
},
|
||||
onTap: onDrawerClick,
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(
|
||||
top: MediaQuery.of(context).padding.top + tapOffset, left: 0),
|
||||
top: MediaQuery.of(context).padding.top + tapOffset,
|
||||
),
|
||||
child: SizedBox(
|
||||
width: AppBar().preferredSize.height,
|
||||
height: AppBar().preferredSize.height,
|
||||
width: kToolbarHeight,
|
||||
height: kToolbarHeight,
|
||||
child: Material(
|
||||
color: Colors.transparent,
|
||||
child: inkWell,
|
||||
|
@ -205,7 +253,7 @@ class _DrawerUserControllerState extends State<DrawerUserController>
|
|||
],
|
||||
);
|
||||
|
||||
var row = Row(
|
||||
final row = Row(
|
||||
children: <Widget>[
|
||||
SizedBox(
|
||||
width: widget.drawerWidth,
|
||||
|
@ -224,7 +272,9 @@ class _DrawerUserControllerState extends State<DrawerUserController>
|
|||
color: Color(Config.drawerColor),
|
||||
boxShadow: <BoxShadow>[
|
||||
BoxShadow(
|
||||
color: AppTheme.drawerBoxerShadowColor, blurRadius: 24),
|
||||
color: AppTheme.drawerBoxerShadowColor,
|
||||
blurRadius: 24,
|
||||
),
|
||||
],
|
||||
),
|
||||
child: stack,
|
||||
|
@ -233,9 +283,9 @@ class _DrawerUserControllerState extends State<DrawerUserController>
|
|||
],
|
||||
);
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: Color(Config.drawerColor),
|
||||
body: SingleChildScrollView(
|
||||
return Material(
|
||||
color: Color(Config.drawerColor),
|
||||
child: SingleChildScrollView(
|
||||
controller: scrollController,
|
||||
scrollDirection: Axis.horizontal,
|
||||
physics: const PageScrollPhysics(parent: ClampingScrollPhysics()),
|
|
@ -17,7 +17,7 @@
|
|||
*/
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:sanmill/style/app_theme.dart';
|
||||
import 'package:sanmill/shared/theme/app_theme.dart';
|
||||
|
||||
class ListItemDivider extends StatelessWidget {
|
||||
const ListItemDivider({
|
||||
|
@ -26,10 +26,11 @@ class ListItemDivider extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
margin: const EdgeInsets.symmetric(horizontal: 16),
|
||||
width: double.infinity,
|
||||
return Divider(
|
||||
indent: 16,
|
||||
endIndent: 16,
|
||||
height: 1.0,
|
||||
thickness: 1.0,
|
||||
color: AppTheme.listItemDividerColor,
|
||||
);
|
||||
}
|
|
@ -16,14 +16,7 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:sanmill/common/config.dart';
|
||||
import 'package:sanmill/mill/game.dart';
|
||||
import 'package:sanmill/mill/types.dart';
|
||||
import 'package:sanmill/style/app_theme.dart';
|
||||
import 'package:sanmill/widgets/game_page.dart';
|
||||
|
||||
import 'painter_base.dart';
|
||||
part of 'package:sanmill/screens/game_page/game_page.dart';
|
||||
|
||||
class BoardPainter extends PiecesBasePainter {
|
||||
BoardPainter({required double width}) : super(width: width);
|
||||
|
@ -45,7 +38,7 @@ class BoardPainter extends PiecesBasePainter {
|
|||
return false;
|
||||
}
|
||||
|
||||
static doPaint(
|
||||
static void doPaint(
|
||||
Canvas canvas,
|
||||
Paint paint,
|
||||
double gridWidth,
|
||||
|
@ -56,47 +49,54 @@ class BoardPainter extends PiecesBasePainter {
|
|||
paint.color = Color(Config.boardLineColor);
|
||||
paint.style = PaintingStyle.stroke;
|
||||
|
||||
var left = offsetX;
|
||||
var top = offsetY;
|
||||
final left = offsetX;
|
||||
final top = offsetY;
|
||||
|
||||
paint.strokeWidth = Config.boardBorderLineWidth;
|
||||
|
||||
if (Config.isPieceCountInHandShown) {
|
||||
var pieceInHandCount =
|
||||
Game.instance.position.pieceInHandCount[PieceColor.black];
|
||||
gameInstance.position.pieceInHandCount[PieceColor.black];
|
||||
|
||||
if (Game.instance.position.pieceOnBoardCount[PieceColor.white] == 0 &&
|
||||
Game.instance.position.pieceOnBoardCount[PieceColor.black] == 0) {
|
||||
if (gameInstance.position.pieceOnBoardCount[PieceColor.white] == 0 &&
|
||||
gameInstance.position.pieceOnBoardCount[PieceColor.black] == 0) {
|
||||
pieceInHandCount = Config.piecesCount;
|
||||
}
|
||||
|
||||
var pieceInHandCountStr = "";
|
||||
|
||||
if (Game.instance.position.phase == Phase.placing) {
|
||||
if (gameInstance.position.phase == Phase.placing) {
|
||||
pieceInHandCountStr = pieceInHandCount.toString();
|
||||
}
|
||||
|
||||
TextSpan textSpan = TextSpan(
|
||||
style: TextStyle(
|
||||
fontSize: 48, color: Color(Config.boardLineColor)), // TODO
|
||||
text: pieceInHandCountStr);
|
||||
final TextSpan textSpan = TextSpan(
|
||||
style: TextStyle(
|
||||
fontSize: 48,
|
||||
color: Color(Config.boardLineColor),
|
||||
), // TODO
|
||||
text: pieceInHandCountStr,
|
||||
);
|
||||
|
||||
TextPainter textPainter = TextPainter(
|
||||
text: textSpan,
|
||||
textAlign: TextAlign.center,
|
||||
textDirection: TextDirection.ltr);
|
||||
final TextPainter textPainter = TextPainter(
|
||||
text: textSpan,
|
||||
textAlign: TextAlign.center,
|
||||
textDirection: TextDirection.ltr,
|
||||
);
|
||||
|
||||
textPainter.layout();
|
||||
|
||||
textPainter.paint(
|
||||
canvas,
|
||||
Offset(left + squareWidth * 3 - textPainter.width / 2,
|
||||
top + squareWidth * 3 - textPainter.height / 2));
|
||||
canvas,
|
||||
Offset(
|
||||
left + squareWidth * 3 - textPainter.width / 2,
|
||||
top + squareWidth * 3 - textPainter.height / 2,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
if (Config.isNotationsShown) {
|
||||
String verticalNotations = "abcdefg";
|
||||
String horizontalNotations = "7654321";
|
||||
const String verticalNotations = "abcdefg";
|
||||
const String horizontalNotations = "7654321";
|
||||
String notationV = "";
|
||||
String notationH = "";
|
||||
|
||||
|
@ -104,25 +104,29 @@ class BoardPainter extends PiecesBasePainter {
|
|||
notationV = verticalNotations[i];
|
||||
notationH = horizontalNotations[i];
|
||||
|
||||
TextSpan notationSpanV = TextSpan(
|
||||
style:
|
||||
TextStyle(fontSize: 20, color: AppTheme.boardLineColor), // TODO
|
||||
final TextSpan notationSpanV = TextSpan(
|
||||
style: TextStyle(
|
||||
fontSize: 20,
|
||||
color: AppTheme.boardLineColor,
|
||||
), // TODO
|
||||
text: notationV,
|
||||
);
|
||||
|
||||
TextSpan notationSpanH = TextSpan(
|
||||
style:
|
||||
TextStyle(fontSize: 20, color: AppTheme.boardLineColor), // TODO
|
||||
final TextSpan notationSpanH = TextSpan(
|
||||
style: TextStyle(
|
||||
fontSize: 20,
|
||||
color: AppTheme.boardLineColor,
|
||||
), // TODO
|
||||
text: notationH,
|
||||
);
|
||||
|
||||
TextPainter notationPainterV = TextPainter(
|
||||
final TextPainter notationPainterV = TextPainter(
|
||||
text: notationSpanV,
|
||||
textAlign: TextAlign.center,
|
||||
textDirection: TextDirection.ltr,
|
||||
);
|
||||
|
||||
TextPainter notationPainterH = TextPainter(
|
||||
final TextPainter notationPainterH = TextPainter(
|
||||
text: notationSpanH,
|
||||
textAlign: TextAlign.center,
|
||||
textDirection: TextDirection.ltr,
|
||||
|
@ -131,37 +135,45 @@ class BoardPainter extends PiecesBasePainter {
|
|||
notationPainterV.layout();
|
||||
notationPainterH.layout();
|
||||
|
||||
var offset = (boardWidth - squareWidth * 6) / 4;
|
||||
final offset = (boardWidth - squareWidth * 6) / 4;
|
||||
|
||||
/* Show notations "a b c d e f" on board */
|
||||
|
||||
if (Config.developerMode) {
|
||||
notationPainterV.paint(
|
||||
canvas,
|
||||
Offset(left + squareWidth * i - notationPainterV.width / 2,
|
||||
top - offset - notationPainterV.height / 2),
|
||||
Offset(
|
||||
left + squareWidth * i - notationPainterV.width / 2,
|
||||
top - offset - notationPainterV.height / 2,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
notationPainterV.paint(
|
||||
canvas,
|
||||
Offset(left + squareWidth * i - notationPainterV.width / 2,
|
||||
top + squareWidth * 6 + offset - notationPainterV.height / 2),
|
||||
Offset(
|
||||
left + squareWidth * i - notationPainterV.width / 2,
|
||||
top + squareWidth * 6 + offset - notationPainterV.height / 2,
|
||||
),
|
||||
);
|
||||
|
||||
/* Show notations "1 2 3 4 5 6 7" on board */
|
||||
|
||||
notationPainterH.paint(
|
||||
canvas,
|
||||
Offset(left - offset - notationPainterH.width / 2,
|
||||
top + squareWidth * i - notationPainterH.height / 2),
|
||||
Offset(
|
||||
left - offset - notationPainterH.width / 2,
|
||||
top + squareWidth * i - notationPainterH.height / 2,
|
||||
),
|
||||
);
|
||||
|
||||
if (Config.developerMode) {
|
||||
notationPainterH.paint(
|
||||
canvas,
|
||||
Offset(left + squareWidth * 6 + offset - notationPainterH.width / 2,
|
||||
top + squareWidth * i - notationPainterH.height / 2),
|
||||
Offset(
|
||||
left + squareWidth * 6 + offset - notationPainterH.width / 2,
|
||||
top + squareWidth * i - notationPainterH.height / 2,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -174,19 +186,27 @@ class BoardPainter extends PiecesBasePainter {
|
|||
);
|
||||
|
||||
paint.strokeWidth = Config.boardInnerLineWidth;
|
||||
double bias = paint.strokeWidth / 2;
|
||||
final double bias = paint.strokeWidth / 2;
|
||||
|
||||
// File B
|
||||
canvas.drawRect(
|
||||
Rect.fromLTWH(left + squareWidth * 1, top + squareWidth * 1,
|
||||
squareWidth * 4, squareWidth * 4),
|
||||
Rect.fromLTWH(
|
||||
left + squareWidth * 1,
|
||||
top + squareWidth * 1,
|
||||
squareWidth * 4,
|
||||
squareWidth * 4,
|
||||
),
|
||||
paint,
|
||||
);
|
||||
|
||||
// File A
|
||||
canvas.drawRect(
|
||||
Rect.fromLTWH(left + squareWidth * 2, top + squareWidth * 2,
|
||||
squareWidth * 2, squareWidth * 2),
|
||||
Rect.fromLTWH(
|
||||
left + squareWidth * 2,
|
||||
top + squareWidth * 2,
|
||||
squareWidth * 2,
|
||||
squareWidth * 2,
|
||||
),
|
||||
paint,
|
||||
);
|
||||
|
||||
|
@ -226,9 +246,9 @@ class BoardPainter extends PiecesBasePainter {
|
|||
paint.style = PaintingStyle.stroke; // TODO: WIP
|
||||
}
|
||||
|
||||
double pointRadius = Config.pointWidth;
|
||||
final double pointRadius = Config.pointWidth;
|
||||
|
||||
var points = [
|
||||
final points = [
|
||||
[0, 0],
|
||||
[0, 3],
|
||||
[0, 6],
|
||||
|
@ -255,11 +275,13 @@ class BoardPainter extends PiecesBasePainter {
|
|||
[6, 6],
|
||||
];
|
||||
|
||||
points.forEach((point) => canvas.drawCircle(
|
||||
Offset(left + squareWidth * point[0], top + squareWidth * point[1]),
|
||||
pointRadius,
|
||||
paint,
|
||||
));
|
||||
for (final point in points) {
|
||||
canvas.drawCircle(
|
||||
Offset(left + squareWidth * point[0], top + squareWidth * point[1]),
|
||||
pointRadius,
|
||||
paint,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (!Config.hasDiagonalLines) {
|
|
@ -16,17 +16,16 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:sanmill/style/app_theme.dart';
|
||||
part of 'package:sanmill/screens/game_page/game_page.dart';
|
||||
|
||||
abstract class PiecesBasePainter extends CustomPainter {
|
||||
final double width;
|
||||
|
||||
final thePaint = Paint();
|
||||
final gridWidth;
|
||||
final squareWidth;
|
||||
final double gridWidth;
|
||||
final double squareWidth;
|
||||
|
||||
PiecesBasePainter({required this.width})
|
||||
: gridWidth = (width - AppTheme.boardPadding * 2),
|
||||
: gridWidth = width - AppTheme.boardPadding * 2,
|
||||
squareWidth = (width - AppTheme.boardPadding * 2) / 7;
|
||||
}
|
|
@ -16,32 +16,29 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:sanmill/common/config.dart';
|
||||
import 'package:sanmill/mill/position.dart';
|
||||
import 'package:sanmill/mill/types.dart';
|
||||
import 'package:sanmill/style/app_theme.dart';
|
||||
|
||||
import 'painter_base.dart';
|
||||
part of 'package:sanmill/screens/game_page/game_page.dart';
|
||||
|
||||
class PiecePaintParam {
|
||||
// TODO: null-safety
|
||||
final String? piece;
|
||||
final Offset? pos;
|
||||
final bool? animated;
|
||||
PiecePaintParam({this.piece, this.pos, this.animated});
|
||||
final String piece;
|
||||
final Offset pos;
|
||||
final bool animated;
|
||||
PiecePaintParam({
|
||||
required this.piece,
|
||||
required this.pos,
|
||||
required this.animated,
|
||||
});
|
||||
}
|
||||
|
||||
class PiecesPainter extends PiecesBasePainter {
|
||||
final Position? position;
|
||||
final int? focusIndex, blurIndex;
|
||||
final animationValue;
|
||||
final int? focusIndex;
|
||||
final int? blurIndex;
|
||||
final double animationValue;
|
||||
|
||||
// TODO: null-safety
|
||||
int? pointStyle = 0;
|
||||
double? pointWidth = 10.0;
|
||||
double? pieceWidth = 0.0;
|
||||
double? animatedPieceWidth = 0.0;
|
||||
int pointStyle = 0;
|
||||
double pointWidth = 10.0;
|
||||
double pieceWidth = 0.0;
|
||||
double animatedPieceWidth = 0.0;
|
||||
|
||||
PiecesPainter({
|
||||
required double width,
|
||||
|
@ -80,7 +77,7 @@ class PiecesPainter extends PiecesBasePainter {
|
|||
return true;
|
||||
}
|
||||
|
||||
static doPaint(
|
||||
static void doPaint(
|
||||
Canvas canvas,
|
||||
Paint paint, {
|
||||
Position? position,
|
||||
|
@ -102,8 +99,7 @@ class PiecesPainter extends PiecesBasePainter {
|
|||
final shadowPath = Path();
|
||||
final piecesToDraw = <PiecePaintParam>[];
|
||||
|
||||
// TODO: null-safety
|
||||
Color? blurPositionColor;
|
||||
late Color blurPositionColor;
|
||||
Color focusPositionColor;
|
||||
|
||||
// Draw pieces on board
|
||||
|
@ -114,8 +110,9 @@ class PiecesPainter extends PiecesBasePainter {
|
|||
|
||||
if (piece == Piece.noPiece) continue;
|
||||
|
||||
var pos = Offset(left! + squareWidth! * col, top! + squareWidth * row);
|
||||
var animated = (focusIndex == index);
|
||||
final pos =
|
||||
Offset(left! + squareWidth! * col, top! + squareWidth * row);
|
||||
final animated = focusIndex == index;
|
||||
|
||||
piecesToDraw
|
||||
.add(PiecePaintParam(piece: piece, pos: pos, animated: animated));
|
||||
|
@ -139,26 +136,26 @@ class PiecesPainter extends PiecesBasePainter {
|
|||
);
|
||||
*/
|
||||
|
||||
piecesToDraw.forEach((pps) {
|
||||
var pieceRadius = pieceWidth! / 2;
|
||||
var pieceInnerRadius = pieceRadius * 0.99;
|
||||
for (final pps in piecesToDraw) {
|
||||
final pieceRadius = pieceWidth! / 2;
|
||||
final pieceInnerRadius = pieceRadius * 0.99;
|
||||
|
||||
var animatedPieceRadius = animatedPieceWidth! / 2;
|
||||
var animatedPieceInnerRadius = animatedPieceRadius * 0.99;
|
||||
final animatedPieceRadius = animatedPieceWidth! / 2;
|
||||
final animatedPieceInnerRadius = animatedPieceRadius * 0.99;
|
||||
|
||||
// Draw Border of Piece
|
||||
switch (pps.piece) {
|
||||
case Piece.whiteStone:
|
||||
paint.color = AppTheme.whitePieceBorderColor;
|
||||
canvas.drawCircle(
|
||||
pps.pos!,
|
||||
pps.animated! ? animatedPieceRadius : pieceRadius,
|
||||
pps.pos,
|
||||
pps.animated ? animatedPieceRadius : pieceRadius,
|
||||
paint,
|
||||
);
|
||||
paint.color = Color(Config.whitePieceColor);
|
||||
canvas.drawCircle(
|
||||
pps.pos!,
|
||||
pps.animated! ? animatedPieceInnerRadius : pieceInnerRadius,
|
||||
pps.pos,
|
||||
pps.animated ? animatedPieceInnerRadius : pieceInnerRadius,
|
||||
paint,
|
||||
);
|
||||
blurPositionColor = Color(Config.whitePieceColor).withOpacity(0.1);
|
||||
|
@ -166,14 +163,14 @@ class PiecesPainter extends PiecesBasePainter {
|
|||
case Piece.blackStone:
|
||||
paint.color = AppTheme.blackPieceBorderColor;
|
||||
canvas.drawCircle(
|
||||
pps.pos!,
|
||||
pps.animated! ? animatedPieceRadius : pieceRadius,
|
||||
pps.pos,
|
||||
pps.animated ? animatedPieceRadius : pieceRadius,
|
||||
paint,
|
||||
);
|
||||
paint.color = Color(Config.blackPieceColor);
|
||||
canvas.drawCircle(
|
||||
pps.pos!,
|
||||
pps.animated! ? animatedPieceInnerRadius : pieceInnerRadius,
|
||||
pps.pos,
|
||||
pps.animated ? animatedPieceInnerRadius : pieceInnerRadius,
|
||||
paint,
|
||||
);
|
||||
blurPositionColor = Color(Config.blackPieceColor).withOpacity(0.1);
|
||||
|
@ -185,13 +182,14 @@ class PiecesPainter extends PiecesBasePainter {
|
|||
assert(false);
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// draw focus and blur position
|
||||
|
||||
if (focusIndex != invalidIndex) {
|
||||
final int row = focusIndex! ~/ 7, column = focusIndex % 7;
|
||||
final int row = focusIndex! ~/ 7;
|
||||
final int column = focusIndex % 7;
|
||||
|
||||
if (focusIndex != invalidIndex) {
|
||||
/*
|
||||
focusPositionColor = Color.fromARGB(
|
||||
(Color(Config.whitePieceColor).alpha +
|
||||
|
@ -223,9 +221,10 @@ class PiecesPainter extends PiecesBasePainter {
|
|||
}
|
||||
|
||||
if (blurIndex != invalidIndex) {
|
||||
final row = blurIndex! ~/ 7, column = blurIndex % 7;
|
||||
final row = blurIndex! ~/ 7;
|
||||
final column = blurIndex % 7;
|
||||
|
||||
paint.color = blurPositionColor!;
|
||||
paint.color = blurPositionColor;
|
||||
paint.style = PaintingStyle.fill;
|
||||
|
||||
canvas.drawCircle(
|
|
@ -0,0 +1,81 @@
|
|||
/*
|
||||
This file is part of Sanmill.
|
||||
Copyright (C) 2019-2021 The Sanmill developers (see AUTHORS file)
|
||||
|
||||
Sanmill is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Sanmill is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_picker/flutter_picker.dart';
|
||||
import 'package:sanmill/generated/l10n.dart';
|
||||
import 'package:sanmill/shared/common/config.dart';
|
||||
import 'package:sanmill/shared/theme/app_theme.dart';
|
||||
|
||||
Future<int> showPickerNumber(
|
||||
BuildContext context,
|
||||
int begin,
|
||||
int end,
|
||||
int initValue,
|
||||
String suffixString,
|
||||
) async {
|
||||
int selectValue = 0;
|
||||
await Picker(
|
||||
adapter: NumberPickerAdapter(
|
||||
data: [
|
||||
NumberPickerColumn(
|
||||
begin: begin,
|
||||
end: end,
|
||||
initValue: initValue,
|
||||
suffix: Text(
|
||||
suffixString,
|
||||
style: TextStyle(
|
||||
fontSize: Config.fontSize,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
hideHeader: true,
|
||||
title: Text(
|
||||
S.of(context).pleaseSelect,
|
||||
style: TextStyle(
|
||||
color: AppTheme.appPrimaryColor,
|
||||
fontSize: Config.fontSize + 4.0,
|
||||
),
|
||||
),
|
||||
textStyle: TextStyle(
|
||||
color: Colors.black,
|
||||
fontSize: Config.fontSize,
|
||||
),
|
||||
selectedTextStyle: const TextStyle(color: AppTheme.appPrimaryColor),
|
||||
cancelText: S.of(context).cancel,
|
||||
cancelTextStyle: TextStyle(
|
||||
color: AppTheme.appPrimaryColor,
|
||||
fontSize: Config.fontSize,
|
||||
),
|
||||
confirmText: S.of(context).confirm,
|
||||
confirmTextStyle: TextStyle(
|
||||
color: AppTheme.appPrimaryColor,
|
||||
fontSize: Config.fontSize,
|
||||
),
|
||||
onConfirm: (Picker picker, List value) async {
|
||||
debugPrint(value.toString());
|
||||
final selectValues = picker.getSelectedValues();
|
||||
debugPrint(selectValues.toString());
|
||||
selectValue = selectValues[0];
|
||||
},
|
||||
).showDialog(context);
|
||||
|
||||
return selectValue;
|
||||
}
|
|
@ -17,24 +17,29 @@
|
|||
*/
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:sanmill/style/app_theme.dart';
|
||||
import 'package:sanmill/shared/list_item_divider.dart';
|
||||
import 'package:sanmill/shared/theme/app_theme.dart';
|
||||
|
||||
class SettingsCard extends StatelessWidget {
|
||||
const SettingsCard({
|
||||
Key? key,
|
||||
required this.context,
|
||||
required this.children,
|
||||
}) : super(key: key);
|
||||
|
||||
final BuildContext context;
|
||||
final children;
|
||||
final List<Widget> children;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Card(
|
||||
color: AppTheme.cardColor,
|
||||
margin: AppTheme.cardMargin,
|
||||
child: Column(children: children),
|
||||
child: ListView.separated(
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
shrinkWrap: true,
|
||||
itemBuilder: (_, index) => children[index],
|
||||
separatorBuilder: (_, __) => const ListItemDivider(),
|
||||
itemCount: children.length,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -18,14 +18,13 @@
|
|||
|
||||
import 'package:fluentui_system_icons/fluentui_system_icons.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:sanmill/common/config.dart';
|
||||
import 'package:sanmill/l10n/resources.dart';
|
||||
import 'package:sanmill/style/app_theme.dart';
|
||||
import 'package:sanmill/shared/common/config.dart';
|
||||
import 'package:sanmill/shared/theme/app_theme.dart';
|
||||
|
||||
class SettingsListTile extends StatelessWidget {
|
||||
const SettingsListTile({
|
||||
Key? key,
|
||||
required this.context,
|
||||
required this.titleString,
|
||||
this.subtitleString,
|
||||
this.trailingString,
|
||||
|
@ -33,16 +32,16 @@ class SettingsListTile extends StatelessWidget {
|
|||
required this.onTap,
|
||||
}) : super(key: key);
|
||||
|
||||
final BuildContext context;
|
||||
final String titleString;
|
||||
final String? subtitleString;
|
||||
final String? trailingString;
|
||||
final int? trailingColor;
|
||||
final onTap;
|
||||
final VoidCallback onTap;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
bool ltr = getBidirectionality(context) == Bidirectionality.leftToRight;
|
||||
final bool ltr =
|
||||
getBidirectionality(context) == Bidirectionality.leftToRight;
|
||||
return ListTile(
|
||||
title: Text(
|
||||
titleString,
|
||||
|
@ -64,9 +63,7 @@ class SettingsListTile extends StatelessWidget {
|
|||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
trailingColor == null
|
||||
? (trailingString == null ? "" : trailingString!)
|
||||
: trailingColor!.toRadixString(16),
|
||||
trailingColor?.toRadixString(16) ?? trailingString ?? '',
|
||||
style: TextStyle(
|
||||
fontSize: Config.fontSize,
|
||||
backgroundColor:
|
||||
|
@ -74,10 +71,11 @@ class SettingsListTile extends StatelessWidget {
|
|||
),
|
||||
),
|
||||
Icon(
|
||||
ltr
|
||||
? FluentIcons.chevron_right_24_regular
|
||||
: FluentIcons.chevron_left_24_regular,
|
||||
color: AppTheme.listTileSubtitleColor)
|
||||
ltr
|
||||
? FluentIcons.chevron_right_24_regular
|
||||
: FluentIcons.chevron_left_24_regular,
|
||||
color: AppTheme.listTileSubtitleColor,
|
||||
)
|
||||
],
|
||||
),
|
||||
onTap: onTap,
|
|
@ -17,24 +17,22 @@
|
|||
*/
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:sanmill/common/config.dart';
|
||||
import 'package:sanmill/style/app_theme.dart';
|
||||
import 'package:sanmill/shared/common/config.dart';
|
||||
import 'package:sanmill/shared/theme/app_theme.dart';
|
||||
|
||||
class SettingsSwitchListTile extends StatelessWidget {
|
||||
const SettingsSwitchListTile({
|
||||
Key? key,
|
||||
required this.context,
|
||||
required this.value,
|
||||
required this.onChanged,
|
||||
required this.titleString,
|
||||
this.subtitleString,
|
||||
}) : super(key: key);
|
||||
|
||||
final BuildContext context;
|
||||
final value;
|
||||
final bool value;
|
||||
final String titleString;
|
||||
final String? subtitleString;
|
||||
final onChanged;
|
||||
final ValueChanged<bool>? onChanged;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
|
@ -17,21 +17,26 @@
|
|||
*/
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:sanmill/common/config.dart';
|
||||
import 'package:sanmill/shared/common/config.dart';
|
||||
|
||||
void showSnackBar(BuildContext context, String message,
|
||||
{Duration duration = const Duration(milliseconds: 4000)}) {
|
||||
void showSnackBar(
|
||||
BuildContext context,
|
||||
String message, {
|
||||
Duration duration = const Duration(milliseconds: 4000),
|
||||
}) {
|
||||
if (!Config.screenReaderSupport) {
|
||||
ScaffoldMessenger.of(context).hideCurrentSnackBar();
|
||||
}
|
||||
|
||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
|
||||
content: Text(
|
||||
message,
|
||||
style: TextStyle(
|
||||
fontSize: Config.fontSize,
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(
|
||||
message,
|
||||
style: TextStyle(
|
||||
fontSize: Config.fontSize,
|
||||
),
|
||||
),
|
||||
duration: duration,
|
||||
),
|
||||
duration: duration,
|
||||
));
|
||||
);
|
||||
}
|
|
@ -0,0 +1,143 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:sanmill/shared/common/config.dart';
|
||||
import 'package:sanmill/shared/common/constants.dart';
|
||||
import 'package:sanmill/shared/theme/colors.dart';
|
||||
|
||||
class AppTheme {
|
||||
const AppTheme._();
|
||||
// TODO: restructure theming. Some theme Elements should be accessed via Theme.of(context)
|
||||
|
||||
// Theme data
|
||||
static final lightThemeData = ThemeData(
|
||||
primarySwatch: AppTheme.appPrimaryColor,
|
||||
brightness: Brightness.light,
|
||||
);
|
||||
|
||||
static final darkThemeData = ThemeData(
|
||||
primarySwatch: AppTheme.appPrimaryColor,
|
||||
brightness: Brightness.dark,
|
||||
);
|
||||
|
||||
// Color
|
||||
static const appPrimaryColor = Colors.green; // Appbar & Dialog button
|
||||
static const dialogTitleColor = appPrimaryColor;
|
||||
|
||||
/// Game page
|
||||
static Color boardBackgroundColor = UIColors.burlyWood;
|
||||
static Color mainToolbarBackgroundColor = UIColors.burlyWood;
|
||||
static Color navigationToolbarBackgroundColor = UIColors.burlyWood;
|
||||
static Color boardLineColor = const Color(0x996D000D);
|
||||
static Color whitePieceColor = const Color.fromARGB(0xFF, 0xFF, 0xFF, 0xFF);
|
||||
static Color whitePieceBorderColor =
|
||||
const Color.fromARGB(0xFF, 0x66, 0x00, 0x00);
|
||||
static Color blackPieceColor = const Color.fromARGB(0xFF, 0x00, 0x00, 0x00);
|
||||
static Color blackPieceBorderColor =
|
||||
const Color.fromARGB(0xFF, 0x22, 0x22, 0x22);
|
||||
static Color pieceHighlightColor = Colors.red;
|
||||
static Color messageColor = Colors.white;
|
||||
static Color banColor =
|
||||
const Color.fromARGB(0xFF, 0xFF, 0x00, 0x00); // unused
|
||||
static Color banBorderColor =
|
||||
const Color.fromARGB(0x80, 0xFF, 0x00, 0x00); // unused
|
||||
static Color mainToolbarIconColor = listTileSubtitleColor;
|
||||
static Color navigationToolbarIconColor = listTileSubtitleColor;
|
||||
static Color toolbarTextColor = mainToolbarIconColor;
|
||||
static Color moveHistoryTextColor = Colors.yellow;
|
||||
static Color moveHistoryDialogBackgroundColor = Colors.transparent;
|
||||
static Color infoDialogBackgroundColor = moveHistoryDialogBackgroundColor;
|
||||
static Color infoTextColor = moveHistoryTextColor;
|
||||
static Color simpleDialogOptionTextColor = Colors.yellow;
|
||||
|
||||
/// Settings page
|
||||
static Color darkBackgroundColor = UIColors.crusoe;
|
||||
static Color lightBackgroundColor = UIColors.papayaWhip;
|
||||
static Color listTileSubtitleColor = const Color(0x99461220);
|
||||
static Color listItemDividerColor = const Color(0x336D000D);
|
||||
static Color switchListTileActiveColor = dialogTitleColor;
|
||||
static Color switchListTileTitleColor = UIColors.crusoe;
|
||||
static Color cardColor = UIColors.floralWhite;
|
||||
static Color settingsHeaderTextColor = UIColors.crusoe;
|
||||
|
||||
/// Help page
|
||||
static Color helpBackgroundColor = boardBackgroundColor;
|
||||
static Color helpTextColor = boardBackgroundColor;
|
||||
|
||||
/// About
|
||||
static Color aboutPageBackgroundColor = lightBackgroundColor;
|
||||
|
||||
/// Drawer
|
||||
static Color drawerColor = Colors.white;
|
||||
static Color drawerBackgroundColor =
|
||||
UIColors.notWhite.withOpacity(0.5); // TODO
|
||||
static Color drawerHighlightItemColor =
|
||||
UIColors.freeSpeechGreen.withOpacity(0.2);
|
||||
static Color drawerDividerColor = UIColors.grey.withOpacity(0.6);
|
||||
static Color drawerBoxerShadowColor = UIColors.grey.withOpacity(0.6);
|
||||
static Color drawerTextColor = UIColors.nearlyBlack;
|
||||
static Color drawerHighlightTextColor = UIColors.nearlyBlack;
|
||||
static Color exitTextColor = UIColors.nearlyBlack;
|
||||
static Color drawerIconColor = drawerTextColor;
|
||||
static Color drawerHighlightIconColor = drawerHighlightTextColor;
|
||||
static Color drawerAnimationIconColor = Colors.white;
|
||||
static Color exitIconColor = Colors.red;
|
||||
static Color drawerSplashColor =
|
||||
Colors.grey.withOpacity(0.1); // TODO: no use?
|
||||
static Color drawerHighlightColor = Colors.transparent; // TODO: no use?
|
||||
static Color navigationHomeScreenBackgroundColor =
|
||||
UIColors.nearlyWhite; // TODO: no use?
|
||||
|
||||
// Theme
|
||||
|
||||
static const sliderThemeData = SliderThemeData(
|
||||
trackHeight: 20,
|
||||
activeTrackColor: Colors.green,
|
||||
inactiveTrackColor: Colors.grey,
|
||||
disabledActiveTrackColor: Colors.yellow,
|
||||
disabledInactiveTrackColor: Colors.cyan,
|
||||
activeTickMarkColor: Colors.black,
|
||||
inactiveTickMarkColor: Colors.green,
|
||||
//overlayColor: Colors.yellow,
|
||||
overlappingShapeStrokeColor: Colors.black,
|
||||
//overlayShape: RoundSliderOverlayShape(),
|
||||
valueIndicatorColor: Colors.green,
|
||||
showValueIndicator: ShowValueIndicator.always,
|
||||
minThumbSeparation: 100,
|
||||
thumbShape: RoundSliderThumbShape(
|
||||
enabledThumbRadius: 2.0,
|
||||
disabledThumbRadius: 1.0,
|
||||
),
|
||||
rangeTrackShape: RoundedRectRangeSliderTrackShape(),
|
||||
tickMarkShape: RoundSliderTickMarkShape(tickMarkRadius: 2.0),
|
||||
valueIndicatorTextStyle: TextStyle(fontSize: 24),
|
||||
);
|
||||
|
||||
static TextStyle simpleDialogOptionTextStyle = TextStyle(
|
||||
fontSize: Config.fontSize + 4.0,
|
||||
color: AppTheme.simpleDialogOptionTextColor,
|
||||
);
|
||||
|
||||
static TextStyle moveHistoryTextStyle = TextStyle(
|
||||
fontSize: Config.fontSize + 2.0,
|
||||
height: 1.5,
|
||||
color: moveHistoryTextColor,
|
||||
);
|
||||
|
||||
static double boardTop = isLargeScreen ? 75.0 : 36.0;
|
||||
static double boardMargin = 10.0;
|
||||
static double boardScreenPaddingH = 10.0;
|
||||
static double boardBorderRadius = 5.0;
|
||||
static double boardPadding = 5.0;
|
||||
|
||||
static TextStyle settingsHeaderStyle =
|
||||
TextStyle(color: settingsHeaderTextColor, fontSize: Config.fontSize + 4);
|
||||
|
||||
static TextStyle settingsTextStyle = TextStyle(fontSize: Config.fontSize);
|
||||
|
||||
static const cardMargin = EdgeInsets.symmetric(vertical: 4.0);
|
||||
|
||||
static const double drawerWidth = 250.0;
|
||||
|
||||
static const double sizedBoxHeight = 16.0;
|
||||
|
||||
static double copyrightFontSize = 12;
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
This file is part of Sanmill.
|
||||
Copyright (C) 2019-2021 The Sanmill developers (see AUTHORS file)
|
||||
|
||||
Sanmill is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Sanmill is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class UIColors {
|
||||
// https://www.htmlcsscolor.com
|
||||
const UIColors._();
|
||||
|
||||
static const Color white = Color(0xFFFFFFFF);
|
||||
static const Color notWhite = Color(0xFFEDF0F2);
|
||||
static const Color nearlyWhite = Color(0xFFFEFEFE);
|
||||
static const Color floralWhite = Color(0xFFFFFAF0);
|
||||
|
||||
static const Color nearlyBlack = Color(0xFF213333);
|
||||
|
||||
static const Color grey = Color(0xFF3A5160);
|
||||
static const Color darkGrey = Color(0xFF313A44);
|
||||
|
||||
static const Color burlyWood = Color(0xFFDEB887);
|
||||
static const Color cottonCandy = Color.fromARGB(0xFF, 255, 189, 219);
|
||||
static const Color turkishRose = Color.fromARGB(0xFF, 163, 109, 173);
|
||||
static const Color crusoe = Color(0xFF165B31);
|
||||
static const Color forestGreen = Color(0xFF228B22);
|
||||
static const Color freeSpeechGreen = Color(0xFF09F911);
|
||||
static const Color oasis = Color.fromARGB(0xFF, 253, 239, 194);
|
||||
static const Color papayaWhip = Color(0xFFFFEFD5);
|
||||
static const Color stormGrey = Color.fromARGB(0xFF, 119, 121, 131);
|
||||
static const Color turmeric = Color.fromARGB(0xFF, 186, 202, 68);
|
||||
static const Color LavenderBlush = Color.fromARGB(0xFF, 255, 240, 246);
|
||||
}
|
|
@ -1,134 +0,0 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:sanmill/common/config.dart';
|
||||
import 'package:sanmill/common/constants.dart';
|
||||
import 'package:sanmill/style/colors.dart';
|
||||
|
||||
class AppTheme {
|
||||
AppTheme._();
|
||||
|
||||
// Theme data
|
||||
static var lightThemeData = ThemeData(
|
||||
primarySwatch: AppTheme.appPrimaryColor,
|
||||
brightness: Brightness.light,
|
||||
);
|
||||
|
||||
static var darkThemeData = ThemeData(
|
||||
primarySwatch: AppTheme.appPrimaryColor,
|
||||
brightness: Brightness.dark,
|
||||
);
|
||||
|
||||
// Color
|
||||
static var appPrimaryColor = Colors.green; // Appbar & Dialog button
|
||||
static var dialogTitleColor = appPrimaryColor;
|
||||
|
||||
/// Game page
|
||||
static var boardBackgroundColor = UIColors.burlyWood;
|
||||
static var mainToolbarBackgroundColor = UIColors.burlyWood;
|
||||
static var navigationToolbarBackgroundColor = UIColors.burlyWood;
|
||||
static var boardLineColor = Color(0x996D000D);
|
||||
static var whitePieceColor = Color.fromARGB(0xFF, 0xFF, 0xFF, 0xFF);
|
||||
static var whitePieceBorderColor = Color.fromARGB(0xFF, 0x66, 0x00, 0x00);
|
||||
static var blackPieceColor = Color.fromARGB(0xFF, 0x00, 0x00, 0x00);
|
||||
static var blackPieceBorderColor = Color.fromARGB(0xFF, 0x22, 0x22, 0x22);
|
||||
static var pieceHighlightColor = Colors.red;
|
||||
static var messageColor = Colors.white;
|
||||
static var banColor = Color.fromARGB(0xFF, 0xFF, 0x00, 0x00); // unused
|
||||
static var banBorderColor = Color.fromARGB(0x80, 0xFF, 0x00, 0x00); // unused
|
||||
static var mainToolbarIconColor = listTileSubtitleColor;
|
||||
static var navigationToolbarIconColor = listTileSubtitleColor;
|
||||
static var toolbarTextColor = mainToolbarIconColor;
|
||||
static var moveHistoryTextColor = Colors.yellow;
|
||||
static var moveHistoryDialogBackgroundColor = Colors.transparent;
|
||||
static var infoDialogBackgroundColor = moveHistoryDialogBackgroundColor;
|
||||
static var infoTextColor = moveHistoryTextColor;
|
||||
static var simpleDialogOptionTextColor = Colors.yellow;
|
||||
|
||||
/// Settings page
|
||||
static var darkBackgroundColor = UIColors.crusoe;
|
||||
static var lightBackgroundColor = UIColors.papayaWhip;
|
||||
static var listTileSubtitleColor = Color(0x99461220);
|
||||
static var listItemDividerColor = Color(0x336D000D);
|
||||
static var switchListTileActiveColor = dialogTitleColor;
|
||||
static var switchListTileTitleColor = UIColors.crusoe;
|
||||
static const cardColor = UIColors.floralWhite;
|
||||
static const settingsHeaderTextColor = UIColors.crusoe;
|
||||
|
||||
/// Help page
|
||||
static var helpBackgroundColor = boardBackgroundColor;
|
||||
static var helpTextColor = boardBackgroundColor;
|
||||
|
||||
/// About
|
||||
static var aboutPageBackgroundColor = lightBackgroundColor;
|
||||
|
||||
/// Drawer
|
||||
static var drawerColor = Colors.white;
|
||||
static var drawerBackgroundColor = UIColors.notWhite.withOpacity(0.5); // TODO
|
||||
static var drawerHighlightItemColor =
|
||||
UIColors.freeSpeechGreen.withOpacity(0.2);
|
||||
static var drawerDividerColor = UIColors.grey.withOpacity(0.6);
|
||||
static var drawerBoxerShadowColor = UIColors.grey.withOpacity(0.6);
|
||||
static var drawerTextColor = UIColors.nearlyBlack;
|
||||
static var drawerHighlightTextColor = UIColors.nearlyBlack;
|
||||
static var exitTextColor = UIColors.nearlyBlack;
|
||||
static var drawerIconColor = drawerTextColor;
|
||||
static var drawerHighlightIconColor = drawerHighlightTextColor;
|
||||
static var drawerAnimationIconColor = Colors.white;
|
||||
static var exitIconColor = Colors.red;
|
||||
static var drawerSplashColor = Colors.grey.withOpacity(0.1); // TODO: no use?
|
||||
static var drawerHighlightColor = Colors.transparent; // TODO: no use?
|
||||
static var navigationHomeScreenBackgroundColor =
|
||||
UIColors.nearlyWhite; // TODO: no use?
|
||||
|
||||
// Theme
|
||||
|
||||
static const sliderThemeData = SliderThemeData(
|
||||
trackHeight: 20,
|
||||
activeTrackColor: Colors.green,
|
||||
inactiveTrackColor: Colors.grey,
|
||||
disabledActiveTrackColor: Colors.yellow,
|
||||
disabledInactiveTrackColor: Colors.cyan,
|
||||
activeTickMarkColor: Colors.black,
|
||||
inactiveTickMarkColor: Colors.green,
|
||||
//overlayColor: Colors.yellow,
|
||||
overlappingShapeStrokeColor: Colors.black,
|
||||
//overlayShape: RoundSliderOverlayShape(),
|
||||
valueIndicatorColor: Colors.green,
|
||||
showValueIndicator: ShowValueIndicator.always,
|
||||
minThumbSeparation: 100,
|
||||
thumbShape: RoundSliderThumbShape(
|
||||
enabledThumbRadius: 2.0, disabledThumbRadius: 1.0),
|
||||
rangeTrackShape: RoundedRectRangeSliderTrackShape(),
|
||||
tickMarkShape: RoundSliderTickMarkShape(tickMarkRadius: 2.0),
|
||||
valueIndicatorTextStyle: TextStyle(fontSize: 24),
|
||||
);
|
||||
|
||||
static var simpleDialogOptionTextStyle = TextStyle(
|
||||
fontSize: Config.fontSize + 4.0,
|
||||
color: AppTheme.simpleDialogOptionTextColor,
|
||||
);
|
||||
|
||||
static var moveHistoryTextStyle = TextStyle(
|
||||
fontSize: Config.fontSize + 2.0,
|
||||
height: 1.5,
|
||||
color: moveHistoryTextColor);
|
||||
|
||||
static double boardTop = isLargeScreen() ? 75.0 : 36.0;
|
||||
static double boardMargin = 10.0;
|
||||
static double boardScreenPaddingH = 10.0;
|
||||
static double boardBorderRadius = 5.0;
|
||||
static double boardPadding = 5.0;
|
||||
|
||||
static var settingsHeaderStyle =
|
||||
TextStyle(color: settingsHeaderTextColor, fontSize: Config.fontSize + 4);
|
||||
|
||||
static var settingsTextStyle = TextStyle(fontSize: Config.fontSize);
|
||||
|
||||
static const cardMargin =
|
||||
const EdgeInsets.symmetric(vertical: 4.0, horizontal: 0);
|
||||
|
||||
static const double drawerWidth = 250.0;
|
||||
|
||||
static const double sizedBoxHeight = 16.0;
|
||||
|
||||
static double copyrightFontSize = 12;
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
/*
|
||||
This file is part of Sanmill.
|
||||
Copyright (C) 2019-2021 The Sanmill developers (see AUTHORS file)
|
||||
|
||||
Sanmill is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Sanmill is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class UIColors {
|
||||
// https://www.htmlcsscolor.com
|
||||
|
||||
static const white = Color(0xFFFFFFFF);
|
||||
static const notWhite = Color(0xFFEDF0F2);
|
||||
static const nearlyWhite = Color(0xFFFEFEFE);
|
||||
static const floralWhite = Color(0xFFFFFAF0);
|
||||
|
||||
static const nearlyBlack = Color(0xFF213333);
|
||||
|
||||
static const grey = Color(0xFF3A5160);
|
||||
static const darkGrey = Color(0xFF313A44);
|
||||
|
||||
static const burlyWood = Color(0xFFDEB887);
|
||||
static const cottonCandy = Color.fromARGB(0xFF, 255, 189, 219);
|
||||
static const turkishRose = Color.fromARGB(0xFF, 163, 109, 173);
|
||||
static const crusoe = Color(0xFF165B31);
|
||||
static const forestGreen = Color(0xFF228B22);
|
||||
static const freeSpeechGreen = Color(0xFF09F911);
|
||||
static const oasis = Color.fromARGB(0xFF, 253, 239, 194);
|
||||
static const papayaWhip = Color(0xFFFFEFD5);
|
||||
static const stormGrey = Color.fromARGB(0xFF, 119, 121, 131);
|
||||
static const turmeric = Color.fromARGB(0xFF, 186, 202, 68);
|
||||
static const LavenderBlush = Color.fromARGB(0xFF, 255, 240, 246);
|
||||
}
|
|
@ -1,62 +0,0 @@
|
|||
/*
|
||||
This file is part of Sanmill.
|
||||
Copyright (C) 2019-2021 The Sanmill developers (see AUTHORS file)
|
||||
|
||||
Sanmill is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Sanmill is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart' show rootBundle;
|
||||
import 'package:sanmill/common/constants.dart';
|
||||
import 'package:sanmill/generated/l10n.dart';
|
||||
|
||||
class EnvironmentVariablesPage extends StatefulWidget {
|
||||
@override
|
||||
_EnvironmentVariablesPageState createState() =>
|
||||
_EnvironmentVariablesPageState();
|
||||
}
|
||||
|
||||
class _EnvironmentVariablesPageState extends State<EnvironmentVariablesPage> {
|
||||
String _data = "";
|
||||
|
||||
Future<void> _loadData() async {
|
||||
final _loadedData =
|
||||
await rootBundle.loadString(Constants.environmentVariablesFilename);
|
||||
setState(() {
|
||||
_data = _loadedData;
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
_loadData();
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(S.of(context).environmentVariables), centerTitle: true),
|
||||
body: ListView(
|
||||
children: <Widget>[
|
||||
Container(
|
||||
padding: const EdgeInsets.only(
|
||||
top: 16, left: 16, right: 16, bottom: 16),
|
||||
child: Text(
|
||||
_data != "" ? _data : 'Nothing to show',
|
||||
style: TextStyle(fontFamily: 'Monospace', fontSize: 12),
|
||||
textAlign: TextAlign.left,
|
||||
))
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,643 +0,0 @@
|
|||
/*
|
||||
This file is part of Sanmill.
|
||||
Copyright (C) 2019-2021 The Sanmill developers (see AUTHORS file)
|
||||
|
||||
Sanmill is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Sanmill is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:sanmill/common/config.dart';
|
||||
import 'package:sanmill/common/settings.dart';
|
||||
import 'package:sanmill/generated/l10n.dart';
|
||||
import 'package:sanmill/style/app_theme.dart';
|
||||
import 'package:sanmill/widgets/settings_card.dart';
|
||||
import 'package:sanmill/widgets/settings_list_tile.dart';
|
||||
import 'package:sanmill/widgets/settings_switch_list_tile.dart';
|
||||
|
||||
import 'dialog.dart';
|
||||
import 'env_page.dart';
|
||||
import 'list_item_divider.dart';
|
||||
|
||||
class Developer {
|
||||
static bool developerModeEnabled = false;
|
||||
}
|
||||
|
||||
class GameSettingsPage extends StatefulWidget {
|
||||
@override
|
||||
_GameSettingsPageState createState() => _GameSettingsPageState();
|
||||
}
|
||||
|
||||
class _GameSettingsPageState extends State<GameSettingsPage> {
|
||||
Color pickerColor = Color(0xFF808080);
|
||||
Color currentColor = Color(0xFF808080);
|
||||
|
||||
late StreamController<int> _events;
|
||||
|
||||
var algorithmNames = ['Alpha-Beta', 'PVS', 'MTD(f)'];
|
||||
|
||||
final String tag = "[game_settings_page]";
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_events = StreamController<int>.broadcast();
|
||||
_events.add(10);
|
||||
}
|
||||
|
||||
void _restore() async {
|
||||
final settings = await Settings.instance();
|
||||
await settings.restore();
|
||||
}
|
||||
|
||||
SliderTheme _skillLevelSliderTheme(context, setState) {
|
||||
return SliderTheme(
|
||||
data: AppTheme.sliderThemeData,
|
||||
child: Semantics(
|
||||
label: S.of(context).skillLevel,
|
||||
child: Slider(
|
||||
value: Config.skillLevel.toDouble(),
|
||||
min: 1,
|
||||
max: 30,
|
||||
divisions: 29,
|
||||
label: Config.skillLevel.round().toString(),
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
print("[config] Slider value: $value");
|
||||
Config.skillLevel = value.toInt();
|
||||
Config.save();
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
SliderTheme _moveTimeSliderTheme(context, setState) {
|
||||
return SliderTheme(
|
||||
data: AppTheme.sliderThemeData,
|
||||
child: Semantics(
|
||||
label: S.of(context).moveTime,
|
||||
child: Slider(
|
||||
value: Config.moveTime.toDouble(),
|
||||
min: 0,
|
||||
max: 60,
|
||||
divisions: 60,
|
||||
label: Config.moveTime.round().toString(),
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
print("[config] Slider value: $value");
|
||||
Config.moveTime = value.toInt();
|
||||
Config.save();
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Restore
|
||||
|
||||
restoreFactoryDefaultSettings() async {
|
||||
confirm() async {
|
||||
Navigator.of(context).pop();
|
||||
if (Platform.isAndroid) {
|
||||
showCountdownDialog(context, 10, _events, _restore);
|
||||
} else {
|
||||
_restore();
|
||||
ScaffoldMessenger.of(context).clearSnackBars();
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(S.of(context).exitAppManually)));
|
||||
}
|
||||
}
|
||||
|
||||
cancel() => Navigator.of(context).pop();
|
||||
|
||||
var prompt = "";
|
||||
|
||||
if (Platform.isAndroid) {
|
||||
prompt = S.of(context).exitApp;
|
||||
}
|
||||
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertDialog(
|
||||
title: Text(S.of(context).restore,
|
||||
style: TextStyle(
|
||||
color: AppTheme.dialogTitleColor,
|
||||
fontSize: Config.fontSize + 4,
|
||||
)),
|
||||
content: SingleChildScrollView(
|
||||
child: Text(
|
||||
S.of(context).restoreDefaultSettings + "?\n" + prompt,
|
||||
style: TextStyle(
|
||||
fontSize: Config.fontSize,
|
||||
),
|
||||
),
|
||||
),
|
||||
actions: <Widget>[
|
||||
TextButton(
|
||||
child: Text(
|
||||
S.of(context).ok,
|
||||
style: TextStyle(
|
||||
fontSize: Config.fontSize,
|
||||
),
|
||||
),
|
||||
onPressed: confirm),
|
||||
TextButton(
|
||||
child: Text(
|
||||
S.of(context).cancel,
|
||||
style: TextStyle(
|
||||
fontSize: Config.fontSize,
|
||||
),
|
||||
),
|
||||
onPressed: cancel),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: AppTheme.lightBackgroundColor,
|
||||
appBar: AppBar(
|
||||
centerTitle: true,
|
||||
title: Text(S.of(context).preferences),
|
||||
),
|
||||
body: SingleChildScrollView(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: children(context),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
List<Widget> children(BuildContext context) {
|
||||
return <Widget>[
|
||||
Text(S.of(context).whoMovesFirst, style: AppTheme.settingsHeaderStyle),
|
||||
SettingsCard(
|
||||
context: context,
|
||||
children: <Widget>[
|
||||
SettingsSwitchListTile(
|
||||
context: context,
|
||||
value: !Config.aiMovesFirst,
|
||||
onChanged: setWhoMovesFirst,
|
||||
titleString:
|
||||
Config.aiMovesFirst ? S.of(context).ai : S.of(context).human,
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(height: AppTheme.sizedBoxHeight),
|
||||
Text(S.of(context).difficulty, style: AppTheme.settingsHeaderStyle),
|
||||
SettingsCard(
|
||||
context: context,
|
||||
children: <Widget>[
|
||||
SettingsListTile(
|
||||
context: context,
|
||||
titleString: S.of(context).skillLevel,
|
||||
//trailingString: "L" + Config.skillLevel.toString(),
|
||||
onTap: setSkillLevel,
|
||||
),
|
||||
ListItemDivider(),
|
||||
SettingsListTile(
|
||||
context: context,
|
||||
titleString: S.of(context).moveTime,
|
||||
onTap: setMoveTime,
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(height: AppTheme.sizedBoxHeight),
|
||||
Text(S.of(context).aisPlayStyle, style: AppTheme.settingsHeaderStyle),
|
||||
SettingsCard(
|
||||
context: context,
|
||||
children: <Widget>[
|
||||
SettingsListTile(
|
||||
context: context,
|
||||
titleString: S.of(context).algorithm,
|
||||
trailingString: algorithmNames[Config.algorithm],
|
||||
onTap: setAlgorithm,
|
||||
),
|
||||
ListItemDivider(),
|
||||
SettingsSwitchListTile(
|
||||
context: context,
|
||||
value: Config.drawOnHumanExperience,
|
||||
onChanged: setDrawOnHumanExperience,
|
||||
titleString: S.of(context).drawOnHumanExperience,
|
||||
),
|
||||
ListItemDivider(),
|
||||
SettingsSwitchListTile(
|
||||
context: context,
|
||||
value: Config.considerMobility,
|
||||
onChanged: setConsiderMobility,
|
||||
titleString: S.of(context).considerMobility,
|
||||
),
|
||||
ListItemDivider(),
|
||||
SettingsSwitchListTile(
|
||||
context: context,
|
||||
value: Config.aiIsLazy,
|
||||
onChanged: setAiIsLazy,
|
||||
titleString: S.of(context).passive,
|
||||
),
|
||||
ListItemDivider(),
|
||||
SettingsSwitchListTile(
|
||||
context: context,
|
||||
value: Config.shufflingEnabled,
|
||||
onChanged: setShufflingEnabled,
|
||||
titleString: S.of(context).shufflingEnabled,
|
||||
),
|
||||
],
|
||||
),
|
||||
!Platform.isWindows
|
||||
? SizedBox(height: AppTheme.sizedBoxHeight)
|
||||
: Container(height: 0.0, width: 0.0),
|
||||
!Platform.isWindows
|
||||
? Text(S.of(context).playSounds, style: AppTheme.settingsHeaderStyle)
|
||||
: Container(height: 0.0, width: 0.0),
|
||||
!Platform.isWindows
|
||||
? SettingsCard(
|
||||
context: context,
|
||||
children: <Widget>[
|
||||
SettingsSwitchListTile(
|
||||
context: context,
|
||||
value: Config.toneEnabled,
|
||||
onChanged: setTone,
|
||||
titleString: S.of(context).playSoundsInTheGame,
|
||||
),
|
||||
ListItemDivider(),
|
||||
SettingsSwitchListTile(
|
||||
context: context,
|
||||
value: Config.keepMuteWhenTakingBack,
|
||||
onChanged: setKeepMuteWhenTakingBack,
|
||||
titleString: S.of(context).keepMuteWhenTakingBack,
|
||||
),
|
||||
],
|
||||
)
|
||||
: Container(height: 0.0, width: 0.0),
|
||||
SizedBox(height: AppTheme.sizedBoxHeight),
|
||||
Text(S.of(context).accessibility, style: AppTheme.settingsHeaderStyle),
|
||||
SettingsCard(
|
||||
context: context,
|
||||
children: <Widget>[
|
||||
SettingsSwitchListTile(
|
||||
context: context,
|
||||
value: Config.screenReaderSupport,
|
||||
onChanged: setScreenReaderSupport,
|
||||
titleString: S.of(context).screenReaderSupport,
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(height: AppTheme.sizedBoxHeight),
|
||||
Text(S.of(context).restore, style: AppTheme.settingsHeaderStyle),
|
||||
SettingsCard(
|
||||
context: context,
|
||||
children: <Widget>[
|
||||
SettingsListTile(
|
||||
context: context,
|
||||
titleString: S.of(context).restoreDefaultSettings,
|
||||
onTap: restoreFactoryDefaultSettings,
|
||||
),
|
||||
ListItemDivider(),
|
||||
],
|
||||
),
|
||||
SizedBox(height: AppTheme.sizedBoxHeight),
|
||||
Developer.developerModeEnabled
|
||||
? Text(S.of(context).forDevelopers,
|
||||
style: AppTheme.settingsHeaderStyle)
|
||||
: SizedBox(height: 1),
|
||||
Developer.developerModeEnabled
|
||||
? SettingsCard(
|
||||
context: context,
|
||||
children: <Widget>[
|
||||
SettingsSwitchListTile(
|
||||
context: context,
|
||||
value: Config.developerMode,
|
||||
onChanged: setDeveloperMode,
|
||||
titleString: S.of(context).developerMode,
|
||||
),
|
||||
ListItemDivider(),
|
||||
SettingsSwitchListTile(
|
||||
context: context,
|
||||
value: Config.experimentsEnabled,
|
||||
onChanged: setExperimentsEnabled,
|
||||
titleString: S.of(context).experiments,
|
||||
),
|
||||
ListItemDivider(),
|
||||
SettingsSwitchListTile(
|
||||
context: context,
|
||||
value: Config.isAutoRestart,
|
||||
onChanged: setIsAutoRestart,
|
||||
titleString: S.of(context).isAutoRestart,
|
||||
),
|
||||
ListItemDivider(),
|
||||
SettingsListTile(
|
||||
context: context,
|
||||
titleString: S.of(context).environmentVariables,
|
||||
onTap: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => EnvironmentVariablesPage(),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
)
|
||||
: SizedBox(height: 1),
|
||||
];
|
||||
}
|
||||
|
||||
setSkillLevel() async {
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
builder: (BuildContext context) => StatefulBuilder(
|
||||
builder: (context, setState) {
|
||||
return _skillLevelSliderTheme(context, setState);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
setMoveTime() async {
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
builder: (BuildContext context) => StatefulBuilder(
|
||||
builder: (context, setState) {
|
||||
return _moveTimeSliderTheme(context, setState);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
setWhoMovesFirst(bool value) async {
|
||||
setState(() {
|
||||
Config.aiMovesFirst = !value;
|
||||
});
|
||||
|
||||
print("[config] aiMovesFirst: ${Config.aiMovesFirst}");
|
||||
|
||||
Config.save();
|
||||
}
|
||||
|
||||
setAiIsLazy(bool value) async {
|
||||
setState(() {
|
||||
Config.aiIsLazy = value;
|
||||
});
|
||||
|
||||
print("[config] aiMovesFirst: $value");
|
||||
|
||||
Config.save();
|
||||
}
|
||||
|
||||
setAlgorithm() {
|
||||
callback(int? algorithm) async {
|
||||
print("[config] algorithm = $algorithm");
|
||||
|
||||
Navigator.of(context).pop();
|
||||
|
||||
setState(() {
|
||||
Config.algorithm = algorithm ?? 2;
|
||||
});
|
||||
|
||||
print("[config] Config.algorithm: ${Config.algorithm}");
|
||||
|
||||
Config.save();
|
||||
}
|
||||
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
builder: (BuildContext context) => Semantics(
|
||||
label: S.of(context).algorithm,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
RadioListTile(
|
||||
activeColor: AppTheme.switchListTileActiveColor,
|
||||
title: Text('Alpha-Beta'),
|
||||
groupValue: Config.algorithm,
|
||||
value: 0,
|
||||
onChanged: callback,
|
||||
),
|
||||
ListItemDivider(),
|
||||
RadioListTile(
|
||||
activeColor: AppTheme.switchListTileActiveColor,
|
||||
title: Text('PVS'),
|
||||
groupValue: Config.algorithm,
|
||||
value: 1,
|
||||
onChanged: callback,
|
||||
),
|
||||
ListItemDivider(),
|
||||
RadioListTile(
|
||||
activeColor: AppTheme.switchListTileActiveColor,
|
||||
title: Text('MTD(f)'),
|
||||
groupValue: Config.algorithm,
|
||||
value: 2,
|
||||
onChanged: callback,
|
||||
),
|
||||
ListItemDivider(),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
setDrawOnHumanExperience(bool value) async {
|
||||
setState(() {
|
||||
Config.drawOnHumanExperience = value;
|
||||
});
|
||||
|
||||
print("[config] drawOnHumanExperience: $value");
|
||||
|
||||
Config.save();
|
||||
}
|
||||
|
||||
setConsiderMobility(bool value) async {
|
||||
setState(() {
|
||||
Config.considerMobility = value;
|
||||
});
|
||||
|
||||
print("[config] considerMobility: $value");
|
||||
|
||||
Config.save();
|
||||
}
|
||||
|
||||
setIsAutoRestart(bool value) async {
|
||||
setState(() {
|
||||
Config.isAutoRestart = value;
|
||||
});
|
||||
|
||||
print("[config] isAutoRestart: $value");
|
||||
|
||||
Config.save();
|
||||
}
|
||||
|
||||
setIsAutoChangeFirstMove(bool value) async {
|
||||
setState(() {
|
||||
Config.isAutoChangeFirstMove = value;
|
||||
});
|
||||
|
||||
print("[config] isAutoChangeFirstMove: $value");
|
||||
|
||||
Config.save();
|
||||
}
|
||||
|
||||
setResignIfMostLose(bool value) async {
|
||||
setState(() {
|
||||
Config.resignIfMostLose = value;
|
||||
});
|
||||
|
||||
print("[config] resignIfMostLose: $value");
|
||||
|
||||
Config.save();
|
||||
}
|
||||
|
||||
setShufflingEnabled(bool value) async {
|
||||
setState(() {
|
||||
Config.shufflingEnabled = value;
|
||||
});
|
||||
|
||||
print("[config] shufflingEnabled: $value");
|
||||
|
||||
Config.save();
|
||||
}
|
||||
|
||||
setLearnEndgame(bool value) async {
|
||||
setState(() {
|
||||
Config.learnEndgame = value;
|
||||
});
|
||||
|
||||
print("[config] learnEndgame: $value");
|
||||
|
||||
Config.save();
|
||||
}
|
||||
|
||||
setOpeningBook(bool value) async {
|
||||
setState(() {
|
||||
Config.openingBook = value;
|
||||
});
|
||||
|
||||
print("[config] openingBook: $value");
|
||||
|
||||
Config.save();
|
||||
}
|
||||
|
||||
setTone(bool value) async {
|
||||
setState(() {
|
||||
Config.toneEnabled = value;
|
||||
});
|
||||
|
||||
print("[config] toneEnabled: $value");
|
||||
|
||||
Config.save();
|
||||
}
|
||||
|
||||
setKeepMuteWhenTakingBack(bool value) async {
|
||||
setState(() {
|
||||
Config.keepMuteWhenTakingBack = value;
|
||||
});
|
||||
|
||||
print("[config] keepMuteWhenTakingBack: $value");
|
||||
|
||||
Config.save();
|
||||
}
|
||||
|
||||
setScreenReaderSupport(bool value) async {
|
||||
setState(() {
|
||||
Config.screenReaderSupport = value;
|
||||
});
|
||||
|
||||
print("[config] screenReaderSupport: $value");
|
||||
|
||||
Config.save();
|
||||
}
|
||||
|
||||
setDeveloperMode(bool value) async {
|
||||
setState(() {
|
||||
Config.developerMode = value;
|
||||
});
|
||||
|
||||
print("[config] developerMode: $value");
|
||||
|
||||
Config.save();
|
||||
}
|
||||
|
||||
setExperimentsEnabled(bool value) async {
|
||||
setState(() {
|
||||
Config.experimentsEnabled = value;
|
||||
});
|
||||
|
||||
print("[config] experimentsEnabled: $value");
|
||||
|
||||
Config.save();
|
||||
}
|
||||
|
||||
// Display
|
||||
|
||||
setLanguage(String value) async {
|
||||
setState(() {
|
||||
Config.languageCode = value;
|
||||
});
|
||||
|
||||
print("[config] languageCode: $value");
|
||||
|
||||
Config.save();
|
||||
}
|
||||
|
||||
setIsPieceCountInHandShown(bool value) async {
|
||||
setState(() {
|
||||
Config.isPieceCountInHandShown = value;
|
||||
});
|
||||
|
||||
print("[config] isPieceCountInHandShown: $value");
|
||||
|
||||
Config.save();
|
||||
}
|
||||
|
||||
setIsNotationsShown(bool value) async {
|
||||
setState(() {
|
||||
Config.isNotationsShown = value;
|
||||
});
|
||||
|
||||
print("[config] isNotationsShown: $value");
|
||||
|
||||
Config.save();
|
||||
}
|
||||
|
||||
setIsHistoryNavigationToolbarShown(bool value) async {
|
||||
setState(() {
|
||||
Config.isHistoryNavigationToolbarShown = value;
|
||||
});
|
||||
|
||||
print("[config] isHistoryNavigationToolbarShown: $value");
|
||||
|
||||
Config.save();
|
||||
}
|
||||
|
||||
setStandardNotationEnabled(bool value) async {
|
||||
setState(() {
|
||||
Config.standardNotationEnabled = value;
|
||||
});
|
||||
|
||||
print("[config] standardNotationEnabled: $value");
|
||||
|
||||
Config.save();
|
||||
}
|
||||
}
|
|
@ -1,58 +0,0 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:sanmill/common/config.dart';
|
||||
import 'package:sanmill/generated/l10n.dart';
|
||||
import 'package:sanmill/style/app_theme.dart';
|
||||
import 'package:sanmill/style/colors.dart';
|
||||
|
||||
class HelpScreen extends StatefulWidget {
|
||||
@override
|
||||
_HelpScreenState createState() => _HelpScreenState();
|
||||
}
|
||||
|
||||
class _HelpScreenState extends State<HelpScreen> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
color: UIColors.nearlyWhite,
|
||||
child: SafeArea(
|
||||
top: false,
|
||||
child: Scaffold(
|
||||
backgroundColor: Color(Config.darkBackgroundColor),
|
||||
body: ListView(
|
||||
children: <Widget>[
|
||||
SizedBox(height: AppTheme.sizedBoxHeight),
|
||||
Container(
|
||||
padding: const EdgeInsets.only(
|
||||
top: 48, left: 16, right: 16, bottom: 16),
|
||||
child: Text(
|
||||
S.of(context).howToPlay,
|
||||
style: TextStyle(
|
||||
fontSize: Config.fontSize + 4,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: AppTheme.helpTextColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
padding: const EdgeInsets.only(
|
||||
top: 16, left: 16, right: 16, bottom: 16),
|
||||
child: Text(
|
||||
S.of(context).helpContent,
|
||||
style: TextStyle(
|
||||
fontSize: Config.fontSize,
|
||||
color: AppTheme.helpTextColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,363 +0,0 @@
|
|||
/*
|
||||
This file is part of Sanmill.
|
||||
Copyright (C) 2019-2021 The Sanmill developers (see AUTHORS file)
|
||||
|
||||
Sanmill is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Sanmill is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:animated_text_kit/animated_text_kit.dart';
|
||||
import 'package:fluentui_system_icons/fluentui_system_icons.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:sanmill/common/config.dart';
|
||||
import 'package:sanmill/common/constants.dart';
|
||||
import 'package:sanmill/generated/l10n.dart';
|
||||
import 'package:sanmill/l10n/resources.dart';
|
||||
import 'package:sanmill/style/app_theme.dart';
|
||||
import 'package:sanmill/widgets/game_settings_page.dart';
|
||||
|
||||
enum DrawerIndex {
|
||||
humanVsAi,
|
||||
humanVsHuman,
|
||||
aiVsAi,
|
||||
preferences,
|
||||
ruleSettings,
|
||||
personalization,
|
||||
feedback,
|
||||
Help,
|
||||
About
|
||||
}
|
||||
|
||||
class DrawerListItem {
|
||||
DrawerListItem({this.index, this.title = '', this.icon});
|
||||
|
||||
DrawerIndex? index;
|
||||
String title;
|
||||
Icon? icon;
|
||||
}
|
||||
|
||||
class HomeDrawer extends StatefulWidget {
|
||||
const HomeDrawer(
|
||||
{Key? key,
|
||||
this.screenIndex,
|
||||
this.iconAnimationController,
|
||||
this.callBackIndex})
|
||||
: super(key: key);
|
||||
|
||||
final AnimationController? iconAnimationController;
|
||||
final DrawerIndex? screenIndex;
|
||||
final Function(DrawerIndex?)? callBackIndex;
|
||||
|
||||
@override
|
||||
_HomeDrawerState createState() => _HomeDrawerState();
|
||||
}
|
||||
|
||||
class _HomeDrawerState extends State<HomeDrawer> {
|
||||
DateTime? lastTapTime;
|
||||
|
||||
final String tag = "[home_drawer]";
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
List<DrawerListItem> drawerList = <DrawerListItem>[
|
||||
DrawerListItem(
|
||||
index: DrawerIndex.humanVsAi,
|
||||
title: S.of(context).humanVsAi,
|
||||
icon: Icon(FluentIcons.person_24_regular),
|
||||
),
|
||||
DrawerListItem(
|
||||
index: DrawerIndex.humanVsHuman,
|
||||
title: S.of(context).humanVsHuman,
|
||||
icon: Icon(FluentIcons.people_24_regular),
|
||||
),
|
||||
DrawerListItem(
|
||||
index: DrawerIndex.aiVsAi,
|
||||
title: S.of(context).aiVsAi,
|
||||
icon: Icon(FluentIcons.bot_24_regular),
|
||||
),
|
||||
DrawerListItem(
|
||||
index: DrawerIndex.preferences,
|
||||
title: S.of(context).preferences,
|
||||
icon: Icon(FluentIcons.options_24_regular),
|
||||
),
|
||||
DrawerListItem(
|
||||
index: DrawerIndex.ruleSettings,
|
||||
title: S.of(context).ruleSettings,
|
||||
icon: Icon(FluentIcons.task_list_ltr_24_regular),
|
||||
),
|
||||
DrawerListItem(
|
||||
index: DrawerIndex.personalization,
|
||||
title: S.of(context).personalization,
|
||||
icon: Icon(FluentIcons.design_ideas_24_regular),
|
||||
),
|
||||
DrawerListItem(
|
||||
index: DrawerIndex.feedback,
|
||||
title: S.of(context).feedback,
|
||||
icon: Icon(FluentIcons.chat_warning_24_regular),
|
||||
),
|
||||
DrawerListItem(
|
||||
index: DrawerIndex.Help,
|
||||
title: S.of(context).help,
|
||||
icon: Icon(FluentIcons.question_circle_24_regular),
|
||||
),
|
||||
DrawerListItem(
|
||||
index: DrawerIndex.About,
|
||||
title: S.of(context).about,
|
||||
icon: Icon(FluentIcons.info_24_regular),
|
||||
),
|
||||
];
|
||||
|
||||
var rotationTransition = RotationTransition(
|
||||
turns: AlwaysStoppedAnimation<double>(Tween<double>(begin: 0.0, end: 24.0)
|
||||
.animate(CurvedAnimation(
|
||||
parent: widget.iconAnimationController!,
|
||||
curve: Curves.fastOutSlowIn))
|
||||
.value /
|
||||
360),
|
||||
);
|
||||
|
||||
var scaleTransition = ScaleTransition(
|
||||
scale: AlwaysStoppedAnimation<double>(
|
||||
1.0 - (widget.iconAnimationController!.value) * 0.2),
|
||||
child: rotationTransition,
|
||||
);
|
||||
|
||||
var animatedBuilder = AnimatedBuilder(
|
||||
animation: widget.iconAnimationController!,
|
||||
builder: (BuildContext context, Widget? child) {
|
||||
return scaleTransition;
|
||||
},
|
||||
);
|
||||
|
||||
var animatedTextsColors = [
|
||||
Color(Config.drawerTextColor),
|
||||
Colors.black,
|
||||
Colors.blue,
|
||||
Colors.yellow,
|
||||
Colors.red,
|
||||
Color(Config.darkBackgroundColor),
|
||||
Color(Config.boardBackgroundColor),
|
||||
Color(Config.drawerHighlightItemColor),
|
||||
];
|
||||
|
||||
var animatedTextKit = AnimatedTextKit(
|
||||
animatedTexts: [
|
||||
ColorizeAnimatedText(
|
||||
S.of(context).appName,
|
||||
textStyle: TextStyle(
|
||||
fontSize: Config.fontSize + 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
colors: animatedTextsColors,
|
||||
textAlign: TextAlign.start,
|
||||
speed: const Duration(milliseconds: 3000),
|
||||
),
|
||||
],
|
||||
pause: const Duration(milliseconds: 30000),
|
||||
repeatForever: true,
|
||||
stopPauseOnTap: true,
|
||||
onTap: () {
|
||||
if (lastTapTime == null ||
|
||||
DateTime.now().difference(lastTapTime!) > Duration(seconds: 1)) {
|
||||
lastTapTime = DateTime.now();
|
||||
print("$tag Tap again in one second to enable developer mode.");
|
||||
} else {
|
||||
lastTapTime = DateTime.now();
|
||||
Developer.developerModeEnabled = true;
|
||||
print("$tag Developer mode enabled.");
|
||||
}
|
||||
});
|
||||
|
||||
var drawerHeader = Container(
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.only(top: 0.0),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
animatedBuilder,
|
||||
Padding(
|
||||
padding:
|
||||
EdgeInsets.only(top: (isLargeScreen() ? 30 : 8), left: 4),
|
||||
child: ExcludeSemantics(child: animatedTextKit),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
/*
|
||||
var exitListTile = ListTile(
|
||||
title: Text(
|
||||
S.of(context).exit,
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 16,
|
||||
color: AppTheme.exitTextColor,
|
||||
),
|
||||
textAlign: TextAlign.left,
|
||||
),
|
||||
trailing: Icon(
|
||||
FluentIcons.power_24_regular,
|
||||
color: AppTheme.exitIconColor,
|
||||
),
|
||||
onTap: () async {
|
||||
if (Config.developerMode) {
|
||||
return;
|
||||
}
|
||||
|
||||
await SystemChannels.platform.invokeMethod<void>('SystemNavigator.pop');
|
||||
},
|
||||
);
|
||||
*/
|
||||
|
||||
/*
|
||||
var drawFooter = Column(
|
||||
children: <Widget>[
|
||||
exitListTile,
|
||||
SizedBox(height: MediaQuery.of(context).padding.bottom)
|
||||
],
|
||||
);
|
||||
*/
|
||||
|
||||
var scaffold = Scaffold(
|
||||
backgroundColor: Color(Config.drawerBackgroundColor),
|
||||
body: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
drawerHeader,
|
||||
const SizedBox(height: 4),
|
||||
Divider(height: 1, color: AppTheme.drawerDividerColor),
|
||||
Expanded(
|
||||
child: ListView.builder(
|
||||
physics: const BouncingScrollPhysics(),
|
||||
padding: const EdgeInsets.all(0.0),
|
||||
itemCount: drawerList.length,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
return buildInkwell(drawerList[index]);
|
||||
},
|
||||
),
|
||||
),
|
||||
Divider(height: 1, color: AppTheme.drawerDividerColor),
|
||||
//drawFooter,
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
return scaffold;
|
||||
}
|
||||
|
||||
Future<void> navigationToScreen(DrawerIndex? index) async {
|
||||
widget.callBackIndex!(index);
|
||||
}
|
||||
|
||||
Widget buildInkwell(DrawerListItem listItem) {
|
||||
bool ltr = getBidirectionality(context) == Bidirectionality.leftToRight;
|
||||
double radius = 28.0;
|
||||
var animatedBuilder = AnimatedBuilder(
|
||||
animation: widget.iconAnimationController!,
|
||||
builder: (BuildContext context, Widget? child) {
|
||||
return Transform(
|
||||
transform: Matrix4.translationValues(
|
||||
(MediaQuery.of(context).size.width * 0.75 - 64) *
|
||||
(1.0 - widget.iconAnimationController!.value - 1.0),
|
||||
0.0,
|
||||
0.0),
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(top: 8, bottom: 8),
|
||||
child: Container(
|
||||
width: MediaQuery.of(context).size.width * 0.75 - 64,
|
||||
height: 46,
|
||||
decoration: BoxDecoration(
|
||||
color: Color(Config.drawerHighlightItemColor),
|
||||
borderRadius: new BorderRadius.only(
|
||||
topLeft: Radius.circular(ltr ? 0 : radius),
|
||||
topRight: Radius.circular(ltr ? radius : 0),
|
||||
bottomLeft: Radius.circular(ltr ? 0 : radius),
|
||||
bottomRight: Radius.circular(ltr ? radius : 0),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
var listItemIcon = Icon(listItem.icon!.icon,
|
||||
color: widget.screenIndex == listItem.index
|
||||
? Color(Config.drawerTextColor) // TODO: drawerHighlightTextColor
|
||||
: Color(Config.drawerTextColor));
|
||||
|
||||
var stack = Stack(
|
||||
children: <Widget>[
|
||||
Container(
|
||||
padding: const EdgeInsets.only(top: 8.0, bottom: 8.0),
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
Container(
|
||||
width: 6.0,
|
||||
height: 46.0,
|
||||
),
|
||||
const Padding(
|
||||
padding: EdgeInsets.all(4.0),
|
||||
),
|
||||
listItemIcon,
|
||||
const Padding(
|
||||
padding: EdgeInsets.all(4.0),
|
||||
),
|
||||
Text(
|
||||
listItem.title,
|
||||
style: TextStyle(
|
||||
fontWeight: widget.screenIndex == listItem.index
|
||||
? FontWeight.w700
|
||||
: FontWeight.w500,
|
||||
fontSize: Config.fontSize,
|
||||
color: widget.screenIndex == listItem.index
|
||||
? Color(Config
|
||||
.drawerTextColor) // TODO: drawerHighlightTextColor
|
||||
: Color(Config.drawerTextColor),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
widget.screenIndex == listItem.index
|
||||
? animatedBuilder
|
||||
: const SizedBox()
|
||||
],
|
||||
);
|
||||
|
||||
return Material(
|
||||
// Semantics: Main menu item
|
||||
color: Colors.transparent,
|
||||
child: InkWell(
|
||||
splashColor: AppTheme.drawerSplashColor,
|
||||
highlightColor: AppTheme.drawerHighlightColor,
|
||||
onTap: () {
|
||||
navigationToScreen(listItem.index);
|
||||
},
|
||||
child: stack,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,60 +0,0 @@
|
|||
/*
|
||||
This file is part of Sanmill.
|
||||
Copyright (C) 2019-2021 The Sanmill developers (see AUTHORS file)
|
||||
|
||||
Sanmill is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Sanmill is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart' show rootBundle;
|
||||
import 'package:sanmill/common/constants.dart';
|
||||
import 'package:sanmill/generated/l10n.dart';
|
||||
|
||||
class LicenseAgreementPage extends StatefulWidget {
|
||||
@override
|
||||
_LicenseAgreementPageState createState() => _LicenseAgreementPageState();
|
||||
}
|
||||
|
||||
class _LicenseAgreementPageState extends State<LicenseAgreementPage> {
|
||||
String _data = "";
|
||||
|
||||
Future<void> _loadData() async {
|
||||
final _loadedData =
|
||||
await rootBundle.loadString(Constants.gplLicenseFilename);
|
||||
setState(() {
|
||||
_data = _loadedData;
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
_loadData();
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: Text(S.of(context).license), centerTitle: true),
|
||||
body: ListView(
|
||||
children: <Widget>[
|
||||
Container(
|
||||
padding: const EdgeInsets.only(
|
||||
top: 16, left: 16, right: 16, bottom: 16),
|
||||
child: Text(
|
||||
_data != "" ? _data : 'Nothing to show',
|
||||
style: TextStyle(fontFamily: 'Monospace', fontSize: 12),
|
||||
textAlign: TextAlign.left,
|
||||
))
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,145 +0,0 @@
|
|||
/*
|
||||
This file is part of Sanmill.
|
||||
Copyright (C) 2019-2021 The Sanmill developers (see AUTHORS file)
|
||||
|
||||
Sanmill is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Sanmill is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import 'package:fluentui_system_icons/fluentui_system_icons.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:sanmill/generated/l10n.dart';
|
||||
import 'package:sanmill/generated/oss_licenses.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
class FlutterLicense extends LicenseEntry {
|
||||
final List<String> packages;
|
||||
final List<LicenseParagraph> paragraphs;
|
||||
|
||||
FlutterLicense(this.packages, this.paragraphs);
|
||||
}
|
||||
|
||||
/// display all used packages and their license
|
||||
class OssLicensesPage extends StatelessWidget {
|
||||
static Future<List<String>> loadLicenses() async {
|
||||
Stream<LicenseEntry> licenses() async* {
|
||||
yield FlutterLicense([
|
||||
'Sound Effects'
|
||||
], [
|
||||
LicenseParagraph(
|
||||
'CC-0\nhttps://freesound.org/people/unfa/sounds/243749/', 0)
|
||||
]);
|
||||
}
|
||||
|
||||
LicenseRegistry.addLicense(licenses);
|
||||
|
||||
// merging non-dart based dependency list using LicenseRegistry.
|
||||
final ossKeys = ossLicenses.keys.toList();
|
||||
final lm = <String, List<String>>{};
|
||||
await for (var l in LicenseRegistry.licenses) {
|
||||
for (var p in l.packages) {
|
||||
if (!ossKeys.contains(p)) {
|
||||
final lp = lm.putIfAbsent(p, () => []);
|
||||
lp.addAll(l.paragraphs.map((p) => p.text));
|
||||
ossKeys.add(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (var key in lm.keys) {
|
||||
ossLicenses[key] = {'license': lm[key]!.join('\n')};
|
||||
}
|
||||
return ossKeys..sort();
|
||||
}
|
||||
|
||||
static final _licenses = loadLicenses();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(S.of(context).ossLicenses),
|
||||
),
|
||||
body: FutureBuilder<List<String>>(
|
||||
future: _licenses,
|
||||
builder: (context, snapshot) => ListView.separated(
|
||||
padding: const EdgeInsets.all(0),
|
||||
itemCount: snapshot.data?.length ?? 0,
|
||||
itemBuilder: (context, index) {
|
||||
final key = snapshot.data![index];
|
||||
final ossl = ossLicenses[key];
|
||||
final version = ossl['version'];
|
||||
final desc = ossl['description'];
|
||||
return ListTile(
|
||||
title: Text('$key ${version ?? ''}'),
|
||||
subtitle: desc != null ? Text(desc) : null,
|
||||
trailing: Icon(FluentIcons.chevron_right_24_regular),
|
||||
onTap: () => Navigator.of(context).push(MaterialPageRoute(
|
||||
builder: (context) =>
|
||||
MiscOssLicenseSingle(name: key, json: ossl))));
|
||||
},
|
||||
separatorBuilder: (context, index) => const Divider())));
|
||||
}
|
||||
|
||||
class MiscOssLicenseSingle extends StatelessWidget {
|
||||
final String name;
|
||||
final Map<String, dynamic> json;
|
||||
|
||||
String get version => json['version'] == null ? "" : json['version'];
|
||||
String? get description => json['description'];
|
||||
String get licenseText => json['license'];
|
||||
String? get homepage => json['homepage'];
|
||||
|
||||
MiscOssLicenseSingle({required this.name, required this.json});
|
||||
|
||||
String _bodyText() => licenseText.split('\n').map((line) {
|
||||
if (line.startsWith('//')) line = line.substring(2);
|
||||
line = line.trim();
|
||||
return line;
|
||||
}).join('\n');
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => Scaffold(
|
||||
appBar: AppBar(title: Text('$name $version')),
|
||||
body: Container(
|
||||
color: Theme.of(context).canvasColor,
|
||||
child: ListView(children: <Widget>[
|
||||
if (description != null)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
top: 12.0, left: 12.0, right: 12.0),
|
||||
child: Text(description!,
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyText2!
|
||||
.copyWith(fontWeight: FontWeight.bold))),
|
||||
if (homepage != null)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
top: 12.0, left: 12.0, right: 12.0),
|
||||
child: InkWell(
|
||||
child: Text(homepage!,
|
||||
style: const TextStyle(
|
||||
color: Colors.blue,
|
||||
decoration: TextDecoration.underline)),
|
||||
onTap: () => launch(homepage!),
|
||||
)),
|
||||
if (description != null || homepage != null) const Divider(),
|
||||
Padding(
|
||||
padding:
|
||||
const EdgeInsets.only(top: 12.0, left: 12.0, right: 12.0),
|
||||
child: Text(_bodyText(),
|
||||
style: Theme.of(context).textTheme.bodyText2),
|
||||
),
|
||||
])),
|
||||
);
|
||||
}
|
|
@ -1,73 +0,0 @@
|
|||
/*
|
||||
This file is part of Sanmill.
|
||||
Copyright (C) 2019-2021 The Sanmill developers (see AUTHORS file)
|
||||
|
||||
Sanmill is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Sanmill is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_picker/flutter_picker.dart';
|
||||
import 'package:sanmill/common/config.dart';
|
||||
import 'package:sanmill/generated/l10n.dart';
|
||||
import 'package:sanmill/style/app_theme.dart';
|
||||
|
||||
Future<int> showPickerNumber(BuildContext context, int begin, int end,
|
||||
int initValue, String suffixString) async {
|
||||
int selectValue = 0;
|
||||
await Picker(
|
||||
adapter: NumberPickerAdapter(
|
||||
data: [
|
||||
NumberPickerColumn(
|
||||
begin: begin,
|
||||
end: end,
|
||||
initValue: initValue,
|
||||
suffix: Text(
|
||||
suffixString,
|
||||
style: TextStyle(
|
||||
fontSize: Config.fontSize,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
hideHeader: true,
|
||||
title: Text(S.of(context).pleaseSelect,
|
||||
style: TextStyle(
|
||||
color: AppTheme.appPrimaryColor,
|
||||
fontSize: Config.fontSize + 4.0,
|
||||
)),
|
||||
textStyle: TextStyle(
|
||||
color: Colors.black,
|
||||
fontSize: Config.fontSize,
|
||||
),
|
||||
selectedTextStyle: TextStyle(color: AppTheme.appPrimaryColor),
|
||||
cancelText: S.of(context).cancel,
|
||||
cancelTextStyle: TextStyle(
|
||||
color: AppTheme.appPrimaryColor,
|
||||
fontSize: Config.fontSize,
|
||||
),
|
||||
confirmText: S.of(context).confirm,
|
||||
confirmTextStyle: TextStyle(
|
||||
color: AppTheme.appPrimaryColor,
|
||||
fontSize: Config.fontSize,
|
||||
),
|
||||
onConfirm: (Picker picker, List value) async {
|
||||
print(value.toString());
|
||||
var selectValues = picker.getSelectedValues();
|
||||
print(selectValues);
|
||||
selectValue = selectValues[0];
|
||||
}).showDialog(context);
|
||||
|
||||
return selectValue;
|
||||
}
|
|
@ -1,37 +1,37 @@
|
|||
name: sanmill
|
||||
description: Sanmill is a open-source, powerful UCI-like Nine Men's Morris (and its variants) program.
|
||||
publish_to: none
|
||||
|
||||
version: 1.1.38+2196
|
||||
|
||||
environment:
|
||||
sdk: '>=2.12.0 <3.0.0'
|
||||
sdk: ">=2.12.0 <3.0.0"
|
||||
|
||||
dependencies:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
flutter_localizations:
|
||||
sdk: flutter
|
||||
cupertino_icons: ^1.0.3
|
||||
soundpool: ^2.2.0
|
||||
path_provider: ^2.0.5
|
||||
package_info_plus: ^1.0.6
|
||||
uuid: ^3.0.4
|
||||
url_launcher: ^6.0.11
|
||||
intl: 0.17.0
|
||||
animated_text_kit: ^4.1.1
|
||||
catcher: ^0.6.8
|
||||
stack_trace: ^1.10.0
|
||||
|
||||
cupertino_icons: ^1.0.3
|
||||
device_info_plus_platform_interface: ^2.1.0
|
||||
devicelocale: ^0.4.3
|
||||
double_back_to_close_app: ^2.0.1
|
||||
flutter_picker: ^2.0.2
|
||||
flutter_email_sender: ^5.0.2
|
||||
feedback:
|
||||
git:
|
||||
url: git://github.com/calcitem/feedback.git
|
||||
animated_text_kit: ^4.1.1
|
||||
flutter_colorpicker: ^0.6.0
|
||||
device_info_plus_platform_interface: ^2.1.0
|
||||
fluentui_system_icons: ^1.1.140
|
||||
flutter:
|
||||
sdk: flutter
|
||||
flutter_colorpicker: ^0.6.0
|
||||
flutter_email_sender: ^5.0.2
|
||||
flutter_localizations:
|
||||
sdk: flutter
|
||||
flutter_picker: ^2.0.2
|
||||
intl: 0.17.0
|
||||
package_info_plus: ^1.0.6
|
||||
path_provider: ^2.0.5
|
||||
soundpool: ^2.2.0
|
||||
stack_trace: ^1.10.0
|
||||
url_launcher: ^6.0.11
|
||||
uuid: ^3.0.4
|
||||
#pref: ^2.3.0
|
||||
#screen_recorder: ^0.0.2
|
||||
|
||||
|
@ -39,6 +39,7 @@ dev_dependencies:
|
|||
flutter_oss_licenses: ^1.0.1
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
lint: ^1.7.2
|
||||
msix: ^2.1.3
|
||||
|
||||
flutter:
|
||||
|
@ -67,5 +68,5 @@ msix_config:
|
|||
publisher_display_name: Calcitem Studio
|
||||
identity_name: 25314CalcitemStudio.Sanmill
|
||||
publisher: CN=3413C020-B420-4E0A-8687-A2C35E878F3A
|
||||
capabilities: ''
|
||||
capabilities: ""
|
||||
store: true
|
||||
|
|
|
@ -20,7 +20,7 @@ import 'package:flutter/material.dart';
|
|||
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:sanmill/generated/l10n.dart';
|
||||
import 'package:sanmill/widgets/navigation_home_screen.dart';
|
||||
import 'package:sanmill/screens/navigation_home_screen.dart';
|
||||
|
||||
void main() {
|
||||
Widget makeTestableWidget({required Widget child, required Locale locale}) {
|
||||
|
@ -38,11 +38,13 @@ void main() {
|
|||
}
|
||||
|
||||
testWidgets('Widget', (WidgetTester tester) async {
|
||||
NavigationHomeScreen screen = NavigationHomeScreen();
|
||||
await tester.pumpWidget(makeTestableWidget(
|
||||
child: screen,
|
||||
locale: const Locale('en'),
|
||||
));
|
||||
final NavigationHomeScreen screen = NavigationHomeScreen();
|
||||
await tester.pumpWidget(
|
||||
makeTestableWidget(
|
||||
child: screen,
|
||||
locale: const Locale('en'),
|
||||
),
|
||||
);
|
||||
await tester.pump();
|
||||
expect(find.text(S.current.appName), findsOneWidget);
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue