reactive storage and db migration

- make new storage reactive
- add Database Migrator

change how the developerMode reacts
This commit is contained in:
Leptopoda 2021-10-18 11:27:59 +02:00
parent ef2b888410
commit e93f62b455
No known key found for this signature in database
GPG Key ID: 661B059EDE309F11
42 changed files with 2529 additions and 2022 deletions

View File

@ -13,4 +13,3 @@ analyzer:
# generated code
- lib/generated/**
- lib/**/**.g.dart
- lib/l10n/**

View File

@ -25,52 +25,52 @@ import 'package:sanmill/shared/constants.dart';
import 'package:sanmill/shared/list_item_divider.dart';
import 'package:sanmill/shared/theme/app_theme.dart';
Map<String, Strings> languageCodeToStrings = {
"ar": ArabicStrings(),
"bg": BulgarianStrings(),
"bn": BengaliStrings(),
"cs": CzechStrings(),
"da": DanishStrings(),
"de": GermanStrings(),
"de_CH": SwissGermanStrings(),
"el": GreekStrings(),
"en": EnglishStrings(),
"es": SpanishStrings(),
"et": EstonianStrings(),
"fa": FarsiStrings(),
"fi": FinnishStrings(),
"fr": FrenchStrings(),
"gu": GujaratiStrings(),
"hi": HindiStrings(),
"hr": CroatianStrings(),
"hu": HungarianStrings(),
"id": IndonesianStrings(),
"it": ItalianStrings(),
"ja": JapaneseStrings(),
"kn": KannadaStrings(),
"ko": KoreanStrings(),
"lt": LithuanianStrings(),
"lv": LatvianStrings(),
"mk": MacedonianStrings(),
"ms": MalayStrings(),
"nl": DutchStrings(),
"nn": NorwegianStrings(),
"pl": PolishStrings(),
"pt": PortugueseStrings(),
"ro": RomanianStrings(),
"ru": RussianStrings(),
"sk": SlovakStrings(),
"sl": SlovenianStrings(),
"sq": AlbanianStrings(),
"sr": SerbianStrings(),
"sv": SwedishStrings(),
"te": TeluguStrings(),
"th": ThaiStrings(),
"tr": TurkishStrings(),
"uz": UzbekStrings(),
"vi": VietnameseStrings(),
"zh": ChineseStrings(),
"zh_Hant": TraditionalChineseStrings(),
Map<Locale, Strings> languageCodeToStrings = {
const Locale("ar"): ArabicStrings(),
const Locale("bg"): BulgarianStrings(),
const Locale("bn"): BengaliStrings(),
const Locale("cs"): CzechStrings(),
const Locale("da"): DanishStrings(),
const Locale("de"): GermanStrings(),
const Locale("de_CH"): SwissGermanStrings(),
const Locale("el"): GreekStrings(),
const Locale("en"): EnglishStrings(),
const Locale("es"): SpanishStrings(),
const Locale("et"): EstonianStrings(),
const Locale("fa"): FarsiStrings(),
const Locale("fi"): FinnishStrings(),
const Locale("fr"): FrenchStrings(),
const Locale("gu"): GujaratiStrings(),
const Locale("hi"): HindiStrings(),
const Locale("hr"): CroatianStrings(),
const Locale("hu"): HungarianStrings(),
const Locale("id"): IndonesianStrings(),
const Locale("it"): ItalianStrings(),
const Locale("ja"): JapaneseStrings(),
const Locale("kn"): KannadaStrings(),
const Locale("ko"): KoreanStrings(),
const Locale("lt"): LithuanianStrings(),
const Locale("lv"): LatvianStrings(),
const Locale("mk"): MacedonianStrings(),
const Locale("ms"): MalayStrings(),
const Locale("nl"): DutchStrings(),
const Locale("nn"): NorwegianStrings(),
const Locale("pl"): PolishStrings(),
const Locale("pt"): PortugueseStrings(),
const Locale("ro"): RomanianStrings(),
const Locale("ru"): RussianStrings(),
const Locale("sk"): SlovakStrings(),
const Locale("sl"): SlovenianStrings(),
const Locale("sq"): AlbanianStrings(),
const Locale("sr"): SerbianStrings(),
const Locale("sv"): SwedishStrings(),
const Locale("te"): TeluguStrings(),
const Locale("th"): ThaiStrings(),
const Locale("tr"): TurkishStrings(),
const Locale("uz"): UzbekStrings(),
const Locale("vi"): VietnameseStrings(),
const Locale("zh"): ChineseStrings(),
const Locale("zh_Hant"): TraditionalChineseStrings(),
};
/// Interface strings
@ -489,8 +489,8 @@ class TraditionalChineseStrings extends Strings {
String get tapBackAgainToLeave => '再次按 Back 鍵退出';
}
final supportedLocales = [
for (var i in languageCodeToStrings.keys) Locale.fromSubtags(languageCode: i),
final List<Locale> supportedLocales = [
...languageCodeToStrings.keys,
];
class Resources {
@ -514,6 +514,7 @@ class Resources {
return ret;
}
// ignore: prefer_constructors_over_static_methods
static Resources of() {
return Resources();
}
@ -539,7 +540,7 @@ Future<void> setLanguage(
activeColor: AppTheme.switchListTileActiveColor,
title: Text(languageCodeToStrings[i]!.languageName),
groupValue: LocalDatabaseService.display.languageCode,
value: Locale(i),
value: i,
onChanged: callback,
),
],
@ -572,7 +573,7 @@ Bidirectionality getBidirectionality(BuildContext context) {
currentLocale.languageCode == "he" ||
currentLocale.languageCode == "ps" ||
currentLocale.languageCode == "ur") {
print("bidirectionality: RTL");
debugPrint("bidirectionality: RTL");
return Bidirectionality.rightToLeft;
} else {
return Bidirectionality.leftToRight;
@ -593,5 +594,5 @@ void setSpecialCountryAndRegion(BuildContext context) {
break;
}
print("Set Special Country and Region to $specialCountryAndRegion.");
debugPrint("Set Special Country and Region to $specialCountryAndRegion.");
}

View File

@ -31,6 +31,7 @@ import 'package:sanmill/l10n/resources.dart';
import 'package:sanmill/screens/navigation_home_screen.dart';
import 'package:sanmill/services/audios.dart';
import 'package:sanmill/services/storage/storage.dart';
import 'package:sanmill/services/storage/storage_v1.dart';
import 'package:sanmill/shared/constants.dart';
import 'package:sanmill/shared/theme/app_theme.dart';
@ -38,8 +39,9 @@ part 'package:sanmill/services/catcher.dart';
Future<void> main() async {
await LocalDatabaseService.initStorage();
await DatabaseV1.migrateDB();
final catcher = Catcher(
rootWidget: BetterFeedback(
rootWidget: const BetterFeedback(
child: SanmillApp(),
//localeOverride: Locale(Resources.of().languageCode),
),
@ -73,10 +75,11 @@ Future<void> main() async {
RouteObserver<PageRoute> routeObserver = RouteObserver<PageRoute>();
class SanmillApp extends StatelessWidget {
final globalScaffoldKey = GlobalKey<ScaffoldState>();
const SanmillApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
final globalScaffoldKey = GlobalKey<ScaffoldState>();
Audios.loadSounds();
setSpecialCountryAndRegion(context);

View File

@ -18,6 +18,7 @@
import 'package:sanmill/l10n/resources.dart';
// TODO: deprecate this thingy. No reason to keep it
class Rule {
String name = "Default Rule";
String description = "";

View File

@ -16,7 +16,8 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import 'package:flutter/widgets.dart';
import 'package:copy_with_extension/copy_with_extension.dart';
import 'package:flutter/widgets.dart' show Color, immutable;
import 'package:hive_flutter/adapters.dart'
show HiveField, HiveType, BinaryReader, BinaryWriter, TypeAdapter;
import 'package:json_annotation/json_annotation.dart';
@ -25,11 +26,15 @@ import 'package:sanmill/shared/theme/app_theme.dart';
part 'color.g.dart';
// TODO: make AppTheme colors const so this file can be cleaner
/// Color data model
///
/// holds the data needed for the Color Settings
@HiveType(typeId: 0)
@JsonSerializable()
@CopyWith()
@immutable
class ColorSettings {
ColorSettings({
Color? boardLineColor,
@ -67,7 +72,7 @@ class ColorSettings {
this.mainToolbarBackgroundColor =
mainToolbarBackgroundColor ?? AppTheme.mainToolbarBackgroundColor;
this.mainToolbarIconColor =
mainToolbarIconColor ?? AppTheme.mainToolbarBackgroundColor;
mainToolbarIconColor ?? AppTheme.mainToolbarIconColor;
this.navigationToolbarBackgroundColor = navigationToolbarBackgroundColor ??
AppTheme.navigationToolbarBackgroundColor;
this.navigationToolbarIconColor =
@ -78,105 +83,105 @@ class ColorSettings {
toJson: ColorAdapter.colorToJson,
)
@HiveField(0)
late Color boardLineColor;
late final Color boardLineColor;
@JsonKey(
fromJson: ColorAdapter.colorFromJson,
toJson: ColorAdapter.colorToJson,
)
@HiveField(1)
late Color darkBackgroundColor;
late final Color darkBackgroundColor;
@JsonKey(
fromJson: ColorAdapter.colorFromJson,
toJson: ColorAdapter.colorToJson,
)
@HiveField(2)
late Color boardBackgroundColor;
late final Color boardBackgroundColor;
@JsonKey(
fromJson: ColorAdapter.colorFromJson,
toJson: ColorAdapter.colorToJson,
)
@HiveField(3)
late Color whitePieceColor;
late final Color whitePieceColor;
@JsonKey(
fromJson: ColorAdapter.colorFromJson,
toJson: ColorAdapter.colorToJson,
)
@HiveField(4)
late Color blackPieceColor;
late final Color blackPieceColor;
@JsonKey(
fromJson: ColorAdapter.colorFromJson,
toJson: ColorAdapter.colorToJson,
)
@HiveField(5)
late Color pieceHighlightColor;
late final Color pieceHighlightColor;
@JsonKey(
fromJson: ColorAdapter.colorFromJson,
toJson: ColorAdapter.colorToJson,
)
@HiveField(6)
late Color messageColor;
late final Color messageColor;
@JsonKey(
fromJson: ColorAdapter.colorFromJson,
toJson: ColorAdapter.colorToJson,
)
@HiveField(7)
late Color drawerColor;
late final Color drawerColor;
@JsonKey(
fromJson: ColorAdapter.colorFromJson,
toJson: ColorAdapter.colorToJson,
)
@HiveField(8)
late Color drawerBackgroundColor;
late final Color drawerBackgroundColor;
@JsonKey(
fromJson: ColorAdapter.colorFromJson,
toJson: ColorAdapter.colorToJson,
)
@HiveField(9)
late Color drawerTextColor;
late final Color drawerTextColor;
@JsonKey(
fromJson: ColorAdapter.colorFromJson,
toJson: ColorAdapter.colorToJson,
)
@HiveField(10)
late Color drawerHighlightItemColor;
late final Color drawerHighlightItemColor;
@JsonKey(
fromJson: ColorAdapter.colorFromJson,
toJson: ColorAdapter.colorToJson,
)
@HiveField(11)
late Color mainToolbarBackgroundColor;
late final Color mainToolbarBackgroundColor;
@JsonKey(
fromJson: ColorAdapter.colorFromJson,
toJson: ColorAdapter.colorToJson,
)
@HiveField(12)
late Color mainToolbarIconColor;
late final Color mainToolbarIconColor;
@JsonKey(
fromJson: ColorAdapter.colorFromJson,
toJson: ColorAdapter.colorToJson,
)
@HiveField(13)
late Color navigationToolbarBackgroundColor;
late final Color navigationToolbarBackgroundColor;
@JsonKey(
fromJson: ColorAdapter.colorFromJson,
toJson: ColorAdapter.colorToJson,
)
@HiveField(14)
late Color navigationToolbarIconColor;
late final Color navigationToolbarIconColor;
/// encodes a Json style map Coloro a [ColorSettings] obbject
factory ColorSettings.fromJson(Map<String, dynamic> json) =>

View File

@ -16,7 +16,8 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import 'package:flutter/material.dart';
import 'package:copy_with_extension/copy_with_extension.dart';
import 'package:flutter/material.dart' show Locale, immutable;
import 'package:hive_flutter/adapters.dart';
import 'package:json_annotation/json_annotation.dart';
import 'package:sanmill/services/storage/adapters/locale_adapter.dart';
@ -29,6 +30,8 @@ part 'display.g.dart';
/// holds the data needed for the Display Settings
@HiveType(typeId: 1)
@JsonSerializable()
@CopyWith()
@immutable
class Display {
Display({
this.languageCode = Constants.defaultLocale,
@ -54,43 +57,43 @@ class Display {
fromJson: LocaleAdapter.colorFromJson,
toJson: LocaleAdapter.colorToJson,
)
Locale languageCode;
final Locale languageCode;
@HiveField(1)
bool standardNotationEnabled;
final bool standardNotationEnabled;
@HiveField(2)
bool isPieceCountInHandShown;
final bool isPieceCountInHandShown;
@HiveField(3)
bool isNotationsShown;
final bool isNotationsShown;
@HiveField(4)
bool isHistoryNavigationToolbarShown;
final bool isHistoryNavigationToolbarShown;
@HiveField(5)
double boardBorderLineWidth;
final double boardBorderLineWidth;
@HiveField(6)
double boardInnerLineWidth;
final double boardInnerLineWidth;
@HiveField(7)
int pointStyle;
final int pointStyle;
@HiveField(8)
double pointWidth;
final double pointWidth;
@HiveField(9)
double pieceWidth;
final double pieceWidth;
@HiveField(10)
double fontSize;
final double fontSize;
@HiveField(11)
late double boardTop;
late final double boardTop;
@HiveField(12)
double animationDuration;
final double animationDuration;
/// encodes a Json style map into a [Display] obbject
factory Display.fromJson(Map<String, dynamic> json) =>

View File

@ -16,6 +16,8 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import 'package:copy_with_extension/copy_with_extension.dart';
import 'package:flutter/foundation.dart' show immutable;
import 'package:hive_flutter/adapters.dart';
import 'package:json_annotation/json_annotation.dart';
@ -26,8 +28,10 @@ part 'preferences.g.dart';
/// holds the data needed for the normal Settings
@HiveType(typeId: 2)
@JsonSerializable()
@CopyWith()
@immutable
class Preferences {
Preferences({
const Preferences({
this.isPrivacyPolicyAccepted = false,
this.usesHiveDB = false,
this.toneEnabled = true,
@ -51,46 +55,46 @@ class Preferences {
});
@HiveField(0)
bool isPrivacyPolicyAccepted;
final bool isPrivacyPolicyAccepted;
@HiveField(1)
bool usesHiveDB;
final bool usesHiveDB;
@HiveField(2)
bool toneEnabled;
final bool toneEnabled;
@HiveField(3)
bool keepMuteWhenTakingBack;
final bool keepMuteWhenTakingBack;
@HiveField(4)
bool screenReaderSupport;
final bool screenReaderSupport;
@HiveField(5)
bool aiMovesFirst;
final bool aiMovesFirst;
@HiveField(6)
bool aiIsLazy;
final bool aiIsLazy;
@HiveField(7)
int skillLevel;
final int skillLevel;
@HiveField(8)
int moveTime;
final int moveTime;
@HiveField(9)
bool isAutoRestart;
final bool isAutoRestart;
@HiveField(10)
bool isAutoChangeFirstMove;
final bool isAutoChangeFirstMove;
@HiveField(11)
bool resignIfMostLose;
final bool resignIfMostLose;
@HiveField(12)
bool shufflingEnabled;
final bool shufflingEnabled;
@HiveField(13)
bool learnEndgame;
final bool learnEndgame;
@HiveField(14)
bool openingBook;
final bool openingBook;
@HiveField(15)
int algorithm;
final int algorithm;
@HiveField(16)
bool drawOnHumanExperience;
final bool drawOnHumanExperience;
@HiveField(17)
bool considerMobility;
final bool considerMobility;
@HiveField(18)
bool developerMode;
final bool developerMode;
@HiveField(19)
bool experimentsEnabled;
final bool experimentsEnabled;
/// encodes a Json style map into a [Preferences] obbject
factory Preferences.fromJson(Map<String, dynamic> json) =>

View File

@ -16,6 +16,8 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import 'package:copy_with_extension/copy_with_extension.dart';
import 'package:flutter/foundation.dart' show immutable;
import 'package:hive_flutter/adapters.dart';
import 'package:json_annotation/json_annotation.dart';
import 'package:sanmill/l10n/resources.dart';
@ -27,6 +29,8 @@ part 'rules.g.dart';
/// holds the data needed for the Rules Settings
@HiveType(typeId: 3)
@JsonSerializable()
@CopyWith()
@immutable
class Rules {
Rules({
int? piecesCount,
@ -53,37 +57,37 @@ class Rules {
}
@HiveField(0)
late int piecesCount;
late final int piecesCount;
@HiveField(1)
int flyPieceCount;
final int flyPieceCount;
@HiveField(2)
int piecesAtLeastCount;
final int piecesAtLeastCount;
@HiveField(3)
late bool hasDiagonalLines;
late final bool hasDiagonalLines;
@HiveField(4)
bool hasBannedLocations;
final bool hasBannedLocations;
@HiveField(5)
bool mayMoveInPlacingPhase;
final bool mayMoveInPlacingPhase;
@HiveField(6)
bool isDefenderMoveFirst;
final bool isDefenderMoveFirst;
@HiveField(7)
bool mayRemoveMultiple;
final bool mayRemoveMultiple;
@HiveField(8)
bool mayRemoveFromMillsAlways;
final bool mayRemoveFromMillsAlways;
@HiveField(9)
bool mayOnlyRemoveUnplacedPieceInPlacingPhase;
final bool mayOnlyRemoveUnplacedPieceInPlacingPhase;
@HiveField(10)
bool isWhiteLoseButNotDrawWhenBoardFull;
final bool isWhiteLoseButNotDrawWhenBoardFull;
@HiveField(11)
bool isLoseButNotChangeSideWhenNoWay;
final bool isLoseButNotChangeSideWhenNoWay;
@HiveField(12)
bool mayFly;
final bool mayFly;
@HiveField(13)
int nMoveRule;
final int nMoveRule;
@HiveField(14)
int endgameNMoveRule;
final int endgameNMoveRule;
@HiveField(15)
bool threefoldRepetitionRule;
final bool threefoldRepetitionRule;
/// encodes a Json style map into a [Rules] obbject
factory Rules.fromJson(Map<String, dynamic> json) => _$RulesFromJson(json);

View File

@ -0,0 +1,27 @@
/*
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/>.
*/
/// Temporary data model
///
/// holds temporary runtime data that isn't yet or shouldn't be saved to the LocalDatabase
class Temp {
const Temp._();
/// represents a temporary value for Preferences.developerMode
static bool developerMode = false;
}

View File

@ -22,6 +22,8 @@ import 'package:sanmill/generated/l10n.dart';
import 'package:sanmill/shared/constants.dart';
class EnvironmentVariablesPage extends StatelessWidget {
const EnvironmentVariablesPage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return FutureBuilder<String>(

View File

@ -30,7 +30,8 @@ import 'package:sanmill/mill/game.dart';
import 'package:sanmill/mill/position.dart';
import 'package:sanmill/mill/rule.dart';
import 'package:sanmill/mill/types.dart';
import 'package:sanmill/screens/game_settings_page.dart';
import 'package:sanmill/models/preferences.dart';
import 'package:sanmill/screens/game_settings/game_settings_page.dart';
import 'package:sanmill/services/audios.dart';
import 'package:sanmill/services/engine/engine.dart';
import 'package:sanmill/services/engine/native_engine.dart';
@ -1186,9 +1187,8 @@ class _GamePageState extends State<GamePage>
}
Future<void> setPrivacyPolicyAccepted(bool value) async {
setState(
() => LocalDatabaseService.preferences.isPrivacyPolicyAccepted = value,
);
LocalDatabaseService.preferences = LocalDatabaseService.preferences
.copyWith(isPrivacyPolicyAccepted: value);
debugPrint("[config] isPrivacyPolicyAccepted: $value");
}
@ -1341,12 +1341,13 @@ class _GamePageState extends State<GamePage>
),
onPressed: () async {
if (!isTopLevel) {
LocalDatabaseService.preferences.skillLevel++;
final _pref = LocalDatabaseService.preferences;
LocalDatabaseService.preferences =
_pref.copyWith(skillLevel: _pref.skillLevel + 1);
debugPrint(
"[config] skillLevel: ${LocalDatabaseService.preferences.skillLevel}",
);
}
await _engine.setOptions(context);
debugPrint(
"[config] skillLevel: ${LocalDatabaseService.preferences.skillLevel}",
);
Navigator.pop(context);
},
),
@ -1785,11 +1786,17 @@ class _GamePageState extends State<GamePage>
gameInstance.init();
_engine.startup();
timer = Timer.periodic(const Duration(microseconds: 100), (Timer t) {
_setReadyState();
});
timer = Timer.periodic(
const Duration(microseconds: 100),
(_) => _setReadyState(),
);
_initAnimation();
LocalDatabaseService.listenPreferences.addListener(() async {
await _engine.setOptions();
debugPrint("$tag reloaded engine options");
});
}
@override
@ -1852,7 +1859,7 @@ class _GamePageState extends State<GamePage>
Future<void> didPush() async {
final route = ModalRoute.of(context)!.settings.name;
debugPrint('$tag Game Page didPush route: $route');
await _engine.setOptions(context);
await _engine.setOptions();
if (LocalDatabaseService.display.languageCode != Constants.defaultLocale) {
S.load(LocalDatabaseService.display.languageCode);
setState(() {});
@ -1863,7 +1870,7 @@ class _GamePageState extends State<GamePage>
Future<void> didPopNext() async {
final route = ModalRoute.of(context)!.settings.name;
debugPrint('$tag Game Page didPopNext route: $route');
await _engine.setOptions(context);
await _engine.setOptions();
if (LocalDatabaseService.display.languageCode != Constants.defaultLocale) {
S.load(LocalDatabaseService.display.languageCode);
}
@ -1873,7 +1880,7 @@ class _GamePageState extends State<GamePage>
Future<void> didPushNext() async {
final route = ModalRoute.of(context)!.settings.name;
debugPrint('$tag Game Page didPushNext route: $route');
await _engine.setOptions(context);
await _engine.setOptions();
if (LocalDatabaseService.display.languageCode != Constants.defaultLocale) {
S.load(LocalDatabaseService.display.languageCode);
}

View File

@ -0,0 +1,63 @@
/*
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_settings/game_settings_page.dart';
class _AlgorithmModal extends StatelessWidget {
const _AlgorithmModal({
Key? key,
required this.algorithm,
required this.onChanged,
}) : super(key: key);
final int algorithm;
final Function(int?)? onChanged;
@override
Widget build(BuildContext context) {
return Semantics(
label: S.of(context).algorithm,
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
RadioListTile(
activeColor: AppTheme.switchListTileActiveColor,
title: const Text('Alpha-Beta'),
groupValue: algorithm,
value: 0,
onChanged: onChanged,
),
RadioListTile(
activeColor: AppTheme.switchListTileActiveColor,
title: const Text('PVS'),
groupValue: algorithm,
value: 1,
onChanged: onChanged,
),
RadioListTile(
activeColor: AppTheme.switchListTileActiveColor,
title: const Text('MTD(f)'),
groupValue: algorithm,
value: 2,
onChanged: onChanged,
),
],
),
);
}
}

View File

@ -0,0 +1,381 @@
/*
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:hive_flutter/hive_flutter.dart' show Box;
import 'package:sanmill/generated/l10n.dart';
import 'package:sanmill/models/display.dart';
import 'package:sanmill/models/preferences.dart';
import 'package:sanmill/models/temporary.dart';
import 'package:sanmill/screens/env_page.dart';
import 'package:sanmill/services/storage/storage.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';
part 'package:sanmill/screens/game_settings/algorithm_modal.dart';
part 'package:sanmill/screens/game_settings/reset_settings_alert.dart';
part 'package:sanmill/screens/game_settings/skill_level_slider.dart';
part 'package:sanmill/screens/game_settings/move_time_slider.dart';
class GameSettingsPage extends StatelessWidget {
static const List<String> _algorithmNames = ['Alpha-Beta', 'PVS', 'MTD(f)'];
static const String _tag = "[game_settings_page]";
// Restore
void _restoreFactoryDefaultSettings(BuildContext context) => showDialog(
context: context,
builder: (_) => const _ResetSettingsAlert(),
);
void _setSkillLevel(BuildContext context) => showModalBottomSheet(
context: context,
builder: (_) => const _SkillLevelSlider(),
);
void _setMoveTime(BuildContext context) => showModalBottomSheet(
context: context,
builder: (_) => const _MoveTimeSlider(),
);
void _setWhoMovesFirst(Preferences _preferences, bool value) {
LocalDatabaseService.preferences =
_preferences.copyWith(aiMovesFirst: value);
debugPrint("$_tag aiMovesFirst: $value");
}
void _setAiIsLazy(Preferences _preferences, bool value) {
LocalDatabaseService.preferences = _preferences.copyWith(aiIsLazy: value);
debugPrint("$_tag aiMovesFirst: $value");
}
void _setAlgorithm(BuildContext context, Preferences _preferences) {
void _callback(int? algorithm) {
Navigator.pop(context);
LocalDatabaseService.preferences =
_preferences.copyWith(algorithm: algorithm);
debugPrint("$_tag algorithm = $algorithm");
}
showModalBottomSheet(
context: context,
builder: (_) => _AlgorithmModal(
algorithm: _preferences.algorithm,
onChanged: _callback,
),
);
}
void _setDrawOnHumanExperience(Preferences _preferences, bool value) {
LocalDatabaseService.preferences =
_preferences.copyWith(drawOnHumanExperience: value);
debugPrint("$_tag drawOnHumanExperience: $value");
}
void _setConsiderMobility(Preferences _preferences, bool value) {
LocalDatabaseService.preferences =
_preferences.copyWith(considerMobility: value);
debugPrint("$_tag considerMobility: $value");
}
void _setIsAutoRestart(Preferences _preferences, bool value) {
LocalDatabaseService.preferences =
_preferences.copyWith(isAutoRestart: value);
debugPrint("$_tag isAutoRestart: $value");
}
void _setIsAutoChangeFirstMove(Preferences _preferences, bool value) {
LocalDatabaseService.preferences =
_preferences.copyWith(isAutoChangeFirstMove: value);
debugPrint("$_tag isAutoChangeFirstMove: $value");
}
void _setResignIfMostLose(Preferences _preferences, bool value) {
LocalDatabaseService.preferences =
_preferences.copyWith(resignIfMostLose: value);
debugPrint("$_tag resignIfMostLose: $value");
}
void _setShufflingEnabled(Preferences _preferences, bool value) {
LocalDatabaseService.preferences =
_preferences.copyWith(shufflingEnabled: value);
debugPrint("$_tag shufflingEnabled: $value");
}
void _setLearnEndgame(Preferences _preferences, bool value) {
LocalDatabaseService.preferences =
_preferences.copyWith(learnEndgame: value);
debugPrint("$_tag learnEndgame: $value");
}
void _setOpeningBook(Preferences _preferences, bool value) {
LocalDatabaseService.preferences =
_preferences.copyWith(openingBook: value);
debugPrint("$_tag openingBook: $value");
}
void _setTone(Preferences _preferences, bool value) {
LocalDatabaseService.preferences =
_preferences.copyWith(toneEnabled: value);
debugPrint("$_tag toneEnabled: $value");
}
void _setKeepMuteWhenTakingBack(Preferences _preferences, bool value) {
LocalDatabaseService.preferences =
_preferences.copyWith(keepMuteWhenTakingBack: value);
debugPrint("$_tag keepMuteWhenTakingBack: $value");
}
void _setScreenReaderSupport(Preferences _preferences, bool value) {
LocalDatabaseService.preferences =
_preferences.copyWith(screenReaderSupport: value);
debugPrint("$_tag screenReaderSupport: $value");
}
void _setDeveloperMode(Preferences _preferences, bool value) {
LocalDatabaseService.preferences =
_preferences.copyWith(developerMode: value);
debugPrint("$_tag developerMode: $value");
}
void _setExperimentsEnabled(Preferences _preferences, bool value) {
LocalDatabaseService.preferences =
_preferences.copyWith(experimentsEnabled: value);
debugPrint("$_tag experimentsEnabled: $value");
}
// Display
void _setLanguage(Display _display, Locale value) {
LocalDatabaseService.display = _display.copyWith(languageCode: value);
debugPrint("$_tag languageCode: $value");
}
void _setIsPieceCountInHandShown(Display _display, bool value) {
LocalDatabaseService.display =
_display.copyWith(isPieceCountInHandShown: value);
debugPrint("$_tag isPieceCountInHandShown: $value");
}
void _setIsNotationsShown(Display _display, bool value) {
LocalDatabaseService.display = _display.copyWith(isNotationsShown: value);
debugPrint("$_tag isNotationsShown: $value");
}
void _setIsHistoryNavigationToolbarShown(Display _display, bool value) {
LocalDatabaseService.display =
_display.copyWith(isHistoryNavigationToolbarShown: value);
debugPrint("$_tag isHistoryNavigationToolbarShown: $value");
}
void _setStandardNotationEnabled(Display _display, bool value) {
LocalDatabaseService.display =
_display.copyWith(standardNotationEnabled: value);
debugPrint("$_tag standardNotationEnabled: $value");
}
Column _buildPrefs(BuildContext context, Box<Preferences> prefBox, _) {
final Preferences _preferences = prefBox.get(
LocalDatabaseService.preferencesKey,
defaultValue: const Preferences(),
)!;
final _widowsSettings = [
const SizedBox(height: AppTheme.sizedBoxHeight),
Text(S.of(context).playSounds, style: AppTheme.settingsHeaderStyle),
SettingsCard(
children: <Widget>[
SettingsSwitchListTile(
value: _preferences.toneEnabled,
onChanged: (val) => _setTone(_preferences, val),
titleString: S.of(context).playSoundsInTheGame,
),
SettingsSwitchListTile(
value: _preferences.keepMuteWhenTakingBack,
onChanged: (val) => _setKeepMuteWhenTakingBack(_preferences, val),
titleString: S.of(context).keepMuteWhenTakingBack,
),
],
),
];
final _developerSettings = [
const SizedBox(height: AppTheme.sizedBoxHeight),
Text(S.of(context).forDevelopers, style: AppTheme.settingsHeaderStyle),
SettingsCard(
children: <Widget>[
SettingsSwitchListTile(
value: _preferences.developerMode,
onChanged: (val) => _setDeveloperMode(_preferences, val),
titleString: S.of(context).developerMode,
),
SettingsSwitchListTile(
value: _preferences.experimentsEnabled,
onChanged: (val) => _setExperimentsEnabled(_preferences, val),
titleString: S.of(context).experiments,
),
SettingsSwitchListTile(
value: _preferences.isAutoRestart,
onChanged: (val) => _setIsAutoRestart(_preferences, val),
titleString: S.of(context).isAutoRestart,
),
SettingsListTile(
titleString: S.of(context).environmentVariables,
onTap: () => Navigator.push(
context,
MaterialPageRoute(
builder: (_) => const EnvironmentVariablesPage(),
),
),
),
],
),
];
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(S.of(context).whoMovesFirst, style: AppTheme.settingsHeaderStyle),
SettingsCard(
children: <Widget>[
SettingsSwitchListTile(
value: !_preferences.aiMovesFirst,
onChanged: (val) => _setWhoMovesFirst(_preferences, !val),
titleString: _preferences.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" + LocalDatabaseService.preferences.skillLevel.toString(),
onTap: () => _setSkillLevel(context),
),
SettingsListTile(
titleString: S.of(context).moveTime,
onTap: () => _setMoveTime(context),
),
],
),
const SizedBox(height: AppTheme.sizedBoxHeight),
Text(S.of(context).aisPlayStyle, style: AppTheme.settingsHeaderStyle),
SettingsCard(
children: <Widget>[
SettingsListTile(
titleString: S.of(context).algorithm,
trailingString: _algorithmNames[_preferences.algorithm],
onTap: () => _setAlgorithm(context, _preferences),
),
SettingsSwitchListTile(
value: _preferences.drawOnHumanExperience,
onChanged: (val) => _setDrawOnHumanExperience(_preferences, val),
titleString: S.of(context).drawOnHumanExperience,
),
SettingsSwitchListTile(
value: _preferences.considerMobility,
onChanged: (val) => _setConsiderMobility(_preferences, val),
titleString: S.of(context).considerMobility,
),
SettingsSwitchListTile(
value: _preferences.aiIsLazy,
onChanged: (val) => _setAiIsLazy(_preferences, val),
titleString: S.of(context).passive,
),
SettingsSwitchListTile(
value: _preferences.shufflingEnabled,
onChanged: (val) => _setShufflingEnabled(_preferences, val),
titleString: S.of(context).shufflingEnabled,
),
],
),
if (!Platform.isWindows) ..._widowsSettings,
const SizedBox(height: AppTheme.sizedBoxHeight),
Text(S.of(context).accessibility, style: AppTheme.settingsHeaderStyle),
SettingsCard(
children: <Widget>[
SettingsSwitchListTile(
value: _preferences.screenReaderSupport,
onChanged: (val) => _setScreenReaderSupport(_preferences, val),
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(context),
),
],
),
if (Temp.developerMode) ..._developerSettings,
],
);
}
@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: ValueListenableBuilder(
valueListenable: LocalDatabaseService.listenPreferences,
builder: _buildPrefs,
),
),
);
}
}

View File

@ -0,0 +1,55 @@
/*
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_settings/game_settings_page.dart';
class _MoveTimeSlider extends StatelessWidget {
const _MoveTimeSlider({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return SliderTheme(
data: AppTheme.sliderThemeData,
child: Semantics(
label: S.of(context).moveTime,
child: ValueListenableBuilder(
valueListenable: LocalDatabaseService.listenPreferences,
builder: (context, Box<Preferences> prefBox, _) {
final Preferences _preferences = prefBox.get(
LocalDatabaseService.preferencesKey,
defaultValue: const Preferences(),
)!;
return Slider(
value: LocalDatabaseService.preferences.moveTime.toDouble(),
max: 60,
divisions: 60,
label: LocalDatabaseService.preferences.moveTime.toString(),
onChanged: (value) {
LocalDatabaseService.preferences =
_preferences.copyWith(moveTime: value.toInt());
debugPrint("Move time Slider value: $value");
},
);
},
),
),
);
}
}

View File

@ -0,0 +1,77 @@
/*
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_settings/game_settings_page.dart';
class _ResetSettingsAlert extends StatelessWidget {
const _ResetSettingsAlert({Key? key}) : super(key: key);
void cancel(BuildContext context) => Navigator.pop(context);
Future<void> _restore(BuildContext context) async {
Navigator.pop(context);
if (!LocalDatabaseService.preferences.developerMode) {
await LocalDatabaseService.resetStorage();
}
}
@override
Widget build(BuildContext context) {
// TODO: remove these strings as they aren't needed anymore
//S.of(context).exitApp;
//S.of(context).exitAppManually
return AlertDialog(
title: Text(
S.of(context).restore,
style: TextStyle(
color: AppTheme.dialogTitleColor,
fontSize: LocalDatabaseService.display.fontSize + 4,
),
),
content: SingleChildScrollView(
child: Text(
"${S.of(context).restoreDefaultSettings}?",
style: TextStyle(
fontSize: LocalDatabaseService.display.fontSize,
),
),
),
actions: <Widget>[
TextButton(
onPressed: () => _restore(context),
child: Text(
S.of(context).ok,
style: TextStyle(
fontSize: LocalDatabaseService.display.fontSize,
),
),
),
TextButton(
onPressed: () => cancel(context),
child: Text(
S.of(context).cancel,
style: TextStyle(
fontSize: LocalDatabaseService.display.fontSize,
),
),
),
],
);
}
}

View File

@ -0,0 +1,55 @@
/*
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_settings/game_settings_page.dart';
class _SkillLevelSlider extends StatelessWidget {
const _SkillLevelSlider({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return SliderTheme(
data: AppTheme.sliderThemeData,
child: Semantics(
label: S.of(context).skillLevel,
child: ValueListenableBuilder(
valueListenable: LocalDatabaseService.listenPreferences,
builder: (context, Box<Preferences> prefBox, _) {
final Preferences _preferences = prefBox.get(
LocalDatabaseService.preferencesKey,
defaultValue: const Preferences(),
)!;
return Slider(
value: _preferences.skillLevel.toDouble(),
min: 1,
max: 30,
divisions: 29,
label: _preferences.skillLevel.toString(),
onChanged: (value) {
LocalDatabaseService.preferences =
_preferences.copyWith(skillLevel: value.toInt());
debugPrint("Skill level Slider value: $value");
},
);
},
),
),
);
}
}

View File

@ -1,535 +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/generated/l10n.dart';
import 'package:sanmill/screens/env_page.dart';
import 'package:sanmill/services/storage/storage.dart';
import 'package:sanmill/services/storage/storage_v1.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: LocalDatabaseService.preferences.skillLevel.toDouble(),
min: 1,
max: 30,
divisions: 29,
label: LocalDatabaseService.preferences.skillLevel.toString(),
onChanged: (value) => setState(() {
debugPrint("[config] Slider value: $value");
LocalDatabaseService.preferences.skillLevel = value.toInt();
}),
),
),
);
}
SliderTheme _moveTimeSliderTheme(BuildContext context, Function setState) {
return SliderTheme(
data: AppTheme.sliderThemeData,
child: Semantics(
label: S.of(context).moveTime,
child: Slider(
value: LocalDatabaseService.preferences.moveTime.toDouble(),
max: 60,
divisions: 60,
label: LocalDatabaseService.preferences.moveTime.toString(),
onChanged: (value) => setState(() {
debugPrint("[config] Slider value: $value");
LocalDatabaseService.preferences.moveTime = value.toInt();
}),
),
),
);
}
// 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: LocalDatabaseService.display.fontSize + 4,
),
),
content: SingleChildScrollView(
child: Text(
"${S.of(context).restoreDefaultSettings}?\n$prompt",
style: TextStyle(
fontSize: LocalDatabaseService.display.fontSize,
),
),
),
actions: <Widget>[
TextButton(
onPressed: confirm,
child: Text(
S.of(context).ok,
style: TextStyle(
fontSize: LocalDatabaseService.display.fontSize,
),
),
),
TextButton(
onPressed: cancel,
child: Text(
S.of(context).cancel,
style: TextStyle(
fontSize: LocalDatabaseService.display.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: !LocalDatabaseService.preferences.aiMovesFirst,
onChanged: setWhoMovesFirst,
titleString: LocalDatabaseService.preferences.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" + LocalDatabaseService.preferences.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[LocalDatabaseService.preferences.algorithm],
onTap: setAlgorithm,
),
SettingsSwitchListTile(
value: LocalDatabaseService.preferences.drawOnHumanExperience,
onChanged: setDrawOnHumanExperience,
titleString: S.of(context).drawOnHumanExperience,
),
SettingsSwitchListTile(
value: LocalDatabaseService.preferences.considerMobility,
onChanged: setConsiderMobility,
titleString: S.of(context).considerMobility,
),
SettingsSwitchListTile(
value: LocalDatabaseService.preferences.aiIsLazy,
onChanged: setAiIsLazy,
titleString: S.of(context).passive,
),
SettingsSwitchListTile(
value: LocalDatabaseService.preferences.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: LocalDatabaseService.preferences.toneEnabled,
onChanged: setTone,
titleString: S.of(context).playSoundsInTheGame,
),
SettingsSwitchListTile(
value: LocalDatabaseService.preferences.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: LocalDatabaseService.preferences.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: LocalDatabaseService.preferences.developerMode,
onChanged: setDeveloperMode,
titleString: S.of(context).developerMode,
),
SettingsSwitchListTile(
value: LocalDatabaseService.preferences.experimentsEnabled,
onChanged: setExperimentsEnabled,
titleString: S.of(context).experiments,
),
SettingsSwitchListTile(
value: LocalDatabaseService.preferences.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(() => LocalDatabaseService.preferences.aiMovesFirst = !value);
debugPrint(
"[config] aiMovesFirst: ${LocalDatabaseService.preferences.aiMovesFirst}",
);
}
Future<void> setAiIsLazy(bool value) async {
setState(() => LocalDatabaseService.preferences.aiIsLazy = value);
debugPrint("[config] aiMovesFirst: $value");
}
void setAlgorithm() {
Future<void> callback(int? algorithm) async {
debugPrint("[config] algorithm = $algorithm");
Navigator.pop(context);
setState(
() => LocalDatabaseService.preferences.algorithm = algorithm ?? 2,
);
debugPrint(
"[config] LocalDatabaseService.preferences.algorithm: ${LocalDatabaseService.preferences.algorithm}",
);
}
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: LocalDatabaseService.preferences.algorithm,
value: 0,
onChanged: callback,
),
RadioListTile(
activeColor: AppTheme.switchListTileActiveColor,
title: const Text('PVS'),
groupValue: LocalDatabaseService.preferences.algorithm,
value: 1,
onChanged: callback,
),
RadioListTile(
activeColor: AppTheme.switchListTileActiveColor,
title: const Text('MTD(f)'),
groupValue: LocalDatabaseService.preferences.algorithm,
value: 2,
onChanged: callback,
),
],
),
),
);
}
Future<void> setDrawOnHumanExperience(bool value) async {
setState(
() => LocalDatabaseService.preferences.drawOnHumanExperience = value,
);
debugPrint("[config] drawOnHumanExperience: $value");
}
Future<void> setConsiderMobility(bool value) async {
setState(() => LocalDatabaseService.preferences.considerMobility = value);
debugPrint("[config] considerMobility: $value");
}
Future<void> setIsAutoRestart(bool value) async {
setState(() => LocalDatabaseService.preferences.isAutoRestart = value);
debugPrint("[config] isAutoRestart: $value");
}
Future<void> setIsAutoChangeFirstMove(bool value) async {
setState(
() => LocalDatabaseService.preferences.isAutoChangeFirstMove = value,
);
debugPrint("[config] isAutoChangeFirstMove: $value");
}
Future<void> setResignIfMostLose(bool value) async {
setState(() => LocalDatabaseService.preferences.resignIfMostLose = value);
debugPrint("[config] resignIfMostLose: $value");
}
Future<void> setShufflingEnabled(bool value) async {
setState(() => LocalDatabaseService.preferences.shufflingEnabled = value);
debugPrint("[config] shufflingEnabled: $value");
}
Future<void> setLearnEndgame(bool value) async {
setState(() => LocalDatabaseService.preferences.learnEndgame = value);
debugPrint("[config] learnEndgame: $value");
}
Future<void> setOpeningBook(bool value) async {
setState(() => LocalDatabaseService.preferences.openingBook = value);
debugPrint("[config] openingBook: $value");
}
Future<void> setTone(bool value) async {
setState(() => LocalDatabaseService.preferences.toneEnabled = value);
debugPrint("[config] toneEnabled: $value");
}
Future<void> setKeepMuteWhenTakingBack(bool value) async {
setState(
() => LocalDatabaseService.preferences.keepMuteWhenTakingBack = value,
);
debugPrint("[config] keepMuteWhenTakingBack: $value");
}
Future<void> setScreenReaderSupport(bool value) async {
setState(
() => LocalDatabaseService.preferences.screenReaderSupport = value,
);
debugPrint("[config] screenReaderSupport: $value");
}
Future<void> setDeveloperMode(bool value) async {
setState(() => LocalDatabaseService.preferences.developerMode = value);
debugPrint("[config] developerMode: $value");
}
Future<void> setExperimentsEnabled(bool value) async {
setState(() => LocalDatabaseService.preferences.experimentsEnabled = value);
debugPrint("[config] experimentsEnabled: $value");
}
// Display
Future<void> setLanguage(Locale value) async {
setState(() => LocalDatabaseService.display.languageCode = value);
debugPrint("[config] languageCode: $value");
}
Future<void> setIsPieceCountInHandShown(bool value) async {
setState(
() => LocalDatabaseService.display.isPieceCountInHandShown = value,
);
debugPrint("[config] isPieceCountInHandShown: $value");
}
Future<void> setIsNotationsShown(bool value) async {
setState(() => LocalDatabaseService.display.isNotationsShown = value);
debugPrint("[config] isNotationsShown: $value");
}
Future<void> setIsHistoryNavigationToolbarShown(bool value) async {
setState(
() =>
LocalDatabaseService.display.isHistoryNavigationToolbarShown = value,
);
debugPrint("[config] isHistoryNavigationToolbarShown: $value");
}
Future<void> setStandardNotationEnabled(bool value) async {
setState(
() => LocalDatabaseService.display.standardNotationEnabled = value,
);
debugPrint("[config] standardNotationEnabled: $value");
}
}

View File

@ -178,10 +178,16 @@ class _DrawerHeader extends StatelessWidget {
final AnimationController iconAnimationController;
static const String _tag = "[home_drawer]";
void _enableDeveloperMode() {
Temp.developerMode = true;
debugPrint("$_tag Developer mode enabled.");
}
@override
Widget build(BuildContext context) {
const String tag = "[home_drawer]";
final List<Color> animatedTextsColors = [
LocalDatabaseService.colorSettings.drawerTextColor,
Colors.black,
@ -220,6 +226,7 @@ class _DrawerHeader extends StatelessWidget {
);
final animation = GestureDetector(
onDoubleTap: _enableDeveloperMode,
child: AnimatedTextKit(
animatedTexts: [
ColorizeAnimatedText(
@ -235,12 +242,8 @@ class _DrawerHeader extends StatelessWidget {
pause: const Duration(seconds: 3),
repeatForever: true,
stopPauseOnTap: true,
onTap: () => debugPrint("$tag DoubleTap to enable developer mode."),
onTap: () => debugPrint("$_tag DoubleTap to enable developer mode."),
),
onDoubleTap: () {
Developer.developerModeEnabled = true;
debugPrint("$tag Developer mode enabled.");
},
);
return Padding(

View File

@ -29,12 +29,13 @@ import 'package:path_provider/path_provider.dart';
import 'package:sanmill/generated/l10n.dart';
import 'package:sanmill/l10n/resources.dart';
import 'package:sanmill/mill/game.dart';
import 'package:sanmill/models/temporary.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/game_settings/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/screens/personalization_settings/personalization_settings_page.dart';
import 'package:sanmill/screens/rule_settings/rule_settings_page.dart';
import 'package:sanmill/services/engine/engine.dart';
import 'package:sanmill/services/storage/storage.dart';
import 'package:sanmill/shared/constants.dart';
@ -98,7 +99,7 @@ class _NavigationHomeScreenState extends State<NavigationHomeScreen> {
} else if (drawerIndex == DrawerIndex.preferences) {
screenView = GameSettingsPage();
} else if (drawerIndex == DrawerIndex.ruleSettings) {
screenView = RuleSettingsPage();
screenView = const RuleSettingsPage();
} else if (drawerIndex == DrawerIndex.personalization) {
screenView = PersonalizationSettingsPage();
} else if (drawerIndex == DrawerIndex.feedback &&

View File

@ -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/>.
*/
part of 'package:sanmill/screens/personalization_settings/personalization_settings_page.dart';
class _AnimationDurationSlider extends StatelessWidget {
const _AnimationDurationSlider({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return SliderTheme(
data: AppTheme.sliderThemeData,
child: Semantics(
label: S.of(context).animationDuration,
child: ValueListenableBuilder(
valueListenable: LocalDatabaseService.listenDisplay,
builder: (context, Box<Display> displayaBox, _) {
final Display _display = displayaBox.get(
LocalDatabaseService.colorSettingsKey,
defaultValue: Display(),
)!;
return Slider(
value: _display.animationDuration,
max: 5.0,
divisions: 50,
label: _display.animationDuration.toStringAsFixed(1),
onChanged: (value) {
debugPrint("[config] AnimationDuration value: $value");
LocalDatabaseService.display =
_display.copyWith(animationDuration: value);
},
);
},
),
),
);
}
}

View File

@ -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/>.
*/
part of 'package:sanmill/screens/personalization_settings/personalization_settings_page.dart';
class _BoardBorderWidthSlider extends StatelessWidget {
const _BoardBorderWidthSlider({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return SliderTheme(
data: AppTheme.sliderThemeData,
child: Semantics(
label: S.of(context).boardBorderLineWidth,
child: ValueListenableBuilder(
valueListenable: LocalDatabaseService.listenDisplay,
builder: (context, Box<Display> displayaBox, _) {
final Display _display = displayaBox.get(
LocalDatabaseService.colorSettingsKey,
defaultValue: Display(),
)!;
return Slider(
value: _display.boardBorderLineWidth,
max: 20.0,
divisions: 200,
label: _display.boardBorderLineWidth.toStringAsFixed(1),
onChanged: (value) {
debugPrint("[config] BoardBorderLineWidth value: $value");
LocalDatabaseService.display =
_display.copyWith(boardBorderLineWidth: value);
},
);
},
),
),
);
}
}

View File

@ -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/>.
*/
part of 'package:sanmill/screens/personalization_settings/personalization_settings_page.dart';
class _BoardInnerWidthSlider extends StatelessWidget {
const _BoardInnerWidthSlider({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return SliderTheme(
data: AppTheme.sliderThemeData,
child: Semantics(
label: S.of(context).boardInnerLineWidth,
child: ValueListenableBuilder(
valueListenable: LocalDatabaseService.listenDisplay,
builder: (context, Box<Display> displayaBox, _) {
final Display _display = displayaBox.get(
LocalDatabaseService.colorSettingsKey,
defaultValue: Display(),
)!;
return Slider(
value: _display.boardInnerLineWidth,
max: 20,
divisions: 200,
label: _display.boardInnerLineWidth.toStringAsFixed(1),
onChanged: (value) {
debugPrint("[config] BoardInnerLineWidth value: $value");
LocalDatabaseService.display =
_display.copyWith(boardInnerLineWidth: value);
},
);
},
),
),
);
}
}

View File

@ -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/>.
*/
part of 'package:sanmill/screens/personalization_settings/personalization_settings_page.dart';
class _BoardTopSlider extends StatelessWidget {
const _BoardTopSlider({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return SliderTheme(
data: AppTheme.sliderThemeData,
child: Semantics(
label: S.of(context).boardTop,
child: ValueListenableBuilder(
valueListenable: LocalDatabaseService.listenDisplay,
builder: (context, Box<Display> displayaBox, _) {
final Display _display = displayaBox.get(
LocalDatabaseService.colorSettingsKey,
defaultValue: Display(),
)!;
return Slider(
value: _display.boardTop,
max: 288.0,
divisions: 288,
label: _display.boardTop.toStringAsFixed(1),
onChanged: (value) {
debugPrint("[config] AnimationDuration value: $value");
LocalDatabaseService.display =
_display.copyWith(boardTop: value);
},
);
},
),
),
);
}
}

View File

@ -0,0 +1,123 @@
/*
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/personalization_settings/personalization_settings_page.dart';
class _ColorSelectorListTile extends StatelessWidget {
const _ColorSelectorListTile({
Key? key,
required this.value,
required this.title,
required this.onChanged,
}) : super(key: key);
final Color value;
final String title;
final Function(Color) onChanged;
Future<void> showColorDialog(BuildContext context) async {
// show the dialog
showDialog(
context: context,
builder: (_) => _ColorPickerAlert(
title: title,
value: value,
onChanged: onChanged,
),
);
}
@override
Widget build(BuildContext context) {
return SettingsListTile(
titleString: title,
trailingColor: value,
onTap: () => showColorDialog(context),
);
}
}
class _ColorPickerAlert extends StatefulWidget {
const _ColorPickerAlert({
Key? key,
required this.value,
required this.title,
required this.onChanged,
}) : super(key: key);
final Color value;
final String title;
final Function(Color) onChanged;
@override
_ColorPickerAlertState createState() => _ColorPickerAlertState();
}
class _ColorPickerAlertState extends State<_ColorPickerAlert> {
late Color pickedColor;
void changeColor(Color color) => setState(() => pickedColor = color);
@override
void initState() {
pickedColor = widget.value;
super.initState();
}
@override
Widget build(BuildContext context) {
return AlertDialog(
title: Text(
"${S.of(context).pick} ${widget.title}",
style: TextStyle(
fontSize: LocalDatabaseService.display.fontSize + 4,
),
),
content: SingleChildScrollView(
child: ColorPicker(
pickerColor: pickedColor,
onColorChanged: changeColor,
),
),
actions: <Widget>[
TextButton(
child: Text(
S.of(context).confirm,
style: TextStyle(
fontSize: LocalDatabaseService.display.fontSize,
),
),
onPressed: () {
debugPrint("[config] pickerColor.value: ${pickedColor.value}");
widget.onChanged(pickedColor);
Navigator.pop(context);
},
),
TextButton(
child: Text(
S.of(context).cancel,
style: TextStyle(
fontSize: LocalDatabaseService.display.fontSize,
),
),
onPressed: () => Navigator.pop(context),
),
],
);
}
}

View File

@ -0,0 +1,55 @@
/*
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/personalization_settings/personalization_settings_page.dart';
class _FontSizeSlider extends StatelessWidget {
const _FontSizeSlider({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return SliderTheme(
data: AppTheme.sliderThemeData,
child: Semantics(
label: S.of(context).fontSize,
child: ValueListenableBuilder(
valueListenable: LocalDatabaseService.listenDisplay,
builder: (context, Box<Display> displayaBox, _) {
final Display _display = displayaBox.get(
LocalDatabaseService.colorSettingsKey,
defaultValue: Display(),
)!;
return Slider(
value: _display.fontSize,
min: 16,
max: 32,
divisions: 16,
label: _display.fontSize.toStringAsFixed(1),
onChanged: (value) {
debugPrint("[config] fontSize value: $value");
LocalDatabaseService.display =
_display.copyWith(fontSize: value);
},
);
},
),
),
);
}
}

View File

@ -0,0 +1,322 @@
/*
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_colorpicker/flutter_colorpicker.dart';
import 'package:hive_flutter/hive_flutter.dart' show Box;
import 'package:sanmill/generated/l10n.dart';
import 'package:sanmill/l10n/resources.dart';
import 'package:sanmill/models/color.dart';
import 'package:sanmill/models/display.dart';
import 'package:sanmill/services/storage/storage.dart';
import 'package:sanmill/shared/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';
part 'package:sanmill/screens/personalization_settings/color_selector_list_tile.dart';
part 'package:sanmill/screens/personalization_settings/animation_duration_slider.dart';
part 'package:sanmill/screens/personalization_settings/board_top_slider.dart';
part 'package:sanmill/screens/personalization_settings/board_boarder_line_width_slider.dart';
part 'package:sanmill/screens/personalization_settings/board_inner_line_width_slider.dart';
part 'package:sanmill/screens/personalization_settings/font_size_slider.dart';
part 'package:sanmill/screens/personalization_settings/point_width_slider.dart';
part 'package:sanmill/screens/personalization_settings/piece_width_slider.dart';
part 'package:sanmill/screens/personalization_settings/point_style_modal.dart';
class PersonalizationSettingsPage extends StatelessWidget {
void setBoardBorderLineWidth(BuildContext context) => showModalBottomSheet(
context: context,
builder: (_) => const _BoardBorderWidthSlider(),
);
void setBoardInnerLineWidth(BuildContext context) => showModalBottomSheet(
context: context,
builder: (_) => const _BoardInnerWidthSlider(),
);
void setPointStyle(BuildContext context, Display _display) {
void _callback(int? pointStyle) {
Navigator.pop(context);
LocalDatabaseService.display = _display.copyWith(pointStyle: pointStyle);
debugPrint("[config] pointStyle: $pointStyle");
}
showModalBottomSheet(
context: context,
builder: (_) => _PointStyleModal(
pointStyle: _display.pointStyle,
onChanged: _callback,
),
);
}
void setPointWidth(BuildContext context) => showModalBottomSheet(
context: context,
builder: (_) => const _PointWidthSlider(),
);
void setPieceWidth(BuildContext context) => showModalBottomSheet(
context: context,
builder: (_) => const _PieceWidthSlider(),
);
void setFontSize(BuildContext context) => showModalBottomSheet(
context: context,
builder: (_) => const _FontSizeSlider(),
);
void setBoardTop(BuildContext context) => showModalBottomSheet(
context: context,
builder: (_) => const _BoardTopSlider(),
);
void setAnimationDuration(BuildContext context) => showModalBottomSheet(
context: context,
builder: (_) => const _AnimationDurationSlider(),
);
void langCallback(BuildContext context, Display _display, [Locale? locale]) {
Navigator.pop(context);
LocalDatabaseService.display = _display.copyWith(languageCode: locale);
debugPrint("[config] languageCode = $locale");
}
Widget _buildColor(BuildContext context, Box<ColorSettings> colorBox, _) {
final ColorSettings _colorSettings = colorBox.get(
LocalDatabaseService.colorSettingsKey,
defaultValue: ColorSettings(),
)!;
return SettingsCard(
children: <Widget>[
_ColorSelectorListTile(
title: S.of(context).boardColor,
value: _colorSettings.boardBackgroundColor,
onChanged: (val) => LocalDatabaseService.colorSettings =
_colorSettings.copyWith(boardBackgroundColor: val),
),
_ColorSelectorListTile(
title: S.of(context).backgroundColor,
value: _colorSettings.darkBackgroundColor,
onChanged: (val) => LocalDatabaseService.colorSettings =
_colorSettings.copyWith(darkBackgroundColor: val),
),
_ColorSelectorListTile(
title: S.of(context).lineColor,
value: _colorSettings.boardLineColor,
onChanged: (val) => LocalDatabaseService.colorSettings =
_colorSettings.copyWith(boardLineColor: val),
),
_ColorSelectorListTile(
title: S.of(context).whitePieceColor,
value: _colorSettings.whitePieceColor,
onChanged: (val) => LocalDatabaseService.colorSettings =
_colorSettings.copyWith(whitePieceColor: val),
),
_ColorSelectorListTile(
title: S.of(context).blackPieceColor,
value: _colorSettings.blackPieceColor,
onChanged: (val) => LocalDatabaseService.colorSettings =
_colorSettings.copyWith(blackPieceColor: val),
),
_ColorSelectorListTile(
title: S.of(context).pieceHighlightColor,
value: _colorSettings.pieceHighlightColor,
onChanged: (val) => LocalDatabaseService.colorSettings =
_colorSettings.copyWith(pieceHighlightColor: val),
),
_ColorSelectorListTile(
title: S.of(context).messageColor,
value: _colorSettings.messageColor,
onChanged: (val) => LocalDatabaseService.colorSettings =
_colorSettings.copyWith(messageColor: val),
),
_ColorSelectorListTile(
title: S.of(context).drawerColor,
value: _colorSettings.drawerColor,
onChanged: (val) => LocalDatabaseService.colorSettings =
_colorSettings.copyWith(drawerColor: val),
),
_ColorSelectorListTile(
title: S.of(context).drawerBackgroundColor,
value: _colorSettings.drawerBackgroundColor,
onChanged: (val) => LocalDatabaseService.colorSettings =
_colorSettings.copyWith(drawerBackgroundColor: val),
),
_ColorSelectorListTile(
title: S.of(context).drawerTextColor,
value: _colorSettings.drawerTextColor,
onChanged: (val) => LocalDatabaseService.colorSettings =
_colorSettings.copyWith(drawerTextColor: val),
),
_ColorSelectorListTile(
title: S.of(context).drawerHighlightItemColor,
value: _colorSettings.drawerHighlightItemColor,
onChanged: (val) =>
LocalDatabaseService.colorSettings = _colorSettings.copyWith(
drawerHighlightItemColor: val,
),
),
_ColorSelectorListTile(
title: S.of(context).mainToolbarBackgroundColor,
value: _colorSettings.mainToolbarBackgroundColor,
onChanged: (val) =>
LocalDatabaseService.colorSettings = _colorSettings.copyWith(
mainToolbarBackgroundColor: val,
),
),
_ColorSelectorListTile(
title: S.of(context).mainToolbarIconColor,
value: _colorSettings.mainToolbarIconColor,
onChanged: (val) => LocalDatabaseService.colorSettings =
_colorSettings.copyWith(mainToolbarIconColor: val),
),
_ColorSelectorListTile(
title: S.of(context).navigationToolbarBackgroundColor,
value: _colorSettings.navigationToolbarBackgroundColor,
onChanged: (val) =>
LocalDatabaseService.colorSettings = _colorSettings.copyWith(
navigationToolbarBackgroundColor: val,
),
),
_ColorSelectorListTile(
title: S.of(context).navigationToolbarIconColor,
value: _colorSettings.navigationToolbarIconColor,
onChanged: (val) =>
LocalDatabaseService.colorSettings = _colorSettings.copyWith(
navigationToolbarIconColor: val,
),
),
],
);
}
Widget _buildDisplay(BuildContext context, Box<Display> displayaBox, _) {
final Display _display = displayaBox.get(
LocalDatabaseService.colorSettingsKey,
defaultValue: Display(),
)!;
return SettingsCard(
children: <Widget>[
SettingsListTile(
titleString: S.of(context).language,
trailingString: LocalDatabaseService.display.languageCode !=
Constants.defaultLocale
? languageCodeToStrings[_display.languageCode]!.languageName
: '',
onTap: () => setLanguage(
context,
(locale) => langCallback(context, _display, locale),
),
),
SettingsSwitchListTile(
value: _display.isPieceCountInHandShown,
onChanged: (val) => LocalDatabaseService.display =
_display.copyWith(isPieceCountInHandShown: val),
titleString: S.of(context).isPieceCountInHandShown,
),
SettingsSwitchListTile(
value: _display.isNotationsShown,
onChanged: (val) => LocalDatabaseService.display =
_display.copyWith(isNotationsShown: val),
titleString: S.of(context).isNotationsShown,
),
SettingsSwitchListTile(
value: _display.isHistoryNavigationToolbarShown,
onChanged: (val) => LocalDatabaseService.display =
_display.copyWith(isHistoryNavigationToolbarShown: val),
titleString: S.of(context).isHistoryNavigationToolbarShown,
),
SettingsListTile(
titleString: S.of(context).boardBorderLineWidth,
onTap: () => setBoardBorderLineWidth(context),
),
SettingsListTile(
titleString: S.of(context).boardInnerLineWidth,
onTap: () => setBoardInnerLineWidth(context),
),
SettingsListTile(
titleString: S.of(context).pointStyle,
onTap: () => setPointStyle(context, _display),
),
SettingsListTile(
titleString: S.of(context).pointWidth,
onTap: () => setPointWidth(context),
),
SettingsListTile(
titleString: S.of(context).pieceWidth,
onTap: () => setPieceWidth(context),
),
SettingsListTile(
titleString: S.of(context).fontSize,
onTap: () => setFontSize(context),
),
SettingsListTile(
titleString: S.of(context).boardTop,
onTap: () => setBoardTop(context),
),
SettingsListTile(
titleString: S.of(context).animationDuration,
onTap: () => setAnimationDuration(context),
),
SettingsSwitchListTile(
value: LocalDatabaseService.display.standardNotationEnabled,
onChanged: (val) => LocalDatabaseService.display =
_display.copyWith(standardNotationEnabled: val),
titleString: S.of(context).standardNotation,
),
],
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: AppTheme.lightBackgroundColor,
appBar: AppBar(
centerTitle: true,
title: Text(S.of(context).personalization),
),
body: SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(S.of(context).display, style: AppTheme.settingsHeaderStyle),
ValueListenableBuilder(
valueListenable: LocalDatabaseService.listenDisplay,
builder: _buildDisplay,
),
const SizedBox(height: AppTheme.sizedBoxHeight),
Text(S.of(context).color, style: AppTheme.settingsHeaderStyle),
ValueListenableBuilder(
valueListenable: LocalDatabaseService.listenColorSettings,
builder: _buildColor,
),
],
),
),
);
}
}

View File

@ -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/>.
*/
part of 'package:sanmill/screens/personalization_settings/personalization_settings_page.dart';
class _PieceWidthSlider extends StatelessWidget {
const _PieceWidthSlider({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return SliderTheme(
data: AppTheme.sliderThemeData,
child: Semantics(
label: S.of(context).pieceWidth,
child: ValueListenableBuilder(
valueListenable: LocalDatabaseService.listenDisplay,
builder: (context, Box<Display> displayaBox, _) {
final Display _display = displayaBox.get(
LocalDatabaseService.colorSettingsKey,
defaultValue: Display(),
)!;
return Slider(
value: _display.pieceWidth,
min: 0.5,
divisions: 50,
label: _display.pieceWidth.toStringAsFixed(1),
onChanged: (value) {
debugPrint("[config] pieceWidth value: $value");
LocalDatabaseService.display =
_display.copyWith(pieceWidth: value);
},
);
},
),
),
);
}
}

View File

@ -0,0 +1,65 @@
/*
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/personalization_settings/personalization_settings_page.dart';
class _PointStyleModal extends StatelessWidget {
const _PointStyleModal({
Key? key,
required this.pointStyle,
required this.onChanged,
}) : super(key: key);
final int pointStyle;
final Function(int?)? onChanged;
@override
Widget build(BuildContext context) {
return Semantics(
label: S.of(context).pointStyle,
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
RadioListTile(
activeColor: AppTheme.switchListTileActiveColor,
title: Text(S.of(context).none),
groupValue: pointStyle,
value: 0,
onChanged: onChanged,
),
RadioListTile(
activeColor: AppTheme.switchListTileActiveColor,
title: Text(S.of(context).solid),
groupValue: pointStyle,
value: 1,
onChanged: onChanged,
),
/*
RadioListTile(
activeColor: AppTheme.switchListTileActiveColor,
title: const Text(S.of(context).hollow),
groupValue: LocalDatabaseService.display.pointStyle,
value: 2,
onChanged: callback,
),
*/
],
),
);
}
}

View File

@ -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/>.
*/
part of 'package:sanmill/screens/personalization_settings/personalization_settings_page.dart';
class _PointWidthSlider extends StatelessWidget {
const _PointWidthSlider({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return SliderTheme(
data: AppTheme.sliderThemeData,
child: Semantics(
label: S.of(context).pointWidth,
child: ValueListenableBuilder(
valueListenable: LocalDatabaseService.listenDisplay,
builder: (context, Box<Display> displayaBox, _) {
final Display _display = displayaBox.get(
LocalDatabaseService.colorSettingsKey,
defaultValue: Display(),
)!;
return Slider(
value: _display.pointWidth,
max: 30.0,
divisions: 30,
label: _display.pointWidth.toStringAsFixed(1),
onChanged: (value) {
debugPrint("[config] pointWidth value: $value");
LocalDatabaseService.display =
_display.copyWith(pointWidth: value);
},
);
},
),
),
);
}
}

View File

@ -1,672 +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_colorpicker/flutter_colorpicker.dart';
import 'package:sanmill/generated/l10n.dart';
import 'package:sanmill/l10n/resources.dart';
import 'package:sanmill/services/storage/storage.dart';
import 'package:sanmill/shared/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
_PersonalizationSettingsPageState createState() =>
_PersonalizationSettingsPageState();
}
class _PersonalizationSettingsPageState
extends State<PersonalizationSettingsPage> {
// create some values
Color pickerColor = const Color(0xFF808080);
Color currentColor = const Color(0xFF808080);
// ValueChanged<Color> callback
void changeColor(Color color) {
setState(() => pickerColor = color);
}
Future<void> showColorDialog(String colorString) async {
final Map<String, Color> colorStrToVal = {
S.of(context).boardColor:
LocalDatabaseService.colorSettings.boardBackgroundColor,
S.of(context).backgroundColor:
LocalDatabaseService.colorSettings.darkBackgroundColor,
S.of(context).lineColor:
LocalDatabaseService.colorSettings.boardLineColor,
S.of(context).whitePieceColor:
LocalDatabaseService.colorSettings.whitePieceColor,
S.of(context).blackPieceColor:
LocalDatabaseService.colorSettings.blackPieceColor,
S.of(context).pieceHighlightColor:
LocalDatabaseService.colorSettings.pieceHighlightColor,
S.of(context).messageColor:
LocalDatabaseService.colorSettings.messageColor,
S.of(context).drawerColor: LocalDatabaseService.colorSettings.drawerColor,
S.of(context).drawerBackgroundColor:
LocalDatabaseService.colorSettings.drawerBackgroundColor,
S.of(context).drawerTextColor:
LocalDatabaseService.colorSettings.drawerTextColor,
S.of(context).drawerHighlightItemColor:
LocalDatabaseService.colorSettings.drawerHighlightItemColor,
S.of(context).mainToolbarBackgroundColor:
LocalDatabaseService.colorSettings.mainToolbarBackgroundColor,
S.of(context).mainToolbarIconColor:
LocalDatabaseService.colorSettings.mainToolbarIconColor,
S.of(context).navigationToolbarBackgroundColor:
LocalDatabaseService.colorSettings.navigationToolbarBackgroundColor,
S.of(context).navigationToolbarIconColor:
LocalDatabaseService.colorSettings.navigationToolbarIconColor,
};
final AlertDialog alert = AlertDialog(
title: Text(
"${S.of(context).pick} $colorString",
style: TextStyle(
fontSize: LocalDatabaseService.display.fontSize + 4,
),
),
content: SingleChildScrollView(
child: ColorPicker(
pickerColor: colorStrToVal[colorString]!,
onColorChanged: changeColor,
),
),
actions: <Widget>[
TextButton(
child: Text(
S.of(context).confirm,
style: TextStyle(
fontSize: LocalDatabaseService.display.fontSize,
),
),
onPressed: () {
setState(() => currentColor = pickerColor);
debugPrint("[config] pickerColor.value: ${pickerColor.value}");
if (colorString == S.of(context).boardColor) {
LocalDatabaseService.colorSettings.boardBackgroundColor =
pickerColor;
} else if (colorString == S.of(context).backgroundColor) {
LocalDatabaseService.colorSettings.darkBackgroundColor =
pickerColor;
} else if (colorString == S.of(context).lineColor) {
LocalDatabaseService.colorSettings.boardLineColor = pickerColor;
} else if (colorString == S.of(context).whitePieceColor) {
LocalDatabaseService.colorSettings.whitePieceColor = pickerColor;
} else if (colorString == S.of(context).blackPieceColor) {
LocalDatabaseService.colorSettings.blackPieceColor = pickerColor;
} else if (colorString == S.of(context).pieceHighlightColor) {
LocalDatabaseService.colorSettings.pieceHighlightColor =
pickerColor;
} else if (colorString == S.of(context).messageColor) {
LocalDatabaseService.colorSettings.messageColor = pickerColor;
} else if (colorString == S.of(context).drawerColor) {
LocalDatabaseService.colorSettings.drawerColor = pickerColor;
} else if (colorString == S.of(context).drawerBackgroundColor) {
LocalDatabaseService.colorSettings.drawerBackgroundColor =
pickerColor;
} else if (colorString == S.of(context).drawerTextColor) {
LocalDatabaseService.colorSettings.drawerTextColor = pickerColor;
} else if (colorString == S.of(context).drawerHighlightItemColor) {
LocalDatabaseService.colorSettings.drawerHighlightItemColor =
pickerColor;
} else if (colorString ==
S.of(context).mainToolbarBackgroundColor) {
LocalDatabaseService.colorSettings.mainToolbarBackgroundColor =
pickerColor;
} else if (colorString == S.of(context).mainToolbarIconColor) {
LocalDatabaseService.colorSettings.mainToolbarIconColor =
pickerColor;
} else if (colorString ==
S.of(context).navigationToolbarBackgroundColor) {
LocalDatabaseService
.colorSettings.navigationToolbarBackgroundColor = pickerColor;
} else if (colorString ==
S.of(context).navigationToolbarIconColor) {
LocalDatabaseService.colorSettings.navigationToolbarIconColor =
pickerColor;
}
Navigator.pop(context);
},
),
TextButton(
child: Text(
S.of(context).cancel,
style: TextStyle(
fontSize: LocalDatabaseService.display.fontSize,
),
),
onPressed: () {
Navigator.pop(context);
},
),
],
);
// show the dialog
showDialog(
context: context,
builder: (BuildContext context) {
return alert;
},
);
}
SliderTheme _boardBorderLineWidthSliderTheme(
BuildContext context,
Function setState,
) {
return SliderTheme(
data: AppTheme.sliderThemeData,
child: Semantics(
label: S.of(context).boardBorderLineWidth,
child: Slider(
value: LocalDatabaseService.display.boardBorderLineWidth,
max: 20.0,
divisions: 200,
label: LocalDatabaseService.display.boardBorderLineWidth
.toStringAsFixed(1),
onChanged: (value) {
setState(() {
debugPrint("[config] BoardBorderLineWidth value: $value");
LocalDatabaseService.display.boardBorderLineWidth = value;
});
},
),
),
);
}
Future<void> setBoardBorderLineWidth() async {
showModalBottomSheet(
context: context,
builder: (_) => StatefulBuilder(
builder: _boardBorderLineWidthSliderTheme,
),
);
}
SliderTheme _boardInnerLineWidthSliderTheme(
BuildContext context,
Function setState,
) {
return SliderTheme(
data: AppTheme.sliderThemeData,
child: Semantics(
label: S.of(context).boardInnerLineWidth,
child: Slider(
value: LocalDatabaseService.display.boardInnerLineWidth,
max: 20.0,
divisions: 200,
label: LocalDatabaseService.display.boardInnerLineWidth
.toStringAsFixed(1),
onChanged: (value) {
setState(() {
debugPrint("[config] BoardInnerLineWidth value: $value");
LocalDatabaseService.display.boardInnerLineWidth = value;
});
},
),
),
);
}
Future<void> setBoardInnerLineWidth() async {
showModalBottomSheet(
context: context,
builder: (_) => StatefulBuilder(
builder: _boardInnerLineWidthSliderTheme,
),
);
}
void setPointStyle() {
Future<void> callback(int? pointStyle) async {
Navigator.pop(context);
setState(
() => LocalDatabaseService.display.pointStyle = pointStyle ?? 0,
);
debugPrint("[config] pointStyle: $pointStyle");
}
showModalBottomSheet(
context: context,
builder: (BuildContext context) => Semantics(
label: S.of(context).pointStyle,
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
RadioListTile(
activeColor: AppTheme.switchListTileActiveColor,
title: Text(S.of(context).none),
groupValue: LocalDatabaseService.display.pointStyle,
value: 0,
onChanged: callback,
),
RadioListTile(
activeColor: AppTheme.switchListTileActiveColor,
title: Text(S.of(context).solid),
groupValue: LocalDatabaseService.display.pointStyle,
value: 1,
onChanged: callback,
),
/*
RadioListTile(
activeColor: AppTheme.switchListTileActiveColor,
title: const Text(S.of(context).hollow),
groupValue: LocalDatabaseService.display.pointStyle,
value: 2,
onChanged: callback,
),
*/
],
),
),
);
}
SliderTheme _pointWidthSliderTheme(BuildContext context, Function setState) {
return SliderTheme(
data: AppTheme.sliderThemeData,
child: Semantics(
label: S.of(context).pointWidth,
child: Slider(
value: LocalDatabaseService.display.pointWidth,
max: 30.0,
divisions: 30,
label: LocalDatabaseService.display.pointWidth.toStringAsFixed(1),
onChanged: (value) {
setState(() {
debugPrint("[config] pointWidth value: $value");
LocalDatabaseService.display.pointWidth = value;
});
},
),
),
);
}
Future<void> setPointWidth() async {
showModalBottomSheet(
context: context,
builder: (_) => StatefulBuilder(
builder: _pointWidthSliderTheme,
),
);
}
SliderTheme _pieceWidthSliderTheme(BuildContext context, Function setState) {
return SliderTheme(
data: AppTheme.sliderThemeData,
child: Semantics(
label: S.of(context).pieceWidth,
child: Slider(
value: LocalDatabaseService.display.pieceWidth,
min: 0.5,
divisions: 50,
label: LocalDatabaseService.display.pieceWidth.toStringAsFixed(1),
onChanged: (value) {
setState(() {
debugPrint("[config] pieceWidth value: $value");
LocalDatabaseService.display.pieceWidth = value;
});
},
),
),
);
}
Future<void> setPieceWidth() async {
showModalBottomSheet(
context: context,
builder: (_) => StatefulBuilder(
builder: _pieceWidthSliderTheme,
),
);
}
SliderTheme _fontSizeSliderTheme(BuildContext context, Function setState) {
return SliderTheme(
data: AppTheme.sliderThemeData,
child: Semantics(
label: S.of(context).fontSize,
child: Slider(
value: LocalDatabaseService.display.fontSize,
min: 16,
max: 32,
divisions: 16,
label: LocalDatabaseService.display.fontSize.toStringAsFixed(1),
onChanged: (value) {
setState(() {
debugPrint("[config] fontSize value: $value");
LocalDatabaseService.display.fontSize = value;
});
},
),
),
);
}
Future<void> setFontSize() async {
showModalBottomSheet(
context: context,
builder: (_) => StatefulBuilder(
builder: _fontSizeSliderTheme,
),
);
}
SliderTheme _boardTopSliderTheme(BuildContext context, Function setState) {
return SliderTheme(
data: AppTheme.sliderThemeData,
child: Semantics(
label: S.of(context).boardTop,
child: Slider(
value: LocalDatabaseService.display.boardTop,
max: 288.0,
divisions: 288,
label: LocalDatabaseService.display.boardTop.toStringAsFixed(1),
onChanged: (value) {
setState(() {
debugPrint("[config] BoardTop value: $value");
LocalDatabaseService.display.boardTop = value;
});
},
),
),
);
}
Future<void> setBoardTop() async {
showModalBottomSheet(
context: context,
builder: (_) => StatefulBuilder(
builder: _boardTopSliderTheme,
),
);
}
SliderTheme _animationDurationSliderTheme(
BuildContext context,
Function setState,
) {
return SliderTheme(
data: AppTheme.sliderThemeData,
child: Semantics(
label: S.of(context).animationDuration,
child: Slider(
value: LocalDatabaseService.display.animationDuration,
max: 5.0,
divisions: 50,
label:
LocalDatabaseService.display.animationDuration.toStringAsFixed(1),
onChanged: (value) {
setState(() {
debugPrint("[config] AnimationDuration value: $value");
LocalDatabaseService.display.animationDuration = value;
});
},
),
),
);
}
Future<void> setAnimationDuration() async {
showModalBottomSheet(
context: context,
builder: (_) => StatefulBuilder(
builder: _animationDurationSliderTheme,
),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: AppTheme.lightBackgroundColor,
appBar: AppBar(
centerTitle: true,
title: Text(S.of(context).personalization),
),
body: SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: children(context),
),
),
);
}
List<Widget> children(BuildContext context) {
Future<void> langCallback([Locale? locale]) async {
debugPrint("[config] languageCode = $locale");
Navigator.pop(context);
setState(() {
LocalDatabaseService.display.languageCode =
locale ?? Constants.defaultLocale;
S.load(Locale(Resources.of().languageCode));
});
debugPrint(
"[config] LocalDatabaseService.display.languageCode: ${LocalDatabaseService.display.languageCode}",
);
}
return <Widget>[
Text(S.of(context).display, style: AppTheme.settingsHeaderStyle),
SettingsCard(
children: <Widget>[
SettingsListTile(
titleString: S.of(context).language,
trailingString: LocalDatabaseService.display.languageCode !=
Constants.defaultLocale
? languageCodeToStrings[
LocalDatabaseService.display.languageCode]!
.languageName
: "",
onTap: () => setLanguage(context, langCallback),
),
SettingsSwitchListTile(
value: LocalDatabaseService.display.isPieceCountInHandShown,
onChanged: setIsPieceCountInHandShown,
titleString: S.of(context).isPieceCountInHandShown,
),
SettingsSwitchListTile(
value: LocalDatabaseService.display.isNotationsShown,
onChanged: setIsNotationsShown,
titleString: S.of(context).isNotationsShown,
),
SettingsSwitchListTile(
value: LocalDatabaseService.display.isHistoryNavigationToolbarShown,
onChanged: setIsHistoryNavigationToolbarShown,
titleString: S.of(context).isHistoryNavigationToolbarShown,
),
SettingsListTile(
titleString: S.of(context).boardBorderLineWidth,
onTap: setBoardBorderLineWidth,
),
SettingsListTile(
titleString: S.of(context).boardInnerLineWidth,
onTap: setBoardInnerLineWidth,
),
SettingsListTile(
titleString: S.of(context).pointStyle,
onTap: setPointStyle,
),
SettingsListTile(
titleString: S.of(context).pointWidth,
onTap: setPointWidth,
),
SettingsListTile(
titleString: S.of(context).pieceWidth,
onTap: setPieceWidth,
),
SettingsListTile(
titleString: S.of(context).fontSize,
onTap: setFontSize,
),
SettingsListTile(
titleString: S.of(context).boardTop,
onTap: setBoardTop,
),
SettingsListTile(
titleString: S.of(context).animationDuration,
onTap: setAnimationDuration,
),
SettingsSwitchListTile(
value: LocalDatabaseService.display.standardNotationEnabled,
onChanged: setStandardNotationEnabled,
titleString: S.of(context).standardNotation,
),
],
),
const SizedBox(height: AppTheme.sizedBoxHeight),
Text(S.of(context).color, style: AppTheme.settingsHeaderStyle),
SettingsCard(
children: <Widget>[
SettingsListTile(
titleString: S.of(context).boardColor,
trailingColor:
LocalDatabaseService.colorSettings.boardBackgroundColor,
onTap: () => showColorDialog(S.of(context).boardColor),
),
SettingsListTile(
titleString: S.of(context).backgroundColor,
trailingColor:
LocalDatabaseService.colorSettings.darkBackgroundColor,
onTap: () => showColorDialog(S.of(context).backgroundColor),
),
SettingsListTile(
titleString: S.of(context).lineColor,
trailingColor: LocalDatabaseService.colorSettings.boardLineColor,
onTap: () => showColorDialog(S.of(context).lineColor),
),
SettingsListTile(
titleString: S.of(context).whitePieceColor,
trailingColor: LocalDatabaseService.colorSettings.whitePieceColor,
onTap: () => showColorDialog(S.of(context).whitePieceColor),
),
SettingsListTile(
titleString: S.of(context).blackPieceColor,
trailingColor: LocalDatabaseService.colorSettings.blackPieceColor,
onTap: () => showColorDialog(S.of(context).blackPieceColor),
),
SettingsListTile(
titleString: S.of(context).pieceHighlightColor,
trailingColor:
LocalDatabaseService.colorSettings.pieceHighlightColor,
onTap: () => showColorDialog(S.of(context).pieceHighlightColor),
),
SettingsListTile(
titleString: S.of(context).messageColor,
trailingColor: LocalDatabaseService.colorSettings.messageColor,
onTap: () => showColorDialog(S.of(context).messageColor),
),
SettingsListTile(
titleString: S.of(context).drawerColor,
trailingColor: LocalDatabaseService.colorSettings.drawerColor,
onTap: () => showColorDialog(S.of(context).drawerColor),
),
SettingsListTile(
titleString: S.of(context).drawerBackgroundColor,
trailingColor:
LocalDatabaseService.colorSettings.drawerBackgroundColor,
onTap: () => showColorDialog(S.of(context).drawerBackgroundColor),
),
SettingsListTile(
titleString: S.of(context).drawerTextColor,
trailingColor: LocalDatabaseService.colorSettings.drawerTextColor,
onTap: () => showColorDialog(S.of(context).drawerTextColor),
),
SettingsListTile(
titleString: S.of(context).drawerHighlightItemColor,
trailingColor:
LocalDatabaseService.colorSettings.drawerHighlightItemColor,
onTap: () =>
showColorDialog(S.of(context).drawerHighlightItemColor),
),
SettingsListTile(
titleString: S.of(context).mainToolbarBackgroundColor,
trailingColor:
LocalDatabaseService.colorSettings.mainToolbarBackgroundColor,
onTap: () =>
showColorDialog(S.of(context).mainToolbarBackgroundColor),
),
SettingsListTile(
titleString: S.of(context).mainToolbarIconColor,
trailingColor:
LocalDatabaseService.colorSettings.mainToolbarIconColor,
onTap: () => showColorDialog(S.of(context).mainToolbarIconColor),
),
SettingsListTile(
titleString: S.of(context).navigationToolbarBackgroundColor,
trailingColor: LocalDatabaseService
.colorSettings.navigationToolbarBackgroundColor,
onTap: () =>
showColorDialog(S.of(context).navigationToolbarBackgroundColor),
),
SettingsListTile(
titleString: S.of(context).navigationToolbarIconColor,
trailingColor:
LocalDatabaseService.colorSettings.navigationToolbarIconColor,
onTap: () =>
showColorDialog(S.of(context).navigationToolbarIconColor),
),
],
),
];
}
// Display
Future<void> setIsPieceCountInHandShown(bool value) async {
setState(
() => LocalDatabaseService.display.isPieceCountInHandShown = value,
);
}
Future<void> setIsNotationsShown(bool value) async {
setState(() => LocalDatabaseService.display.isNotationsShown = value);
}
Future<void> setIsHistoryNavigationToolbarShown(bool value) async {
setState(
() =>
LocalDatabaseService.display.isHistoryNavigationToolbarShown = value,
);
}
Future<void> setStandardNotationEnabled(bool value) async {
setState(
() => LocalDatabaseService.display.standardNotationEnabled = value,
);
debugPrint("[config] standardNotationEnabled: $value");
}
}

View File

@ -0,0 +1,100 @@
/*
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/rule_settings/rule_settings_page.dart';
class _EndGameNMoveRuleModal extends StatelessWidget {
const _EndGameNMoveRuleModal({
Key? key,
required this.endgameNMoveRule,
required this.onChanged,
}) : super(key: key);
final int endgameNMoveRule;
final Function(int?)? onChanged;
@override
Widget build(BuildContext context) {
return Semantics(
label: S.of(context).endgameNMoveRule,
child: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
RadioListTile(
activeColor: AppTheme.switchListTileActiveColor,
title: const Text('5'),
groupValue: endgameNMoveRule,
value: 5,
onChanged: onChanged,
),
RadioListTile(
activeColor: AppTheme.switchListTileActiveColor,
title: const Text('10'),
groupValue: endgameNMoveRule,
value: 10,
onChanged: onChanged,
),
RadioListTile(
activeColor: AppTheme.switchListTileActiveColor,
title: const Text('20'),
groupValue: endgameNMoveRule,
value: 20,
onChanged: onChanged,
),
RadioListTile(
activeColor: AppTheme.switchListTileActiveColor,
title: const Text('30'),
groupValue: endgameNMoveRule,
value: 30,
onChanged: onChanged,
),
RadioListTile(
activeColor: AppTheme.switchListTileActiveColor,
title: const Text('50'),
groupValue: endgameNMoveRule,
value: 50,
onChanged: onChanged,
),
RadioListTile(
activeColor: AppTheme.switchListTileActiveColor,
title: const Text('60'),
groupValue: endgameNMoveRule,
value: 60,
onChanged: onChanged,
),
RadioListTile(
activeColor: AppTheme.switchListTileActiveColor,
title: const Text('100'),
groupValue: endgameNMoveRule,
value: 100,
onChanged: onChanged,
),
RadioListTile(
activeColor: AppTheme.switchListTileActiveColor,
title: const Text('200'),
groupValue: endgameNMoveRule,
value: 200,
onChanged: onChanged,
),
],
),
),
);
}
}

View File

@ -0,0 +1,56 @@
/*
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/rule_settings/rule_settings_page.dart';
class _FlyPieceCountModal extends StatelessWidget {
const _FlyPieceCountModal({
Key? key,
required this.flyPieceCount,
required this.onChanged,
}) : super(key: key);
final int flyPieceCount;
final Function(int?)? onChanged;
@override
Widget build(BuildContext context) {
return Semantics(
label: S.of(context).flyPieceCount,
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
RadioListTile(
activeColor: AppTheme.switchListTileActiveColor,
title: const Text('3'),
groupValue: flyPieceCount,
value: 3,
onChanged: onChanged,
),
RadioListTile(
activeColor: AppTheme.switchListTileActiveColor,
title: const Text('4'),
groupValue: flyPieceCount,
value: 4,
onChanged: onChanged,
),
],
),
);
}
}

View File

@ -0,0 +1,77 @@
/*
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/rule_settings/rule_settings_page.dart';
class _NMoveRuleModal extends StatelessWidget {
const _NMoveRuleModal({
Key? key,
required this.nMoveRule,
required this.onChanged,
}) : super(key: key);
final int nMoveRule;
final Function(int?)? onChanged;
@override
Widget build(BuildContext context) {
return Semantics(
label: S.of(context).nMoveRule,
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
RadioListTile(
activeColor: AppTheme.switchListTileActiveColor,
title: const Text('30'),
groupValue: nMoveRule,
value: 30,
onChanged: onChanged,
),
RadioListTile(
activeColor: AppTheme.switchListTileActiveColor,
title: const Text('50'),
groupValue: nMoveRule,
value: 50,
onChanged: onChanged,
),
RadioListTile(
activeColor: AppTheme.switchListTileActiveColor,
title: const Text('60'),
groupValue: nMoveRule,
value: 60,
onChanged: onChanged,
),
RadioListTile(
activeColor: AppTheme.switchListTileActiveColor,
title: const Text('100'),
groupValue: nMoveRule,
value: 100,
onChanged: onChanged,
),
RadioListTile(
activeColor: AppTheme.switchListTileActiveColor,
title: const Text('200'),
groupValue: nMoveRule,
value: 200,
onChanged: onChanged,
),
],
),
);
}
}

View File

@ -0,0 +1,70 @@
/*
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/rule_settings/rule_settings_page.dart';
class _PieceCountModal extends StatelessWidget {
const _PieceCountModal({
Key? key,
required this.piecesCount,
required this.onChanged,
}) : super(key: key);
final int piecesCount;
final Function(int?)? onChanged;
@override
Widget build(BuildContext context) {
return Semantics(
label: S.of(context).piecesCount,
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
RadioListTile(
activeColor: AppTheme.switchListTileActiveColor,
title: const Text('9'),
groupValue: piecesCount,
value: 9,
onChanged: onChanged,
),
RadioListTile(
activeColor: AppTheme.switchListTileActiveColor,
title: const Text('10'),
groupValue: piecesCount,
value: 10,
onChanged: onChanged,
),
RadioListTile(
activeColor: AppTheme.switchListTileActiveColor,
title: const Text('11'),
groupValue: piecesCount,
value: 11,
onChanged: onChanged,
),
RadioListTile(
activeColor: AppTheme.switchListTileActiveColor,
title: const Text('12'),
groupValue: piecesCount,
value: 12,
onChanged: onChanged,
),
],
),
);
}
}

View File

@ -0,0 +1,358 @@
/*
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:hive_flutter/hive_flutter.dart' show Box;
import 'package:sanmill/generated/l10n.dart';
import 'package:sanmill/models/rules.dart';
import 'package:sanmill/services/storage/storage.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';
part 'package:sanmill/screens/rule_settings/fly_piece_count_modal.dart';
part 'package:sanmill/screens/rule_settings/endgame_n_move_rule_modal.dart';
part 'package:sanmill/screens/rule_settings/piece_count_modal.dart';
part 'package:sanmill/screens/rule_settings/n_move_rule_modal.dart';
class RuleSettingsPage extends StatelessWidget {
const RuleSettingsPage({Key? key}) : super(key: key);
// General
void _setNTotalPiecesEachSide(BuildContext context, Rules _rules) {
void _callback(int? piecesCount) {
Navigator.pop(context);
LocalDatabaseService.rules = _rules.copyWith(piecesCount: piecesCount);
debugPrint("[config] piecesCount = $piecesCount");
}
showModalBottomSheet(
context: context,
builder: (_) => _PieceCountModal(
piecesCount: _rules.piecesCount,
onChanged: _callback,
),
);
}
void _setNMoveRule(BuildContext context, Rules _rules) {
void _callback(int? nMoveRule) {
Navigator.pop(context);
LocalDatabaseService.rules = _rules.copyWith(nMoveRule: nMoveRule);
debugPrint("[config] nMoveRule = $nMoveRule");
}
showModalBottomSheet(
context: context,
builder: (_) => _NMoveRuleModal(
nMoveRule: _rules.nMoveRule,
onChanged: _callback,
),
);
}
void _setEndgameNMoveRule(BuildContext context, Rules _rules) {
void _callback(int? endgameNMoveRule) {
Navigator.pop(context);
LocalDatabaseService.rules =
_rules.copyWith(endgameNMoveRule: endgameNMoveRule);
debugPrint("[config] endgameNMoveRule = $endgameNMoveRule");
}
showModalBottomSheet(
context: context,
builder: (_) => _EndGameNMoveRuleModal(
endgameNMoveRule: _rules.endgameNMoveRule,
onChanged: _callback,
),
);
}
void _setFlyPieceCount(BuildContext context, Rules _rules) {
void _callback(int? flyPieceCount) {
Navigator.pop(context);
LocalDatabaseService.rules =
_rules.copyWith(flyPieceCount: flyPieceCount);
debugPrint("[config] flyPieceCount = $flyPieceCount");
}
showModalBottomSheet(
context: context,
builder: (_) => _FlyPieceCountModal(
flyPieceCount: _rules.flyPieceCount,
onChanged: _callback,
),
);
}
void _setHasDiagonalLines(Rules _rules, bool value) {
LocalDatabaseService.rules = _rules.copyWith(hasDiagonalLines: value);
debugPrint("[config] hasDiagonalLines: $value");
}
void _setAllowFlyingAllowed(Rules _rules, bool value) {
LocalDatabaseService.rules = _rules.copyWith(mayFly: value);
debugPrint("[config] mayFly: $value");
}
void _setThreefoldRepetitionRule(Rules _rules, bool value) {
LocalDatabaseService.rules =
_rules.copyWith(threefoldRepetitionRule: value);
debugPrint("[config] threefoldRepetitionRule: $value");
}
// Placing
void _setHasBannedLocations(Rules _rules, bool value) {
LocalDatabaseService.rules = _rules.copyWith(hasBannedLocations: value);
debugPrint("[config] hasBannedLocations: $value");
}
void _setIsWhiteLoseButNotDrawWhenBoardFull(Rules _rules, bool value) {
LocalDatabaseService.rules =
_rules.copyWith(isWhiteLoseButNotDrawWhenBoardFull: value);
debugPrint("[config] isWhiteLoseButNotDrawWhenBoardFull: $value");
}
void _setMayOnlyRemoveUnplacedPieceInPlacingPhase(Rules _rules, bool value) {
LocalDatabaseService.rules =
_rules.copyWith(mayOnlyRemoveUnplacedPieceInPlacingPhase: value);
debugPrint("[config] mayOnlyRemoveUnplacedPieceInPlacingPhase: $value");
}
// Moving
void _setMayMoveInPlacingPhase(
BuildContext context,
Rules _rules,
bool value,
) {
LocalDatabaseService.rules = _rules.copyWith(mayMoveInPlacingPhase: value);
debugPrint("[config] mayMoveInPlacingPhase: $value");
if (value) {
ScaffoldMessenger.of(context).clearSnackBars();
showSnackBar(context, S.of(context).experimental);
}
}
void _setIsDefenderMoveFirst(Rules _rules, bool value) {
LocalDatabaseService.rules = _rules.copyWith(isDefenderMoveFirst: value);
debugPrint("[config] isDefenderMoveFirst: $value");
}
void _setIsLoseButNotChangeSideWhenNoWay(Rules _rules, bool value) {
LocalDatabaseService.rules =
_rules.copyWith(isLoseButNotChangeSideWhenNoWay: value);
debugPrint("[config] isLoseButNotChangeSideWhenNoWay: $value");
}
// Removing
void _setAllowRemovePieceInMill(Rules _rules, bool value) {
LocalDatabaseService.rules =
_rules.copyWith(mayRemoveFromMillsAlways: value);
debugPrint("[config] mayRemoveFromMillsAlways: $value");
}
void _setAllowRemoveMultiPiecesWhenCloseMultiMill(Rules _rules, bool value) {
LocalDatabaseService.rules = _rules.copyWith(mayRemoveMultiple: value);
debugPrint("[config] mayRemoveMultiple: $value");
}
// Unused
void _setNPiecesAtLeast(Rules _rules, int value) {
LocalDatabaseService.rules = _rules.copyWith(piecesAtLeastCount: value);
debugPrint("[config] piecesAtLeastCount: $value");
}
Widget _buildRules(BuildContext context, Box<Rules> rulesBox, _) {
final Rules _rules = rulesBox.get(
LocalDatabaseService.rulesKey,
defaultValue: Rules(),
)!;
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(S.of(context).general, style: AppTheme.settingsHeaderStyle),
SettingsCard(
children: <Widget>[
SettingsListTile(
titleString: S.of(context).piecesCount,
subtitleString: S.of(context).piecesCount_Detail,
trailingString: _rules.piecesCount.toString(),
onTap: () => _setNTotalPiecesEachSide(context, _rules),
),
SettingsSwitchListTile(
value: _rules.hasDiagonalLines,
onChanged: (val) => _setHasDiagonalLines(_rules, val),
titleString: S.of(context).hasDiagonalLines,
subtitleString: S.of(context).hasDiagonalLines_Detail,
),
SettingsListTile(
titleString: S.of(context).nMoveRule,
subtitleString: S.of(context).nMoveRule_Detail,
trailingString: _rules.nMoveRule.toString(),
onTap: () => _setNMoveRule(context, _rules),
),
SettingsListTile(
titleString: S.of(context).endgameNMoveRule,
subtitleString: S.of(context).endgameNMoveRule_Detail,
trailingString: _rules.endgameNMoveRule.toString(),
onTap: () => _setEndgameNMoveRule(context, _rules),
),
SettingsSwitchListTile(
value: _rules.threefoldRepetitionRule,
onChanged: (val) => _setThreefoldRepetitionRule(_rules, val),
titleString: S.of(context).threefoldRepetitionRule,
subtitleString: S.of(context).threefoldRepetitionRule_Detail,
),
],
),
const SizedBox(height: AppTheme.sizedBoxHeight),
Text(S.of(context).placing, style: AppTheme.settingsHeaderStyle),
SettingsCard(
children: <Widget>[
SettingsSwitchListTile(
value: _rules.hasBannedLocations,
onChanged: (val) => _setHasBannedLocations(_rules, val),
titleString: S.of(context).hasBannedLocations,
subtitleString: S.of(context).hasBannedLocations_Detail,
),
SettingsSwitchListTile(
value: _rules.isWhiteLoseButNotDrawWhenBoardFull,
onChanged: (val) =>
_setIsWhiteLoseButNotDrawWhenBoardFull(_rules, val),
titleString: S.of(context).isWhiteLoseButNotDrawWhenBoardFull,
subtitleString:
S.of(context).isWhiteLoseButNotDrawWhenBoardFull_Detail,
),
SettingsSwitchListTile(
value: _rules.mayOnlyRemoveUnplacedPieceInPlacingPhase,
onChanged: (val) =>
_setMayOnlyRemoveUnplacedPieceInPlacingPhase(_rules, val),
titleString: S.of(context).removeUnplacedPiece,
subtitleString: S.of(context).removeUnplacedPiece_Detail,
),
],
),
const SizedBox(height: AppTheme.sizedBoxHeight),
Text(S.of(context).moving, style: AppTheme.settingsHeaderStyle),
SettingsCard(
children: <Widget>[
if (LocalDatabaseService.preferences.experimentsEnabled)
SettingsSwitchListTile(
value: _rules.mayMoveInPlacingPhase,
onChanged: (val) =>
_setMayMoveInPlacingPhase(context, _rules, val),
titleString: S.of(context).mayMoveInPlacingPhase,
subtitleString: S.of(context).mayMoveInPlacingPhase_Detail,
)
else
SettingsSwitchListTile(
value: _rules.isDefenderMoveFirst,
onChanged: (val) => _setIsDefenderMoveFirst(_rules, val),
titleString: S.of(context).isDefenderMoveFirst,
subtitleString: S.of(context).isDefenderMoveFirst_Detail,
),
SettingsSwitchListTile(
value: _rules.isLoseButNotChangeSideWhenNoWay,
onChanged: (val) =>
_setIsLoseButNotChangeSideWhenNoWay(_rules, val),
titleString: S.of(context).isLoseButNotChangeSideWhenNoWay,
subtitleString:
S.of(context).isLoseButNotChangeSideWhenNoWay_Detail,
),
],
),
const SizedBox(height: AppTheme.sizedBoxHeight),
Text(S.of(context).mayFly, style: AppTheme.settingsHeaderStyle),
SettingsCard(
children: <Widget>[
SettingsSwitchListTile(
value: _rules.mayFly,
onChanged: (val) => _setAllowFlyingAllowed(_rules, val),
titleString: S.of(context).mayFly,
subtitleString: S.of(context).mayFly_Detail,
),
SettingsListTile(
titleString: S.of(context).flyPieceCount,
subtitleString: S.of(context).flyPieceCount_Detail,
trailingString: _rules.flyPieceCount.toString(),
onTap: () => _setFlyPieceCount(context, _rules),
),
],
),
const SizedBox(height: AppTheme.sizedBoxHeight),
Text(S.of(context).removing, style: AppTheme.settingsHeaderStyle),
SettingsCard(
children: <Widget>[
SettingsSwitchListTile(
value: _rules.mayRemoveFromMillsAlways,
onChanged: (val) => _setAllowRemovePieceInMill(_rules, val),
titleString: S.of(context).mayRemoveFromMillsAlways,
subtitleString: S.of(context).mayRemoveFromMillsAlways_Detail,
),
SettingsSwitchListTile(
value: _rules.mayRemoveMultiple,
onChanged: (val) =>
_setAllowRemoveMultiPiecesWhenCloseMultiMill(_rules, val),
titleString: S.of(context).mayRemoveMultiple,
subtitleString: S.of(context).mayRemoveMultiple_Detail,
),
],
),
],
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: AppTheme.lightBackgroundColor,
appBar:
AppBar(centerTitle: true, title: Text(S.of(context).ruleSettings)),
body: SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: ValueListenableBuilder(
valueListenable: LocalDatabaseService.listenRules,
builder: _buildRules,
),
),
);
}
}

View File

@ -1,545 +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:sanmill/generated/l10n.dart';
import 'package:sanmill/l10n/resources.dart';
import 'package:sanmill/mill/rule.dart';
import 'package:sanmill/services/storage/storage.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
_RuleSettingsPageState createState() => _RuleSettingsPageState();
}
class _RuleSettingsPageState extends State<RuleSettingsPage> {
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: AppTheme.lightBackgroundColor,
appBar:
AppBar(centerTitle: true, title: Text(S.of(context).ruleSettings)),
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).general, style: AppTheme.settingsHeaderStyle),
SettingsCard(
children: <Widget>[
SettingsListTile(
titleString: S.of(context).piecesCount,
subtitleString: S.of(context).piecesCount_Detail,
trailingString: LocalDatabaseService.rules.piecesCount.toString(),
onTap: setNTotalPiecesEachSide,
),
SettingsSwitchListTile(
value: LocalDatabaseService.rules.hasDiagonalLines,
onChanged: setHasDiagonalLines,
titleString: S.of(context).hasDiagonalLines,
subtitleString: S.of(context).hasDiagonalLines_Detail,
),
SettingsListTile(
titleString: S.of(context).nMoveRule,
subtitleString: S.of(context).nMoveRule_Detail,
trailingString: LocalDatabaseService.rules.nMoveRule.toString(),
onTap: setNMoveRule,
),
SettingsListTile(
titleString: S.of(context).endgameNMoveRule,
subtitleString: S.of(context).endgameNMoveRule_Detail,
trailingString:
LocalDatabaseService.rules.endgameNMoveRule.toString(),
onTap: setEndgameNMoveRule,
),
SettingsSwitchListTile(
value: LocalDatabaseService.rules.threefoldRepetitionRule,
onChanged: setThreefoldRepetitionRule,
titleString: S.of(context).threefoldRepetitionRule,
subtitleString: S.of(context).threefoldRepetitionRule_Detail,
),
],
),
const SizedBox(height: AppTheme.sizedBoxHeight),
Text(S.of(context).placing, style: AppTheme.settingsHeaderStyle),
SettingsCard(
children: <Widget>[
SettingsSwitchListTile(
value: LocalDatabaseService.rules.hasBannedLocations,
onChanged: setHasBannedLocations,
titleString: S.of(context).hasBannedLocations,
subtitleString: S.of(context).hasBannedLocations_Detail,
),
SettingsSwitchListTile(
value:
LocalDatabaseService.rules.isWhiteLoseButNotDrawWhenBoardFull,
onChanged: setIsWhiteLoseButNotDrawWhenBoardFull,
titleString: S.of(context).isWhiteLoseButNotDrawWhenBoardFull,
subtitleString:
S.of(context).isWhiteLoseButNotDrawWhenBoardFull_Detail,
),
SettingsSwitchListTile(
value: LocalDatabaseService
.rules.mayOnlyRemoveUnplacedPieceInPlacingPhase,
onChanged: setMayOnlyRemoveUnplacedPieceInPlacingPhase,
titleString: S.of(context).removeUnplacedPiece,
subtitleString: S.of(context).removeUnplacedPiece_Detail,
),
],
),
const SizedBox(height: AppTheme.sizedBoxHeight),
Text(S.of(context).moving, style: AppTheme.settingsHeaderStyle),
SettingsCard(
children: <Widget>[
if (LocalDatabaseService.preferences.experimentsEnabled)
SettingsSwitchListTile(
value: LocalDatabaseService.rules.mayMoveInPlacingPhase,
onChanged: setMayMoveInPlacingPhase,
titleString: S.of(context).mayMoveInPlacingPhase,
subtitleString: S.of(context).mayMoveInPlacingPhase_Detail,
)
else
SettingsSwitchListTile(
value: LocalDatabaseService.rules.isDefenderMoveFirst,
onChanged: setIsDefenderMoveFirst,
titleString: S.of(context).isDefenderMoveFirst,
subtitleString: S.of(context).isDefenderMoveFirst_Detail,
),
SettingsSwitchListTile(
value: LocalDatabaseService.rules.isLoseButNotChangeSideWhenNoWay,
onChanged: setIsLoseButNotChangeSideWhenNoWay,
titleString: S.of(context).isLoseButNotChangeSideWhenNoWay,
subtitleString:
S.of(context).isLoseButNotChangeSideWhenNoWay_Detail,
),
],
),
const SizedBox(height: AppTheme.sizedBoxHeight),
Text(S.of(context).mayFly, style: AppTheme.settingsHeaderStyle),
SettingsCard(
children: <Widget>[
SettingsSwitchListTile(
value: LocalDatabaseService.rules.mayFly,
onChanged: setAllowFlyingAllowed,
titleString: S.of(context).mayFly,
subtitleString: S.of(context).mayFly_Detail,
),
SettingsListTile(
titleString: S.of(context).flyPieceCount,
subtitleString: S.of(context).flyPieceCount_Detail,
trailingString: LocalDatabaseService.rules.flyPieceCount.toString(),
onTap: setFlyPieceCount,
),
],
),
const SizedBox(height: AppTheme.sizedBoxHeight),
Text(S.of(context).removing, style: AppTheme.settingsHeaderStyle),
SettingsCard(
children: <Widget>[
SettingsSwitchListTile(
value: LocalDatabaseService.rules.mayRemoveFromMillsAlways,
onChanged: setAllowRemovePieceInMill,
titleString: S.of(context).mayRemoveFromMillsAlways,
subtitleString: S.of(context).mayRemoveFromMillsAlways_Detail,
),
SettingsSwitchListTile(
value: LocalDatabaseService.rules.mayRemoveMultiple,
onChanged: setAllowRemoveMultiPiecesWhenCloseMultiMill,
titleString: S.of(context).mayRemoveMultiple,
subtitleString: S.of(context).mayRemoveMultiple_Detail,
),
],
),
];
}
// General
void setNTotalPiecesEachSide() {
Future<void> callback(int? piecesCount) async {
debugPrint("[config] piecesCount = $piecesCount");
Navigator.pop(context);
setState(
() => rule.piecesCount = LocalDatabaseService.rules.piecesCount =
piecesCount ?? (specialCountryAndRegion == "Iran" ? 12 : 9),
);
debugPrint("[config] rule.piecesCount: ${rule.piecesCount}");
}
showModalBottomSheet(
context: context,
builder: (BuildContext context) => Semantics(
label: S.of(context).piecesCount,
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
RadioListTile(
activeColor: AppTheme.switchListTileActiveColor,
title: const Text('9'),
groupValue: LocalDatabaseService.rules.piecesCount,
value: 9,
onChanged: callback,
),
RadioListTile(
activeColor: AppTheme.switchListTileActiveColor,
title: const Text('10'),
groupValue: LocalDatabaseService.rules.piecesCount,
value: 10,
onChanged: callback,
),
RadioListTile(
activeColor: AppTheme.switchListTileActiveColor,
title: const Text('11'),
groupValue: LocalDatabaseService.rules.piecesCount,
value: 11,
onChanged: callback,
),
RadioListTile(
activeColor: AppTheme.switchListTileActiveColor,
title: const Text('12'),
groupValue: LocalDatabaseService.rules.piecesCount,
value: 12,
onChanged: callback,
),
],
),
),
);
}
void setNMoveRule() {
Future<void> callback(int? nMoveRule) async {
debugPrint("[config] nMoveRule = $nMoveRule");
Navigator.pop(context);
setState(
() => rule.nMoveRule =
LocalDatabaseService.rules.nMoveRule = nMoveRule ?? 100,
);
debugPrint("[config] rule.nMoveRule: ${rule.nMoveRule}");
}
showModalBottomSheet(
context: context,
builder: (BuildContext context) => Semantics(
label: S.of(context).nMoveRule,
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
RadioListTile(
activeColor: AppTheme.switchListTileActiveColor,
title: const Text('30'),
groupValue: LocalDatabaseService.rules.nMoveRule,
value: 30,
onChanged: callback,
),
RadioListTile(
activeColor: AppTheme.switchListTileActiveColor,
title: const Text('50'),
groupValue: LocalDatabaseService.rules.nMoveRule,
value: 50,
onChanged: callback,
),
RadioListTile(
activeColor: AppTheme.switchListTileActiveColor,
title: const Text('60'),
groupValue: LocalDatabaseService.rules.nMoveRule,
value: 60,
onChanged: callback,
),
RadioListTile(
activeColor: AppTheme.switchListTileActiveColor,
title: const Text('100'),
groupValue: LocalDatabaseService.rules.nMoveRule,
value: 100,
onChanged: callback,
),
RadioListTile(
activeColor: AppTheme.switchListTileActiveColor,
title: const Text('200'),
groupValue: LocalDatabaseService.rules.nMoveRule,
value: 200,
onChanged: callback,
),
],
),
),
);
}
void setEndgameNMoveRule() {
Future<void> callback(int? endgameNMoveRule) async {
debugPrint("[config] endgameNMoveRule = $endgameNMoveRule");
Navigator.pop(context);
setState(
() => rule.endgameNMoveRule = LocalDatabaseService
.rules.endgameNMoveRule = endgameNMoveRule ?? 100,
);
debugPrint("[config] rule.endgameNMoveRule: ${rule.endgameNMoveRule}");
}
showModalBottomSheet(
context: context,
builder: (BuildContext context) => Semantics(
label: S.of(context).endgameNMoveRule,
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
RadioListTile(
activeColor: AppTheme.switchListTileActiveColor,
title: const Text('5'),
groupValue: LocalDatabaseService.rules.endgameNMoveRule,
value: 5,
onChanged: callback,
),
RadioListTile(
activeColor: AppTheme.switchListTileActiveColor,
title: const Text('10'),
groupValue: LocalDatabaseService.rules.endgameNMoveRule,
value: 10,
onChanged: callback,
),
RadioListTile(
activeColor: AppTheme.switchListTileActiveColor,
title: const Text('20'),
groupValue: LocalDatabaseService.rules.endgameNMoveRule,
value: 20,
onChanged: callback,
),
RadioListTile(
activeColor: AppTheme.switchListTileActiveColor,
title: const Text('30'),
groupValue: LocalDatabaseService.rules.endgameNMoveRule,
value: 30,
onChanged: callback,
),
RadioListTile(
activeColor: AppTheme.switchListTileActiveColor,
title: const Text('50'),
groupValue: LocalDatabaseService.rules.endgameNMoveRule,
value: 50,
onChanged: callback,
),
RadioListTile(
activeColor: AppTheme.switchListTileActiveColor,
title: const Text('60'),
groupValue: LocalDatabaseService.rules.endgameNMoveRule,
value: 60,
onChanged: callback,
),
RadioListTile(
activeColor: AppTheme.switchListTileActiveColor,
title: const Text('100'),
groupValue: LocalDatabaseService.rules.endgameNMoveRule,
value: 100,
onChanged: callback,
),
RadioListTile(
activeColor: AppTheme.switchListTileActiveColor,
title: const Text('200'),
groupValue: LocalDatabaseService.rules.endgameNMoveRule,
value: 200,
onChanged: callback,
),
],
),
),
);
}
void setFlyPieceCount() {
Future<void> callback(int? flyPieceCount) async {
debugPrint("[config] flyPieceCount = $flyPieceCount");
Navigator.pop(context);
setState(
() => rule.flyPieceCount =
LocalDatabaseService.rules.flyPieceCount = flyPieceCount ?? 3,
);
debugPrint("[config] rule.flyPieceCount: ${rule.flyPieceCount}");
}
showModalBottomSheet(
context: context,
builder: (BuildContext context) => Semantics(
label: S.of(context).flyPieceCount,
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
RadioListTile(
activeColor: AppTheme.switchListTileActiveColor,
title: const Text('3'),
groupValue: LocalDatabaseService.rules.flyPieceCount,
value: 3,
onChanged: callback,
),
RadioListTile(
activeColor: AppTheme.switchListTileActiveColor,
title: const Text('4'),
groupValue: LocalDatabaseService.rules.flyPieceCount,
value: 4,
onChanged: callback,
),
],
),
),
);
}
Future<void> setHasDiagonalLines(bool value) async {
setState(
() => rule.hasDiagonalLines =
LocalDatabaseService.rules.hasDiagonalLines = value,
);
debugPrint("[config] rule.hasDiagonalLines: $value");
}
Future<void> setAllowFlyingAllowed(bool value) async {
setState(() => rule.mayFly = LocalDatabaseService.rules.mayFly = value);
debugPrint("[config] rule.mayFly: $value");
}
Future<void> setThreefoldRepetitionRule(bool value) async {
setState(
() => rule.threefoldRepetitionRule =
LocalDatabaseService.rules.threefoldRepetitionRule = value,
);
debugPrint("[config] rule.threefoldRepetitionRule: $value");
}
// Placing
Future<void> setHasBannedLocations(bool value) async {
setState(
() => rule.hasBannedLocations =
LocalDatabaseService.rules.hasBannedLocations = value,
);
debugPrint("[config] rule.hasBannedLocations: $value");
}
Future<void> setIsWhiteLoseButNotDrawWhenBoardFull(bool value) async {
setState(
() => rule.isWhiteLoseButNotDrawWhenBoardFull =
LocalDatabaseService.rules.isWhiteLoseButNotDrawWhenBoardFull = value,
);
debugPrint("[config] rule.isWhiteLoseButNotDrawWhenBoardFull: $value");
}
Future<void> setMayOnlyRemoveUnplacedPieceInPlacingPhase(bool value) async {
setState(
() => rule.mayOnlyRemoveUnplacedPieceInPlacingPhase = LocalDatabaseService
.rules.mayOnlyRemoveUnplacedPieceInPlacingPhase = value,
);
debugPrint(
"[config] rule.mayOnlyRemoveUnplacedPieceInPlacingPhase: $value",
);
}
// Moving
Future<void> setMayMoveInPlacingPhase(bool value) async {
setState(
() => rule.mayMoveInPlacingPhase =
LocalDatabaseService.rules.mayMoveInPlacingPhase = value,
);
debugPrint("[config] rule.mayMoveInPlacingPhase: $value");
if (value) {
ScaffoldMessenger.of(context).clearSnackBars();
showSnackBar(context, S.of(context).experimental);
}
}
Future<void> setIsDefenderMoveFirst(bool value) async {
setState(
() => rule.isDefenderMoveFirst =
LocalDatabaseService.rules.isDefenderMoveFirst = value,
);
debugPrint("[config] rule.isDefenderMoveFirst: $value");
}
Future<void> setIsLoseButNotChangeSideWhenNoWay(bool value) async {
setState(
() => rule.isLoseButNotChangeSideWhenNoWay =
LocalDatabaseService.rules.isLoseButNotChangeSideWhenNoWay = value,
);
debugPrint("[config] rule.isLoseButNotChangeSideWhenNoWay: $value");
}
// Removing
Future<void> setAllowRemovePieceInMill(bool value) async {
setState(
() => rule.mayRemoveFromMillsAlways =
LocalDatabaseService.rules.mayRemoveFromMillsAlways = value,
);
debugPrint("[config] rule.mayRemoveFromMillsAlways: $value");
}
Future<void> setAllowRemoveMultiPiecesWhenCloseMultiMill(bool value) async {
setState(
() => rule.mayRemoveMultiple =
LocalDatabaseService.rules.mayRemoveMultiple = value,
);
debugPrint("[config] rule.mayRemoveMultiple: $value");
}
// Unused
Future<void> setNPiecesAtLeast(int value) async {
setState(
() => rule.piecesAtLeastCount =
LocalDatabaseService.rules.piecesAtLeastCount = value,
);
debugPrint("[config] rule.piecesAtLeastCount: $value");
}
}

View File

@ -36,7 +36,7 @@ class EngineResponse {
}
abstract class Engine {
Future<void> setOptions(BuildContext context) async {}
Future<void> setOptions() async {}
Future<void> startup() async {}
Future<void> shutdown() async {}
Future<EngineResponse> search(Position? position);

View File

@ -102,16 +102,18 @@ class NativeEngine extends Engine {
int sleep = 100,
int times = 0,
}) async {
var timeLimit = LocalDatabaseService.preferences.developerMode ? 100 : 6000;
final _pref = LocalDatabaseService.preferences;
if (LocalDatabaseService.preferences.moveTime > 0) {
var timeLimit = _pref.developerMode ? 100 : 6000;
if (_pref.moveTime > 0) {
// TODO: Accurate timeLimit
timeLimit = LocalDatabaseService.preferences.moveTime * 10 * 64 + 10;
timeLimit = _pref.moveTime * 10 * 64 + 10;
}
if (times > timeLimit) {
debugPrint("[engine] Timeout. sleep = $sleep, times = $times");
if (LocalDatabaseService.preferences.developerMode && isActive) {
if (_pref.developerMode && isActive) {
throw "Exception: waitResponse timeout.";
}
return '';
@ -142,78 +144,81 @@ class NativeEngine extends Engine {
}
@override
Future<void> setOptions(BuildContext context) async {
Future<void> setOptions() async {
final _pref = LocalDatabaseService.preferences;
final _rules = LocalDatabaseService.rules;
await send(
'setoption name DeveloperMode value ${LocalDatabaseService.preferences.developerMode}',
'setoption name DeveloperMode value ${_pref.developerMode}',
);
await send(
'setoption name Algorithm value ${LocalDatabaseService.preferences.algorithm}',
'setoption name Algorithm value ${_pref.algorithm}',
);
await send(
'setoption name DrawOnHumanExperience value ${LocalDatabaseService.preferences.drawOnHumanExperience}',
'setoption name DrawOnHumanExperience value ${_pref.drawOnHumanExperience}',
);
await send(
'setoption name ConsiderMobility value ${LocalDatabaseService.preferences.considerMobility}',
'setoption name ConsiderMobility value ${_pref.considerMobility}',
);
await send(
'setoption name SkillLevel value ${LocalDatabaseService.preferences.skillLevel}',
'setoption name SkillLevel value ${_pref.skillLevel}',
);
await send(
'setoption name MoveTime value ${LocalDatabaseService.preferences.moveTime}',
'setoption name MoveTime value ${_pref.moveTime}',
);
await send(
'setoption name AiIsLazy value ${LocalDatabaseService.preferences.aiIsLazy}',
'setoption name AiIsLazy value ${_pref.aiIsLazy}',
);
await send(
'setoption name Shuffling value ${LocalDatabaseService.preferences.shufflingEnabled}',
'setoption name Shuffling value ${_pref.shufflingEnabled}',
);
await send(
'setoption name PiecesCount value ${LocalDatabaseService.rules.piecesCount}',
'setoption name PiecesCount value ${_rules.piecesCount}',
);
await send(
'setoption name FlyPieceCount value ${LocalDatabaseService.rules.flyPieceCount}',
'setoption name FlyPieceCount value ${_rules.flyPieceCount}',
);
await send(
'setoption name PiecesAtLeastCount value ${LocalDatabaseService.rules.piecesAtLeastCount}',
'setoption name PiecesAtLeastCount value ${_rules.piecesAtLeastCount}',
);
await send(
'setoption name HasDiagonalLines value ${LocalDatabaseService.rules.hasDiagonalLines}',
'setoption name HasDiagonalLines value ${_rules.hasDiagonalLines}',
);
await send(
'setoption name HasBannedLocations value ${LocalDatabaseService.rules.hasBannedLocations}',
'setoption name HasBannedLocations value ${_rules.hasBannedLocations}',
);
await send(
'setoption name MayMoveInPlacingPhase value ${LocalDatabaseService.rules.mayMoveInPlacingPhase}',
'setoption name MayMoveInPlacingPhase value ${_rules.mayMoveInPlacingPhase}',
);
await send(
'setoption name IsDefenderMoveFirst value ${LocalDatabaseService.rules.isDefenderMoveFirst}',
'setoption name IsDefenderMoveFirst value ${_rules.isDefenderMoveFirst}',
);
await send(
'setoption name MayRemoveMultiple value ${LocalDatabaseService.rules.mayRemoveMultiple}',
'setoption name MayRemoveMultiple value ${_rules.mayRemoveMultiple}',
);
await send(
'setoption name MayRemoveFromMillsAlways value ${LocalDatabaseService.rules.mayRemoveFromMillsAlways}',
'setoption name MayRemoveFromMillsAlways value ${_rules.mayRemoveFromMillsAlways}',
);
await send(
'setoption name MayOnlyRemoveUnplacedPieceInPlacingPhase value ${LocalDatabaseService.rules.mayOnlyRemoveUnplacedPieceInPlacingPhase}',
'setoption name MayOnlyRemoveUnplacedPieceInPlacingPhase value ${_rules.mayOnlyRemoveUnplacedPieceInPlacingPhase}',
);
await send(
'setoption name IsWhiteLoseButNotDrawWhenBoardFull value ${LocalDatabaseService.rules.isWhiteLoseButNotDrawWhenBoardFull}',
'setoption name IsWhiteLoseButNotDrawWhenBoardFull value ${_rules.isWhiteLoseButNotDrawWhenBoardFull}',
);
await send(
'setoption name IsLoseButNotChangeSideWhenNoWay value ${LocalDatabaseService.rules.isLoseButNotChangeSideWhenNoWay}',
'setoption name IsLoseButNotChangeSideWhenNoWay value ${_rules.isLoseButNotChangeSideWhenNoWay}',
);
await send(
'setoption name MayFly value ${LocalDatabaseService.rules.mayFly}',
'setoption name MayFly value ${_rules.mayFly}',
);
await send(
'setoption name NMoveRule value ${LocalDatabaseService.rules.nMoveRule}',
'setoption name NMoveRule value ${_rules.nMoveRule}',
);
await send(
'setoption name EndgameNMoveRule value ${LocalDatabaseService.rules.endgameNMoveRule}',
'setoption name EndgameNMoveRule value ${_rules.endgameNMoveRule}',
);
await send(
'setoption name ThreefoldRepetitionRule value ${LocalDatabaseService.rules.threefoldRepetitionRule}',
'setoption name ThreefoldRepetitionRule value ${_rules.threefoldRepetitionRule}',
);
}

View File

@ -13,6 +13,7 @@ import 'package:sanmill/models/preferences.dart';
import 'package:sanmill/models/rules.dart';
import 'package:sanmill/services/storage/adapters/color_adapter.dart';
import 'package:sanmill/services/storage/adapters/locale_adapter.dart';
import 'package:sanmill/services/storage/storage_v1.dart';
/// Helpers to handle local data storage
class LocalDatabaseService {
@ -22,7 +23,7 @@ class LocalDatabaseService {
static late Box<ColorSettings> _colorSettingsBox;
/// key at wich the [ColorSettings] will be saved in the [_colorSettingsBox]
static const String _colorSettingsKey = 'settings';
static const String colorSettingsKey = 'settings';
/// key at wich the [_colorSettingsBox] will be saved
static const String _colorSettingsBoxName = 'colors';
@ -31,7 +32,7 @@ class LocalDatabaseService {
static late Box<Display> _displayBox;
/// key at wich the [Display] will be saved in the [_displayBox]
static const String _displayKey = 'settings';
static const String displayKey = 'settings';
/// key at wich the [_displayBox] will be saved
static const String _displayBoxName = 'display';
@ -40,7 +41,7 @@ class LocalDatabaseService {
static late Box<Preferences> _preferencesBox;
/// key at wich the [Preferences] will be saved in the [_preferencesBox]
static const String _preferencesKey = 'settings';
static const String preferencesKey = 'settings';
/// key at wich the [_preferencesBox] will be saved
static const String _preferencesBoxName = 'preferences';
@ -49,7 +50,7 @@ class LocalDatabaseService {
static late Box<Rules> _rulesBox;
/// key at wich the [Rules] will be saved in the [_rulesBox]
static const String _rulesKey = 'settings';
static const String rulesKey = 'settings';
/// key at wich the [_rulesBox] will be saved
static const String _rulesBoxName = 'rules';
@ -61,63 +62,71 @@ class LocalDatabaseService {
await _initDisplay();
await _initPreferences();
await _initRules();
DatabaseV1.initRules();
}
/// resets the storage
static Future<void> resetStorage() async {
await _colorSettingsBox.delete(colorSettingsKey);
await _displayBox.delete(displayKey);
await _preferencesBox.delete(preferencesKey);
await _rulesBox.delete(rulesKey);
}
/// initilizes the [ColorSettings] reference
static Future<void> _initColorSettings() async {
Hive.registerAdapter<ColorSettings>(ColorSettingsAdapter());
Hive.registerAdapter<Color>(ColorAdapter());
Hive.registerAdapter<ColorSettings>(ColorSettingsAdapter());
_colorSettingsBox =
await Hive.openBox<ColorSettings>(_colorSettingsBoxName);
}
/// listens to changes inside the settings Box
static ValueListenable<Box<ColorSettings>> get listenColorSettings =>
_colorSettingsBox.listenable(keys: [_colorSettingsKey]);
_colorSettingsBox.listenable(keys: [colorSettingsKey]);
/// saves the given [settings] to the settings Box
static set colorSettings(ColorSettings settings) =>
_colorSettingsBox.put(_colorSettingsKey, settings);
/// saves the given [colors] to the settings Box
static set colorSettings(ColorSettings colors) =>
_colorSettingsBox.put(colorSettingsKey, colors);
/// gets the given [ColorSettings] from the settings Box
static ColorSettings get colorSettings =>
_colorSettingsBox.get(_colorSettingsKey) ?? ColorSettings();
_colorSettingsBox.get(colorSettingsKey) ?? ColorSettings();
/// initilizes the [Display] reference
static Future<void> _initDisplay() async {
Hive.registerAdapter<Locale>(LocaleAdapter());
Hive.registerAdapter<Display>(DisplayAdapter());
_displayBox = await Hive.openBox<Display>(_displayBoxName);
}
/// listens to changes inside the settings Box
static ValueListenable<Box<Display>> get listenDisplay =>
_displayBox.listenable(keys: [_displayKey]);
_displayBox.listenable(keys: [displayKey]);
/// saves the given [settings] to the settings Box
static set display(Display settings) =>
_displayBox.put(_displayKey, settings);
/// saves the given [display] to the settings Box
static set display(Display display) => _displayBox.put(displayKey, display);
/// gets the given [Display] from the settings Box
static Display get display => _displayBox.get(_displayKey) ?? Display();
static Display get display => _displayBox.get(displayKey) ?? Display();
/// initilizes the [Preferences] reference
static Future<void> _initPreferences() async {
Hive.registerAdapter<Preferences>(PreferencesAdapter());
Hive.registerAdapter<Locale>(LocaleAdapter());
_preferencesBox = await Hive.openBox<Preferences>(_preferencesBoxName);
}
/// listens to changes inside the settings Box
static ValueListenable<Box<Preferences>> get listenPreferences =>
_preferencesBox.listenable(keys: [_preferencesKey]);
_preferencesBox.listenable(keys: [preferencesKey]);
/// saves the given [settings] to the settings Box
static set preferences(Preferences settings) =>
_preferencesBox.put(_preferencesKey, settings);
_preferencesBox.put(preferencesKey, settings);
/// gets the given [Preferences] from the settings Box
static Preferences get preferences =>
_preferencesBox.get(_preferencesKey) ?? Preferences();
_preferencesBox.get(preferencesKey) ?? const Preferences();
/// initilizes the [Rules] reference
static Future<void> _initRules() async {
@ -127,11 +136,14 @@ class LocalDatabaseService {
/// listens to changes inside the settings Box
static ValueListenable<Box<Rules>> get listenRules =>
_rulesBox.listenable(keys: [_rulesKey]);
_rulesBox.listenable(keys: [rulesKey]);
/// saves the given [settings] to the settings Box
static set rules(Rules settings) => _rulesBox.put(_rulesKey, settings);
/// saves the given [rules] to the settings Box
static set rules(Rules rules) {
_rulesBox.put(rulesKey, rules);
DatabaseV1.initRules();
}
/// gets the given [Rules] from the settings Box
static Rules get rules => _rulesBox.get(_rulesKey) ?? Rules();
static Rules get rules => _rulesBox.get(rulesKey) ?? Rules();
}

View File

@ -22,90 +22,96 @@ import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:path_provider/path_provider.dart';
import 'package:sanmill/mill/rule.dart';
import 'package:sanmill/models/color.dart';
import 'package:sanmill/models/display.dart';
import 'package:sanmill/models/preferences.dart';
import 'package:sanmill/models/rules.dart';
import 'package:sanmill/services/storage/storage.dart';
import 'package:sanmill/shared/constants.dart';
@Deprecated('use [LocalDatabaseService] instead')
class Settings {
static final settingsFileName = Constants.settingsFilename;
static Settings? _instance;
class Settings {}
late File _file;
Map<String, dynamic>? _values = {};
class DatabaseV1 {
const DatabaseV1._();
// TODO: add constructor
static Future<Settings> instance() async {
if (_instance == null) {
_instance = Settings();
await _instance!._load(settingsFileName);
debugPrint("[settings] $settingsFileName loaded.");
}
static const _tag = "[Database Migration]";
return _instance!;
}
dynamic operator [](String key) => _values![key];
void operator []=(String key, dynamic value) => _values![key] = value;
/// migrates the deprecated [Settings] to the new [LocalDatabaseService]
Future<void> migrate() async {}
Future<bool> _load(String fileName) async {
// TODO: main() ExternalStorage
// var docDir = await getExternalStorageDirectory();
static Future<File> _getFile() async {
final fileName = Constants.settingsFilename;
final docDir = await getApplicationDocumentsDirectory();
_file = File('${docDir.path}/$fileName');
return File('${docDir.path}/$fileName');
}
debugPrint("[settings] Loading $_file ...");
/// loads the preferences from the old datastore
static Future<Map<String, dynamic>?> _loadFile(File _file) async {
debugPrint("$_tag Loading $_file ...");
try {
final contents = await _file.readAsString();
_values = jsonDecode(contents) as Map<String, dynamic>?;
final _values = jsonDecode(contents) as Map<String, dynamic>?;
debugPrint(_values.toString());
return _values;
} catch (e) {
debugPrint(e.toString());
return false;
}
return true;
}
Future<void> restore() async {
debugPrint("[settings] Restoring Settings...");
if (_file.existsSync()) {
_file.deleteSync();
debugPrint("[settings] $_file deleted");
/// migrates the deprecated Settings to the new [LocalDatabaseService]
static Future<void> migrateDB() async {
final _pref = LocalDatabaseService.preferences;
if (!_pref.usesHiveDB) {
debugPrint("$_tag migrate DB");
final _file = await _getFile();
final _json = await _loadFile(_file);
if (_json != null) {
LocalDatabaseService.colorSettings = ColorSettings.fromJson(_json);
LocalDatabaseService.display = Display.fromJson(_json);
LocalDatabaseService.preferences = Preferences.fromJson(_json);
LocalDatabaseService.rules = Rules.fromJson(_json);
}
await _deleteFile(_file);
LocalDatabaseService.preferences = _pref.copyWith(usesHiveDB: true);
} else {
debugPrint("[settings] $_file does not exist");
debugPrint("$_tag we allready use HiveDB. Skipping migration.");
}
}
void initRules() {
/// deletes the old settings file
static Future<void> _deleteFile(File _file) async {
debugPrint("$_tag deleting Settings...");
if (await _file.exists()) {
await _file.delete();
debugPrint("$_tag $_file deleted");
} else {
debugPrint("$_tag $_file does not exist");
}
}
/// initializes the [Rules] object with the contents of [LocalDatabaseService.rules]
static void initRules() {
final _rules = LocalDatabaseService.rules;
// Rules
rule.piecesCount = LocalDatabaseService.rules.piecesCount;
rule.flyPieceCount = LocalDatabaseService.rules.flyPieceCount;
rule.piecesAtLeastCount = LocalDatabaseService.rules.piecesAtLeastCount;
rule.hasDiagonalLines = LocalDatabaseService.rules.hasDiagonalLines;
rule.hasBannedLocations = LocalDatabaseService.rules.hasBannedLocations;
rule.mayMoveInPlacingPhase =
LocalDatabaseService.rules.mayMoveInPlacingPhase;
rule.isDefenderMoveFirst = LocalDatabaseService.rules.isDefenderMoveFirst;
rule.mayRemoveMultiple = LocalDatabaseService.rules.mayRemoveMultiple;
rule.mayRemoveFromMillsAlways =
LocalDatabaseService.rules.mayRemoveFromMillsAlways;
rule.piecesCount = _rules.piecesCount;
rule.flyPieceCount = _rules.flyPieceCount;
rule.piecesAtLeastCount = _rules.piecesAtLeastCount;
rule.hasDiagonalLines = _rules.hasDiagonalLines;
rule.hasBannedLocations = _rules.hasBannedLocations;
rule.mayMoveInPlacingPhase = _rules.mayMoveInPlacingPhase;
rule.isDefenderMoveFirst = _rules.isDefenderMoveFirst;
rule.mayRemoveMultiple = _rules.mayRemoveMultiple;
rule.mayRemoveFromMillsAlways = _rules.mayRemoveFromMillsAlways;
rule.mayOnlyRemoveUnplacedPieceInPlacingPhase =
LocalDatabaseService.rules.mayOnlyRemoveUnplacedPieceInPlacingPhase;
_rules.mayOnlyRemoveUnplacedPieceInPlacingPhase;
rule.isWhiteLoseButNotDrawWhenBoardFull =
LocalDatabaseService.rules.isWhiteLoseButNotDrawWhenBoardFull;
_rules.isWhiteLoseButNotDrawWhenBoardFull;
rule.isLoseButNotChangeSideWhenNoWay =
LocalDatabaseService.rules.isLoseButNotChangeSideWhenNoWay;
rule.mayFly = LocalDatabaseService.rules.mayFly;
rule.nMoveRule = LocalDatabaseService.rules.nMoveRule;
rule.endgameNMoveRule = LocalDatabaseService.rules.endgameNMoveRule;
rule.threefoldRepetitionRule =
LocalDatabaseService.rules.threefoldRepetitionRule;
_rules.isLoseButNotChangeSideWhenNoWay;
rule.mayFly = _rules.mayFly;
rule.nMoveRule = _rules.nMoveRule;
rule.endgameNMoveRule = _rules.endgameNMoveRule;
rule.threefoldRepetitionRule = _rules.threefoldRepetitionRule;
}
}

View File

@ -54,12 +54,7 @@ void showCountdownDialog(
builder: (BuildContext context, AsyncSnapshot<int> snapshot) {
debugPrint("Count down: ${snapshot.data}");
if (snapshot.data == 0) {
fun();
if (Platform.isAndroid) {
SystemChannels.platform.invokeMethod('SystemNavigator.pop');
} else {}
}
if (snapshot.data == 0) fun();
return SizedBox(
height: 128,
@ -71,9 +66,7 @@ void showCountdownDialog(
),
const SizedBox(height: 20),
InkWell(
onTap: () {
Navigator.pop(context);
},
onTap: () => Navigator.pop(context),
child: Center(
child: Text(
S.of(ctx).cancel,

View File

@ -10,6 +10,7 @@ environment:
dependencies:
animated_text_kit: ^4.1.1
catcher: ^0.6.8
copy_with_extension: ^2.0.2
cupertino_icons: ^1.0.3
device_info_plus_platform_interface: ^2.1.0
devicelocale: ^0.4.3
@ -45,12 +46,13 @@ dependencies:
dev_dependencies:
build_runner: ^2.1.4
copy_with_extension_gen: ^2.0.3
flutter_oss_licenses: ^1.0.1
flutter_test:
sdk: flutter
hive_generator: ^1.1.0
json_serializable: ^5.0.2
lint: ^1.7.2
msix: ^2.1.3