diff --git a/android/gradle.properties b/android/gradle.properties index 9742e8d9ac872ece07c6ae62a041bc05c2a8fcc6..658a39bc7c0e90e2bbe56b1441edd5a76f884770 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.31 -app.versionCode=31 +app.versionName=0.1.0 +app.versionCode=32 diff --git a/assets/translations/en.json b/assets/translations/en.json index f434c73f8ed825abb5d940da7811389085433a5e..6924de1c80dc4230424a788d29994fb7c2639864 100644 --- a/assets/translations/en.json +++ b/assets/translations/en.json @@ -1,14 +1,12 @@ { "app_name": "Jeweled", - "bottom_nav_game": "Game", - "bottom_nav_settings": "Settings", - "bottom_nav_about": "About", - "settings_title": "Settings", "settings_label_theme": "Theme mode", "about_title": "About", "about_content": "Simple and classic Jeweled game.", - "about_version": "Version: {version}" + "about_version": "Version: {version}", + + "": "" } diff --git a/assets/translations/fr.json b/assets/translations/fr.json index 0952f4587bc4a97d34806a58ff48cd6f0c57b1e7..23adc4c3871a3b8ceb09eab619465d95e7d23aff 100644 --- a/assets/translations/fr.json +++ b/assets/translations/fr.json @@ -1,14 +1,12 @@ { "app_name": "Jeweled", - "bottom_nav_game": "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": "Jeweled, jeu simple et classique d'associations.", - "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/icons/button_delete_saved_game.png b/assets/ui/button_delete_saved_game.png similarity index 100% rename from assets/icons/button_delete_saved_game.png rename to assets/ui/button_delete_saved_game.png diff --git a/assets/icons/button_resume_game.png b/assets/ui/button_resume_game.png similarity index 100% rename from assets/icons/button_resume_game.png rename to assets/ui/button_resume_game.png 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/icons/game_fail.png b/assets/ui/game_fail.png similarity index 100% rename from assets/icons/game_fail.png rename to assets/ui/game_fail.png 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/devtools_options.yaml b/devtools_options.yaml new file mode 100644 index 0000000000000000000000000000000000000000..fa0b357c4f4a29c3de7c5abfa47ba9ea8e52dd92 --- /dev/null +++ b/devtools_options.yaml @@ -0,0 +1,3 @@ +description: This file stores settings for Dart & Flutter DevTools. +documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states +extensions: diff --git a/fastlane/metadata/android/en-US/changelogs/32.txt b/fastlane/metadata/android/en-US/changelogs/32.txt new file mode 100644 index 0000000000000000000000000000000000000000..d4afd512e55b3fd8ffbfd795adb9b00832e5aaef --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/32.txt @@ -0,0 +1 @@ +Improve/normalize game architecture. diff --git a/fastlane/metadata/android/fr-FR/changelogs/32.txt b/fastlane/metadata/android/fr-FR/changelogs/32.txt new file mode 100644 index 0000000000000000000000000000000000000000..6a9871a5eb8eb3c6e9106520f1cbf1f39f9e5ef7 --- /dev/null +++ b/fastlane/metadata/android/fr-FR/changelogs/32.txt @@ -0,0 +1 @@ +Amélioration/normalisation de l'architecture du jeu. diff --git a/icons/build_game_icons.sh b/icons/build_game_icons.sh deleted file mode 100755 index 57b8c1350cd34b6f93131df7102c27b8df8d8768..0000000000000000000000000000000000000000 --- a/icons/build_game_icons.sh +++ /dev/null @@ -1,84 +0,0 @@ -#! /bin/bash - -# Check dependencies -command -v inkscape >/dev/null 2>&1 || { echo >&2 "I require inkscape but it's not installed. Aborting."; exit 1; } -command -v scour >/dev/null 2>&1 || { echo >&2 "I require scour but it's not installed. Aborting."; exit 1; } -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}")" -ASSETS_DIR="${BASE_DIR}/assets" - -OPTIPNG_OPTIONS="-preserve -quiet -o7" -ICON_SIZE=192 - -####################################################### - -# Game images -AVAILABLE_GAME_IMAGES=" - button_back - button_start - button_resume_game - button_delete_saved_game - game_fail - game_win - placeholder -" - -####################################################### - -# optimize svg -function optimize_svg() { - SOURCE="$1" - - cp ${SOURCE} ${SOURCE}.tmp - scour \ - --remove-descriptive-elements \ - --enable-id-stripping \ - --enable-viewboxing \ - --enable-comment-stripping \ - --nindent=4 \ - --quiet \ - -i ${SOURCE}.tmp \ - -o ${SOURCE} - rm ${SOURCE}.tmp -} - -# build icons -function build_icon() { - SOURCE="$1" - TARGET="$2" - - echo "Building ${TARGET}" - - if [ ! -f "${SOURCE}" ]; then - echo "Missing file: ${SOURCE}" - exit 1 - fi - - optimize_svg "${SOURCE}" - - inkscape \ - --export-width=${ICON_SIZE} \ - --export-height=${ICON_SIZE} \ - --export-filename=${TARGET} \ - ${SOURCE} - - optipng ${OPTIPNG_OPTIONS} ${TARGET} -} - -####################################################### - -# 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 - -# build game images -for GAME_IMAGE in ${AVAILABLE_GAME_IMAGES} -do - build_icon ${CURRENT_DIR}/${GAME_IMAGE}.svg ${ASSETS_DIR}/icons/${GAME_IMAGE}.png -done diff --git a/icons/skin_default.svg b/icons/skin_default.svg deleted file mode 100644 index f7344d57776c1eb3ca5699290db81c999f83cc0a..0000000000000000000000000000000000000000 --- a/icons/skin_default.svg +++ /dev/null @@ -1,2 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<svg enable-background="new 0 0 100 100" version="1.1" viewBox="0 0 102 102" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><rect x="1" y="1" width="100" height="100" ry="0" fill="#be6ade" stroke="#000" stroke-width="2"/></svg> diff --git a/lib/utils/color_theme.dart b/lib/config/color_theme.dart similarity index 71% rename from lib/utils/color_theme.dart rename to lib/config/color_theme.dart index 86369215493c9cc01c0d639da0a4bd191dac5474..c2d0f8fad8c16af1744dfc5dd55ca06401ca709b 100644 --- a/lib/utils/color_theme.dart +++ b/lib/config/color_theme.dart @@ -1,12 +1,12 @@ import 'package:flutter/material.dart'; class ColorTheme { - static Map<int, List<int>> itemColors = { + static Map<String, List<int>> itemColors = { // legacy // 0:[0x9D9D9D,0xFFFFFF,0xBE2633,0xE06F8B,0x493C2B,0xA46422,0xEB8931,0xF7E26B,0x2F484E,0x44891A,0xA3CE27,0x1B2632,0x005784,0x31A2F2,0xB2DCEF,], // https://lospec.com/palette-list/gothic-bit - 1: [ + 'gothic-bit': [ 0x0e0e12, 0x1a1a24, 0x333346, @@ -18,7 +18,7 @@ class ColorTheme { ], // https://lospec.com/palette-list/sweethope - 2: [ + 'sweethope': [ 0x615e85, 0x9c8dc2, 0xd9a3cd, @@ -30,7 +30,7 @@ class ColorTheme { ], // https://lospec.com/palette-list/nostalgic-dreams - 3: [ + 'nostalgic-dreams': [ 0xd9af80, 0xb07972, 0x524352, @@ -42,7 +42,7 @@ class ColorTheme { ], // https://lospec.com/palette-list/arjibi8 - 4: [ + 'arjibi8': [ 0x8bc7bf, 0x5796a1, 0x524bb3, @@ -71,9 +71,9 @@ class ColorTheme { static int defaultItemColor = 0x808080; - static int getColorsCount(final int skin) { - if (itemColors.containsKey(skin) && null != itemColors[skin]) { - List<int> skinColors = itemColors[skin] ?? []; + static int getColorsCount(String colorsTheme) { + if (itemColors.containsKey(colorsTheme) && null != itemColors[colorsTheme]) { + List<int> skinColors = itemColors[colorsTheme] ?? []; return skinColors.length; } @@ -81,16 +81,18 @@ class ColorTheme { return 0; } - static int getColorCode(int? value, final int skin) { - if (value != null && itemColors.containsKey(skin) && null != itemColors[skin]) { - List<int> skinColors = itemColors[skin] ?? []; - return (skinColors[value % getColorsCount(skin)]) | 0xFF000000; + static int getColorCode(int? value, String colorsTheme) { + if (value != null && + itemColors.containsKey(colorsTheme) && + null != itemColors[colorsTheme]) { + List<int> skinColors = itemColors[colorsTheme] ?? []; + return (skinColors[value % getColorsCount(colorsTheme)]) | 0xFF000000; } return defaultItemColor | 0xFF000000; } - static Color getColor(int? value, final int skin) { - return Color(getColorCode(value, skin)); + static Color getColor(int? value, String colorsTheme) { + return Color(getColorCode(value, colorsTheme)); } static Color getBorderColor() { diff --git a/lib/config/default_game_settings.dart b/lib/config/default_game_settings.dart index f8426a7f7fefdc7f754e79f0ff6a716ae61391c8..21f433f69be3a2e36d00f7260306878c4509469e 100644 --- a/lib/config/default_game_settings.dart +++ b/lib/config/default_game_settings.dart @@ -1,42 +1,48 @@ -import 'package:jeweled/utils/tools.dart'; +import 'package:jeweled/utils/tools.dart'; class DefaultGameSettings { + // available game parameters codes + static const String parameterCodeBoardSize = 'boardSize'; + static const String parameterCodeColorsCount = 'colorsCount'; static const List<String> availableParameters = [ - 'boardSize', - 'colorsCount', + parameterCodeBoardSize, + parameterCodeColorsCount, ]; - static const int boardSizeValueSmall = 6; - static const int boardSizeValueMedium = 10; - static const int boardSizeValueLarge = 14; - static const int boardSizeValueExtraLarge = 18; - - static const int defaultBoardSizeValue = boardSizeValueMedium; - static const List<int> allowedBoardSizeValues = [ + // board size: available values + static const String boardSizeValueSmall = '6'; + static const String boardSizeValueMedium = '10'; + static const String boardSizeValueLarge = '14'; + static const String boardSizeValueExtraLarge = '18'; + static const List<String> allowedBoardSizeValues = [ boardSizeValueSmall, boardSizeValueMedium, boardSizeValueLarge, boardSizeValueExtraLarge, ]; - - static const int colorsCountValueLow = 5; - static const int colorsCountValueMedium = 6; - static const int colorsCountValueHigh = 7; - static const int colorsCountValueVeryHigh = 8; - - static const int defaultColorsCountValue = colorsCountValueMedium; - static const List<int> allowedColorsCountValues = [ + // board size: default value + static const String defaultBoardSizeValue = boardSizeValueMedium; + + // colors count: available values + static const String colorsCountValueLow = '5'; + static const String colorsCountValueMedium = '6'; + static const String colorsCountValueHigh = '7'; + static const String colorsCountValueVeryHigh = '8'; + static const List<String> allowedColorsCountValues = [ colorsCountValueLow, colorsCountValueMedium, colorsCountValueHigh, colorsCountValueVeryHigh, ]; + // colors count: default value + static const String defaultColorsCountValue = colorsCountValueMedium; - static List<int> getAvailableValues(String parameterCode) { + // available values from parameter code + static List<String> getAvailableValues(String parameterCode) { switch (parameterCode) { - case 'boardSize': + case parameterCodeBoardSize: return DefaultGameSettings.allowedBoardSizeValues; - case 'colorsCount': + case parameterCodeColorsCount: return DefaultGameSettings.allowedColorsCountValues; } @@ -44,5 +50,10 @@ class DefaultGameSettings { return []; } + // parameters displayed with assets (instead of painter) + static List<String> displayedWithAssets = [ + // + ]; + static int blockMinimumCellsCount = 3; } diff --git a/lib/config/default_global_settings.dart b/lib/config/default_global_settings.dart index 28fac839ae6f6b8f7bdfe6c32c4737a6a7048ee8..105f9b60b65b3804f49a268ae13f41b5b73f8a89 100644 --- a/lib/config/default_global_settings.dart +++ b/lib/config/default_global_settings.dart @@ -1,38 +1,39 @@ -import 'package:jeweled/utils/tools.dart'; +import 'package:jeweled/utils/tools.dart'; class DefaultGlobalSettings { + // available global parameters codes + static const String parameterCodeColorsTheme = 'colorsTheme'; + static const String parameterCodeGraphicsTheme = 'graphicTheme'; static const List<String> availableParameters = [ - 'colorsTheme', - 'graphicTheme', + parameterCodeColorsTheme, + parameterCodeGraphicsTheme, ]; - static const int defaultColorsThemeValue = 1; - static const List<int> allowedColorsThemeValues = [ - // 0, // 0x9D9D9D,0xFFFFFF,0xBE2633,0xE06F8B,0x493C2B,0xA46422,0xEB8931,0xF7E26B,0x2F484E,0x44891A,0xA3CE27,0x1B2632,0x005784,0x31A2F2,0xB2DCEF,// legacy - 1, // 0x0e0e12,0x1a1a24,0x333346,0x535373,0x8080a4,0xa6a6bf,0xc1c1d2,0xe6e6ec, // https://lospec.com/palette-list/gothic-bit - 2, // 0x615e85,0x9c8dc2,0xd9a3cd,0xebc3a7,0xe0e0dc,0xa3d1af,0x90b4de,0x717fb0, // https://lospec.com/palette-list/sweethope - 3, // 0xd9af80,0xb07972,0x524352,0x686887,0x7f9bb0,0xbfd4b0,0x90b870,0x628c70, // https://lospec.com/palette-list/nostalgic-dreams - 4, // 0x8bc7bf,0x5796a1,0x524bb3,0x471b6e,0x702782,0xb0455a,0xde8b6f,0xebd694, // https://lospec.com/palette-list/arjibi8 - // 5, // 0x40263e,0x5979a6,0x84c2a3,0xefe8c3,0xefefef,0xcbc7d6,0xd06060,0x773971, // https://lospec.com/palette-list/kotomasho-8 - // 6, // 0xf0f0eb,0xffff8f,0x7be098,0x849ad8,0xe8b382,0xd8828e,0xa776c1,0x545155, // https://lospec.com/palette-list/desatur8 - // 7, // 0x211d38,0x2e2a4f,0x3b405e,0x60556e,0x9a6278,0xc7786f,0xcfa98a,0xcdd4a5, // https://lospec.com/palette-list/purplemorning8 - // 8, // 0x332422,0xc95b40,0xff9b5e,0xfcdf76,0x4c2f7f,0x3a66ad,0x39cec2,0xfafff9, // https://lospec.com/palette-list/cold-war-8 - // 9, // 0x111323,0x374566,0x50785d,0x8497b3,0xe8dcd8,0xcfb463,0xb35447,0x692e4b, // https://lospec.com/palette-list/low-8 + static const String colorThemeGothicBit = 'gothic-bit'; + static const String colorThemeSweethope = 'sweethope'; + static const String colorThemeNostalgicDreams = 'nostalgic-dreams'; + static const String colorThemeArjibi8 = 'arjibi8'; + static const String defaultColorsThemeValue = colorThemeSweethope; + static const List<String> allowedColorsThemeValues = [ + colorThemeGothicBit, + colorThemeSweethope, + colorThemeNostalgicDreams, + colorThemeArjibi8, ]; - static const int graphicThemeSolidBackground = 0; - static const int graphicThemeGradientAndBorder = 1; - static const int graphicThemeEmojis = 2; - static const int graphicThemePatterns = 3; - - static const int defaultGraphicThemeValue = graphicThemeSolidBackground; - static const List<int> allowedGraphicThemeValues = [ + static const String graphicThemeSolidBackground = 'SolidBackground'; + static const String graphicThemeGradientAndBorder = 'GradientAndBorder'; + static const String graphicThemeEmojis = 'Emojis'; + static const String graphicThemePatterns = 'Patterns'; + static const List<String> allowedGraphicThemeValues = [ graphicThemeSolidBackground, graphicThemeGradientAndBorder, graphicThemeEmojis, graphicThemePatterns, ]; + static const String defaultGraphicThemeValue = graphicThemeSolidBackground; + static const List<String> graphicThemeContentEmojiStrings = [ '🍏', '🤍', @@ -54,15 +55,21 @@ class DefaultGlobalSettings { '⧧', ]; - static List<int> getAvailableValues(String parameterCode) { + // available values from parameter code + static List<String> getAvailableValues(String parameterCode) { switch (parameterCode) { - case 'colorsTheme': + case parameterCodeColorsTheme: return DefaultGlobalSettings.allowedColorsThemeValues; - case 'graphicTheme': + case parameterCodeGraphicsTheme: return DefaultGlobalSettings.allowedGraphicThemeValues; } 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 79af6d0abecd90f7a6292def370ef5a511af60e1..aa89aa5707e955ccb37e5c6778693cae4f89b205 100644 --- a/lib/config/menu.dart +++ b/lib/config/menu.dart @@ -1,17 +1,15 @@ import 'package:flutter/material.dart'; import 'package:unicons/unicons.dart'; -import 'package:jeweled/ui/screens/about_page.dart'; -import 'package:jeweled/ui/screens/game_page.dart'; -import 'package:jeweled/ui/screens/settings_page.dart'; +import 'package:jeweled/ui/screens/page_game.dart'; +import 'package:jeweled/ui/screens/page_settings.dart'; +import 'package:jeweled/ui/screens/page_about.dart'; class MenuItem { - final String code; final Icon icon; final Widget page; const MenuItem({ - required this.code, required this.icon, required this.page, }); @@ -20,23 +18,20 @@ class MenuItem { class Menu { static const indexGame = 0; static const menuItemGame = MenuItem( - code: 'bottom_nav_game', icon: Icon(UniconsLine.home), - page: GamePage(), + page: PageGame(), ); static const indexSettings = 1; static const menuItemSettings = MenuItem( - code: 'bottom_nav_settings', icon: Icon(UniconsLine.setting), - page: SettingsPage(), + page: PageSettings(), ); static const indexAbout = 2; static const menuItemAbout = MenuItem( - code: 'bottom_nav_about', icon: Icon(UniconsLine.info_circle), - page: AboutPage(), + page: PageAbout(), ); static Map<int, MenuItem> items = { 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 a0b81fd1ffec52a90ead3e2a572d6e5fbd27f3db..276c94597d030b4e6167907b87b3fefb1b1407fe 100644 --- a/lib/cubit/game_cubit.dart +++ b/lib/cubit/game_cubit.dart @@ -3,10 +3,10 @@ import 'package:flutter/material.dart'; import 'package:hydrated_bloc/hydrated_bloc.dart'; import 'package:jeweled/config/default_game_settings.dart'; -import 'package:jeweled/models/game.dart'; -import 'package:jeweled/models/cell_location.dart'; -import 'package:jeweled/models/settings_game.dart'; -import 'package:jeweled/models/settings_global.dart'; +import 'package:jeweled/models/game/cell_location.dart'; +import 'package:jeweled/models/game/game.dart'; +import 'package:jeweled/models/settings/settings_game.dart'; +import 'package:jeweled/models/settings/settings_global.dart'; import 'package:jeweled/utils/tools.dart'; part 'game_state.dart'; @@ -14,7 +14,7 @@ part 'game_state.dart'; class GameCubit extends HydratedCubit<GameState> { GameCubit() : super(GameState( - currentGame: Game.createNull(), + currentGame: Game.createEmpty(), )); void updateState(Game game) { @@ -25,23 +25,55 @@ class GameCubit extends HydratedCubit<GameState> { void refresh() { final Game game = Game( - board: state.currentGame.board, + // Settings gameSettings: state.currentGame.gameSettings, globalSettings: state.currentGame.globalSettings, - shuffledColors: state.currentGame.shuffledColors, + // 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 + shuffledColors: state.currentGame.shuffledColors, availableBlocksCount: state.currentGame.availableBlocksCount, - movesCount: state.currentGame.movesCount, score: state.currentGame.score, + movesCount: state.currentGame.movesCount, ); // game.dump(); updateState(game); } + void startNewGame({ + required GameSettings gameSettings, + required GlobalSettings globalSettings, + }) { + final Game newGame = Game.createNew( + gameSettings: gameSettings, + globalSettings: globalSettings, + ); + + newGame.dump(); + + updateState(newGame); + postAnimate(); + } + void quitGame() { - state.currentGame.updateGameIsRunning(false); + state.currentGame.isRunning = false; + refresh(); + } + + void resumeSavedGame() { + state.currentGame.isRunning = true; + refresh(); + } + + void deleteSavedGame() { + state.currentGame.isRunning = false; + state.currentGame.isFinished = true; refresh(); } @@ -51,6 +83,7 @@ class GameCubit extends HydratedCubit<GameState> { } void increaseMovesCount() { + state.currentGame.isStarted = true; state.currentGame.increaseMovesCount(); refresh(); } @@ -66,19 +99,19 @@ class GameCubit extends HydratedCubit<GameState> { } void updateGameIsFinished(bool gameIsFinished) { - state.currentGame.updateGameIsFinished(gameIsFinished); + state.currentGame.isFinished = gameIsFinished; refresh(); } - void shuffleColors(final int colorsTheme) { + void shuffleColors(final String colorsTheme) { state.currentGame.shuffleColorsAgain(colorsTheme); } moveCellsDown() { final Game currentGame = state.currentGame; - final int boardSizeHorizontal = currentGame.gameSettings.boardSize; - final int boardSizeVertical = currentGame.gameSettings.boardSize; + final int boardSizeHorizontal = currentGame.gameSettings.boardSizeValue; + final int boardSizeVertical = currentGame.gameSettings.boardSizeValue; for (int row = 0; row < boardSizeVertical; row++) { for (int col = 0; col < boardSizeHorizontal; col++) { @@ -139,24 +172,9 @@ class GameCubit extends HydratedCubit<GameState> { } } - void startNewGame({ - required GameSettings gameSettings, - required GlobalSettings globalSettings, - }) { - Game newGame = Game.createNew( - gameSettings: gameSettings, - globalSettings: globalSettings, - ); - - newGame.dump(); - - updateState(newGame); - postAnimate(); - } - @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/nav_cubit.dart b/lib/cubit/nav_cubit.dart index f2f46286f1994d17eb3c6d840938039e205633b8..47db0b126c57d4b39b343a417909f61bdd45ce9c 100644 --- a/lib/cubit/nav_cubit.dart +++ b/lib/cubit/nav_cubit.dart @@ -17,20 +17,12 @@ class NavCubit extends HydratedCubit<int> { emit(Menu.indexGame); } - void switchToSettingsPage() { - if (state != Menu.indexSettings) { - emit(Menu.indexSettings); - } else { - goToGamePage(); - } + void goToSettingsPage() { + emit(Menu.indexSettings); } - void switchToAboutPage() { - if (state != Menu.indexAbout) { - emit(Menu.indexAbout); - } else { - goToGamePage(); - } + void goToAboutPage() { + emit(Menu.indexAbout); } @override diff --git a/lib/cubit/settings_game_cubit.dart b/lib/cubit/settings_game_cubit.dart index 0aa8b0afe46b42fe7bef9e0ab1df9c602260aae3..53df322a469307b409a03e89fb91ffcf549acf5f 100644 --- a/lib/cubit/settings_game_cubit.dart +++ b/lib/cubit/settings_game_cubit.dart @@ -2,8 +2,8 @@ import 'package:equatable/equatable.dart'; import 'package:flutter/material.dart'; import 'package:hydrated_bloc/hydrated_bloc.dart'; -import 'package:jeweled/models/settings_game.dart'; -import 'package:jeweled/utils/tools.dart'; +import 'package:jeweled/config/default_game_settings.dart'; +import 'package:jeweled/models/settings/settings_game.dart'; part 'settings_game_state.dart'; @@ -11,8 +11,8 @@ class GameSettingsCubit extends HydratedCubit<GameSettingsState> { GameSettingsCubit() : super(GameSettingsState(settings: GameSettings.createDefault())); void setValues({ - int? boardSize, - int? colorsCount, + String? boardSize, + String? colorsCount, }) { emit( GameSettingsState( @@ -24,22 +24,24 @@ class GameSettingsCubit extends HydratedCubit<GameSettingsState> { ); } - int getParameterValue(String code) { + String getParameterValue(String code) { switch (code) { - case 'boardSize': + case DefaultGameSettings.parameterCodeBoardSize: return GameSettings.getBoardSizeValueFromUnsafe(state.settings.boardSize); - case 'colorsCount': + case DefaultGameSettings.parameterCodeColorsCount: return GameSettings.getColorsCountValueFromUnsafe(state.settings.colorsCount); } - return 0; - } - void setParameterValue(String code, int value) { - printlog('GameSettingsCubit.setParameterValue'); - printlog('code: $code / value: $value'); + return ''; + } - int boardSize = code == 'boardSize' ? value : getParameterValue('boardSize'); - int colorsCount = code == 'colorsCount' ? value : getParameterValue('colorsCount'); + void setParameterValue(String code, String value) { + final String boardSize = code == DefaultGameSettings.parameterCodeBoardSize + ? value + : getParameterValue(DefaultGameSettings.parameterCodeBoardSize); + final String colorsCount = code == DefaultGameSettings.parameterCodeColorsCount + ? value + : getParameterValue(DefaultGameSettings.parameterCodeColorsCount); setValues( boardSize: boardSize, @@ -49,8 +51,8 @@ class GameSettingsCubit extends HydratedCubit<GameSettingsState> { @override GameSettingsState? fromJson(Map<String, dynamic> json) { - int boardSize = json['boardSize'] as int; - int colorsCount = json['colorsCount'] as int; + final String boardSize = json[DefaultGameSettings.parameterCodeBoardSize] as String; + final String colorsCount = json[DefaultGameSettings.parameterCodeColorsCount] as String; return GameSettingsState( settings: GameSettings( @@ -63,8 +65,8 @@ class GameSettingsCubit extends HydratedCubit<GameSettingsState> { @override Map<String, dynamic>? toJson(GameSettingsState state) { return <String, dynamic>{ - 'boardSize': state.settings.boardSize, - 'colorsCount': state.settings.colorsCount, + DefaultGameSettings.parameterCodeBoardSize: state.settings.boardSize, + DefaultGameSettings.parameterCodeColorsCount: state.settings.colorsCount, }; } } 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 181364238f56ea748d6eeb743e7da0e2002578f8..eadc407a1e445975ac5d9b00b98179d2f2fb42e8 100644 --- a/lib/cubit/settings_global_cubit.dart +++ b/lib/cubit/settings_global_cubit.dart @@ -2,8 +2,8 @@ import 'package:equatable/equatable.dart'; import 'package:flutter/material.dart'; import 'package:hydrated_bloc/hydrated_bloc.dart'; -import 'package:jeweled/models/settings_global.dart'; -import 'package:jeweled/utils/tools.dart'; +import 'package:jeweled/config/default_global_settings.dart'; +import 'package:jeweled/models/settings/settings_global.dart'; part 'settings_global_state.dart'; @@ -11,8 +11,8 @@ class GlobalSettingsCubit extends HydratedCubit<GlobalSettingsState> { GlobalSettingsCubit() : super(GlobalSettingsState(settings: GlobalSettings.createDefault())); void setValues({ - int? colorsTheme, - int? graphicTheme, + String? colorsTheme, + String? graphicTheme, }) { emit( GlobalSettingsState( @@ -24,22 +24,23 @@ class GlobalSettingsCubit extends HydratedCubit<GlobalSettingsState> { ); } - int getParameterValue(String code) { + String getParameterValue(String code) { switch (code) { - case 'colorsTheme': + case DefaultGlobalSettings.parameterCodeColorsTheme: return GlobalSettings.getColorsThemeValueFromUnsafe(state.settings.colorsTheme); - case 'graphicTheme': + case DefaultGlobalSettings.parameterCodeGraphicsTheme: return GlobalSettings.getGraphicThemeValueFromUnsafe(state.settings.graphicTheme); } - return 0; + return ''; } - void setParameterValue(String code, int value) { - printlog('GlobalSettingsCubit.setParameterValue'); - printlog('code: $code / value: $value'); - - int colorsTheme = code == 'colorsTheme' ? value : getParameterValue('colorsTheme'); - int graphicTheme = code == 'graphicTheme' ? value : getParameterValue('graphicTheme'); + void setParameterValue(String code, String value) { + final String colorsTheme = code == DefaultGlobalSettings.parameterCodeColorsTheme + ? value + : getParameterValue(DefaultGlobalSettings.parameterCodeColorsTheme); + final String graphicTheme = code == DefaultGlobalSettings.parameterCodeGraphicsTheme + ? value + : getParameterValue(DefaultGlobalSettings.parameterCodeGraphicsTheme); setValues( colorsTheme: colorsTheme, @@ -49,8 +50,9 @@ class GlobalSettingsCubit extends HydratedCubit<GlobalSettingsState> { @override GlobalSettingsState? fromJson(Map<String, dynamic> json) { - int colorsTheme = json['colorsTheme'] as int; - int graphicTheme = json['graphicTheme'] as int; + final String colorsTheme = json[DefaultGlobalSettings.parameterCodeColorsTheme] as String; + final String graphicTheme = + json[DefaultGlobalSettings.parameterCodeGraphicsTheme] as String; return GlobalSettingsState( settings: GlobalSettings( @@ -63,8 +65,8 @@ class GlobalSettingsCubit extends HydratedCubit<GlobalSettingsState> { @override Map<String, dynamic>? toJson(GlobalSettingsState state) { return <String, dynamic>{ - 'colorsTheme': state.settings.colorsTheme, - 'graphicTheme': state.settings.graphicTheme, + DefaultGlobalSettings.parameterCodeColorsTheme: state.settings.colorsTheme, + DefaultGlobalSettings.parameterCodeGraphicsTheme: state.settings.graphicTheme, }; } } 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 11da37222d1ca96a98c851cf6ce5b00423bb9d33..f62d4d593cf6c79a4e466f8c91f35c5a70fe4f33 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -2,18 +2,19 @@ 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:jeweled/cubit/nav_cubit.dart'; +import 'package:jeweled/cubit/settings_game_cubit.dart'; +import 'package:jeweled/cubit/settings_global_cubit.dart'; +import 'package:jeweled/ui/skeleton.dart'; import 'package:path_provider/path_provider.dart'; import 'package:jeweled/config/theme.dart'; import 'package:jeweled/cubit/game_cubit.dart'; -import 'package:jeweled/cubit/nav_cubit.dart'; -import 'package:jeweled/cubit/settings_game_cubit.dart'; -import 'package:jeweled/cubit/settings_global_cubit.dart'; import 'package:jeweled/cubit/theme_cubit.dart'; -import 'package:jeweled/ui/skeleton.dart'; void main() async { // Initialize packages @@ -25,18 +26,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 +44,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 +78,24 @@ 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'); + } + + return assets; + } } diff --git a/lib/models/game_board.dart b/lib/models/game/board.dart similarity index 64% rename from lib/models/game_board.dart rename to lib/models/game/board.dart index f30f306ca73e61857a87882926990efaddef7953..50db4a0f50f395a05bf0ce97ba4c9b21fec774e9 100644 --- a/lib/models/game_board.dart +++ b/lib/models/game/board.dart @@ -1,38 +1,38 @@ import 'dart:math'; -import 'package:jeweled/models/game_cell.dart'; -import 'package:jeweled/models/settings_game.dart'; +import 'package:jeweled/models/game/cell.dart'; +import 'package:jeweled/models/settings/settings_game.dart'; import 'package:jeweled/utils/tools.dart'; -class GameBoard { - final List<List<GameCell>> cells; +class Board { + final List<List<Cell>> cells; - GameBoard({ + Board({ required this.cells, }); - factory GameBoard.createNull() { - return GameBoard(cells: []); + factory Board.createEmpty() { + return Board(cells: []); } - factory GameBoard.createRandom(GameSettings gameSettings) { - final int boardSizeHorizontal = gameSettings.boardSize; - final int boardSizeVertical = gameSettings.boardSize; - final int maxValue = gameSettings.colorsCount; + factory Board.createRandom(GameSettings gameSettings) { + final int boardSizeHorizontal = gameSettings.boardSizeValue; + final int boardSizeVertical = gameSettings.boardSizeValue; + final int maxValue = gameSettings.colorsCountValue; final rand = Random(); - List<List<GameCell>> cells = []; + List<List<Cell>> cells = []; for (int rowIndex = 0; rowIndex < boardSizeVertical; rowIndex++) { - List<GameCell> row = []; + List<Cell> row = []; for (int colIndex = 0; colIndex < boardSizeHorizontal; colIndex++) { int value = 1 + rand.nextInt(maxValue); - row.add(GameCell(value)); + row.add(Cell(value)); } cells.add(row); } - return GameBoard( + return Board( cells: cells, ); } diff --git a/lib/models/game_cell.dart b/lib/models/game/cell.dart similarity index 87% rename from lib/models/game_cell.dart rename to lib/models/game/cell.dart index 4cba17ba8adef606942ce8932dcbf429886edd5d..8c28528898c05a0ee5e5c9b021ffce1ddfcc4179 100644 --- a/lib/models/game_cell.dart +++ b/lib/models/game/cell.dart @@ -1,7 +1,7 @@ -class GameCell { +class Cell { int? value; - GameCell( + Cell( this.value, ); diff --git a/lib/models/cell_location.dart b/lib/models/game/cell_location.dart similarity index 100% rename from lib/models/cell_location.dart rename to lib/models/game/cell_location.dart diff --git a/lib/models/game.dart b/lib/models/game/game.dart similarity index 71% rename from lib/models/game.dart rename to lib/models/game/game.dart index c302b35382fa1055bcf4d05757feb3df3c453eac..6c4f4ae8d95482da0ccb8c73dedf04d6b732489e 100644 --- a/lib/models/game.dart +++ b/lib/models/game/game.dart @@ -1,42 +1,63 @@ import 'dart:math'; +import 'package:jeweled/config/color_theme.dart'; import 'package:jeweled/config/default_global_settings.dart'; -import 'package:jeweled/models/game_board.dart'; -import 'package:jeweled/models/game_cell.dart'; -import 'package:jeweled/models/cell_location.dart'; -import 'package:jeweled/models/settings_game.dart'; -import 'package:jeweled/models/settings_global.dart'; -import 'package:jeweled/utils/color_theme.dart'; +import 'package:jeweled/models/game/board.dart'; +import 'package:jeweled/models/game/cell.dart'; +import 'package:jeweled/models/game/cell_location.dart'; +import 'package:jeweled/models/settings/settings_game.dart'; +import 'package:jeweled/models/settings/settings_global.dart'; import 'package:jeweled/utils/tools.dart'; class Game { - final GameBoard board; - final GameSettings gameSettings; - final GlobalSettings globalSettings; - List<int> shuffledColors = []; - bool isRunning = false; - bool isFinished = false; - int availableBlocksCount = 0; - int movesCount = 0; - int score = 0; - Game({ - required this.board, + // Settings required this.gameSettings, required this.globalSettings, - required this.shuffledColors, + + // State this.isRunning = false, + this.isStarted = false, this.isFinished = false, + this.animationInProgress = false, + + // Base data + required this.board, + + // Game data + required this.shuffledColors, this.availableBlocksCount = 0, - this.movesCount = 0, this.score = 0, + this.movesCount = 0, }); - factory Game.createNull() { + // Settings + final GameSettings gameSettings; + final GlobalSettings globalSettings; + + // State + bool isRunning; + bool isStarted; + bool isFinished; + bool animationInProgress; + + // Base data + final Board board; + + // Game data + List<int> shuffledColors; + int availableBlocksCount; + int score; + int movesCount; + + factory Game.createEmpty() { return Game( - board: GameBoard.createNull(), + // Settings gameSettings: GameSettings.createDefault(), globalSettings: GlobalSettings.createDefault(), + // Base data + board: Board.createEmpty(), + // Game data shuffledColors: shuffleColors(DefaultGlobalSettings.defaultColorsThemeValue), ); } @@ -45,19 +66,27 @@ 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( - board: GameBoard.createRandom(newGameSettings), + // Settings gameSettings: newGameSettings, globalSettings: newGlobalSettings, - shuffledColors: shuffleColors(newGlobalSettings.colorsTheme), + // State isRunning: true, + // Base data + board: Board.createRandom(newGameSettings), + // Game data + shuffledColors: shuffleColors(newGlobalSettings.colorsTheme), ); } - static List<int> shuffleColors(final int colorsTheme) { + bool get canBeResumed => isStarted && !isFinished; + + bool get gameWon => isRunning && isStarted && isFinished; + + static List<int> shuffleColors(final String colorsTheme) { List<int> values = List<int>.generate(ColorTheme.getColorsCount(colorsTheme), (i) => i + 1); values.shuffle(); @@ -65,11 +94,11 @@ class Game { return values; } - void shuffleColorsAgain(final int colorsTheme) { + void shuffleColorsAgain(final String colorsTheme) { shuffledColors = shuffleColors(colorsTheme); } - GameCell getCell(CellLocation cellLocation) { + Cell getCell(CellLocation cellLocation) { return board.cells[cellLocation.row][cellLocation.col]; } @@ -98,20 +127,12 @@ class Game { availableBlocksCount = getAvailableBlocks(this).length; } - void updateGameIsRunning(bool gameIsRunning) { - isRunning = gameIsRunning; - } - - void updateGameIsFinished(bool gameIsFinished) { - isFinished = gameIsFinished; - } - List<CellLocation> getSiblingCells( final CellLocation referenceCellLocation, List<CellLocation> siblingCells, ) { - final int boardSizeHorizontal = gameSettings.boardSize; - final int boardSizeVertical = gameSettings.boardSize; + final int boardSizeHorizontal = gameSettings.boardSizeValue; + final int boardSizeVertical = gameSettings.boardSizeValue; final int? referenceValue = getCellValue(referenceCellLocation); @@ -147,8 +168,8 @@ class Game { } List<List<CellLocation>> getAvailableBlocks(final Game game) { - final int boardSizeHorizontal = game.gameSettings.boardSize; - final int boardSizeVertical = game.gameSettings.boardSize; + final int boardSizeHorizontal = game.gameSettings.boardSizeValue; + final int boardSizeVertical = game.gameSettings.boardSizeValue; final List<List<CellLocation>> blocks = []; @@ -180,8 +201,8 @@ class Game { } bool hasAtLeastOneAvailableBlock() { - final int boardSizeHorizontal = gameSettings.boardSize; - final int boardSizeVertical = gameSettings.boardSize; + final int boardSizeHorizontal = gameSettings.boardSizeValue; + final int boardSizeVertical = gameSettings.boardSizeValue; for (int row = 0; row < boardSizeVertical; row++) { for (int col = 0; col < boardSizeHorizontal; col++) { @@ -201,10 +222,9 @@ class Game { } bool isInBoard(CellLocation cell) { - if (cell.row > 0 && - cell.row < gameSettings.boardSize && - cell.col > 0 && - cell.col < gameSettings.boardSize) { + final int boardSize = gameSettings.boardSizeValue; + + if (cell.row > 0 && cell.row < boardSize && cell.col > 0 && cell.col < boardSize) { return true; } return false; @@ -218,14 +238,14 @@ class Game { final List<int> values = []; // All eligible values (twice) - final int maxValue = gameSettings.colorsCount; + final int maxValue = gameSettings.colorsCountValue; for (int i = 1; i <= maxValue; i++) { values.add(i); values.add(i); } // Add values of current col (twice) - for (int r = 0; r <= gameSettings.boardSize; r++) { + for (int r = 0; r <= gameSettings.boardSizeValue; r++) { if (isInBoard(CellLocation.go(r, col))) { final int? value = getCellValue(CellLocation.go(r, col)); if (value != null) { @@ -238,12 +258,12 @@ class Game { // Add values of sibling cols (twice for top rows) for (int deltaCol = -1; deltaCol <= 1; deltaCol++) { final int c = col + deltaCol; - for (int r = 0; r < gameSettings.boardSize; r++) { + for (int r = 0; r < gameSettings.boardSizeValue; r++) { if (isInBoard(CellLocation.go(r, c))) { final int? value = getCellValue(CellLocation.go(r, c)); if (value != null) { values.add(value); - if (row < gameSettings.boardSize / 3) { + if (row < gameSettings.boardSizeValue / 3) { values.add(value); } } @@ -273,37 +293,47 @@ class Game { printlog(''); printlog('## Current game dump:'); printlog(''); + printlog('$Game:'); + printlog(' Settings'); gameSettings.dump(); globalSettings.dump(); - printlog(''); + printlog(' State'); + printlog(' isRunning: $isRunning'); + printlog(' isStarted: $isStarted'); + printlog(' isFinished: $isFinished'); + printlog(' animationInProgress: $animationInProgress'); + printlog(' Base data'); board.dump(); - printlog(''); - printlog('Game: '); - printlog(' isRunning: $isRunning'); - printlog(' isFinished: $isFinished'); - printlog(' movesCount: $movesCount'); - printlog(' score: $score'); - printlog(' availableBlocksCount: $availableBlocksCount'); - printlog(' shuffledColors: $shuffledColors'); + printlog(' Game data'); + printlog(' shuffledColors: $shuffledColors'); + printlog(' availableBlocksCount: $availableBlocksCount'); + printlog(' score: $score'); + printlog(' movesCount: $movesCount'); printlog(''); } @override String toString() { - return 'Game(${toJson()})'; + return '$Game(${toJson()})'; } Map<String, dynamic>? toJson() { return <String, dynamic>{ - 'board': board.toJson(), + // Settings 'gameSettings': gameSettings.toJson(), 'globalSettings': globalSettings.toJson(), - 'shuffledColors': shuffledColors, + // State 'isRunning': isRunning, + 'isStarted': isStarted, 'isFinished': isFinished, + 'animationInProgress': animationInProgress, + // Base data + 'board': board.toJson(), + // Game data + 'shuffledColors': shuffledColors, 'availableBlocksCount': availableBlocksCount, - 'movesCount': movesCount, 'score': score, + 'movesCount': movesCount, }; } } diff --git a/lib/models/settings_game.dart b/lib/models/settings/settings_game.dart similarity index 52% rename from lib/models/settings_game.dart rename to lib/models/settings/settings_game.dart index 39cdebf29bfe450ca507ed63b2d6b3edb1484011..da6f4e36a8df0bcfdda10b4efc76b9e12f8db980 100644 --- a/lib/models/settings_game.dart +++ b/lib/models/settings/settings_game.dart @@ -1,16 +1,20 @@ import 'package:jeweled/config/default_game_settings.dart'; -import 'package:jeweled/utils/tools.dart'; +import 'package:jeweled/utils/tools.dart'; class GameSettings { - final int boardSize; - final int colorsCount; + final String boardSize; + final String colorsCount; GameSettings({ required this.boardSize, required this.colorsCount, }); - static int getBoardSizeValueFromUnsafe(int size) { + // Getters to convert String to int + int get boardSizeValue => int.parse(boardSize); + int get colorsCountValue => int.parse(colorsCount); + + static String getBoardSizeValueFromUnsafe(String size) { if (DefaultGameSettings.allowedBoardSizeValues.contains(size)) { return size; } @@ -18,7 +22,7 @@ class GameSettings { return DefaultGameSettings.defaultBoardSizeValue; } - static int getColorsCountValueFromUnsafe(int colorsCount) { + static String getColorsCountValueFromUnsafe(String colorsCount) { if (DefaultGameSettings.allowedColorsCountValues.contains(colorsCount)) { return colorsCount; } @@ -34,20 +38,21 @@ class GameSettings { } void dump() { - printlog('Settings: '); - printlog(' boardSize: $boardSize'); - printlog(' colorsCount: $colorsCount'); + printlog('$GameSettings:'); + printlog(' ${DefaultGameSettings.parameterCodeBoardSize}: $boardSize'); + printlog(' ${DefaultGameSettings.parameterCodeColorsCount}: $colorsCount'); + printlog(''); } @override String toString() { - return 'GameSettings(${toJson()})'; + return '$GameSettings(${toJson()})'; } Map<String, dynamic>? toJson() { return <String, dynamic>{ - 'boardSize': boardSize, - 'colorsCount': colorsCount, + DefaultGameSettings.parameterCodeBoardSize: boardSize, + DefaultGameSettings.parameterCodeColorsCount: colorsCount, }; } } diff --git a/lib/models/settings_global.dart b/lib/models/settings/settings_global.dart similarity index 58% rename from lib/models/settings_global.dart rename to lib/models/settings/settings_global.dart index 231917c72a9fe26d3c4bbc488ddfe6c64c303520..8212f83365dd6f9579422b457391f109c62fe76f 100644 --- a/lib/models/settings_global.dart +++ b/lib/models/settings/settings_global.dart @@ -1,16 +1,16 @@ import 'package:jeweled/config/default_global_settings.dart'; -import 'package:jeweled/utils/tools.dart'; +import 'package:jeweled/utils/tools.dart'; class GlobalSettings { - final int colorsTheme; - final int graphicTheme; + final String colorsTheme; + final String graphicTheme; GlobalSettings({ required this.colorsTheme, required this.graphicTheme, }); - static int getColorsThemeValueFromUnsafe(int colorsTheme) { + static String getColorsThemeValueFromUnsafe(String colorsTheme) { if (DefaultGlobalSettings.allowedColorsThemeValues.contains(colorsTheme)) { return colorsTheme; } @@ -18,7 +18,7 @@ class GlobalSettings { return DefaultGlobalSettings.defaultColorsThemeValue; } - static int getGraphicThemeValueFromUnsafe(int graphicTheme) { + static String getGraphicThemeValueFromUnsafe(String graphicTheme) { if (DefaultGlobalSettings.allowedGraphicThemeValues.contains(graphicTheme)) { return graphicTheme; } @@ -34,20 +34,21 @@ class GlobalSettings { } void dump() { - printlog('Settings: '); - printlog(' colorsTheme: $colorsTheme'); - printlog(' graphicTheme: $graphicTheme'); + printlog('$GlobalSettings:'); + printlog(' ${DefaultGlobalSettings.parameterCodeColorsTheme}: $colorsTheme'); + printlog(' ${DefaultGlobalSettings.parameterCodeGraphicsTheme}: $graphicTheme'); + printlog(''); } @override String toString() { - return 'GlobalSettings(${toJson()})'; + return '$GlobalSettings(${toJson()})'; } Map<String, dynamic>? toJson() { return <String, dynamic>{ - 'colorsTheme': colorsTheme, - 'graphicTheme': graphicTheme, + DefaultGlobalSettings.parameterCodeColorsTheme: colorsTheme, + DefaultGlobalSettings.parameterCodeGraphicsTheme: graphicTheme, }; } } diff --git a/lib/ui/widgets/game_bottom_buttons.dart b/lib/ui/game/game_end.dart similarity index 64% rename from lib/ui/widgets/game_bottom_buttons.dart rename to lib/ui/game/game_end.dart index 4faea09427554cfd20d477eb2569e6805bfca580..71196a05b2f15dec9376355cd523011a36df7b87 100644 --- a/lib/ui/widgets/game_bottom_buttons.dart +++ b/lib/ui/game/game_end.dart @@ -3,19 +3,19 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:jeweled/cubit/game_cubit.dart'; -class GameBottomButtonsWidget extends StatelessWidget { - const GameBottomButtonsWidget({super.key}); +class GameEndWidget extends StatelessWidget { + const GameEndWidget({super.key}); @override Widget build(BuildContext context) { - String decorationImageAssetName = 'assets/icons/game_fail.png'; + const String decorationImageAssetName = 'assets/ui/placeholder.png'; - Widget decorationWidget = TextButton( + const Widget decorationWidget = TextButton( + onPressed: null, child: Image( image: AssetImage(decorationImageAssetName), fit: BoxFit.fill, ), - onPressed: () {}, ); return Table( @@ -23,24 +23,23 @@ class GameBottomButtonsWidget extends StatelessWidget { children: [ TableRow( children: [ - Column( + const Column( children: [decorationWidget], ), Column( children: [ TextButton( child: const Image( - image: AssetImage('assets/icons/button_back.png'), + image: AssetImage('assets/ui/button_back.png'), fit: BoxFit.fill, ), onPressed: () { - final GameCubit gameCubit = BlocProvider.of<GameCubit>(context); - gameCubit.quitGame(); + BlocProvider.of<GameCubit>(context).quitGame(); }, ) ], ), - Column( + const Column( children: [decorationWidget], ), ], diff --git a/lib/ui/widgets/game_top_indicator.dart b/lib/ui/game/game_top.dart similarity index 90% rename from lib/ui/widgets/game_top_indicator.dart rename to lib/ui/game/game_top.dart index 3d639b72138c8d162f43e84c49157d2a9dfbcf00..36f45daa6f1fde3225c9350827b13ce64c748a17 100644 --- a/lib/ui/widgets/game_top_indicator.dart +++ b/lib/ui/game/game_top.dart @@ -2,14 +2,14 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:jeweled/cubit/game_cubit.dart'; -import 'package:jeweled/models/game.dart'; +import 'package:jeweled/models/game/game.dart'; import 'package:jeweled/ui/widgets/indicators/indicator_available_blocks.dart'; import 'package:jeweled/ui/widgets/indicators/indicator_moves_count.dart'; import 'package:jeweled/ui/widgets/indicators/indicator_score.dart'; import 'package:jeweled/ui/widgets/indicators/indicator_shuffle_button.dart'; -class GameTopIndicatorWidget extends StatelessWidget { - const GameTopIndicatorWidget({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/widgets/helpers/outlined_text_widget.dart b/lib/ui/helpers/outlined_text_widget.dart similarity index 69% rename from lib/ui/widgets/helpers/outlined_text_widget.dart rename to lib/ui/helpers/outlined_text_widget.dart index 8e33709bd71085ea7a64084a231f692953af203d..99c26587ba0ae4323777e9bea53064e36e3af88a 100644 --- a/lib/ui/widgets/helpers/outlined_text_widget.dart +++ b/lib/ui/helpers/outlined_text_widget.dart @@ -1,22 +1,24 @@ import 'package:flutter/material.dart'; +import 'package:jeweled/utils/color_extensions.dart'; + class OutlinedText extends StatelessWidget { const OutlinedText({ super.key, required this.text, required this.fontSize, required this.textColor, - required this.outlineColor, + this.outlineColor, }); final String text; final double fontSize; final Color textColor; - final Color outlineColor; + final Color? outlineColor; @override Widget build(BuildContext context) { - final double delta = fontSize / 35; + final double delta = fontSize / 30; return Text( text, @@ -28,19 +30,19 @@ class OutlinedText extends StatelessWidget { shadows: [ Shadow( offset: Offset(-delta, -delta), - color: outlineColor, + color: outlineColor ?? textColor.darken(), ), Shadow( offset: Offset(delta, -delta), - color: outlineColor, + color: outlineColor ?? textColor.darken(), ), Shadow( offset: Offset(delta, delta), - color: outlineColor, + color: outlineColor ?? textColor.darken(), ), Shadow( offset: Offset(-delta, delta), - color: outlineColor, + color: outlineColor ?? textColor.darken(), ), ], ), diff --git a/lib/ui/widgets/game.dart b/lib/ui/layouts/game_layout.dart similarity index 53% rename from lib/ui/widgets/game.dart rename to lib/ui/layouts/game_layout.dart index 443c554a062e2b6ccf10e29971d9905e0399689b..4c9e4a6067984e2902cecb5f7669df4bf7fc7beb 100644 --- a/lib/ui/widgets/game.dart +++ b/lib/ui/layouts/game_layout.dart @@ -2,13 +2,13 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:jeweled/cubit/game_cubit.dart'; -import 'package:jeweled/models/game.dart'; -import 'package:jeweled/ui/widgets/game_board.dart'; -import 'package:jeweled/ui/widgets/game_bottom_buttons.dart'; -import 'package:jeweled/ui/widgets/game_top_indicator.dart'; +import 'package:jeweled/models/game/game.dart'; +import 'package:jeweled/ui/game/game_end.dart'; +import 'package:jeweled/ui/game/game_top.dart'; +import 'package:jeweled/ui/widgets/game/board.dart'; -class GameWidget extends StatelessWidget { - const GameWidget({super.key}); +class GameLayout extends StatelessWidget { + const GameLayout({super.key}); @override Widget build(BuildContext context) { @@ -17,19 +17,18 @@ class GameWidget extends StatelessWidget { 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 GameTopIndicatorWidget(), - const SizedBox(height: 2), - const GameBoard(), - const SizedBox(height: 2), - currentGame.isFinished - ? const GameBottomButtonsWidget() - : const SizedBox.shrink(), + const BoardWidget(), + 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 51% rename from lib/ui/widgets/parameters.dart rename to lib/ui/layouts/parameters_layout.dart index c8f858ab2bc1d882e7957c17b1e4f5fa86923b9e..6432f851cb337c53a93d7fc7f10ad210b2d9de26 100644 --- a/lib/ui/widgets/parameters.dart +++ b/lib/ui/layouts/parameters_layout.dart @@ -3,76 +3,20 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:jeweled/config/default_game_settings.dart'; import 'package:jeweled/config/default_global_settings.dart'; -import 'package:jeweled/cubit/game_cubit.dart'; import 'package:jeweled/cubit/settings_game_cubit.dart'; import 'package:jeweled/cubit/settings_global_cubit.dart'; -import 'package:jeweled/ui/painters/parameter_painter.dart'; +import 'package:jeweled/ui/parameters/parameter_image.dart'; +import 'package:jeweled/ui/parameters/parameter_painter.dart'; +import 'package:jeweled/ui/widgets/actions/button_delete_saved_game.dart'; +import 'package:jeweled/ui/widgets/actions/button_game_start_new.dart'; +import 'package:jeweled/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 double separatorHeight = 8.0; - - List<Widget> buildParametersLine({ - required String code, - required bool isGlobal, - }) { - final List<Widget> parameterButtons = []; - - final List<int> availableValues = isGlobal - ? DefaultGlobalSettings.getAvailableValues(code) - : DefaultGameSettings.getAvailableValues(code); - - for (int value in availableValues) { - final Widget parameterButton = BlocBuilder<GameSettingsCubit, GameSettingsState>( - builder: (BuildContext context, GameSettingsState gameSettingsState) { - return BlocBuilder<GlobalSettingsCubit, GlobalSettingsState>( - builder: (BuildContext context, GlobalSettingsState globalSettingsState) { - final GameSettingsCubit gameSettingsCubit = - BlocProvider.of<GameSettingsCubit>(context); - final GlobalSettingsCubit globalSettingsCubit = - BlocProvider.of<GlobalSettingsCubit>(context); - - final int currentValue = isGlobal - ? globalSettingsCubit.getParameterValue(code) - : gameSettingsCubit.getParameterValue(code); - - final bool isActive = (value == currentValue); + final bool canResume; - final double displayWidth = MediaQuery.of(context).size.width; - final double itemWidth = displayWidth / availableValues.length - 25; - - return TextButton( - child: Container( - margin: const EdgeInsets.all(0), - padding: const EdgeInsets.all(0), - child: 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), - ); - }, - ); - }, - ); - - parameterButtons.add(parameterButton); - } - - return parameterButtons; - } + final double separatorHeight = 8.0; @override Widget build(BuildContext context) { @@ -92,7 +36,24 @@ class Parameters extends StatelessWidget { } lines.add(SizedBox(height: separatorHeight)); - lines.add(Expanded(child: buildStartNewGameButton())); + + 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 @@ -113,47 +74,81 @@ class Parameters extends StatelessWidget { ); } - static Image buildImageWidget(String imageAssetCode) { - return Image( - image: AssetImage('assets/icons/$imageAssetCode.png'), - fit: BoxFit.fill, - ); - } + List<Widget> buildParametersLine({ + required String code, + required bool isGlobal, + }) { + final List<Widget> parameterButtons = []; - static Container buildImageContainerWidget(String imageAssetCode) { - return Container( - child: buildImageWidget(imageAssetCode), - ); - } + final List<String> availableValues = isGlobal + ? DefaultGlobalSettings.getAvailableValues(code) + : DefaultGameSettings.getAvailableValues(code); - static Column buildDecorationImageWidget() { - return Column( - children: [ - TextButton( - child: buildImageContainerWidget('placeholder'), - onPressed: () {}, - ), - ], - ); - } + if (availableValues.length <= 1) { + return []; + } - static Widget buildStartNewGameButton() { - return BlocBuilder<GameSettingsCubit, GameSettingsState>( - builder: (BuildContext context, GameSettingsState gameSettingsState) { - return BlocBuilder<GlobalSettingsCubit, GlobalSettingsState>( - builder: (BuildContext context, GlobalSettingsState globalSettingsState) { - final GameCubit gameCubit = BlocProvider.of<GameCubit>(context); - - return TextButton( - child: buildImageContainerWidget('button_start'), - onPressed: () => gameCubit.startNewGame( - gameSettings: gameSettingsState.settings, - globalSettings: globalSettingsState.settings, - ), - ); - }, - ); - }, - ); + for (String value in availableValues) { + final Widget parameterButton = BlocBuilder<GameSettingsCubit, GameSettingsState>( + builder: (BuildContext context, GameSettingsState gameSettingsState) { + return BlocBuilder<GlobalSettingsCubit, GlobalSettingsState>( + builder: (BuildContext context, GlobalSettingsState globalSettingsState) { + final GameSettingsCubit gameSettingsCubit = + BlocProvider.of<GameSettingsCubit>(context); + final GlobalSettingsCubit globalSettingsCubit = + BlocProvider.of<GlobalSettingsCubit>(context); + + final String currentValue = isGlobal + ? globalSettingsCubit.getParameterValue(code) + : gameSettingsCubit.getParameterValue(code); + + final bool isActive = (value == currentValue); + + 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: 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); + }, + ); + }, + ); + }, + ); + + parameterButtons.add(parameterButton); + } + + return parameterButtons; } } diff --git a/lib/ui/painters/game_board_painter.dart b/lib/ui/painters/game_board_painter.dart index 2b99234219598ca5d9d1a66b27f72e0e1c33056b..2c490e4309d8f674b1e09f4f3049ef4483db2a00 100644 --- a/lib/ui/painters/game_board_painter.dart +++ b/lib/ui/painters/game_board_painter.dart @@ -3,11 +3,11 @@ import 'dart:ui' as ui; import 'package:flutter/material.dart'; +import 'package:jeweled/config/color_theme.dart'; import 'package:jeweled/config/default_global_settings.dart'; -import 'package:jeweled/models/game.dart'; -import 'package:jeweled/models/cell_location.dart'; +import 'package:jeweled/models/game/cell_location.dart'; +import 'package:jeweled/models/game/game.dart'; import 'package:jeweled/utils/color_extensions.dart'; -import 'package:jeweled/utils/color_theme.dart'; class GameBoardPainter extends CustomPainter { const GameBoardPainter({ @@ -20,7 +20,7 @@ class GameBoardPainter extends CustomPainter { @override void paint(Canvas canvas, Size size) { - int graphicTheme = game.globalSettings.graphicTheme; + String graphicTheme = game.globalSettings.graphicTheme; final double canvasSize = min(size.width, size.height); @@ -97,9 +97,9 @@ class GameBoardPainter extends CustomPainter { double borderWidth = 0, List<String>? contentStrings, }) { - final int cellsCountHorizontal = game.gameSettings.boardSize; - final int cellsCountVertical = game.gameSettings.boardSize; - final int colorsTheme = game.globalSettings.colorsTheme; + final int cellsCountHorizontal = game.gameSettings.boardSizeValue; + final int cellsCountVertical = game.gameSettings.boardSizeValue; + final String colorsTheme = game.globalSettings.colorsTheme; final double size = canvasSize / max(cellsCountHorizontal, cellsCountVertical); @@ -138,7 +138,7 @@ class GameBoardPainter extends CustomPainter { required double y, required double cellSize, required int cellValue, - required int colorsTheme, + required String colorsTheme, required double overlapping, required int gradientFrom, required int gradientTo, 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 92% rename from lib/ui/painters/parameter_painter.dart rename to lib/ui/parameters/parameter_painter.dart index 5e4a952b2fd08996067d469cc83a23f5a0f28bb6..739985105a1aec5efbfb5cabfba6633850ee1dd5 100644 --- a/lib/ui/painters/parameter_painter.dart +++ b/lib/ui/parameters/parameter_painter.dart @@ -3,12 +3,12 @@ import 'dart:ui' as ui; import 'package:flutter/material.dart'; +import 'package:jeweled/config/color_theme.dart'; import 'package:jeweled/config/default_game_settings.dart'; import 'package:jeweled/config/default_global_settings.dart'; -import 'package:jeweled/models/settings_game.dart'; -import 'package:jeweled/models/settings_global.dart'; +import 'package:jeweled/models/settings/settings_game.dart'; +import 'package:jeweled/models/settings/settings_global.dart'; import 'package:jeweled/utils/color_extensions.dart'; -import 'package:jeweled/utils/color_theme.dart'; import 'package:jeweled/utils/tools.dart'; class ParameterPainter extends CustomPainter { @@ -21,7 +21,7 @@ class ParameterPainter extends CustomPainter { }); final String code; - final int value; + final String value; final bool isSelected; final GameSettings gameSettings; final GlobalSettings globalSettings; @@ -39,22 +39,22 @@ class ParameterPainter extends CustomPainter { paint.style = PaintingStyle.stroke; paint.color = isSelected ? borderColorEnabled : borderColorDisabled; paint.strokeJoin = StrokeJoin.round; - paint.strokeWidth = 20 / 100 * canvasSize; + paint.strokeWidth = 10; canvas.drawRect( Rect.fromPoints(const Offset(0, 0), Offset(canvasSize, canvasSize)), paint); // content switch (code) { - case 'colorsCount': + case DefaultGameSettings.parameterCodeColorsCount: paintColorsCountParameterItem(value, canvas, canvasSize); break; - case 'boardSize': + case DefaultGameSettings.parameterCodeBoardSize: paintBoardSizeParameterItem(value, canvas, canvasSize); break; - case 'colorsTheme': + case DefaultGlobalSettings.parameterCodeColorsTheme: paintColorsThemeParameterItem(value, canvas, canvasSize); break; - case 'graphicTheme': + case DefaultGlobalSettings.parameterCodeGraphicsTheme: paintGraphicThemeParameterItem(value, canvas, canvasSize); break; default: @@ -68,15 +68,15 @@ class ParameterPainter extends CustomPainter { return false; } - // "unknown" parameter -> simple bock with text + // "unknown" parameter -> simple block with text void paintUnknownParameterItem( - final int value, + final String value, final Canvas canvas, final double size, ) { final paint = Paint(); paint.strokeJoin = StrokeJoin.round; - paint.strokeWidth = 3 / 100 * size; + paint.strokeWidth = 3; paint.color = Colors.grey; paint.style = PaintingStyle.fill; @@ -93,6 +93,7 @@ class ParameterPainter extends CustomPainter { final textPainter = TextPainter( text: textSpan, textDirection: TextDirection.ltr, + textAlign: TextAlign.center, ); textPainter.layout(); textPainter.paint( @@ -105,7 +106,7 @@ class ParameterPainter extends CustomPainter { } void paintBoardSizeParameterItem( - final int value, + final String value, final Canvas canvas, final double size, ) { @@ -168,7 +169,7 @@ class ParameterPainter extends CustomPainter { } void paintColorsCountParameterItem( - final int value, + final String value, final Canvas canvas, final double size, ) { @@ -193,7 +194,7 @@ class ParameterPainter extends CustomPainter { final paint = Paint(); paint.strokeJoin = StrokeJoin.round; - paint.strokeWidth = 3 / 100 * size; + paint.strokeWidth = 3; // Colored background paint.color = backgroundColor; @@ -216,7 +217,9 @@ class ParameterPainter extends CustomPainter { final double margin = 3 / 100 * size; final double width = ((size - 2 * padding) / 3) - 2 * margin; - for (int colorIndex = 0; colorIndex < value; colorIndex++) { + final colorsCount = int.parse(value); + + for (int colorIndex = 0; colorIndex < colorsCount; colorIndex++) { final Offset position = positions[colorIndex]; final Offset topLeft = Offset(padding + margin + position.dx * (width + 2 * margin), @@ -248,6 +251,7 @@ class ParameterPainter extends CustomPainter { final textPainter = TextPainter( text: textSpan, textDirection: TextDirection.ltr, + textAlign: TextAlign.center, ); textPainter.layout(); textPainter.paint( @@ -260,7 +264,7 @@ class ParameterPainter extends CustomPainter { } void paintColorsThemeParameterItem( - final int value, + final String value, final Canvas canvas, final double size, ) { @@ -301,7 +305,7 @@ class ParameterPainter extends CustomPainter { } void paintGraphicThemeParameterItem( - final int value, + final String value, final Canvas canvas, final double size, ) { @@ -309,7 +313,7 @@ class ParameterPainter extends CustomPainter { final paint = Paint(); paint.strokeJoin = StrokeJoin.round; - paint.strokeWidth = 3 / 100 * size; + paint.strokeWidth = 3; // Colored background paint.color = backgroundColor; @@ -399,6 +403,7 @@ class ParameterPainter extends CustomPainter { final textPainter = TextPainter( text: textSpan, textDirection: TextDirection.ltr, + textAlign: TextAlign.center, ); textPainter.layout(); textPainter.paint( diff --git a/lib/ui/screens/game_page.dart b/lib/ui/screens/game_page.dart deleted file mode 100644 index 24694bc5e0db1d9efb841c1a2bc1a3dd601fdd65..0000000000000000000000000000000000000000 --- a/lib/ui/screens/game_page.dart +++ /dev/null @@ -1,22 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; - -import 'package:jeweled/cubit/game_cubit.dart'; -import 'package:jeweled/ui/widgets/game.dart'; -import 'package:jeweled/ui/widgets/parameters.dart'; - -class GamePage extends StatelessWidget { - const GamePage({super.key}); - - @override - Widget build(BuildContext context) { - return Material( - color: Theme.of(context).colorScheme.background, - child: BlocBuilder<GameCubit, GameState>( - builder: (BuildContext context, GameState gameState) { - return gameState.currentGame.isRunning ? const GameWidget() : const Parameters(); - }, - ), - ); - } -} diff --git a/lib/ui/screens/about_page.dart b/lib/ui/screens/page_about.dart similarity index 86% rename from lib/ui/screens/about_page.dart rename to lib/ui/screens/page_about.dart index 0ffecbc5bcd11b53642e2b8bc80bd73961707b82..f359df109b71dfd1d61a40dd163174525a10a73d 100644 --- a/lib/ui/screens/about_page.dart +++ b/lib/ui/screens/page_about.dart @@ -2,10 +2,10 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:package_info_plus/package_info_plus.dart'; -import 'package:jeweled/ui/widgets/header_app.dart'; +import 'package:jeweled/ui/helpers/app_titles.dart'; -class AboutPage extends StatelessWidget { - const AboutPage({super.key}); +class PageAbout extends StatelessWidget { + const PageAbout({super.key}); @override Widget build(BuildContext context) { @@ -17,7 +17,7 @@ class AboutPage 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 new file mode 100644 index 0000000000000000000000000000000000000000..a7e380720505fecae87050b94067bc79b3a73511 --- /dev/null +++ b/lib/ui/screens/page_game.dart @@ -0,0 +1,24 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +import 'package:jeweled/cubit/game_cubit.dart'; +import 'package:jeweled/models/game/game.dart'; +import 'package:jeweled/ui/layouts/game_layout.dart'; +import 'package:jeweled/ui/layouts/parameters_layout.dart'; + +class PageGame extends StatelessWidget { + const PageGame({super.key}); + + @override + Widget build(BuildContext context) { + return BlocBuilder<GameCubit, GameState>( + builder: (BuildContext context, GameState gameState) { + final Game currentGame = gameState.currentGame; + + return currentGame.isRunning + ? const GameLayout() + : ParametersLayout(canResume: currentGame.canBeResumed); + }, + ); + } +} diff --git a/lib/ui/screens/settings_page.dart b/lib/ui/screens/page_settings.dart similarity index 66% rename from lib/ui/screens/settings_page.dart rename to lib/ui/screens/page_settings.dart index 0bf6979befe32f9f30d8d5a21f7db28b0f7faaff..51389bc3dc109a18034de618512bad8463f0051b 100644 --- a/lib/ui/screens/settings_page.dart +++ b/lib/ui/screens/page_settings.dart @@ -1,10 +1,10 @@ import 'package:flutter/material.dart'; -import 'package:jeweled/ui/widgets/header_app.dart'; -import 'package:jeweled/ui/widgets/settings/settings_form.dart'; +import 'package:jeweled/ui/helpers/app_titles.dart'; +import 'package:jeweled/ui/settings/settings_form.dart'; -class SettingsPage extends StatelessWidget { - const SettingsPage({super.key}); +class PageSettings extends StatelessWidget { + const PageSettings({super.key}); @override Widget build(BuildContext context) { @@ -16,7 +16,7 @@ class SettingsPage 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 9ae13f325638dcb9219561c4098cf3df1584490d..703e9c9049a2004dea9282a5b9c7aaf92ace48e4 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:jeweled/ui/widgets/settings/theme_card.dart'; +import 'package:jeweled/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 38225b2039ea32532381646c461cacbb6d2bcc4a..8513856b1c8a5a96c2b3ac11c4ee8550d7c7d56c 100644 --- a/lib/ui/skeleton.dart +++ b/lib/ui/skeleton.dart @@ -18,7 +18,7 @@ class SkeletonScreen extends StatelessWidget { return Menu.getPageWidget(pageIndex); }, ), - 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..27b5828ddbc68f65567f4533ca6b9fdfc84cf2f4 --- /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:jeweled/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..b6f561cbe92633f9cfa4f7d6a3413629787747ac --- /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:jeweled/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/actions/button_game_start_new.dart b/lib/ui/widgets/actions/button_game_start_new.dart new file mode 100644 index 0000000000000000000000000000000000000000..d946a0143ee7058250d6bdfdbd3c0d3cc503adab --- /dev/null +++ b/lib/ui/widgets/actions/button_game_start_new.dart @@ -0,0 +1,34 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +import 'package:jeweled/cubit/game_cubit.dart'; +import 'package:jeweled/cubit/settings_game_cubit.dart'; +import 'package:jeweled/cubit/settings_global_cubit.dart'; + +class StartNewGameButton extends StatelessWidget { + const StartNewGameButton({super.key}); + + @override + Widget build(BuildContext context) { + return BlocBuilder<GameSettingsCubit, GameSettingsState>( + builder: (BuildContext context, GameSettingsState gameSettingsState) { + return BlocBuilder<GlobalSettingsCubit, GlobalSettingsState>( + builder: (BuildContext context, GlobalSettingsState globalSettingsState) { + return TextButton( + child: const Image( + image: AssetImage('assets/ui/button_start.png'), + fit: BoxFit.fill, + ), + 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..c75cce7b7efb95f7e0cb13af30cff75193b87b91 --- /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:jeweled/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_board.dart b/lib/ui/widgets/game/board.dart similarity index 81% rename from lib/ui/widgets/game_board.dart rename to lib/ui/widgets/game/board.dart index beba562c5da23c3dfae9000b1101265b185f5058..5daec54bbf8b20d5d5ff2aad19d1b007ac0ad49d 100644 --- a/lib/ui/widgets/game_board.dart +++ b/lib/ui/widgets/game/board.dart @@ -2,18 +2,18 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:jeweled/cubit/game_cubit.dart'; -import 'package:jeweled/models/game.dart'; -import 'package:jeweled/models/cell_location.dart'; +import 'package:jeweled/models/game/cell_location.dart'; +import 'package:jeweled/models/game/game.dart'; import 'package:jeweled/ui/painters/game_board_painter.dart'; -class GameBoard extends StatefulWidget { - const GameBoard({super.key}); +class BoardWidget extends StatefulWidget { + const BoardWidget({super.key}); @override - State<GameBoard> createState() => _GameBoard(); + State<BoardWidget> createState() => _BoardWidget(); } -class _GameBoard extends State<GameBoard> with TickerProviderStateMixin { +class _BoardWidget extends State<BoardWidget> with TickerProviderStateMixin { final int animationDuration = 500; List<List<Animation<double>?>> animations = []; @@ -57,7 +57,7 @@ class _GameBoard extends State<GameBoard> with TickerProviderStateMixin { if (status == AnimationStatus.completed) { gameCubit.postAnimate(); - resetAnimations(currentGame.gameSettings.boardSize); + resetAnimations(currentGame.gameSettings.boardSizeValue); setState(() {}); controller.dispose(); @@ -66,9 +66,9 @@ class _GameBoard extends State<GameBoard> with TickerProviderStateMixin { // Count translation length for each cell to move final List<List<int>> stepsDownCounts = List.generate( - currentGame.gameSettings.boardSize, + currentGame.gameSettings.boardSizeValue, (i) => List.generate( - currentGame.gameSettings.boardSize, + currentGame.gameSettings.boardSizeValue, (i) => 0, ), ); @@ -105,7 +105,7 @@ class _GameBoard extends State<GameBoard> with TickerProviderStateMixin { final Game currentGame = gameState.currentGame; if (animations.isEmpty) { - resetAnimations(currentGame.gameSettings.boardSize); + resetAnimations(currentGame.gameSettings.boardSizeValue); } return GestureDetector( @@ -122,11 +122,13 @@ class _GameBoard extends State<GameBoard> with TickerProviderStateMixin { if (!animationInProgress) { final double xTap = details.localPosition.dx; final double yTap = details.localPosition.dy; - final int col = xTap ~/ (displayWidth / currentGame.gameSettings.boardSize); - final int row = yTap ~/ (displayWidth / currentGame.gameSettings.boardSize); + final int col = + xTap ~/ (displayWidth / currentGame.gameSettings.boardSizeValue); + final int row = + yTap ~/ (displayWidth / currentGame.gameSettings.boardSizeValue); - final GameCubit gameCubit = BlocProvider.of<GameCubit>(context); - animateCells(gameCubit.tapOnCell(CellLocation.go(row, col))); + animateCells( + BlocProvider.of<GameCubit>(context).tapOnCell(CellLocation.go(row, col))); } }, child: CustomPaint( diff --git a/lib/ui/widgets/global_app_bar.dart b/lib/ui/widgets/global_app_bar.dart index 51a5fe011afd2297c553f3f3b5b517f4819473d1..d09ae4deb476dc717a7bb5c9541a3292688b6267 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:jeweled/config/menu.dart'; import 'package:jeweled/cubit/game_cubit.dart'; import 'package:jeweled/cubit/nav_cubit.dart'; -import 'package:jeweled/models/game.dart'; -import 'package:jeweled/ui/widgets/helpers/app_titles.dart'; +import 'package:jeweled/models/game/game.dart'; +import 'package:jeweled/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 { @@ -37,7 +36,7 @@ class GlobalAppBar extends StatelessWidget implements PreferredSizeWidget { // go to Settings page menuActions.add(ElevatedButton( onPressed: () { - context.read<NavCubit>().switchToSettingsPage(); + context.read<NavCubit>().goToSettingsPage(); }, style: ElevatedButton.styleFrom( shape: const CircleBorder(), @@ -48,7 +47,7 @@ class GlobalAppBar extends StatelessWidget implements PreferredSizeWidget { // go to About page menuActions.add(ElevatedButton( onPressed: () { - context.read<NavCubit>().switchToAboutPage(); + context.read<NavCubit>().goToAboutPage(); }, style: ElevatedButton.styleFrom( shape: const CircleBorder(), diff --git a/lib/ui/widgets/header_app.dart b/lib/ui/widgets/header_app.dart deleted file mode 100644 index b5c5be05f6636cf488dcdb5bbc4d6f049b98de11..0000000000000000000000000000000000000000 --- a/lib/ui/widgets/header_app.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_titles.dart b/lib/ui/widgets/helpers/app_titles.dart deleted file mode 100644 index 7cbbb2030419047b3dcf093a2195a498bd8e8ce9..0000000000000000000000000000000000000000 --- a/lib/ui/widgets/helpers/app_titles.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/ui/widgets/indicators/indicator_available_blocks.dart b/lib/ui/widgets/indicators/indicator_available_blocks.dart index 865b933c041a978504d3cb10df18fad2adaf52c2..acffb94f18fedb69b03a269ffeb6e1e0f58e883f 100644 --- a/lib/ui/widgets/indicators/indicator_available_blocks.dart +++ b/lib/ui/widgets/indicators/indicator_available_blocks.dart @@ -2,8 +2,8 @@ import 'dart:math'; import 'package:flutter/material.dart'; -import 'package:jeweled/models/game.dart'; -import 'package:jeweled/ui/widgets/helpers/outlined_text_widget.dart'; +import 'package:jeweled/models/game/game.dart'; +import 'package:jeweled/ui/helpers/outlined_text_widget.dart'; import 'package:jeweled/utils/color_extensions.dart'; class AvailableBlocksCountIndicator extends StatelessWidget { diff --git a/lib/ui/widgets/indicators/indicator_moves_count.dart b/lib/ui/widgets/indicators/indicator_moves_count.dart index f6ebaa860eef77bef8ec88c50c9a1d8aea05d1b3..8693ab308c7bf58b3cdd430109280539952c8678 100644 --- a/lib/ui/widgets/indicators/indicator_moves_count.dart +++ b/lib/ui/widgets/indicators/indicator_moves_count.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; -import 'package:jeweled/models/game.dart'; -import 'package:jeweled/ui/widgets/helpers/outlined_text_widget.dart'; +import 'package:jeweled/models/game/game.dart'; +import 'package:jeweled/ui/helpers/outlined_text_widget.dart'; import 'package:jeweled/utils/color_extensions.dart'; class MovesCountsIndicator extends StatelessWidget { diff --git a/lib/ui/widgets/indicators/indicator_score.dart b/lib/ui/widgets/indicators/indicator_score.dart index 6f7dc833a2d4dd1e369cb736e1a606ca4bb67887..97f940254454aff2d7615c366f5733ef8774f60d 100644 --- a/lib/ui/widgets/indicators/indicator_score.dart +++ b/lib/ui/widgets/indicators/indicator_score.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; -import 'package:jeweled/models/game.dart'; -import 'package:jeweled/ui/widgets/helpers/outlined_text_widget.dart'; +import 'package:jeweled/models/game/game.dart'; +import 'package:jeweled/ui/helpers/outlined_text_widget.dart'; import 'package:jeweled/utils/color_extensions.dart'; class ScoreIndicator extends StatelessWidget { diff --git a/lib/ui/widgets/indicators/indicator_shuffle_button.dart b/lib/ui/widgets/indicators/indicator_shuffle_button.dart index 5688bf1b883f34aa80594a638474ad48fdea5ccb..26cc17fb68081f065f70fc95db4302d7d06030e2 100644 --- a/lib/ui/widgets/indicators/indicator_shuffle_button.dart +++ b/lib/ui/widgets/indicators/indicator_shuffle_button.dart @@ -2,8 +2,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:jeweled/cubit/game_cubit.dart'; -import 'package:jeweled/models/game.dart'; -import 'package:jeweled/ui/widgets/helpers/outlined_text_widget.dart'; +import 'package:jeweled/models/game/game.dart'; +import 'package:jeweled/ui/helpers/outlined_text_widget.dart'; import 'package:jeweled/utils/color_extensions.dart'; class ShuffleButton extends StatelessWidget { @@ -24,8 +24,7 @@ class ShuffleButton extends StatelessWidget { outlineColor: outlineColor, ), onPressed: () { - final GameCubit gameCubit = BlocProvider.of<GameCubit>(context); - gameCubit.shuffleColors(game.globalSettings.colorsTheme); + BlocProvider.of<GameCubit>(context).shuffleColors(game.globalSettings.colorsTheme); }, ); } diff --git a/pubspec.lock b/pubspec.lock index 25b5be0248780b099e0f72fc5dab70bd5ce13f92..e0ab96ebb656b1260018d45af586d9ec14ba4a7e 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -61,10 +61,10 @@ packages: dependency: "direct main" description: name: easy_localization - sha256: "432698c31a488dd64c56d4759f20d04844baba5e9e4f2cb1abb9676257918b17" + sha256: fa59bcdbbb911a764aa6acf96bbb6fa7a5cf8234354fc45ec1a43a0349ef0201 url: "https://pub.dev" source: hosted - version: "3.0.6" + version: "3.0.7" easy_logger: dependency: transitive description: @@ -106,18 +106,18 @@ 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: name: flutter_lints - sha256: "9e8c3858111da373efc5aa341de011d9bd23e2c5c5e0c62bccf32438e192d7b1" + sha256: "3f41d009ba7172d5ff9be5f6e6e6abb4300e263aab8866d2a0842ed2a70f8f0c" url: "https://pub.dev" source: hosted - version: "3.0.2" + version: "4.0.0" flutter_localizations: dependency: transitive description: flutter @@ -164,18 +164,18 @@ 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: name: lints - sha256: cbf8d4b858bb0134ef3ef87841abdf8d63bfc255c266b7bf6b39daa1085c4290 + sha256: "976c774dd944a42e83e2467f4cc670daef7eed6295b10b36ae8c85bcbf828235" url: "https://pub.dev" source: hosted - version: "3.0.0" + version: "4.0.0" material_color_utilities: 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,18 +236,18 @@ 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: name: path_provider_foundation - sha256: "5a7999be66e000916500be4f15a3633ebceb8302719b47b9cc49ce924125350f" + sha256: f234384a3fdd67f989b4d54a5d73ca2a6c422fa55ae694381ae0f4375cd1ea16 url: "https://pub.dev" source: hosted - version: "2.3.2" + version: "2.4.0" path_provider_linux: 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,18 +308,18 @@ 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: name: shared_preferences_foundation - sha256: "7708d83064f38060c7b39db12aefe449cb8cdc031d6062280087bc4cdb988f5c" + sha256: "0a8a893bf4fd1152f93fec03a415d11c27c74454d96e2318a7ac38dd18683ab7" url: "https://pub.dev" source: hosted - version: "2.3.5" + version: "2.4.0" shared_preferences_linux: 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 03827d382239dd86590e513682880db972935257..470cf2c963b7fce259823dace6461b2c1609e829 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -3,7 +3,7 @@ description: Jeweled Game publish_to: "none" -version: 0.0.31+31 +version: 0.1.0+32 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,13 +22,16 @@ dependencies: path_provider: ^2.0.11 unicons: ^2.1.1 + # specific + # (none) + dev_dependencies: - flutter_lints: ^3.0.1 + flutter_lints: ^4.0.0 flutter: uses-material-design: true assets: - - assets/icons/ + - assets/ui/ - assets/translations/ fonts: @@ -41,3 +45,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/resources/ui/build_ui_resources.sh b/resources/ui/build_ui_resources.sh new file mode 100755 index 0000000000000000000000000000000000000000..4f365ede7d83140ce6309a3083580f2662b30990 --- /dev/null +++ b/resources/ui/build_ui_resources.sh @@ -0,0 +1,110 @@ +#! /bin/bash + +# Check dependencies +command -v inkscape >/dev/null 2>&1 || { echo >&2 "I require inkscape but it's not installed. Aborting."; exit 1; } +command -v scour >/dev/null 2>&1 || { echo >&2 "I require scour but it's not installed. Aborting."; exit 1; } +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 "$(dirname "${CURRENT_DIR}")")" +ASSETS_DIR="${BASE_DIR}/assets" + +OPTIPNG_OPTIONS="-preserve -quiet -o7" +ICON_SIZE=192 + +####################################################### + +# 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 + +####################################################### + +# optimize svg +function optimize_svg() { + SOURCE="$1" + + cp ${SOURCE} ${SOURCE}.tmp + scour \ + --remove-descriptive-elements \ + --enable-id-stripping \ + --enable-viewboxing \ + --enable-comment-stripping \ + --nindent=4 \ + --quiet \ + -i ${SOURCE}.tmp \ + -o ${SOURCE} + rm ${SOURCE}.tmp +} + +# build icons +function build_image() { + SOURCE="$1" + TARGET="$2" + + echo "Building ${TARGET}" + + if [ ! -f "${SOURCE}" ]; then + echo "Missing file: ${SOURCE}" + exit 1 + fi + + optimize_svg "${SOURCE}" + + mkdir -p "$(dirname "${TARGET}")" + + inkscape \ + --export-width=${ICON_SIZE} \ + --export-height=${ICON_SIZE} \ + --export-filename=${TARGET} \ + "${SOURCE}" + + optipng ${OPTIPNG_OPTIONS} "${TARGET}" +} + +function build_image_for_skin() { + SKIN_CODE="$1" + + # skin images + for SKIN_IMAGE in ${SKIN_IMAGES} + do + build_image ${CURRENT_DIR}/skins/${SKIN_CODE}/${SKIN_IMAGE}.svg ${ASSETS_DIR}/skins/${SKIN_CODE}_${SKIN_IMAGE}.png + done +} + +####################################################### + +# Delete existing generated images +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_image ${CURRENT_DIR}/images/${GAME_IMAGE}.svg ${ASSETS_DIR}/ui/${GAME_IMAGE}.png +done + +# build skins images +for SKIN in ${AVAILABLE_SKINS} +do + 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/icons/button_delete_saved_game.svg b/resources/ui/images/button_delete_saved_game.svg similarity index 100% rename from icons/button_delete_saved_game.svg rename to resources/ui/images/button_delete_saved_game.svg diff --git a/icons/button_resume_game.svg b/resources/ui/images/button_resume_game.svg similarity index 100% rename from icons/button_resume_game.svg rename to resources/ui/images/button_resume_game.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/icons/game_fail.svg b/resources/ui/images/game_fail.svg similarity index 100% rename from icons/game_fail.svg rename to resources/ui/images/game_fail.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