From e93f62b455f323bd14477f757b7fa39b6bbf3554 Mon Sep 17 00:00:00 2001 From: Leptopoda Date: Mon, 18 Oct 2021 11:27:59 +0200 Subject: [PATCH] reactive storage and db migration - make new storage reactive - add Database Migrator change how the developerMode reacts --- src/ui/flutter_app/analysis_options.yaml | 1 - src/ui/flutter_app/lib/l10n/resources.dart | 103 +-- src/ui/flutter_app/lib/main.dart | 7 +- src/ui/flutter_app/lib/mill/rule.dart | 1 + src/ui/flutter_app/lib/models/color.dart | 39 +- src/ui/flutter_app/lib/models/display.dart | 31 +- .../flutter_app/lib/models/preferences.dart | 46 +- src/ui/flutter_app/lib/models/rules.dart | 36 +- src/ui/flutter_app/lib/models/temporary.dart | 27 + src/ui/flutter_app/lib/screens/env_page.dart | 2 + .../lib/screens/game_page/game_page.dart | 37 +- .../game_settings/algorithm_modal.dart | 63 ++ .../game_settings/game_settings_page.dart | 381 ++++++++++ .../game_settings/move_time_slider.dart | 55 ++ .../game_settings/reset_settings_alert.dart | 77 ++ .../game_settings/skill_level_slider.dart | 55 ++ .../lib/screens/game_settings_page.dart | 535 -------------- .../flutter_app/lib/screens/home_drawer.dart | 17 +- .../lib/screens/navigation_home_screen.dart | 9 +- .../animation_duration_slider.dart | 54 ++ .../board_boarder_line_width_slider.dart | 54 ++ .../board_inner_line_width_slider.dart | 54 ++ .../board_top_slider.dart | 54 ++ .../color_selector_list_tile.dart | 123 ++++ .../font_size_slider.dart | 55 ++ .../personalization_settings_page.dart | 322 +++++++++ .../piece_width_slider.dart | 54 ++ .../point_style_modal.dart | 65 ++ .../point_width_slider.dart | 54 ++ .../personalization_settings_page.dart | 672 ------------------ .../endgame_n_move_rule_modal.dart | 100 +++ .../rule_settings/fly_piece_count_modal.dart | 56 ++ .../rule_settings/n_move_rule_modal.dart | 77 ++ .../rule_settings/piece_count_modal.dart | 70 ++ .../rule_settings/rule_settings_page.dart | 358 ++++++++++ .../lib/screens/rule_settings_page.dart | 545 -------------- .../lib/services/engine/engine.dart | 2 +- .../lib/services/engine/native_engine.dart | 63 +- .../lib/services/storage/storage.dart | 58 +- .../lib/services/storage/storage_v1.dart | 122 ++-- src/ui/flutter_app/lib/shared/dialog.dart | 11 +- src/ui/flutter_app/pubspec.yaml | 6 +- 42 files changed, 2529 insertions(+), 2022 deletions(-) create mode 100644 src/ui/flutter_app/lib/models/temporary.dart create mode 100644 src/ui/flutter_app/lib/screens/game_settings/algorithm_modal.dart create mode 100644 src/ui/flutter_app/lib/screens/game_settings/game_settings_page.dart create mode 100644 src/ui/flutter_app/lib/screens/game_settings/move_time_slider.dart create mode 100644 src/ui/flutter_app/lib/screens/game_settings/reset_settings_alert.dart create mode 100644 src/ui/flutter_app/lib/screens/game_settings/skill_level_slider.dart delete mode 100644 src/ui/flutter_app/lib/screens/game_settings_page.dart create mode 100644 src/ui/flutter_app/lib/screens/personalization_settings/animation_duration_slider.dart create mode 100644 src/ui/flutter_app/lib/screens/personalization_settings/board_boarder_line_width_slider.dart create mode 100644 src/ui/flutter_app/lib/screens/personalization_settings/board_inner_line_width_slider.dart create mode 100644 src/ui/flutter_app/lib/screens/personalization_settings/board_top_slider.dart create mode 100644 src/ui/flutter_app/lib/screens/personalization_settings/color_selector_list_tile.dart create mode 100644 src/ui/flutter_app/lib/screens/personalization_settings/font_size_slider.dart create mode 100644 src/ui/flutter_app/lib/screens/personalization_settings/personalization_settings_page.dart create mode 100644 src/ui/flutter_app/lib/screens/personalization_settings/piece_width_slider.dart create mode 100644 src/ui/flutter_app/lib/screens/personalization_settings/point_style_modal.dart create mode 100644 src/ui/flutter_app/lib/screens/personalization_settings/point_width_slider.dart delete mode 100644 src/ui/flutter_app/lib/screens/personalization_settings_page.dart create mode 100644 src/ui/flutter_app/lib/screens/rule_settings/endgame_n_move_rule_modal.dart create mode 100644 src/ui/flutter_app/lib/screens/rule_settings/fly_piece_count_modal.dart create mode 100644 src/ui/flutter_app/lib/screens/rule_settings/n_move_rule_modal.dart create mode 100644 src/ui/flutter_app/lib/screens/rule_settings/piece_count_modal.dart create mode 100644 src/ui/flutter_app/lib/screens/rule_settings/rule_settings_page.dart delete mode 100644 src/ui/flutter_app/lib/screens/rule_settings_page.dart diff --git a/src/ui/flutter_app/analysis_options.yaml b/src/ui/flutter_app/analysis_options.yaml index 912fdea5..fc372f7c 100644 --- a/src/ui/flutter_app/analysis_options.yaml +++ b/src/ui/flutter_app/analysis_options.yaml @@ -13,4 +13,3 @@ analyzer: # generated code - lib/generated/** - lib/**/**.g.dart - - lib/l10n/** diff --git a/src/ui/flutter_app/lib/l10n/resources.dart b/src/ui/flutter_app/lib/l10n/resources.dart index c87ea849..3c39e79b 100644 --- a/src/ui/flutter_app/lib/l10n/resources.dart +++ b/src/ui/flutter_app/lib/l10n/resources.dart @@ -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 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 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 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 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."); } diff --git a/src/ui/flutter_app/lib/main.dart b/src/ui/flutter_app/lib/main.dart index ac9bf0dc..57e3ef3b 100644 --- a/src/ui/flutter_app/lib/main.dart +++ b/src/ui/flutter_app/lib/main.dart @@ -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 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 main() async { RouteObserver routeObserver = RouteObserver(); class SanmillApp extends StatelessWidget { - final globalScaffoldKey = GlobalKey(); + const SanmillApp({Key? key}) : super(key: key); @override Widget build(BuildContext context) { + final globalScaffoldKey = GlobalKey(); Audios.loadSounds(); setSpecialCountryAndRegion(context); diff --git a/src/ui/flutter_app/lib/mill/rule.dart b/src/ui/flutter_app/lib/mill/rule.dart index 37db70e5..a9e9c39b 100644 --- a/src/ui/flutter_app/lib/mill/rule.dart +++ b/src/ui/flutter_app/lib/mill/rule.dart @@ -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 = ""; diff --git a/src/ui/flutter_app/lib/models/color.dart b/src/ui/flutter_app/lib/models/color.dart index 973753d0..bcd4a115 100644 --- a/src/ui/flutter_app/lib/models/color.dart +++ b/src/ui/flutter_app/lib/models/color.dart @@ -16,7 +16,8 @@ along with this program. If not, see . */ -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 json) => diff --git a/src/ui/flutter_app/lib/models/display.dart b/src/ui/flutter_app/lib/models/display.dart index 6ea95008..1473cfe9 100644 --- a/src/ui/flutter_app/lib/models/display.dart +++ b/src/ui/flutter_app/lib/models/display.dart @@ -16,7 +16,8 @@ along with this program. If not, see . */ -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 json) => diff --git a/src/ui/flutter_app/lib/models/preferences.dart b/src/ui/flutter_app/lib/models/preferences.dart index 273ec607..38dcb2cc 100644 --- a/src/ui/flutter_app/lib/models/preferences.dart +++ b/src/ui/flutter_app/lib/models/preferences.dart @@ -16,6 +16,8 @@ along with this program. If not, see . */ +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 json) => diff --git a/src/ui/flutter_app/lib/models/rules.dart b/src/ui/flutter_app/lib/models/rules.dart index f070150a..4fec4507 100644 --- a/src/ui/flutter_app/lib/models/rules.dart +++ b/src/ui/flutter_app/lib/models/rules.dart @@ -16,6 +16,8 @@ along with this program. If not, see . */ +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 json) => _$RulesFromJson(json); diff --git a/src/ui/flutter_app/lib/models/temporary.dart b/src/ui/flutter_app/lib/models/temporary.dart new file mode 100644 index 00000000..077f46e8 --- /dev/null +++ b/src/ui/flutter_app/lib/models/temporary.dart @@ -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 . +*/ + +/// 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; +} diff --git a/src/ui/flutter_app/lib/screens/env_page.dart b/src/ui/flutter_app/lib/screens/env_page.dart index 800366af..6dda2136 100644 --- a/src/ui/flutter_app/lib/screens/env_page.dart +++ b/src/ui/flutter_app/lib/screens/env_page.dart @@ -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( diff --git a/src/ui/flutter_app/lib/screens/game_page/game_page.dart b/src/ui/flutter_app/lib/screens/game_page/game_page.dart index 62262d2d..c238010a 100644 --- a/src/ui/flutter_app/lib/screens/game_page/game_page.dart +++ b/src/ui/flutter_app/lib/screens/game_page/game_page.dart @@ -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 } Future 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 ), 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 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 Future 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 Future 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 Future 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); } diff --git a/src/ui/flutter_app/lib/screens/game_settings/algorithm_modal.dart b/src/ui/flutter_app/lib/screens/game_settings/algorithm_modal.dart new file mode 100644 index 00000000..2c31a3b1 --- /dev/null +++ b/src/ui/flutter_app/lib/screens/game_settings/algorithm_modal.dart @@ -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 . +*/ + +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: [ + 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, + ), + ], + ), + ); + } +} diff --git a/src/ui/flutter_app/lib/screens/game_settings/game_settings_page.dart b/src/ui/flutter_app/lib/screens/game_settings/game_settings_page.dart new file mode 100644 index 00000000..63fb53be --- /dev/null +++ b/src/ui/flutter_app/lib/screens/game_settings/game_settings_page.dart @@ -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 . +*/ + +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 _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 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: [ + 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: [ + 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: [ + 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: [ + 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: [ + 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: [ + 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: [ + 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, + ), + ), + ); + } +} diff --git a/src/ui/flutter_app/lib/screens/game_settings/move_time_slider.dart b/src/ui/flutter_app/lib/screens/game_settings/move_time_slider.dart new file mode 100644 index 00000000..0a7ff68e --- /dev/null +++ b/src/ui/flutter_app/lib/screens/game_settings/move_time_slider.dart @@ -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 . +*/ + +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 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"); + }, + ); + }, + ), + ), + ); + } +} diff --git a/src/ui/flutter_app/lib/screens/game_settings/reset_settings_alert.dart b/src/ui/flutter_app/lib/screens/game_settings/reset_settings_alert.dart new file mode 100644 index 00000000..63dcd7e7 --- /dev/null +++ b/src/ui/flutter_app/lib/screens/game_settings/reset_settings_alert.dart @@ -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 . +*/ + +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 _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: [ + 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, + ), + ), + ), + ], + ); + } +} diff --git a/src/ui/flutter_app/lib/screens/game_settings/skill_level_slider.dart b/src/ui/flutter_app/lib/screens/game_settings/skill_level_slider.dart new file mode 100644 index 00000000..3366f57d --- /dev/null +++ b/src/ui/flutter_app/lib/screens/game_settings/skill_level_slider.dart @@ -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 . +*/ + +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 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"); + }, + ); + }, + ), + ), + ); + } +} diff --git a/src/ui/flutter_app/lib/screens/game_settings_page.dart b/src/ui/flutter_app/lib/screens/game_settings_page.dart deleted file mode 100644 index 93f595d0..00000000 --- a/src/ui/flutter_app/lib/screens/game_settings_page.dart +++ /dev/null @@ -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 . -*/ - -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 { - Color pickerColor = const Color(0xFF808080); - Color currentColor = const Color(0xFF808080); - - late StreamController _events; - - List algorithmNames = ['Alpha-Beta', 'PVS', 'MTD(f)']; - - final String tag = "[game_settings_page]"; - - @override - void initState() { - super.initState(); - _events = StreamController.broadcast(); - _events.add(10); - } - - Future _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 restoreFactoryDefaultSettings() async { - Future 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: [ - 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 children(BuildContext context) { - return [ - Text(S.of(context).whoMovesFirst, style: AppTheme.settingsHeaderStyle), - SettingsCard( - children: [ - 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: [ - 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: [ - 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: [ - 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: [ - 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: [ - 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: [ - 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 setSkillLevel() async { - showModalBottomSheet( - context: context, - builder: (_) => StatefulBuilder( - builder: _skillLevelSliderTheme, - ), - ); - } - - Future setMoveTime() async { - showModalBottomSheet( - context: context, - builder: (_) => StatefulBuilder( - builder: _moveTimeSliderTheme, - ), - ); - } - - Future setWhoMovesFirst(bool value) async { - setState(() => LocalDatabaseService.preferences.aiMovesFirst = !value); - - debugPrint( - "[config] aiMovesFirst: ${LocalDatabaseService.preferences.aiMovesFirst}", - ); - } - - Future setAiIsLazy(bool value) async { - setState(() => LocalDatabaseService.preferences.aiIsLazy = value); - - debugPrint("[config] aiMovesFirst: $value"); - } - - void setAlgorithm() { - Future 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: [ - 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 setDrawOnHumanExperience(bool value) async { - setState( - () => LocalDatabaseService.preferences.drawOnHumanExperience = value, - ); - - debugPrint("[config] drawOnHumanExperience: $value"); - } - - Future setConsiderMobility(bool value) async { - setState(() => LocalDatabaseService.preferences.considerMobility = value); - - debugPrint("[config] considerMobility: $value"); - } - - Future setIsAutoRestart(bool value) async { - setState(() => LocalDatabaseService.preferences.isAutoRestart = value); - - debugPrint("[config] isAutoRestart: $value"); - } - - Future setIsAutoChangeFirstMove(bool value) async { - setState( - () => LocalDatabaseService.preferences.isAutoChangeFirstMove = value, - ); - - debugPrint("[config] isAutoChangeFirstMove: $value"); - } - - Future setResignIfMostLose(bool value) async { - setState(() => LocalDatabaseService.preferences.resignIfMostLose = value); - - debugPrint("[config] resignIfMostLose: $value"); - } - - Future setShufflingEnabled(bool value) async { - setState(() => LocalDatabaseService.preferences.shufflingEnabled = value); - - debugPrint("[config] shufflingEnabled: $value"); - } - - Future setLearnEndgame(bool value) async { - setState(() => LocalDatabaseService.preferences.learnEndgame = value); - - debugPrint("[config] learnEndgame: $value"); - } - - Future setOpeningBook(bool value) async { - setState(() => LocalDatabaseService.preferences.openingBook = value); - - debugPrint("[config] openingBook: $value"); - } - - Future setTone(bool value) async { - setState(() => LocalDatabaseService.preferences.toneEnabled = value); - - debugPrint("[config] toneEnabled: $value"); - } - - Future setKeepMuteWhenTakingBack(bool value) async { - setState( - () => LocalDatabaseService.preferences.keepMuteWhenTakingBack = value, - ); - - debugPrint("[config] keepMuteWhenTakingBack: $value"); - } - - Future setScreenReaderSupport(bool value) async { - setState( - () => LocalDatabaseService.preferences.screenReaderSupport = value, - ); - - debugPrint("[config] screenReaderSupport: $value"); - } - - Future setDeveloperMode(bool value) async { - setState(() => LocalDatabaseService.preferences.developerMode = value); - - debugPrint("[config] developerMode: $value"); - } - - Future setExperimentsEnabled(bool value) async { - setState(() => LocalDatabaseService.preferences.experimentsEnabled = value); - - debugPrint("[config] experimentsEnabled: $value"); - } - - // Display - - Future setLanguage(Locale value) async { - setState(() => LocalDatabaseService.display.languageCode = value); - - debugPrint("[config] languageCode: $value"); - } - - Future setIsPieceCountInHandShown(bool value) async { - setState( - () => LocalDatabaseService.display.isPieceCountInHandShown = value, - ); - - debugPrint("[config] isPieceCountInHandShown: $value"); - } - - Future setIsNotationsShown(bool value) async { - setState(() => LocalDatabaseService.display.isNotationsShown = value); - - debugPrint("[config] isNotationsShown: $value"); - } - - Future setIsHistoryNavigationToolbarShown(bool value) async { - setState( - () => - LocalDatabaseService.display.isHistoryNavigationToolbarShown = value, - ); - - debugPrint("[config] isHistoryNavigationToolbarShown: $value"); - } - - Future setStandardNotationEnabled(bool value) async { - setState( - () => LocalDatabaseService.display.standardNotationEnabled = value, - ); - - debugPrint("[config] standardNotationEnabled: $value"); - } -} diff --git a/src/ui/flutter_app/lib/screens/home_drawer.dart b/src/ui/flutter_app/lib/screens/home_drawer.dart index 5d63e374..1aca709a 100644 --- a/src/ui/flutter_app/lib/screens/home_drawer.dart +++ b/src/ui/flutter_app/lib/screens/home_drawer.dart @@ -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 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( diff --git a/src/ui/flutter_app/lib/screens/navigation_home_screen.dart b/src/ui/flutter_app/lib/screens/navigation_home_screen.dart index e6b163be..d65263a0 100644 --- a/src/ui/flutter_app/lib/screens/navigation_home_screen.dart +++ b/src/ui/flutter_app/lib/screens/navigation_home_screen.dart @@ -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 { } 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 && diff --git a/src/ui/flutter_app/lib/screens/personalization_settings/animation_duration_slider.dart b/src/ui/flutter_app/lib/screens/personalization_settings/animation_duration_slider.dart new file mode 100644 index 00000000..3c1dbb57 --- /dev/null +++ b/src/ui/flutter_app/lib/screens/personalization_settings/animation_duration_slider.dart @@ -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 . +*/ + +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 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); + }, + ); + }, + ), + ), + ); + } +} diff --git a/src/ui/flutter_app/lib/screens/personalization_settings/board_boarder_line_width_slider.dart b/src/ui/flutter_app/lib/screens/personalization_settings/board_boarder_line_width_slider.dart new file mode 100644 index 00000000..5c365542 --- /dev/null +++ b/src/ui/flutter_app/lib/screens/personalization_settings/board_boarder_line_width_slider.dart @@ -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 . +*/ + +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 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); + }, + ); + }, + ), + ), + ); + } +} diff --git a/src/ui/flutter_app/lib/screens/personalization_settings/board_inner_line_width_slider.dart b/src/ui/flutter_app/lib/screens/personalization_settings/board_inner_line_width_slider.dart new file mode 100644 index 00000000..134fada4 --- /dev/null +++ b/src/ui/flutter_app/lib/screens/personalization_settings/board_inner_line_width_slider.dart @@ -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 . +*/ + +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 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); + }, + ); + }, + ), + ), + ); + } +} diff --git a/src/ui/flutter_app/lib/screens/personalization_settings/board_top_slider.dart b/src/ui/flutter_app/lib/screens/personalization_settings/board_top_slider.dart new file mode 100644 index 00000000..221bec3d --- /dev/null +++ b/src/ui/flutter_app/lib/screens/personalization_settings/board_top_slider.dart @@ -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 . +*/ + +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 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); + }, + ); + }, + ), + ), + ); + } +} diff --git a/src/ui/flutter_app/lib/screens/personalization_settings/color_selector_list_tile.dart b/src/ui/flutter_app/lib/screens/personalization_settings/color_selector_list_tile.dart new file mode 100644 index 00000000..798ce357 --- /dev/null +++ b/src/ui/flutter_app/lib/screens/personalization_settings/color_selector_list_tile.dart @@ -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 . +*/ + +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 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: [ + 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), + ), + ], + ); + } +} diff --git a/src/ui/flutter_app/lib/screens/personalization_settings/font_size_slider.dart b/src/ui/flutter_app/lib/screens/personalization_settings/font_size_slider.dart new file mode 100644 index 00000000..3311269a --- /dev/null +++ b/src/ui/flutter_app/lib/screens/personalization_settings/font_size_slider.dart @@ -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 . +*/ + +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 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); + }, + ); + }, + ), + ), + ); + } +} diff --git a/src/ui/flutter_app/lib/screens/personalization_settings/personalization_settings_page.dart b/src/ui/flutter_app/lib/screens/personalization_settings/personalization_settings_page.dart new file mode 100644 index 00000000..8f90a6b2 --- /dev/null +++ b/src/ui/flutter_app/lib/screens/personalization_settings/personalization_settings_page.dart @@ -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 . +*/ + +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 colorBox, _) { + final ColorSettings _colorSettings = colorBox.get( + LocalDatabaseService.colorSettingsKey, + defaultValue: ColorSettings(), + )!; + + return SettingsCard( + children: [ + _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 displayaBox, _) { + final Display _display = displayaBox.get( + LocalDatabaseService.colorSettingsKey, + defaultValue: Display(), + )!; + return SettingsCard( + children: [ + 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, + ), + ], + ), + ), + ); + } +} diff --git a/src/ui/flutter_app/lib/screens/personalization_settings/piece_width_slider.dart b/src/ui/flutter_app/lib/screens/personalization_settings/piece_width_slider.dart new file mode 100644 index 00000000..1ea9a8ac --- /dev/null +++ b/src/ui/flutter_app/lib/screens/personalization_settings/piece_width_slider.dart @@ -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 . +*/ + +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 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); + }, + ); + }, + ), + ), + ); + } +} diff --git a/src/ui/flutter_app/lib/screens/personalization_settings/point_style_modal.dart b/src/ui/flutter_app/lib/screens/personalization_settings/point_style_modal.dart new file mode 100644 index 00000000..eb34fc91 --- /dev/null +++ b/src/ui/flutter_app/lib/screens/personalization_settings/point_style_modal.dart @@ -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 . +*/ + +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: [ + 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, + ), + */ + ], + ), + ); + } +} diff --git a/src/ui/flutter_app/lib/screens/personalization_settings/point_width_slider.dart b/src/ui/flutter_app/lib/screens/personalization_settings/point_width_slider.dart new file mode 100644 index 00000000..2e817424 --- /dev/null +++ b/src/ui/flutter_app/lib/screens/personalization_settings/point_width_slider.dart @@ -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 . +*/ + +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 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); + }, + ); + }, + ), + ), + ); + } +} diff --git a/src/ui/flutter_app/lib/screens/personalization_settings_page.dart b/src/ui/flutter_app/lib/screens/personalization_settings_page.dart deleted file mode 100644 index 8bba0203..00000000 --- a/src/ui/flutter_app/lib/screens/personalization_settings_page.dart +++ /dev/null @@ -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 . -*/ - -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 { - // create some values - Color pickerColor = const Color(0xFF808080); - Color currentColor = const Color(0xFF808080); - - // ValueChanged callback - void changeColor(Color color) { - setState(() => pickerColor = color); - } - - Future showColorDialog(String colorString) async { - final Map 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: [ - 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 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 setBoardInnerLineWidth() async { - showModalBottomSheet( - context: context, - builder: (_) => StatefulBuilder( - builder: _boardInnerLineWidthSliderTheme, - ), - ); - } - - void setPointStyle() { - Future 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: [ - 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 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 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 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 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 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 children(BuildContext context) { - Future 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 [ - Text(S.of(context).display, style: AppTheme.settingsHeaderStyle), - SettingsCard( - children: [ - 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: [ - 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 setIsPieceCountInHandShown(bool value) async { - setState( - () => LocalDatabaseService.display.isPieceCountInHandShown = value, - ); - } - - Future setIsNotationsShown(bool value) async { - setState(() => LocalDatabaseService.display.isNotationsShown = value); - } - - Future setIsHistoryNavigationToolbarShown(bool value) async { - setState( - () => - LocalDatabaseService.display.isHistoryNavigationToolbarShown = value, - ); - } - - Future setStandardNotationEnabled(bool value) async { - setState( - () => LocalDatabaseService.display.standardNotationEnabled = value, - ); - - debugPrint("[config] standardNotationEnabled: $value"); - } -} diff --git a/src/ui/flutter_app/lib/screens/rule_settings/endgame_n_move_rule_modal.dart b/src/ui/flutter_app/lib/screens/rule_settings/endgame_n_move_rule_modal.dart new file mode 100644 index 00000000..27d2660c --- /dev/null +++ b/src/ui/flutter_app/lib/screens/rule_settings/endgame_n_move_rule_modal.dart @@ -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 . +*/ + +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: [ + 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, + ), + ], + ), + ), + ); + } +} diff --git a/src/ui/flutter_app/lib/screens/rule_settings/fly_piece_count_modal.dart b/src/ui/flutter_app/lib/screens/rule_settings/fly_piece_count_modal.dart new file mode 100644 index 00000000..c414264a --- /dev/null +++ b/src/ui/flutter_app/lib/screens/rule_settings/fly_piece_count_modal.dart @@ -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 . +*/ + +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: [ + 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, + ), + ], + ), + ); + } +} diff --git a/src/ui/flutter_app/lib/screens/rule_settings/n_move_rule_modal.dart b/src/ui/flutter_app/lib/screens/rule_settings/n_move_rule_modal.dart new file mode 100644 index 00000000..442fc9f5 --- /dev/null +++ b/src/ui/flutter_app/lib/screens/rule_settings/n_move_rule_modal.dart @@ -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 . +*/ + +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: [ + 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, + ), + ], + ), + ); + } +} diff --git a/src/ui/flutter_app/lib/screens/rule_settings/piece_count_modal.dart b/src/ui/flutter_app/lib/screens/rule_settings/piece_count_modal.dart new file mode 100644 index 00000000..22bf19a0 --- /dev/null +++ b/src/ui/flutter_app/lib/screens/rule_settings/piece_count_modal.dart @@ -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 . +*/ + +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: [ + 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, + ), + ], + ), + ); + } +} diff --git a/src/ui/flutter_app/lib/screens/rule_settings/rule_settings_page.dart b/src/ui/flutter_app/lib/screens/rule_settings/rule_settings_page.dart new file mode 100644 index 00000000..e6ab1f77 --- /dev/null +++ b/src/ui/flutter_app/lib/screens/rule_settings/rule_settings_page.dart @@ -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 . +*/ + +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 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: [ + 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: [ + 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: [ + 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: [ + 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: [ + 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, + ), + ), + ); + } +} diff --git a/src/ui/flutter_app/lib/screens/rule_settings_page.dart b/src/ui/flutter_app/lib/screens/rule_settings_page.dart deleted file mode 100644 index 80254d31..00000000 --- a/src/ui/flutter_app/lib/screens/rule_settings_page.dart +++ /dev/null @@ -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 . -*/ - -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 { - @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 children(BuildContext context) { - return [ - Text(S.of(context).general, style: AppTheme.settingsHeaderStyle), - SettingsCard( - children: [ - 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: [ - 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: [ - 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: [ - 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: [ - 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 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: [ - 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 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: [ - 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 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: [ - 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 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: [ - 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 setHasDiagonalLines(bool value) async { - setState( - () => rule.hasDiagonalLines = - LocalDatabaseService.rules.hasDiagonalLines = value, - ); - - debugPrint("[config] rule.hasDiagonalLines: $value"); - } - - Future setAllowFlyingAllowed(bool value) async { - setState(() => rule.mayFly = LocalDatabaseService.rules.mayFly = value); - - debugPrint("[config] rule.mayFly: $value"); - } - - Future setThreefoldRepetitionRule(bool value) async { - setState( - () => rule.threefoldRepetitionRule = - LocalDatabaseService.rules.threefoldRepetitionRule = value, - ); - - debugPrint("[config] rule.threefoldRepetitionRule: $value"); - } - - // Placing - - Future setHasBannedLocations(bool value) async { - setState( - () => rule.hasBannedLocations = - LocalDatabaseService.rules.hasBannedLocations = value, - ); - - debugPrint("[config] rule.hasBannedLocations: $value"); - } - - Future setIsWhiteLoseButNotDrawWhenBoardFull(bool value) async { - setState( - () => rule.isWhiteLoseButNotDrawWhenBoardFull = - LocalDatabaseService.rules.isWhiteLoseButNotDrawWhenBoardFull = value, - ); - - debugPrint("[config] rule.isWhiteLoseButNotDrawWhenBoardFull: $value"); - } - - Future setMayOnlyRemoveUnplacedPieceInPlacingPhase(bool value) async { - setState( - () => rule.mayOnlyRemoveUnplacedPieceInPlacingPhase = LocalDatabaseService - .rules.mayOnlyRemoveUnplacedPieceInPlacingPhase = value, - ); - - debugPrint( - "[config] rule.mayOnlyRemoveUnplacedPieceInPlacingPhase: $value", - ); - } - - // Moving - - Future 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 setIsDefenderMoveFirst(bool value) async { - setState( - () => rule.isDefenderMoveFirst = - LocalDatabaseService.rules.isDefenderMoveFirst = value, - ); - - debugPrint("[config] rule.isDefenderMoveFirst: $value"); - } - - Future setIsLoseButNotChangeSideWhenNoWay(bool value) async { - setState( - () => rule.isLoseButNotChangeSideWhenNoWay = - LocalDatabaseService.rules.isLoseButNotChangeSideWhenNoWay = value, - ); - - debugPrint("[config] rule.isLoseButNotChangeSideWhenNoWay: $value"); - } - - // Removing - - Future setAllowRemovePieceInMill(bool value) async { - setState( - () => rule.mayRemoveFromMillsAlways = - LocalDatabaseService.rules.mayRemoveFromMillsAlways = value, - ); - - debugPrint("[config] rule.mayRemoveFromMillsAlways: $value"); - } - - Future setAllowRemoveMultiPiecesWhenCloseMultiMill(bool value) async { - setState( - () => rule.mayRemoveMultiple = - LocalDatabaseService.rules.mayRemoveMultiple = value, - ); - - debugPrint("[config] rule.mayRemoveMultiple: $value"); - } - - // Unused - - Future setNPiecesAtLeast(int value) async { - setState( - () => rule.piecesAtLeastCount = - LocalDatabaseService.rules.piecesAtLeastCount = value, - ); - - debugPrint("[config] rule.piecesAtLeastCount: $value"); - } -} diff --git a/src/ui/flutter_app/lib/services/engine/engine.dart b/src/ui/flutter_app/lib/services/engine/engine.dart index 6d946eed..12c36589 100644 --- a/src/ui/flutter_app/lib/services/engine/engine.dart +++ b/src/ui/flutter_app/lib/services/engine/engine.dart @@ -36,7 +36,7 @@ class EngineResponse { } abstract class Engine { - Future setOptions(BuildContext context) async {} + Future setOptions() async {} Future startup() async {} Future shutdown() async {} Future search(Position? position); diff --git a/src/ui/flutter_app/lib/services/engine/native_engine.dart b/src/ui/flutter_app/lib/services/engine/native_engine.dart index e3280182..96e759d4 100644 --- a/src/ui/flutter_app/lib/services/engine/native_engine.dart +++ b/src/ui/flutter_app/lib/services/engine/native_engine.dart @@ -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 setOptions(BuildContext context) async { + Future 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}', ); } diff --git a/src/ui/flutter_app/lib/services/storage/storage.dart b/src/ui/flutter_app/lib/services/storage/storage.dart index 59c6ba96..3fd6b52e 100644 --- a/src/ui/flutter_app/lib/services/storage/storage.dart +++ b/src/ui/flutter_app/lib/services/storage/storage.dart @@ -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 _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 _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 _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 _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 resetStorage() async { + await _colorSettingsBox.delete(colorSettingsKey); + await _displayBox.delete(displayKey); + await _preferencesBox.delete(preferencesKey); + await _rulesBox.delete(rulesKey); } /// initilizes the [ColorSettings] reference static Future _initColorSettings() async { - Hive.registerAdapter(ColorSettingsAdapter()); Hive.registerAdapter(ColorAdapter()); + Hive.registerAdapter(ColorSettingsAdapter()); _colorSettingsBox = await Hive.openBox(_colorSettingsBoxName); } /// listens to changes inside the settings Box static ValueListenable> 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 _initDisplay() async { + Hive.registerAdapter(LocaleAdapter()); Hive.registerAdapter(DisplayAdapter()); _displayBox = await Hive.openBox(_displayBoxName); } /// listens to changes inside the settings Box static ValueListenable> 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 _initPreferences() async { Hive.registerAdapter(PreferencesAdapter()); - Hive.registerAdapter(LocaleAdapter()); _preferencesBox = await Hive.openBox(_preferencesBoxName); } /// listens to changes inside the settings Box static ValueListenable> 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 _initRules() async { @@ -127,11 +136,14 @@ class LocalDatabaseService { /// listens to changes inside the settings Box static ValueListenable> 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(); } diff --git a/src/ui/flutter_app/lib/services/storage/storage_v1.dart b/src/ui/flutter_app/lib/services/storage/storage_v1.dart index d972c8b7..6052e84d 100644 --- a/src/ui/flutter_app/lib/services/storage/storage_v1.dart +++ b/src/ui/flutter_app/lib/services/storage/storage_v1.dart @@ -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? _values = {}; +class DatabaseV1 { + const DatabaseV1._(); - // TODO: add constructor - static Future 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 migrate() async {} - - Future _load(String fileName) async { - // TODO: main() ExternalStorage - // var docDir = await getExternalStorageDirectory(); + static Future _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?> _loadFile(File _file) async { + debugPrint("$_tag Loading $_file ..."); try { final contents = await _file.readAsString(); - _values = jsonDecode(contents) as Map?; + final _values = jsonDecode(contents) as Map?; debugPrint(_values.toString()); + return _values; } catch (e) { debugPrint(e.toString()); - return false; } - - return true; } - Future 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 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 _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; } } diff --git a/src/ui/flutter_app/lib/shared/dialog.dart b/src/ui/flutter_app/lib/shared/dialog.dart index 8125f5e9..5352343b 100644 --- a/src/ui/flutter_app/lib/shared/dialog.dart +++ b/src/ui/flutter_app/lib/shared/dialog.dart @@ -54,12 +54,7 @@ void showCountdownDialog( builder: (BuildContext context, AsyncSnapshot 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, diff --git a/src/ui/flutter_app/pubspec.yaml b/src/ui/flutter_app/pubspec.yaml index 56b2ddb0..f8f75d0c 100644 --- a/src/ui/flutter_app/pubspec.yaml +++ b/src/ui/flutter_app/pubspec.yaml @@ -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