diff --git a/README.md b/README.md index 594b8fe7db488b8c159a3ad855953f3cd32e0571..5359b042f597ebc085cd983f9ca59ef71e339f7e 100644 --- a/README.md +++ b/README.md @@ -1 +1,3 @@ # Solitaire Game + +Simple, easy, with 4 available layouts. diff --git a/android/gradle.properties b/android/gradle.properties index 30298b3b3f04073678e48519b8c043edba635df8..a392afe6f07c995cecb66b32859bb129bf2a0055 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -1,5 +1,5 @@ org.gradle.jvmargs=-Xmx1536M android.useAndroidX=true android.enableJetifier=true -app.versionName=0.0.18 -app.versionCode=18 +app.versionName=0.1.0 +app.versionCode=19 diff --git a/assets/translations/en.json b/assets/translations/en.json index 13da5fcbf5903240f481642c2ef432873c7a3d7c..daff9b7f06d928cf6547c4e7d90f7f28be18339b 100644 --- a/assets/translations/en.json +++ b/assets/translations/en.json @@ -1,14 +1,12 @@ { "app_name": "Solitaire", - "bottom_nav_home": "Game", - "bottom_nav_settings": "Settings", - "bottom_nav_about": "About", - "settings_title": "Settings", "settings_label_theme": "Theme mode", "about_title": "About", "about_content": "Solitaire.", - "about_version": "Version: {version}" + "about_version": "Version: {version}", + + "": "" } diff --git a/assets/translations/fr.json b/assets/translations/fr.json index 0d43876a4b6ebfadf9222ed4ac88d5c53d5064ef..ebc19d92280150d15e4c787a22887f6ce74ea66c 100644 --- a/assets/translations/fr.json +++ b/assets/translations/fr.json @@ -1,14 +1,12 @@ { "app_name": "Solitaire", - "bottom_nav_home": "Jeu", - "bottom_nav_settings": "Réglages", - "bottom_nav_about": "Infos", - "settings_title": "Réglages", "settings_label_theme": "Thème de couleurs", "about_title": "Informations", "about_content": "Solitaire.", - "about_version": "Version : {version}" + "about_version": "Version : {version}", + + "": "" } diff --git a/assets/icons/button_back.png b/assets/ui/button_back.png similarity index 100% rename from assets/icons/button_back.png rename to assets/ui/button_back.png diff --git a/assets/ui/button_delete_saved_game.png b/assets/ui/button_delete_saved_game.png new file mode 100644 index 0000000000000000000000000000000000000000..5e4f217689b11e444b7163557d7e5d68f3bbfe7d Binary files /dev/null and b/assets/ui/button_delete_saved_game.png differ diff --git a/assets/ui/button_resume_game.png b/assets/ui/button_resume_game.png new file mode 100644 index 0000000000000000000000000000000000000000..b2ea0a02d05e42377eb551a4b51428b511a32f5d Binary files /dev/null and b/assets/ui/button_resume_game.png differ diff --git a/assets/icons/button_start.png b/assets/ui/button_start.png similarity index 100% rename from assets/icons/button_start.png rename to assets/ui/button_start.png diff --git a/assets/ui/game_fail.png b/assets/ui/game_fail.png new file mode 100644 index 0000000000000000000000000000000000000000..93f2801f9d6bb2ce508e1293cd64d6ff2e9970ec Binary files /dev/null and b/assets/ui/game_fail.png differ diff --git a/assets/icons/game_win.png b/assets/ui/game_win.png similarity index 100% rename from assets/icons/game_win.png rename to assets/ui/game_win.png diff --git a/assets/icons/placeholder.png b/assets/ui/placeholder.png similarity index 100% rename from assets/icons/placeholder.png rename to assets/ui/placeholder.png diff --git a/fastlane/metadata/android/en-US/changelogs/19.txt b/fastlane/metadata/android/en-US/changelogs/19.txt new file mode 100644 index 0000000000000000000000000000000000000000..d4afd512e55b3fd8ffbfd795adb9b00832e5aaef --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/19.txt @@ -0,0 +1 @@ +Improve/normalize game architecture. diff --git a/fastlane/metadata/android/fr-FR/changelogs/19.txt b/fastlane/metadata/android/fr-FR/changelogs/19.txt new file mode 100644 index 0000000000000000000000000000000000000000..6a9871a5eb8eb3c6e9106520f1cbf1f39f9e5ef7 --- /dev/null +++ b/fastlane/metadata/android/fr-FR/changelogs/19.txt @@ -0,0 +1 @@ +Amélioration/normalisation de l'architecture du jeu. diff --git a/lib/config/default_game_settings.dart b/lib/config/default_game_settings.dart index 6ac27dcf613094e37f638ebe8d69573e4219deca..65486565481923ce8d0a0280446c85ccb858198b 100644 --- a/lib/config/default_game_settings.dart +++ b/lib/config/default_game_settings.dart @@ -1,4 +1,4 @@ -import 'package:solitaire/utils/tools.dart'; +import 'package:solitaire/utils/tools.dart'; class DefaultGameSettings { // available game parameters codes @@ -21,6 +21,7 @@ class DefaultGameSettings { // layout: default value static const String defaultLayoutValue = layoutValueEnglish; + // available values from parameter code static List<String> getAvailableValues(String parameterCode) { switch (parameterCode) { case parameterCodeLayout: @@ -30,4 +31,9 @@ class DefaultGameSettings { printlog('Did not find any available value for game parameter "$parameterCode".'); return []; } + + // parameters displayed with assets (instead of painter) + static List<String> displayedWithAssets = [ + // + ]; } diff --git a/lib/config/default_global_settings.dart b/lib/config/default_global_settings.dart index 622a8b66d293815ec6ce9d50f7cefd9701606f1d..5e6ddc08eaa65f6837a222def09f0bcf249de698 100644 --- a/lib/config/default_global_settings.dart +++ b/lib/config/default_global_settings.dart @@ -1,4 +1,4 @@ -import 'package:solitaire/utils/tools.dart'; +import 'package:solitaire/utils/tools.dart'; class DefaultGlobalSettings { // available global parameters codes @@ -15,6 +15,7 @@ class DefaultGlobalSettings { // skin: default value static const String defaultSkinValue = skinValueDefault; + // available values from parameter code static List<String> getAvailableValues(String parameterCode) { switch (parameterCode) { case parameterCodeSkin: @@ -24,4 +25,9 @@ class DefaultGlobalSettings { printlog('Did not find any available value for global parameter "$parameterCode".'); return []; } + + // parameters displayed with assets (instead of painter) + static List<String> displayedWithAssets = [ + // + ]; } diff --git a/lib/config/menu.dart b/lib/config/menu.dart index 3d2fcfacf3e2ca175870530e2f4e33f74264bb79..dcb8a6fe45d9fe20f244e449a89634b4c692a0ae 100644 --- a/lib/config/menu.dart +++ b/lib/config/menu.dart @@ -6,12 +6,10 @@ import 'package:solitaire/ui/screens/page_game.dart'; import 'package:solitaire/ui/screens/page_settings.dart'; class MenuItem { - final String code; final Icon icon; final Widget page; const MenuItem({ - required this.code, required this.icon, required this.page, }); @@ -20,21 +18,18 @@ class MenuItem { class Menu { static const indexGame = 0; static const menuItemGame = MenuItem( - code: 'bottom_nav_game', icon: Icon(UniconsLine.home), page: PageGame(), ); static const indexSettings = 1; static const menuItemSettings = MenuItem( - code: 'bottom_nav_settings', icon: Icon(UniconsLine.setting), page: PageSettings(), ); static const indexAbout = 2; static const menuItemAbout = MenuItem( - code: 'bottom_nav_about', icon: Icon(UniconsLine.info_circle), page: PageAbout(), ); diff --git a/lib/config/theme.dart b/lib/config/theme.dart index be390348c7868e7c63387df13e13c46de43f8a23..74f532fd5abf693979118609564d29167e902009 100644 --- a/lib/config/theme.dart +++ b/lib/config/theme.dart @@ -39,11 +39,9 @@ final ColorScheme lightColorScheme = ColorScheme.light( secondary: primarySwatch.shade500, onSecondary: Colors.white, error: errorColor, - background: textSwatch.shade200, - onBackground: textSwatch.shade500, onSurface: textSwatch.shade500, surface: textSwatch.shade50, - surfaceVariant: Colors.white, + surfaceContainerHighest: Colors.white, shadow: textSwatch.shade900.withOpacity(.1), ); @@ -52,11 +50,9 @@ final ColorScheme darkColorScheme = ColorScheme.dark( secondary: primarySwatch.shade500, onSecondary: Colors.white, error: errorColor, - background: const Color(0xFF171724), - onBackground: textSwatch.shade400, onSurface: textSwatch.shade300, surface: const Color(0xFF262630), - surfaceVariant: const Color(0xFF282832), + surfaceContainerHighest: const Color(0xFF282832), shadow: textSwatch.shade900.withOpacity(.2), ); @@ -192,5 +188,3 @@ final ThemeData darkTheme = lightTheme.copyWith( ), ), ); - -final ThemeData appTheme = darkTheme; diff --git a/lib/cubit/game_cubit.dart b/lib/cubit/game_cubit.dart index aba2472b6c5e923a383e4da46abd8a1611a43e88..1ce79e07584c3642b3614dce0c50df0b279bdcb8 100644 --- a/lib/cubit/game_cubit.dart +++ b/lib/cubit/game_cubit.dart @@ -2,10 +2,10 @@ import 'package:equatable/equatable.dart'; import 'package:flutter/material.dart'; import 'package:hydrated_bloc/hydrated_bloc.dart'; -import 'package:solitaire/models/cell_location.dart'; -import 'package:solitaire/models/game.dart'; -import 'package:solitaire/models/settings_game.dart'; -import 'package:solitaire/models/settings_global.dart'; +import 'package:solitaire/models/game/cell_location.dart'; +import 'package:solitaire/models/game/game.dart'; +import 'package:solitaire/models/settings/settings_game.dart'; +import 'package:solitaire/models/settings/settings_global.dart'; import 'package:solitaire/utils/tools.dart'; part 'game_state.dart'; @@ -13,7 +13,7 @@ part 'game_state.dart'; class GameCubit extends HydratedCubit<GameState> { GameCubit() : super(GameState( - currentGame: Game.createNull(), + currentGame: Game.createEmpty(), )); void updateState(Game game) { @@ -24,11 +24,17 @@ class GameCubit extends HydratedCubit<GameState> { void refresh() { final Game game = Game( + // Settings gameSettings: state.currentGame.gameSettings, globalSettings: state.currentGame.globalSettings, - board: state.currentGame.board, + // State isRunning: state.currentGame.isRunning, + isStarted: state.currentGame.isStarted, isFinished: state.currentGame.isFinished, + animationInProgress: state.currentGame.animationInProgress, + // Base data + board: state.currentGame.board, + // Game data movesCount: state.currentGame.movesCount, remainingPegsCount: state.currentGame.remainingPegsCount, allowedMovesCount: state.currentGame.allowedMovesCount, @@ -42,7 +48,8 @@ class GameCubit extends HydratedCubit<GameState> { required GameSettings gameSettings, required GlobalSettings globalSettings, }) { - Game newGame = Game.createNew( + final Game newGame = Game.createNew( + // Settings gameSettings: gameSettings, globalSettings: globalSettings, ); @@ -62,12 +69,24 @@ class GameCubit extends HydratedCubit<GameState> { refresh(); } + void resumeSavedGame() { + state.currentGame.isRunning = true; + refresh(); + } + + void deleteSavedGame() { + state.currentGame.isRunning = false; + state.currentGame.isFinished = true; + refresh(); + } + void updatePegValue(CellLocation location, bool hasPeg) { state.currentGame.board.cells[location.row][location.col].hasPeg = hasPeg; refresh(); } void incrementMovesCount() { + state.currentGame.isStarted = true; state.currentGame.movesCount++; refresh(); } @@ -111,7 +130,7 @@ class GameCubit extends HydratedCubit<GameState> { @override GameState? fromJson(Map<String, dynamic> json) { - Game currentGame = json['currentGame'] as Game; + final Game currentGame = json['currentGame'] as Game; return GameState( currentGame: currentGame, diff --git a/lib/cubit/game_state.dart b/lib/cubit/game_state.dart index 3fd161a0915313722b7a15c55c7cf538a7e3b6e1..00e211668c3269255926939324355792abd61c41 100644 --- a/lib/cubit/game_state.dart +++ b/lib/cubit/game_state.dart @@ -12,8 +12,4 @@ class GameState extends Equatable { List<dynamic> get props => <dynamic>[ currentGame, ]; - - Map<String, dynamic> get values => <String, dynamic>{ - 'currentGame': currentGame, - }; } diff --git a/lib/cubit/settings_game_cubit.dart b/lib/cubit/settings_game_cubit.dart index 7399314684c139542eb3559b67e6dceeb9641bde..18848abeb795ba9358a804c672234f011f1d8c77 100644 --- a/lib/cubit/settings_game_cubit.dart +++ b/lib/cubit/settings_game_cubit.dart @@ -3,8 +3,7 @@ import 'package:flutter/material.dart'; import 'package:hydrated_bloc/hydrated_bloc.dart'; import 'package:solitaire/config/default_game_settings.dart'; -import 'package:solitaire/models/settings_game.dart'; -import 'package:solitaire/utils/tools.dart'; +import 'package:solitaire/models/settings/settings_game.dart'; part 'settings_game_state.dart'; @@ -28,14 +27,12 @@ class GameSettingsCubit extends HydratedCubit<GameSettingsState> { case DefaultGameSettings.parameterCodeLayout: return GameSettings.getLayoutValueFromUnsafe(state.settings.layout); } + return ''; } void setParameterValue(String code, String value) { - printlog('GameSettingsCubit.setParameterValue'); - printlog('code: $code / value: $value'); - - String layout = code == DefaultGameSettings.parameterCodeLayout + final String layout = code == DefaultGameSettings.parameterCodeLayout ? value : getParameterValue(DefaultGameSettings.parameterCodeLayout); @@ -46,7 +43,7 @@ class GameSettingsCubit extends HydratedCubit<GameSettingsState> { @override GameSettingsState? fromJson(Map<String, dynamic> json) { - String layout = json[DefaultGameSettings.parameterCodeLayout] as String; + final String layout = json[DefaultGameSettings.parameterCodeLayout] as String; return GameSettingsState( settings: GameSettings( diff --git a/lib/cubit/settings_game_state.dart b/lib/cubit/settings_game_state.dart index b773dc69be12673b158e880e2d7e6e7bec465506..5acd85b44ba541e1c5e9c26af1c4be26a385b9ed 100644 --- a/lib/cubit/settings_game_state.dart +++ b/lib/cubit/settings_game_state.dart @@ -12,8 +12,4 @@ class GameSettingsState extends Equatable { List<dynamic> get props => <dynamic>[ settings, ]; - - Map<String, dynamic> get values => <String, dynamic>{ - 'settings': settings, - }; } diff --git a/lib/cubit/settings_global_cubit.dart b/lib/cubit/settings_global_cubit.dart index 274c113cff2bfa94e9e3ea492e9359ca11d149ca..a4042135ab4420ed235b635b01ca14ba596f6dfb 100644 --- a/lib/cubit/settings_global_cubit.dart +++ b/lib/cubit/settings_global_cubit.dart @@ -3,7 +3,7 @@ import 'package:flutter/material.dart'; import 'package:hydrated_bloc/hydrated_bloc.dart'; import 'package:solitaire/config/default_global_settings.dart'; -import 'package:solitaire/models/settings_global.dart'; +import 'package:solitaire/models/settings/settings_global.dart'; part 'settings_global_state.dart'; diff --git a/lib/cubit/settings_global_state.dart b/lib/cubit/settings_global_state.dart index 4e4fbdf707b4e805f2092d0ca6a68a2de1c957c6..ebcddd700f252257223ca8e16c85202b04f3ff24 100644 --- a/lib/cubit/settings_global_state.dart +++ b/lib/cubit/settings_global_state.dart @@ -12,8 +12,4 @@ class GlobalSettingsState extends Equatable { List<dynamic> get props => <dynamic>[ settings, ]; - - Map<String, dynamic> get values => <String, dynamic>{ - 'settings': settings, - }; } diff --git a/lib/main.dart b/lib/main.dart index d507c2d5d0495158dbf6f02d6a0fb3ba4a6faaa8..48897d6132ef277ec095c43263ee0e9643de8662 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -2,11 +2,13 @@ import 'dart:io'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:hive/hive.dart'; import 'package:hydrated_bloc/hydrated_bloc.dart'; import 'package:path_provider/path_provider.dart'; +import 'package:solitaire/config/default_global_settings.dart'; import 'package:solitaire/config/theme.dart'; import 'package:solitaire/cubit/game_cubit.dart'; import 'package:solitaire/cubit/nav_cubit.dart'; @@ -25,18 +27,17 @@ void main() async { storageDirectory: tmpDir, ); - runApp( - EasyLocalization( - path: 'assets/translations', - supportedLocales: const <Locale>[ - Locale('en'), - Locale('fr'), - ], - fallbackLocale: const Locale('en'), - useFallbackTranslations: true, - child: const MyApp(), - ), - ); + SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]) + .then((value) => runApp(EasyLocalization( + path: 'assets/translations', + supportedLocales: const <Locale>[ + Locale('en'), + Locale('fr'), + ], + fallbackLocale: const Locale('en'), + useFallbackTranslations: true, + child: const MyApp(), + ))); } class MyApp extends StatelessWidget { @@ -44,6 +45,11 @@ class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { + final List<String> assets = getImagesAssets(); + for (String asset in assets) { + precacheImage(AssetImage(asset), context); + } + return MultiBlocProvider( providers: [ BlocProvider<NavCubit>(create: (context) => NavCubit()), @@ -73,4 +79,36 @@ class MyApp extends StatelessWidget { ), ); } + + List<String> getImagesAssets() { + final List<String> assets = []; + + final List<String> gameImages = [ + 'button_back', + 'button_delete_saved_game', + 'button_resume_game', + 'button_start', + 'game_fail', + 'game_win', + 'placeholder', + ]; + + for (String image in gameImages) { + assets.add('assets/ui/$image.png'); + } + + final List<String> skinImages = [ + 'board', + 'hole', + 'peg', + ]; + + for (String skin in DefaultGlobalSettings.allowedSkinValues) { + for (String image in skinImages) { + assets.add('assets/skins/${skin}_$image.png'); + } + } + + return assets; + } } diff --git a/lib/models/board.dart b/lib/models/game/board.dart similarity index 96% rename from lib/models/board.dart rename to lib/models/game/board.dart index eb1ce73259a307e58116df681052bb81b551d6b2..2834c6d212e0ba45b869c7309547db07339dfda6 100644 --- a/lib/models/board.dart +++ b/lib/models/game/board.dart @@ -1,7 +1,7 @@ import 'package:solitaire/data/game_data.dart'; -import 'package:solitaire/models/cell.dart'; -import 'package:solitaire/models/cell_location.dart'; -import 'package:solitaire/models/settings_game.dart'; +import 'package:solitaire/models/game/cell.dart'; +import 'package:solitaire/models/game/cell_location.dart'; +import 'package:solitaire/models/settings/settings_game.dart'; import 'package:solitaire/utils/tools.dart'; typedef BoardCells = List<List<Cell>>; diff --git a/lib/models/cell.dart b/lib/models/game/cell.dart similarity index 86% rename from lib/models/cell.dart rename to lib/models/game/cell.dart index 127c3006372ae606f25206aa7ef1209ec78a1ecc..91b6d5e59931d314787fae7408fbec1009501c88 100644 --- a/lib/models/cell.dart +++ b/lib/models/game/cell.dart @@ -1,5 +1,5 @@ -import 'package:solitaire/models/cell_location.dart'; -import 'package:solitaire/utils/tools.dart'; +import 'package:solitaire/models/game/cell_location.dart'; +import 'package:solitaire/utils/tools.dart'; class Cell { Cell({ diff --git a/lib/models/cell_location.dart b/lib/models/game/cell_location.dart similarity index 92% rename from lib/models/cell_location.dart rename to lib/models/game/cell_location.dart index f9b90220feff4828ca6fa846d4933cac195608b2..f0fc583307968c15324e589410255bd674a27f0a 100644 --- a/lib/models/cell_location.dart +++ b/lib/models/game/cell_location.dart @@ -1,4 +1,4 @@ -import 'package:solitaire/utils/tools.dart'; +import 'package:solitaire/utils/tools.dart'; class CellLocation { final int col; diff --git a/lib/models/game.dart b/lib/models/game/game.dart similarity index 60% rename from lib/models/game.dart rename to lib/models/game/game.dart index 009ff4f3b07ce3370c5b6764f89e8c3f2256fea2..76f09e526f76c6675e3635dfd2a832f591861521 100644 --- a/lib/models/game.dart +++ b/lib/models/game/game.dart @@ -1,37 +1,54 @@ -import 'package:solitaire/models/board.dart'; -import 'package:solitaire/models/cell.dart'; -import 'package:solitaire/models/settings_game.dart'; -import 'package:solitaire/models/settings_global.dart'; +import 'package:solitaire/models/game/board.dart'; +import 'package:solitaire/models/game/cell.dart'; +import 'package:solitaire/models/settings/settings_game.dart'; +import 'package:solitaire/models/settings/settings_global.dart'; import 'package:solitaire/utils/tools.dart'; class Game { Game({ + // Settings required this.gameSettings, required this.globalSettings, - required this.board, + + // State this.isRunning = false, + this.isStarted = false, this.isFinished = false, + this.animationInProgress = false, + + // Base data + required this.board, + + // Game data this.movesCount = 0, this.remainingPegsCount = 0, this.allowedMovesCount = 0, }); + // Settings final GameSettings gameSettings; final GlobalSettings globalSettings; - bool isRunning = false; - bool isFinished = false; + // State + bool isRunning; + bool isStarted; + bool isFinished; + bool animationInProgress; - Board board; + // Base data + final Board board; - int movesCount = 0; - int remainingPegsCount = 0; - int allowedMovesCount = 0; + // Game data + int movesCount; + int remainingPegsCount; + int allowedMovesCount; - factory Game.createNull() { + factory Game.createEmpty() { return Game( + // Settings gameSettings: GameSettings.createDefault(), globalSettings: GlobalSettings.createDefault(), + // Base data board: Board.createEmpty(), ); } @@ -40,22 +57,25 @@ class Game { GameSettings? gameSettings, GlobalSettings? globalSettings, }) { - GameSettings newGameSettings = gameSettings ?? GameSettings.createDefault(); - GlobalSettings newGlobalSettings = globalSettings ?? GlobalSettings.createDefault(); + final GameSettings newGameSettings = gameSettings ?? GameSettings.createDefault(); + final GlobalSettings newGlobalSettings = globalSettings ?? GlobalSettings.createDefault(); return Game( + // Settings gameSettings: newGameSettings, globalSettings: newGlobalSettings, - board: Board.createNew( - gameSettings: newGameSettings, - ), + // State isRunning: true, + // Base data + board: Board.createNew(gameSettings: newGameSettings), ); } - int get boardSize => board.boardSize; + bool get canBeResumed => isStarted && !isFinished; + + bool get gameWon => isRunning && isStarted && isFinished; - bool get gameWon => (isFinished && (remainingPegsCount == 1)); + int get boardSize => board.boardSize; List<Cell> listRemainingPegs() { final List<Cell> pegs = []; @@ -106,19 +126,21 @@ class Game { void dump() { printlog(''); printlog('## Current game dump:'); - printlog(''); + printlog('$Game:'); + printlog(' Settings'); gameSettings.dump(); globalSettings.dump(); - printlog(''); - printlog(''); - printlog('$Game: '); - printlog(' isRunning: $isRunning'); - printlog(' isFinished: $isFinished'); - printlog(' movesCount: $movesCount'); - printlog(' remainingPegsCount: $remainingPegsCount'); - printlog(' allowedMovesCount: $allowedMovesCount'); - printlog(''); + printlog(' State'); + printlog(' isRunning: $isRunning'); + printlog(' isStarted: $isStarted'); + printlog(' isFinished: $isFinished'); + printlog(' animationInProgress: $animationInProgress'); + printlog(' Base data'); board.dump(); + printlog(' Game data'); + printlog(' movesCount: $movesCount'); + printlog(' remainingPegsCount: $remainingPegsCount'); + printlog(' allowedMovesCount: $allowedMovesCount'); printlog(''); } @@ -129,11 +151,17 @@ class Game { Map<String, dynamic>? toJson() { return <String, dynamic>{ + // Settings 'gameSettings': gameSettings.toJson(), 'globalSettings': globalSettings.toJson(), - 'board': board.toJson(), + // State 'isRunning': isRunning, + 'isStarted': isStarted, 'isFinished': isFinished, + 'animationInProgress': animationInProgress, + // Base data + 'board': board.toJson(), + // Game data 'movesCount': movesCount, 'remainingPegsCount': remainingPegsCount, 'allowedMovesCount': allowedMovesCount, diff --git a/lib/models/settings_game.dart b/lib/models/settings/settings_game.dart similarity index 91% rename from lib/models/settings_game.dart rename to lib/models/settings/settings_game.dart index cb00bc94a4ce5dab5065076202e0215e1cd21b97..006584e7922673a67c471deec0aa9427230a4cf0 100644 --- a/lib/models/settings_game.dart +++ b/lib/models/settings/settings_game.dart @@ -1,5 +1,5 @@ import 'package:solitaire/config/default_game_settings.dart'; -import 'package:solitaire/utils/tools.dart'; +import 'package:solitaire/utils/tools.dart'; class GameSettings { final String layout; @@ -23,7 +23,7 @@ class GameSettings { } void dump() { - printlog('$GameSettings: '); + printlog('$GameSettings:'); printlog(' ${DefaultGameSettings.parameterCodeLayout}: $layout'); printlog(''); } diff --git a/lib/models/settings_global.dart b/lib/models/settings/settings_global.dart similarity index 91% rename from lib/models/settings_global.dart rename to lib/models/settings/settings_global.dart index dd4b7c86673d9ed4e43f5cdbb0f8e9a0bd10f851..e67b92d4293993d8516a9345a6a7a112a910deca 100644 --- a/lib/models/settings_global.dart +++ b/lib/models/settings/settings_global.dart @@ -1,5 +1,5 @@ import 'package:solitaire/config/default_global_settings.dart'; -import 'package:solitaire/utils/tools.dart'; +import 'package:solitaire/utils/tools.dart'; class GlobalSettings { String skin; @@ -23,7 +23,7 @@ class GlobalSettings { } void dump() { - printlog('$GlobalSettings: '); + printlog('$GlobalSettings:'); printlog(' ${DefaultGlobalSettings.parameterCodeSkin}: $skin'); printlog(''); } diff --git a/lib/models/types.dart b/lib/models/types.dart deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/lib/ui/game/game_end.dart b/lib/ui/game/game_end.dart new file mode 100644 index 0000000000000000000000000000000000000000..7f0d3f7a80389a9aebb2602e32faef679440f27b --- /dev/null +++ b/lib/ui/game/game_end.dart @@ -0,0 +1,52 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +import 'package:solitaire/cubit/game_cubit.dart'; +import 'package:solitaire/models/game/game.dart'; +import 'package:solitaire/ui/widgets/actions/button_game_quit.dart'; + +class GameEndWidget extends StatelessWidget { + const GameEndWidget({super.key}); + + @override + Widget build(BuildContext context) { + return BlocBuilder<GameCubit, GameState>( + builder: (BuildContext context, GameState gameState) { + final Game currentGame = gameState.currentGame; + + final Image decorationImage = Image( + image: AssetImage( + currentGame.gameWon ? 'assets/ui/game_win.png' : 'assets/ui/game_fail.png'), + fit: BoxFit.fill, + ); + + return Container( + margin: const EdgeInsets.all(2), + padding: const EdgeInsets.all(2), + child: Table( + defaultColumnWidth: const IntrinsicColumnWidth(), + children: [ + TableRow( + children: [ + Column( + children: [decorationImage], + ), + Column( + children: [ + currentGame.animationInProgress == true + ? decorationImage + : const QuitGameButton() + ], + ), + Column( + children: [decorationImage], + ), + ], + ), + ], + ), + ); + }, + ); + } +} diff --git a/lib/ui/widgets/game/indicator_top.dart b/lib/ui/game/game_top.dart similarity index 91% rename from lib/ui/widgets/game/indicator_top.dart rename to lib/ui/game/game_top.dart index bc66f795fb1c03308aa1a4492367d87c9cb89dd4..b6319e7591694b2d57ac850f64c12a9999a1fb92 100644 --- a/lib/ui/widgets/game/indicator_top.dart +++ b/lib/ui/game/game_top.dart @@ -2,10 +2,10 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:solitaire/cubit/game_cubit.dart'; -import 'package:solitaire/models/game.dart'; +import 'package:solitaire/models/game/game.dart'; -class TopIndicator extends StatelessWidget { - const TopIndicator({super.key}); +class GameTopWidget extends StatelessWidget { + const GameTopWidget({super.key}); @override Widget build(BuildContext context) { diff --git a/lib/ui/helpers/app_titles.dart b/lib/ui/helpers/app_titles.dart new file mode 100644 index 0000000000000000000000000000000000000000..b98107b12fabc3114ebfbec994166b588abcf1ad --- /dev/null +++ b/lib/ui/helpers/app_titles.dart @@ -0,0 +1,32 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; + +class AppHeader extends StatelessWidget { + const AppHeader({super.key, required this.text}); + + final String text; + + @override + Widget build(BuildContext context) { + return Text( + tr(text), + textAlign: TextAlign.start, + style: Theme.of(context).textTheme.headlineMedium!.apply(fontWeightDelta: 2), + ); + } +} + +class AppTitle extends StatelessWidget { + const AppTitle({super.key, required this.text}); + + final String text; + + @override + Widget build(BuildContext context) { + return Text( + tr(text), + textAlign: TextAlign.start, + style: Theme.of(context).textTheme.titleLarge!.apply(fontWeightDelta: 2), + ); + } +} diff --git a/lib/ui/helpers/outlined_text_widget.dart b/lib/ui/helpers/outlined_text_widget.dart new file mode 100644 index 0000000000000000000000000000000000000000..0fbd95533de0b23f2c599134eac9447a09a98e31 --- /dev/null +++ b/lib/ui/helpers/outlined_text_widget.dart @@ -0,0 +1,51 @@ +import 'package:flutter/material.dart'; + +import 'package:solitaire/utils/color_extensions.dart'; + +class OutlinedText extends StatelessWidget { + const OutlinedText({ + super.key, + required this.text, + required this.fontSize, + required this.textColor, + this.outlineColor, + }); + + final String text; + final double fontSize; + final Color textColor; + final Color? outlineColor; + + @override + Widget build(BuildContext context) { + final double delta = fontSize / 30; + + return Text( + text, + style: TextStyle( + inherit: true, + fontSize: fontSize, + fontWeight: FontWeight.w600, + color: textColor, + shadows: [ + Shadow( + offset: Offset(-delta, -delta), + color: outlineColor ?? textColor.darken(), + ), + Shadow( + offset: Offset(delta, -delta), + color: outlineColor ?? textColor.darken(), + ), + Shadow( + offset: Offset(delta, delta), + color: outlineColor ?? textColor.darken(), + ), + Shadow( + offset: Offset(-delta, delta), + color: outlineColor ?? textColor.darken(), + ), + ], + ), + ); + } +} diff --git a/lib/ui/layouts/game_layout.dart b/lib/ui/layouts/game_layout.dart new file mode 100644 index 0000000000000000000000000000000000000000..36356465d23ec0f96e49ae1a412c4950aefaea1a --- /dev/null +++ b/lib/ui/layouts/game_layout.dart @@ -0,0 +1,38 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +import 'package:solitaire/cubit/game_cubit.dart'; +import 'package:solitaire/models/game/game.dart'; +import 'package:solitaire/ui/game/game_end.dart'; +import 'package:solitaire/ui/game/game_top.dart'; +import 'package:solitaire/ui/widgets/game/game_board.dart'; + +class GameLayout extends StatelessWidget { + const GameLayout({super.key}); + + @override + Widget build(BuildContext context) { + return BlocBuilder<GameCubit, GameState>( + builder: (BuildContext context, GameState gameState) { + final Game currentGame = gameState.currentGame; + + return Container( + alignment: AlignmentDirectional.topCenter, + padding: const EdgeInsets.all(4), + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + const GameTopWidget(), + const SizedBox(height: 8), + const GameBoardWidget(), + const SizedBox(height: 8), + const Expanded(child: SizedBox.shrink()), + currentGame.isFinished ? const GameEndWidget() : const SizedBox.shrink(), + ], + ), + ); + }, + ); + } +} diff --git a/lib/ui/widgets/parameters.dart b/lib/ui/layouts/parameters_layout.dart similarity index 55% rename from lib/ui/widgets/parameters.dart rename to lib/ui/layouts/parameters_layout.dart index 11b080d42b81d7c750212791a67537e8979818ba..d0520208a67def5f488ffb2f41d119f543ad5857 100644 --- a/lib/ui/widgets/parameters.dart +++ b/lib/ui/layouts/parameters_layout.dart @@ -5,11 +5,16 @@ import 'package:solitaire/config/default_game_settings.dart'; import 'package:solitaire/config/default_global_settings.dart'; import 'package:solitaire/cubit/settings_game_cubit.dart'; import 'package:solitaire/cubit/settings_global_cubit.dart'; -import 'package:solitaire/ui/painters/parameter_painter.dart'; -import 'package:solitaire/ui/widgets/button_game_start_new.dart'; +import 'package:solitaire/ui/parameters/parameter_image.dart'; +import 'package:solitaire/ui/parameters/parameter_painter.dart'; +import 'package:solitaire/ui/widgets/actions/button_delete_saved_game.dart'; +import 'package:solitaire/ui/widgets/actions/button_game_start_new.dart'; +import 'package:solitaire/ui/widgets/actions/button_resume_saved_game.dart'; -class Parameters extends StatelessWidget { - const Parameters({super.key}); +class ParametersLayout extends StatelessWidget { + const ParametersLayout({super.key, required this.canResume}); + + final bool canResume; final double separatorHeight = 8.0; @@ -31,7 +36,24 @@ class Parameters extends StatelessWidget { } lines.add(SizedBox(height: separatorHeight)); - lines.add(const Expanded(child: StartNewGameButton())); + + if (canResume == false) { + // Start new game + lines.add(const Expanded( + child: StartNewGameButton(), + )); + } else { + // Resume game + lines.add(const Expanded( + child: ResumeSavedGameButton(), + )); + // Delete saved game + lines.add(SizedBox.square( + dimension: MediaQuery.of(context).size.width / 4, + child: const DeleteSavedGameButton(), + )); + } + lines.add(SizedBox(height: separatorHeight)); // Global settings @@ -85,22 +107,39 @@ class Parameters extends StatelessWidget { final double displayWidth = MediaQuery.of(context).size.width; final double itemWidth = displayWidth / availableValues.length - 26; + final bool displayedWithAssets = + DefaultGlobalSettings.displayedWithAssets.contains(code) || + DefaultGameSettings.displayedWithAssets.contains(code); + return TextButton( - child: CustomPaint( - size: Size(itemWidth, itemWidth), - willChange: false, - painter: ParameterPainter( - code: code, - value: value, - isSelected: isActive, - gameSettings: gameSettingsState.settings, - globalSettings: globalSettingsState.settings, - ), - isComplex: true, + child: Container( + child: displayedWithAssets + ? SizedBox.square( + dimension: itemWidth, + child: ParameterImage( + code: code, + value: value, + isSelected: isActive, + ), + ) + : CustomPaint( + size: Size(itemWidth, itemWidth), + willChange: false, + painter: ParameterPainter( + code: code, + value: value, + isSelected: isActive, + gameSettings: gameSettingsState.settings, + globalSettings: globalSettingsState.settings, + ), + isComplex: true, + ), ), - onPressed: () => isGlobal - ? globalSettingsCubit.setParameterValue(code, value) - : gameSettingsCubit.setParameterValue(code, value), + onPressed: () { + isGlobal + ? globalSettingsCubit.setParameterValue(code, value) + : gameSettingsCubit.setParameterValue(code, value); + }, ); }, ); diff --git a/lib/ui/parameters/parameter_image.dart b/lib/ui/parameters/parameter_image.dart new file mode 100644 index 0000000000000000000000000000000000000000..fc4b576f85b01158b74548400d11a4d027c57fbe --- /dev/null +++ b/lib/ui/parameters/parameter_image.dart @@ -0,0 +1,38 @@ +import 'package:flutter/material.dart'; + +class ParameterImage extends StatelessWidget { + const ParameterImage({ + super.key, + required this.code, + required this.value, + required this.isSelected, + }); + + final String code; + final String value; + final bool isSelected; + + static const Color buttonBackgroundColor = Colors.white; + static const Color buttonBorderColorActive = Colors.blue; + static const Color buttonBorderColorInactive = Colors.white; + static const double buttonBorderWidth = 8.0; + static const double buttonBorderRadius = 8.0; + + @override + Widget build(BuildContext context) { + return Container( + decoration: BoxDecoration( + color: buttonBackgroundColor, + borderRadius: BorderRadius.circular(buttonBorderRadius), + border: Border.all( + color: isSelected ? buttonBorderColorActive : buttonBorderColorInactive, + width: buttonBorderWidth, + ), + ), + child: Image( + image: AssetImage('assets/ui/${code}_$value.png'), + fit: BoxFit.fill, + ), + ); + } +} diff --git a/lib/ui/painters/parameter_painter.dart b/lib/ui/parameters/parameter_painter.dart similarity index 94% rename from lib/ui/painters/parameter_painter.dart rename to lib/ui/parameters/parameter_painter.dart index 4ed92d7734eb935bbe24925a49fcab2864476d33..9b86f6faac98cecffe804d246ce42f10df91d272 100644 --- a/lib/ui/painters/parameter_painter.dart +++ b/lib/ui/parameters/parameter_painter.dart @@ -4,11 +4,11 @@ import 'package:flutter/material.dart'; import 'package:solitaire/config/default_game_settings.dart'; import 'package:solitaire/data/game_data.dart'; -import 'package:solitaire/models/board.dart'; -import 'package:solitaire/models/cell.dart'; -import 'package:solitaire/models/cell_location.dart'; -import 'package:solitaire/models/settings_game.dart'; -import 'package:solitaire/models/settings_global.dart'; +import 'package:solitaire/models/game/board.dart'; +import 'package:solitaire/models/game/cell.dart'; +import 'package:solitaire/models/game/cell_location.dart'; +import 'package:solitaire/models/settings/settings_game.dart'; +import 'package:solitaire/models/settings/settings_global.dart'; import 'package:solitaire/utils/tools.dart'; class ParameterPainter extends CustomPainter { diff --git a/lib/ui/screens/page_about.dart b/lib/ui/screens/page_about.dart index f63e1530869dd9aaf2a093e2610f326c3050589f..d4e5947305863d030a6888e0055a31761f696ace 100644 --- a/lib/ui/screens/page_about.dart +++ b/lib/ui/screens/page_about.dart @@ -2,7 +2,7 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:package_info_plus/package_info_plus.dart'; -import 'package:solitaire/ui/widgets/helpers/app_header.dart'; +import 'package:solitaire/ui/helpers/app_titles.dart'; class PageAbout extends StatelessWidget { const PageAbout({super.key}); @@ -17,7 +17,7 @@ class PageAbout extends StatelessWidget { mainAxisSize: MainAxisSize.max, children: <Widget>[ const SizedBox(height: 8), - const AppHeader(text: 'about_title'), + const AppTitle(text: 'about_title'), const Text('about_content').tr(), FutureBuilder<PackageInfo>( future: PackageInfo.fromPlatform(), diff --git a/lib/ui/screens/page_game.dart b/lib/ui/screens/page_game.dart index 287a3832dc66424363f99183602f6b3faf4785fd..2a5509f9b02d5473ab874711470ec6398ac95738 100644 --- a/lib/ui/screens/page_game.dart +++ b/lib/ui/screens/page_game.dart @@ -2,8 +2,9 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:solitaire/cubit/game_cubit.dart'; -import 'package:solitaire/ui/widgets/game/game_widget.dart'; -import 'package:solitaire/ui/widgets/parameters.dart'; +import 'package:solitaire/models/game/game.dart'; +import 'package:solitaire/ui/layouts/game_layout.dart'; +import 'package:solitaire/ui/layouts/parameters_layout.dart'; class PageGame extends StatelessWidget { const PageGame({super.key}); @@ -12,7 +13,11 @@ class PageGame extends StatelessWidget { Widget build(BuildContext context) { return BlocBuilder<GameCubit, GameState>( builder: (BuildContext context, GameState gameState) { - return gameState.currentGame.isRunning ? const GameWidget() : const Parameters(); + final Game currentGame = gameState.currentGame; + + return currentGame.isRunning + ? const GameLayout() + : ParametersLayout(canResume: currentGame.canBeResumed); }, ); } diff --git a/lib/ui/screens/page_settings.dart b/lib/ui/screens/page_settings.dart index ae65ab921d0840b9d014f098187ae4b02076a9b2..868ad1a9b43d7267c78197fc3207c545c0545896 100644 --- a/lib/ui/screens/page_settings.dart +++ b/lib/ui/screens/page_settings.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; -import 'package:solitaire/ui/widgets/helpers/app_header.dart'; -import 'package:solitaire/ui/widgets/settings/settings_form.dart'; +import 'package:solitaire/ui/helpers/app_titles.dart'; +import 'package:solitaire/ui/settings/settings_form.dart'; class PageSettings extends StatelessWidget { const PageSettings({super.key}); @@ -16,7 +16,7 @@ class PageSettings extends StatelessWidget { mainAxisSize: MainAxisSize.max, children: <Widget>[ SizedBox(height: 8), - AppHeader(text: 'settings_title'), + AppTitle(text: 'settings_title'), SizedBox(height: 8), SettingsForm(), ], diff --git a/lib/ui/widgets/settings/settings_form.dart b/lib/ui/settings/settings_form.dart similarity index 96% rename from lib/ui/widgets/settings/settings_form.dart rename to lib/ui/settings/settings_form.dart index 91e9fd50423ac3bdadcccfde1abb3d0939e0dcba..5b03fe6f88f5a888d71f852fd9291f2ae9ab7da1 100644 --- a/lib/ui/widgets/settings/settings_form.dart +++ b/lib/ui/settings/settings_form.dart @@ -2,7 +2,7 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:unicons/unicons.dart'; -import 'package:solitaire/ui/widgets/settings/theme_card.dart'; +import 'package:solitaire/ui/settings/theme_card.dart'; class SettingsForm extends StatefulWidget { const SettingsForm({super.key}); diff --git a/lib/ui/widgets/settings/theme_card.dart b/lib/ui/settings/theme_card.dart similarity index 100% rename from lib/ui/widgets/settings/theme_card.dart rename to lib/ui/settings/theme_card.dart diff --git a/lib/ui/skeleton.dart b/lib/ui/skeleton.dart index c57f5c2ce6db287ee0efbcde7f0f0dfd7afd59fd..eb5336eb8006db4e0df6a13de9e93d9aac70d4c3 100644 --- a/lib/ui/skeleton.dart +++ b/lib/ui/skeleton.dart @@ -14,7 +14,7 @@ class SkeletonScreen extends StatelessWidget { appBar: const GlobalAppBar(), extendBodyBehindAppBar: false, body: Material( - color: Theme.of(context).colorScheme.background, + color: Theme.of(context).colorScheme.surface, child: BlocBuilder<NavCubit, int>( builder: (BuildContext context, int pageIndex) { return Padding( @@ -28,7 +28,7 @@ class SkeletonScreen extends StatelessWidget { }, ), ), - backgroundColor: Theme.of(context).colorScheme.background, + backgroundColor: Theme.of(context).colorScheme.surface, ); } } diff --git a/lib/ui/widgets/actions/button_delete_saved_game.dart b/lib/ui/widgets/actions/button_delete_saved_game.dart new file mode 100644 index 0000000000000000000000000000000000000000..df484d7423e312369cfe6c5fb40ae3bd01e2a21d --- /dev/null +++ b/lib/ui/widgets/actions/button_delete_saved_game.dart @@ -0,0 +1,21 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +import 'package:solitaire/cubit/game_cubit.dart'; + +class DeleteSavedGameButton extends StatelessWidget { + const DeleteSavedGameButton({super.key}); + + @override + Widget build(BuildContext context) { + return TextButton( + child: const Image( + image: AssetImage('assets/ui/button_delete_saved_game.png'), + fit: BoxFit.fill, + ), + onPressed: () { + BlocProvider.of<GameCubit>(context).deleteSavedGame(); + }, + ); + } +} diff --git a/lib/ui/widgets/actions/button_game_quit.dart b/lib/ui/widgets/actions/button_game_quit.dart new file mode 100644 index 0000000000000000000000000000000000000000..a90e6cbc638cc5c4d58ba14c9d2e10f57e7c5563 --- /dev/null +++ b/lib/ui/widgets/actions/button_game_quit.dart @@ -0,0 +1,21 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +import 'package:solitaire/cubit/game_cubit.dart'; + +class QuitGameButton extends StatelessWidget { + const QuitGameButton({super.key}); + + @override + Widget build(BuildContext context) { + return TextButton( + child: const Image( + image: AssetImage('assets/ui/button_back.png'), + fit: BoxFit.fill, + ), + onPressed: () { + BlocProvider.of<GameCubit>(context).quitGame(); + }, + ); + } +} diff --git a/lib/ui/widgets/button_game_start_new.dart b/lib/ui/widgets/actions/button_game_start_new.dart similarity index 72% rename from lib/ui/widgets/button_game_start_new.dart rename to lib/ui/widgets/actions/button_game_start_new.dart index cd4b8f01caebb1ed5195e2e702b62d01806cfc01..e4a4b593bc1740b4e5523c206a39e41bc15b06c2 100644 --- a/lib/ui/widgets/button_game_start_new.dart +++ b/lib/ui/widgets/actions/button_game_start_new.dart @@ -14,17 +14,17 @@ class StartNewGameButton extends StatelessWidget { builder: (BuildContext context, GameSettingsState gameSettingsState) { return BlocBuilder<GlobalSettingsCubit, GlobalSettingsState>( builder: (BuildContext context, GlobalSettingsState globalSettingsState) { - final GameCubit gameCubit = BlocProvider.of<GameCubit>(context); - return TextButton( child: const Image( - image: AssetImage('assets/icons/button_start.png'), + image: AssetImage('assets/ui/button_start.png'), fit: BoxFit.fill, ), - onPressed: () => gameCubit.startNewGame( - gameSettings: gameSettingsState.settings, - globalSettings: globalSettingsState.settings, - ), + onPressed: () { + BlocProvider.of<GameCubit>(context).startNewGame( + gameSettings: gameSettingsState.settings, + globalSettings: globalSettingsState.settings, + ); + }, ); }, ); diff --git a/lib/ui/widgets/actions/button_resume_saved_game.dart b/lib/ui/widgets/actions/button_resume_saved_game.dart new file mode 100644 index 0000000000000000000000000000000000000000..d93fcd4d5850e003cc7c993f550618d83481d84a --- /dev/null +++ b/lib/ui/widgets/actions/button_resume_saved_game.dart @@ -0,0 +1,21 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +import 'package:solitaire/cubit/game_cubit.dart'; + +class ResumeSavedGameButton extends StatelessWidget { + const ResumeSavedGameButton({super.key}); + + @override + Widget build(BuildContext context) { + return TextButton( + child: const Image( + image: AssetImage('assets/ui/button_resume_game.png'), + fit: BoxFit.fill, + ), + onPressed: () { + BlocProvider.of<GameCubit>(context).resumeSavedGame(); + }, + ); + } +} diff --git a/lib/ui/widgets/game/tileset.dart b/lib/ui/widgets/game/game_board.dart similarity index 90% rename from lib/ui/widgets/game/tileset.dart rename to lib/ui/widgets/game/game_board.dart index 5462a796ad34ad7fc042d41dea00fabc0faf35fd..a05d73b5b8f96b0925b79e115afe6b72e487402c 100644 --- a/lib/ui/widgets/game/tileset.dart +++ b/lib/ui/widgets/game/game_board.dart @@ -2,11 +2,11 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:solitaire/cubit/game_cubit.dart'; -import 'package:solitaire/models/board.dart'; +import 'package:solitaire/models/game/board.dart'; import 'package:solitaire/ui/widgets/game/tile_widget.dart'; -class Tileset extends StatelessWidget { - const Tileset({super.key}); +class GameBoardWidget extends StatelessWidget { + const GameBoardWidget({super.key}); @override Widget build(BuildContext context) { diff --git a/lib/ui/widgets/game/game_widget.dart b/lib/ui/widgets/game/game_widget.dart deleted file mode 100644 index e2c7cafae7bcbca07d3309d47a967452329e4b1b..0000000000000000000000000000000000000000 --- a/lib/ui/widgets/game/game_widget.dart +++ /dev/null @@ -1,38 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; - -import 'package:solitaire/cubit/game_cubit.dart'; -import 'package:solitaire/models/game.dart'; -import 'package:solitaire/ui/widgets/game/indicator_top.dart'; -import 'package:solitaire/ui/widgets/game/message_game_end.dart'; -import 'package:solitaire/ui/widgets/game/tileset.dart'; - -class GameWidget extends StatelessWidget { - const GameWidget({super.key}); - - @override - Widget build(BuildContext context) { - return BlocBuilder<GameCubit, GameState>( - builder: (BuildContext context, GameState gameState) { - final Game currentGame = gameState.currentGame; - - final bool gameIsFinished = currentGame.isFinished; - - return Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - const SizedBox(height: 8), - const TopIndicator(), - const SizedBox(height: 2), - const Expanded( - child: Tileset(), - ), - const SizedBox(height: 2), - gameIsFinished ? const EndGameMessage() : const SizedBox(height: 2), - ], - ); - }, - ); - } -} diff --git a/lib/ui/widgets/game/message_game_end.dart b/lib/ui/widgets/game/message_game_end.dart deleted file mode 100644 index b1b4faa4d361a8c7ddff40d35028c381d2378c58..0000000000000000000000000000000000000000 --- a/lib/ui/widgets/game/message_game_end.dart +++ /dev/null @@ -1,56 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; - -import 'package:solitaire/cubit/game_cubit.dart'; -import 'package:solitaire/models/game.dart'; - -class EndGameMessage extends StatelessWidget { - const EndGameMessage({super.key}); - - @override - Widget build(BuildContext context) { - return BlocBuilder<GameCubit, GameState>( - builder: (BuildContext context, GameState gameState) { - final Game currentGame = gameState.currentGame; - - String decorationImageAssetName = ''; - if (currentGame.gameWon) { - decorationImageAssetName = 'assets/icons/game_win.png'; - } else { - decorationImageAssetName = 'assets/icons/placeholder.png'; - } - - final Image decorationImage = Image( - image: AssetImage(decorationImageAssetName), - fit: BoxFit.fill, - ); - - return Container( - margin: const EdgeInsets.all(2), - padding: const EdgeInsets.all(2), - child: Table( - defaultColumnWidth: const IntrinsicColumnWidth(), - children: [ - TableRow( - children: [ - Column(children: [decorationImage]), - TextButton( - child: const Image( - image: AssetImage('assets/icons/button_back.png'), - fit: BoxFit.fill, - ), - onPressed: () { - final GameCubit gameCubit = BlocProvider.of<GameCubit>(context); - gameCubit.quitGame(); - }, - ), - Column(children: [decorationImage]), - ], - ), - ], - ), - ); - }, - ); - } -} diff --git a/lib/ui/widgets/game/tile_widget.dart b/lib/ui/widgets/game/tile_widget.dart index 7d71df58bb998882634777b00081a0ce99d0be6a..ee6eb80752aa91217ef426e77b6e6a1fceda74b8 100644 --- a/lib/ui/widgets/game/tile_widget.dart +++ b/lib/ui/widgets/game/tile_widget.dart @@ -2,8 +2,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:solitaire/cubit/game_cubit.dart'; -import 'package:solitaire/models/cell.dart'; -import 'package:solitaire/models/game.dart'; +import 'package:solitaire/models/game/cell.dart'; +import 'package:solitaire/models/game/game.dart'; class TileWidget extends StatelessWidget { const TileWidget({ @@ -21,12 +21,10 @@ class TileWidget extends StatelessWidget { builder: (BuildContext context, GameState gameState) { final Game currentGame = gameState.currentGame; - final GameCubit gameCubit = BlocProvider.of<GameCubit>(context); - if (tile.hasHole) { return render( currentGame: currentGame, - gameCubit: gameCubit, + gameCubit: BlocProvider.of<GameCubit>(context), ); } else { return Image( diff --git a/lib/ui/widgets/global_app_bar.dart b/lib/ui/widgets/global_app_bar.dart index 82121035102232012623d4532250a5c551412535..35439421d4723df7b1391ea20ee4f66745ab0128 100644 --- a/lib/ui/widgets/global_app_bar.dart +++ b/lib/ui/widgets/global_app_bar.dart @@ -4,8 +4,8 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:solitaire/config/menu.dart'; import 'package:solitaire/cubit/game_cubit.dart'; import 'package:solitaire/cubit/nav_cubit.dart'; -import 'package:solitaire/models/game.dart'; -import 'package:solitaire/ui/widgets/helpers/app_title.dart'; +import 'package:solitaire/models/game/game.dart'; +import 'package:solitaire/ui/helpers/app_titles.dart'; class GlobalAppBar extends StatelessWidget implements PreferredSizeWidget { const GlobalAppBar({super.key}); @@ -20,16 +20,15 @@ class GlobalAppBar extends StatelessWidget implements PreferredSizeWidget { final List<Widget> menuActions = []; - if (currentGame.isRunning) { + if (currentGame.isRunning && !currentGame.isFinished) { menuActions.add(TextButton( child: const Image( - image: AssetImage('assets/icons/button_back.png'), + image: AssetImage('assets/ui/button_back.png'), fit: BoxFit.fill, ), onPressed: () {}, onLongPress: () { - final GameCubit gameCubit = BlocProvider.of<GameCubit>(context); - gameCubit.quitGame(); + BlocProvider.of<GameCubit>(context).quitGame(); }, )); } else { @@ -70,7 +69,7 @@ class GlobalAppBar extends StatelessWidget implements PreferredSizeWidget { } return AppBar( - title: const AppTitle(text: 'app_name'), + title: const AppHeader(text: 'app_name'), actions: menuActions, ); }, diff --git a/lib/ui/widgets/helpers/app_header.dart b/lib/ui/widgets/helpers/app_header.dart deleted file mode 100644 index b5c5be05f6636cf488dcdb5bbc4d6f049b98de11..0000000000000000000000000000000000000000 --- a/lib/ui/widgets/helpers/app_header.dart +++ /dev/null @@ -1,24 +0,0 @@ -import 'package:easy_localization/easy_localization.dart'; -import 'package:flutter/material.dart'; - -class AppHeader extends StatelessWidget { - const AppHeader({super.key, required this.text}); - - final String text; - - @override - Widget build(BuildContext context) { - return Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - tr(text), - textAlign: TextAlign.start, - style: Theme.of(context).textTheme.headlineSmall!.apply(fontWeightDelta: 2), - ), - const SizedBox(height: 8), - ], - ); - } -} diff --git a/lib/ui/widgets/helpers/app_title.dart b/lib/ui/widgets/helpers/app_title.dart deleted file mode 100644 index 7cbbb2030419047b3dcf093a2195a498bd8e8ce9..0000000000000000000000000000000000000000 --- a/lib/ui/widgets/helpers/app_title.dart +++ /dev/null @@ -1,17 +0,0 @@ -import 'package:easy_localization/easy_localization.dart'; -import 'package:flutter/material.dart'; - -class AppTitle extends StatelessWidget { - const AppTitle({super.key, required this.text}); - - final String text; - - @override - Widget build(BuildContext context) { - return Text( - tr(text), - textAlign: TextAlign.start, - style: Theme.of(context).textTheme.headlineLarge!.apply(fontWeightDelta: 2), - ); - } -} diff --git a/lib/utils/color_extensions.dart b/lib/utils/color_extensions.dart new file mode 100644 index 0000000000000000000000000000000000000000..4e55e338f0d3ed98b233d1ef887b7b3e17e29d97 --- /dev/null +++ b/lib/utils/color_extensions.dart @@ -0,0 +1,33 @@ +import 'dart:ui'; + +extension ColorExtension on Color { + Color darken([int percent = 40]) { + assert(1 <= percent && percent <= 100); + final value = 1 - percent / 100; + return Color.fromARGB( + alpha, + (red * value).round(), + (green * value).round(), + (blue * value).round(), + ); + } + + Color lighten([int percent = 40]) { + assert(1 <= percent && percent <= 100); + final value = percent / 100; + return Color.fromARGB( + alpha, + (red + ((255 - red) * value)).round(), + (green + ((255 - green) * value)).round(), + (blue + ((255 - blue) * value)).round(), + ); + } + + Color avg(Color other) { + final red = (this.red + other.red) ~/ 2; + final green = (this.green + other.green) ~/ 2; + final blue = (this.blue + other.blue) ~/ 2; + final alpha = (this.alpha + other.alpha) ~/ 2; + return Color.fromARGB(alpha, red, green, blue); + } +} diff --git a/pubspec.lock b/pubspec.lock index bdba0ef12aaa5e28404023f4cff3d604ec53b942..e0ab96ebb656b1260018d45af586d9ec14ba4a7e 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -106,10 +106,10 @@ packages: dependency: "direct main" description: name: flutter_bloc - sha256: f0ecf6e6eb955193ca60af2d5ca39565a86b8a142452c5b24d96fb477428f4d2 + sha256: b594505eac31a0518bdcb4b5b79573b8d9117b193cc80cc12e17d639b10aa27a url: "https://pub.dev" source: hosted - version: "8.1.5" + version: "8.1.6" flutter_lints: dependency: "direct dev" description: @@ -164,10 +164,10 @@ packages: dependency: transitive description: name: intl - sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d" + sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf url: "https://pub.dev" source: hosted - version: "0.18.1" + version: "0.19.0" lints: dependency: transitive description: @@ -188,10 +188,10 @@ packages: dependency: transitive description: name: meta - sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 + sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" url: "https://pub.dev" source: hosted - version: "1.11.0" + version: "1.12.0" nested: dependency: transitive description: @@ -236,10 +236,10 @@ packages: dependency: transitive description: name: path_provider_android - sha256: a248d8146ee5983446bf03ed5ea8f6533129a12b11f12057ad1b4a67a2b3b41d + sha256: "9c96da072b421e98183f9ea7464898428e764bc0ce5567f27ec8693442e72514" url: "https://pub.dev" source: hosted - version: "2.2.4" + version: "2.2.5" path_provider_foundation: dependency: transitive description: @@ -276,10 +276,10 @@ packages: dependency: transitive description: name: platform - sha256: "12220bb4b65720483f8fa9450b4332347737cf8213dd2840d8b2c823e47243ec" + sha256: "9b71283fc13df574056616011fb138fd3b793ea47cc509c189a6c3fa5f8a1a65" url: "https://pub.dev" source: hosted - version: "3.1.4" + version: "3.1.5" plugin_platform_interface: dependency: transitive description: @@ -308,10 +308,10 @@ packages: dependency: transitive description: name: shared_preferences_android - sha256: "1ee8bf911094a1b592de7ab29add6f826a7331fb854273d55918693d5364a1f2" + sha256: "93d0ec9dd902d85f326068e6a899487d1f65ffcd5798721a95330b26c8131577" url: "https://pub.dev" source: hosted - version: "2.2.2" + version: "2.2.3" shared_preferences_foundation: dependency: transitive description: @@ -425,10 +425,10 @@ packages: dependency: transitive description: name: win32 - sha256: "0eaf06e3446824099858367950a813472af675116bf63f008a4c2a75ae13e9cb" + sha256: a79dbe579cb51ecd6d30b17e0cae4e0ea15e2c0e66f69ad4198f22a6789e94f4 url: "https://pub.dev" source: hosted - version: "5.5.0" + version: "5.5.1" xdg_directories: dependency: transitive description: @@ -438,5 +438,5 @@ packages: source: hosted version: "1.0.4" sdks: - dart: ">=3.3.0 <4.0.0" - flutter: ">=3.19.0" + dart: ">=3.4.0 <4.0.0" + flutter: ">=3.22.0" diff --git a/pubspec.yaml b/pubspec.yaml index 5c638793da858ad6a393a3f36a889d1a87ab3897..4068df449b5b92c410cfc80f0b9976470d3d5625 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -3,7 +3,7 @@ description: Solitaire Game publish_to: "none" -version: 0.0.18+18 +version: 0.1.0+19 environment: sdk: "^3.0.0" @@ -12,6 +12,7 @@ dependencies: flutter: sdk: flutter + # base easy_localization: ^3.0.1 equatable: ^2.0.5 flutter_bloc: ^8.1.1 @@ -21,14 +22,17 @@ dependencies: path_provider: ^2.0.11 unicons: ^2.1.1 + # specific + # (none) + dev_dependencies: flutter_lints: ^4.0.0 flutter: uses-material-design: true assets: - - assets/icons/ - assets/skins/ + - assets/ui/ - assets/translations/ fonts: @@ -42,3 +46,4 @@ flutter: weight: 400 - asset: assets/fonts/Nunito-Light.ttf weight: 300 + diff --git a/icons/build_application_icons.sh b/resources/app/build_application_resources.sh similarity index 98% rename from icons/build_application_icons.sh rename to resources/app/build_application_resources.sh index 27dbe2647fe4e6d562fbd99451716d1b7d448570..6d67b8f4f9eca701d1aed7331ef41dfb0bd44f20 100755 --- a/icons/build_application_icons.sh +++ b/resources/app/build_application_resources.sh @@ -6,7 +6,7 @@ command -v scour >/dev/null 2>&1 || { echo >&2 "I require scour but it's not ins command -v optipng >/dev/null 2>&1 || { echo >&2 "I require optipng but it's not installed. Aborting."; exit 1; } CURRENT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)" -BASE_DIR="$(dirname "${CURRENT_DIR}")" +BASE_DIR="$(dirname "$(dirname "${CURRENT_DIR}")")" SOURCE_ICON="${CURRENT_DIR}/icon.svg" SOURCE_FASTLANE="${CURRENT_DIR}/featureGraphic.svg" diff --git a/icons/featureGraphic.svg b/resources/app/featureGraphic.svg similarity index 100% rename from icons/featureGraphic.svg rename to resources/app/featureGraphic.svg diff --git a/icons/icon.svg b/resources/app/icon.svg similarity index 100% rename from icons/icon.svg rename to resources/app/icon.svg diff --git a/resources/build_resources.sh b/resources/build_resources.sh new file mode 100755 index 0000000000000000000000000000000000000000..659697a1c043cfe1c7654635cfaec3e4a0ff8a1a --- /dev/null +++ b/resources/build_resources.sh @@ -0,0 +1,7 @@ +#! /bin/bash + +CURRENT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)" + +${CURRENT_DIR}/app/build_application_resources.sh +${CURRENT_DIR}/ui/build_ui_resources.sh + diff --git a/icons/build_game_icons.sh b/resources/ui/build_ui_resources.sh similarity index 54% rename from icons/build_game_icons.sh rename to resources/ui/build_ui_resources.sh index 2191261bef769712d228d7a08e5dc97a727ee484..4f365ede7d83140ce6309a3083580f2662b30990 100755 --- a/icons/build_game_icons.sh +++ b/resources/ui/build_ui_resources.sh @@ -6,7 +6,7 @@ command -v scour >/dev/null 2>&1 || { echo >&2 "I require scour but it's not ins command -v optipng >/dev/null 2>&1 || { echo >&2 "I require optipng but it's not installed. Aborting."; exit 1; } CURRENT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)" -BASE_DIR="$(dirname "${CURRENT_DIR}")" +BASE_DIR="$(dirname "$(dirname "${CURRENT_DIR}")")" ASSETS_DIR="${BASE_DIR}/assets" OPTIPNG_OPTIONS="-preserve -quiet -o7" @@ -14,25 +14,23 @@ ICON_SIZE=192 ####################################################### -# Game images -AVAILABLE_GAME_IMAGES=" - button_back - button_start - game_win - placeholder -" - -# Skins -AVAILABLE_SKINS=" - default -" - -# Images per skin -SKIN_IMAGES=" - board - hole - peg -" +# Game images (svg files found in `images` folder) +AVAILABLE_GAME_IMAGES="" +if [ -d "${CURRENT_DIR}/images" ]; then + AVAILABLE_GAME_IMAGES="$(find "${CURRENT_DIR}/images" -type f -name "*.svg" | awk -F/ '{print $NF}' | cut -d"." -f1 | sort)" +fi + +# Skins (subfolders found in `skins` folder) +AVAILABLE_SKINS="" +if [ -d "${CURRENT_DIR}/skins" ]; then + AVAILABLE_SKINS="$(find "${CURRENT_DIR}/skins" -mindepth 1 -type d | awk -F/ '{print $NF}')" +fi + +# Images per skin (svg files found recursively in `skins` folder and subfolders) +SKIN_IMAGES="" +if [ -d "${CURRENT_DIR}/skins" ]; then + SKIN_IMAGES="$(find "${CURRENT_DIR}/skins" -type f -name "*.svg" | awk -F/ '{print $NF}' | cut -d"." -f1 | sort | uniq)" +fi ####################################################### @@ -54,7 +52,7 @@ function optimize_svg() { } # build icons -function build_icon() { +function build_image() { SOURCE="$1" TARGET="$2" @@ -67,43 +65,46 @@ function build_icon() { optimize_svg "${SOURCE}" + mkdir -p "$(dirname "${TARGET}")" + inkscape \ --export-width=${ICON_SIZE} \ --export-height=${ICON_SIZE} \ --export-filename=${TARGET} \ - ${SOURCE} + "${SOURCE}" - optipng ${OPTIPNG_OPTIONS} ${TARGET} + optipng ${OPTIPNG_OPTIONS} "${TARGET}" } -function build_icon_for_skin() { +function build_image_for_skin() { SKIN_CODE="$1" # skin images for SKIN_IMAGE in ${SKIN_IMAGES} do - build_icon ${CURRENT_DIR}/skins/${SKIN_CODE}/${SKIN_IMAGE}.svg ${ASSETS_DIR}/skins/${SKIN_CODE}_${SKIN_IMAGE}.png + build_image ${CURRENT_DIR}/skins/${SKIN_CODE}/${SKIN_IMAGE}.svg ${ASSETS_DIR}/skins/${SKIN_CODE}_${SKIN_IMAGE}.png done } ####################################################### -# Create output folders -mkdir -p ${ASSETS_DIR}/icons -mkdir -p ${ASSETS_DIR}/skins - # Delete existing generated images -find ${ASSETS_DIR}/icons -type f -name "*.png" -delete -find ${ASSETS_DIR}/skins -type f -name "*.png" -delete +if [ -d "${ASSETS_DIR}/ui" ]; then + find ${ASSETS_DIR}/ui -type f -name "*.png" -delete +fi +if [ -d "${ASSETS_DIR}/skins" ]; then + find ${ASSETS_DIR}/skins -type f -name "*.png" -delete +fi # build game images for GAME_IMAGE in ${AVAILABLE_GAME_IMAGES} do - build_icon ${CURRENT_DIR}/${GAME_IMAGE}.svg ${ASSETS_DIR}/icons/${GAME_IMAGE}.png + build_image ${CURRENT_DIR}/images/${GAME_IMAGE}.svg ${ASSETS_DIR}/ui/${GAME_IMAGE}.png done # build skins images for SKIN in ${AVAILABLE_SKINS} do - build_icon_for_skin "${SKIN}" + build_image_for_skin "${SKIN}" done + diff --git a/icons/button_back.svg b/resources/ui/images/button_back.svg similarity index 100% rename from icons/button_back.svg rename to resources/ui/images/button_back.svg diff --git a/resources/ui/images/button_delete_saved_game.svg b/resources/ui/images/button_delete_saved_game.svg new file mode 100644 index 0000000000000000000000000000000000000000..ac7eefef476f761903fe781b8c86d0c94323550a --- /dev/null +++ b/resources/ui/images/button_delete_saved_game.svg @@ -0,0 +1,2 @@ +<?xml version="1.0" encoding="UTF-8"?> +<svg enable-background="new 0 0 100 100" version="1.1" viewBox="0 0 93.665 93.676" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><rect x=".44662" y=".89101" width="92.772" height="91.894" ry="11.689" fill="#ee7d49" stroke="#fff" stroke-width=".238"/><path d="m61.07 35.601-1.7399 27.837c-0.13442 2.1535-1.9205 3.8312-4.0781 3.8312h-16.84c-2.1576 0-3.9437-1.6777-4.0781-3.8312l-1.7399-27.837h-2.6176c-0.84621 0-1.5323-0.68613-1.5323-1.5323 0-0.84655 0.68613-1.5323 1.5323-1.5323h33.711c0.84621 0 1.5323 0.68578 1.5323 1.5323 0 0.84621-0.68613 1.5323-1.5323 1.5323zm-3.2617 0h-21.953l1.4715 26.674c0.05985 1.0829 0.95531 1.9305 2.0403 1.9305h14.929c1.085 0 1.9804-0.84757 2.0403-1.9305zm-10.977 3.0647c0.78977 0 1.4301 0.6403 1.4301 1.4301v19.614c0 0.78977-0.6403 1.4301-1.4301 1.4301s-1.4301-0.6403-1.4301-1.4301v-19.614c0-0.78977 0.6403-1.4301 1.4301-1.4301zm-6.1293 0c0.80004 0 1.4588 0.62935 1.495 1.4286l0.89647 19.719c0.03182 0.70016-0.50998 1.2933-1.2101 1.3255-0.01915 7.02e-4 -0.03831 1e-3 -0.05781 1e-3 -0.74462 0-1.3596-0.58215-1.4003-1.3261l-1.0757-19.719c-0.0407-0.74701 0.53188-1.3852 1.2786-1.4259 0.02462-0.0014 0.04926-2e-3 0.07388-2e-3zm12.259 0c0.74804 0 1.3541 0.60609 1.3541 1.3541 0 0.02462-3.28e-4 0.04926-0.0017 0.07388l-1.0703 19.618c-0.04379 0.80106-0.70597 1.4281-1.5081 1.4281-0.74804 0-1.3541-0.60609-1.3541-1.3541 0-0.02462 3.49e-4 -0.04925 0.0017-0.07388l1.0703-19.618c0.04379-0.80106 0.70597-1.4281 1.5081-1.4281zm-10.216-12.259h8.1728c2.2567 0 4.086 1.8293 4.086 4.086v2.0433h-16.344v-2.0433c0-2.2567 1.8293-4.086 4.086-4.086zm0.20453 3.0647c-0.67725 0-1.2259 0.54863-1.2259 1.2259v1.8388h10.215v-1.8388c0-0.67725-0.54863-1.2259-1.2259-1.2259z" fill="#fff" fill-rule="evenodd" stroke="#bd4812" stroke-width=".75383"/></svg> diff --git a/resources/ui/images/button_resume_game.svg b/resources/ui/images/button_resume_game.svg new file mode 100644 index 0000000000000000000000000000000000000000..6ad8b64202d0e70f898c16c520e756fe8a934add --- /dev/null +++ b/resources/ui/images/button_resume_game.svg @@ -0,0 +1,2 @@ +<?xml version="1.0" encoding="UTF-8"?> +<svg enable-background="new 0 0 100 100" version="1.1" viewBox="0 0 93.665 93.676" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><rect x=".44662" y=".89101" width="92.772" height="91.894" ry="11.689" fill="#49a1ee" stroke="#fff" stroke-width=".238"/><path d="m39.211 31.236c-0.84086-0.84489-2.9911-0.84489-2.9911 0v34.329c0 0.84594 2.1554 0.84594 2.9993 0l28.178-15.637c0.84392-0.84086 0.85812-2.2091 0.01623-3.053z" fill="#fefeff" stroke="#105ca1" stroke-linecap="round" stroke-linejoin="round" stroke-width="6.1726"/><path d="m40.355 33.714c-0.71948-0.72294-2.5594-0.72294-2.5594 0v29.373c0 0.72383 1.8442 0.72383 2.5663 0l24.11-13.38c0.7221-0.71948 0.73426-1.8902 0.01389-2.6124z" fill="#fefeff" stroke="#feffff" stroke-linecap="round" stroke-linejoin="round" stroke-width="3.225"/><path d="m28.369 66.919v-37.591" fill="#105ca2" stroke="#105ca2" stroke-linecap="round" stroke-width="4.0337"/></svg> diff --git a/icons/button_start.svg b/resources/ui/images/button_start.svg similarity index 100% rename from icons/button_start.svg rename to resources/ui/images/button_start.svg diff --git a/resources/ui/images/game_fail.svg b/resources/ui/images/game_fail.svg new file mode 100644 index 0000000000000000000000000000000000000000..2922fd7adc2bd2e813836c728f095376c73d4143 --- /dev/null +++ b/resources/ui/images/game_fail.svg @@ -0,0 +1,2 @@ +<?xml version="1.0" encoding="UTF-8"?> +<svg enable-background="new 0 0 100 100" version="1.1" viewBox="0 0 93.665 93.676" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><rect x=".44662" y=".89101" width="92.772" height="91.894" ry="11.689" fill="#d11717" stroke="#fff" stroke-width=".238"/><path d="m71.624 59.304c3.5089 3.5089 3.5089 9.0561 0 12.565-1.6976 1.6976-3.9623 2.6034-6.2261 2.6034s-4.5275-0.90569-6.2261-2.6034l-12.452-12.452-12.452 12.452c-1.6976 1.6976-3.9623 2.6034-6.2261 2.6034s-4.5275-0.90569-6.2261-2.6034c-3.5089-3.5089-3.5089-9.0561 0-12.565l12.452-12.452-12.452-12.452c-3.5089-3.5089-3.5089-9.0561 0-12.565s9.0561-3.5089 12.565 0l12.452 12.452 12.452-12.452c3.5089-3.5089 9.0561-3.5089 12.565 0s3.5089 9.0561 0 12.565l-12.452 12.452z" fill="#e7e7e7" stroke-width=".20213"/></svg> diff --git a/icons/game_win.svg b/resources/ui/images/game_win.svg similarity index 100% rename from icons/game_win.svg rename to resources/ui/images/game_win.svg diff --git a/icons/placeholder.svg b/resources/ui/images/placeholder.svg similarity index 100% rename from icons/placeholder.svg rename to resources/ui/images/placeholder.svg diff --git a/icons/skins/default/board.svg b/resources/ui/skins/default/board.svg similarity index 100% rename from icons/skins/default/board.svg rename to resources/ui/skins/default/board.svg diff --git a/icons/skins/default/hole.svg b/resources/ui/skins/default/hole.svg similarity index 100% rename from icons/skins/default/hole.svg rename to resources/ui/skins/default/hole.svg diff --git a/icons/skins/default/peg.svg b/resources/ui/skins/default/peg.svg similarity index 100% rename from icons/skins/default/peg.svg rename to resources/ui/skins/default/peg.svg