Skip to content
Snippets Groups Projects
Commit c83c778d authored by Benoît Harrault's avatar Benoît Harrault
Browse files

Merge branch '19-add-custom-painter-with-cells-borders' into 'master'

Resolve "Add custom painter with cells borders"

Closes #19

See merge request !26
parents 6aaa98cf 2c643a71
No related branches found
No related tags found
1 merge request!26Resolve "Add custom painter with cells borders"
Pipeline #4999 passed
org.gradle.jvmargs=-Xmx1536M org.gradle.jvmargs=-Xmx1536M
android.useAndroidX=true android.useAndroidX=true
android.enableJetifier=true android.enableJetifier=true
app.versionName=0.0.22 app.versionName=0.0.23
app.versionCode=22 app.versionCode=23
Add graphic themes.
Ajout de différents thèmes graphiques.
...@@ -38,6 +38,7 @@ class DefaultGameSettings { ...@@ -38,6 +38,7 @@ class DefaultGameSettings {
return DefaultGameSettings.allowedColorsCountValues; return DefaultGameSettings.allowedColorsCountValues;
} }
print('Did not find any available value for game parameter \"' + parameterCode + '\".');
return []; return [];
} }
......
class DefaultGlobalSettings { class DefaultGlobalSettings {
static const List<String> availableParameters = [ static const List<String> availableParameters = [
'colorsTheme', 'colorsTheme',
'graphicTheme',
]; ];
static const int defaultColorsThemeValue = 1; static const int defaultColorsThemeValue = 1;
...@@ -17,12 +18,49 @@ class DefaultGlobalSettings { ...@@ -17,12 +18,49 @@ class DefaultGlobalSettings {
// 9, // 0x111323,0x374566,0x50785d,0x8497b3,0xe8dcd8,0xcfb463,0xb35447,0x692e4b, // https://lospec.com/palette-list/low-8 // 9, // 0x111323,0x374566,0x50785d,0x8497b3,0xe8dcd8,0xcfb463,0xb35447,0x692e4b, // https://lospec.com/palette-list/low-8
]; ];
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 = [
graphicThemeSolidBackground,
graphicThemeGradientAndBorder,
graphicThemeEmojis,
graphicThemePatterns,
];
static const List<String> graphicThemeContentEmojiStrings = [
'🍏',
'🤍',
'🦋',
'🐞',
'⭐',
'🍄',
'🍒',
'🐤',
];
static const List<String> graphicThemeContentPatternStrings = [
'✖',
'✚',
'▲',
'■',
'●',
'◆',
'━',
'⧧',
];
static List<int> getAvailableValues(String parameterCode) { static List<int> getAvailableValues(String parameterCode) {
switch (parameterCode) { switch (parameterCode) {
case 'colorsTheme': case 'colorsTheme':
return DefaultGlobalSettings.allowedColorsThemeValues; return DefaultGlobalSettings.allowedColorsThemeValues;
case 'graphicTheme':
return DefaultGlobalSettings.allowedGraphicThemeValues;
} }
print('Did not find any available value for global parameter \"' + parameterCode + '\".');
return []; return [];
} }
} }
...@@ -11,11 +11,13 @@ class GlobalSettingsCubit extends HydratedCubit<GlobalSettingsState> { ...@@ -11,11 +11,13 @@ class GlobalSettingsCubit extends HydratedCubit<GlobalSettingsState> {
void setValues({ void setValues({
int? colorsTheme, int? colorsTheme,
int? graphicTheme,
}) { }) {
emit( emit(
GlobalSettingsState( GlobalSettingsState(
settings: GlobalSettings( settings: GlobalSettings(
colorsTheme: colorsTheme ?? state.settings.colorsTheme, colorsTheme: colorsTheme ?? state.settings.colorsTheme,
graphicTheme: graphicTheme ?? state.settings.graphicTheme,
), ),
), ),
); );
...@@ -25,6 +27,8 @@ class GlobalSettingsCubit extends HydratedCubit<GlobalSettingsState> { ...@@ -25,6 +27,8 @@ class GlobalSettingsCubit extends HydratedCubit<GlobalSettingsState> {
switch (code) { switch (code) {
case 'colorsTheme': case 'colorsTheme':
return GlobalSettings.getColorsThemeValueFromUnsafe(state.settings.colorsTheme); return GlobalSettings.getColorsThemeValueFromUnsafe(state.settings.colorsTheme);
case 'graphicTheme':
return GlobalSettings.getGraphicThemeValueFromUnsafe(state.settings.graphicTheme);
} }
return 0; return 0;
} }
...@@ -34,19 +38,23 @@ class GlobalSettingsCubit extends HydratedCubit<GlobalSettingsState> { ...@@ -34,19 +38,23 @@ class GlobalSettingsCubit extends HydratedCubit<GlobalSettingsState> {
print('code: ' + code + ' / value: ' + value.toString()); print('code: ' + code + ' / value: ' + value.toString());
int colorsTheme = code == 'colorsTheme' ? value : getParameterValue('colorsTheme'); int colorsTheme = code == 'colorsTheme' ? value : getParameterValue('colorsTheme');
int graphicTheme = code == 'graphicTheme' ? value : getParameterValue('graphicTheme');
setValues( setValues(
colorsTheme: colorsTheme, colorsTheme: colorsTheme,
graphicTheme: graphicTheme,
); );
} }
@override @override
GlobalSettingsState? fromJson(Map<String, dynamic> json) { GlobalSettingsState? fromJson(Map<String, dynamic> json) {
int colorsTheme = json['colorsTheme'] as int; int colorsTheme = json['colorsTheme'] as int;
int graphicTheme = json['graphicTheme'] as int;
return GlobalSettingsState( return GlobalSettingsState(
settings: GlobalSettings( settings: GlobalSettings(
colorsTheme: colorsTheme, colorsTheme: colorsTheme,
graphicTheme: graphicTheme,
), ),
); );
} }
...@@ -55,6 +63,7 @@ class GlobalSettingsCubit extends HydratedCubit<GlobalSettingsState> { ...@@ -55,6 +63,7 @@ class GlobalSettingsCubit extends HydratedCubit<GlobalSettingsState> {
Map<String, dynamic>? toJson(GlobalSettingsState state) { Map<String, dynamic>? toJson(GlobalSettingsState state) {
return <String, dynamic>{ return <String, dynamic>{
'colorsTheme': state.settings.colorsTheme, 'colorsTheme': state.settings.colorsTheme,
'graphicTheme': state.settings.graphicTheme,
}; };
} }
} }
...@@ -2,9 +2,11 @@ import 'package:jeweled/config/default_global_settings.dart'; ...@@ -2,9 +2,11 @@ import 'package:jeweled/config/default_global_settings.dart';
class GlobalSettings { class GlobalSettings {
final int colorsTheme; final int colorsTheme;
final int graphicTheme;
GlobalSettings({ GlobalSettings({
required this.colorsTheme, required this.colorsTheme,
required this.graphicTheme,
}); });
static int getColorsThemeValueFromUnsafe(int colorsTheme) { static int getColorsThemeValueFromUnsafe(int colorsTheme) {
...@@ -15,15 +17,25 @@ class GlobalSettings { ...@@ -15,15 +17,25 @@ class GlobalSettings {
return DefaultGlobalSettings.defaultColorsThemeValue; return DefaultGlobalSettings.defaultColorsThemeValue;
} }
static int getGraphicThemeValueFromUnsafe(int graphicTheme) {
if (DefaultGlobalSettings.allowedGraphicThemeValues.contains(graphicTheme)) {
return graphicTheme;
}
return DefaultGlobalSettings.defaultGraphicThemeValue;
}
factory GlobalSettings.createDefault() { factory GlobalSettings.createDefault() {
return GlobalSettings( return GlobalSettings(
colorsTheme: DefaultGlobalSettings.defaultColorsThemeValue, colorsTheme: DefaultGlobalSettings.defaultColorsThemeValue,
graphicTheme: DefaultGlobalSettings.defaultGraphicThemeValue,
); );
} }
void dump() { void dump() {
print('Settings: '); print('Settings: ');
print(' colorsTheme: ' + colorsTheme.toString()); print(' colorsTheme: ' + colorsTheme.toString());
print(' graphicTheme: ' + graphicTheme.toString());
} }
String toString() { String toString() {
...@@ -33,6 +45,7 @@ class GlobalSettings { ...@@ -33,6 +45,7 @@ class GlobalSettings {
Map<String, dynamic>? toJson() { Map<String, dynamic>? toJson() {
return <String, dynamic>{ return <String, dynamic>{
'colorsTheme': this.colorsTheme, 'colorsTheme': this.colorsTheme,
'graphicTheme': this.graphicTheme,
}; };
} }
} }
import 'dart:math'; import 'dart:math';
import 'dart:ui' as ui;
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:jeweled/config/default_global_settings.dart';
import 'package:jeweled/models/game.dart'; import 'package:jeweled/models/game.dart';
import 'package:jeweled/models/cell_location.dart'; import 'package:jeweled/models/cell_location.dart';
import 'package:jeweled/utils/color_extensions.dart';
import 'package:jeweled/utils/color_theme.dart'; import 'package:jeweled/utils/color_theme.dart';
class GameBoardPainter extends CustomPainter { class GameBoardPainter extends CustomPainter {
const GameBoardPainter({required this.game, required this.animations}); const GameBoardPainter({
required this.game,
required this.animations,
});
final Game game; final Game game;
final List<List<Animation<double>?>> animations; final List<List<Animation<double>?>> animations;
@override @override
void paint(Canvas canvas, Size size) { void paint(Canvas canvas, Size size) {
final double canvasSize = min(size.width, size.height); int graphicTheme = game.globalSettings.graphicTheme;
final int cellsCountHorizontal = game.gameSettings.boardSize;
final int cellsCountVertical = game.gameSettings.boardSize;
final double cellSize = canvasSize / max(cellsCountHorizontal, cellsCountVertical);
const double overlapping = 1;
const double borderSize = 4;
// background
for (int row = 0; row < cellsCountVertical; row++) {
final double yOrigin = cellSize * row;
for (int col = 0; col < cellsCountHorizontal; col++) {
final double x = cellSize * col;
final CellLocation cellLocation = CellLocation.go(row, col);
final int? cellValue = game.getCellValueShuffled(cellLocation);
if (cellValue != null) {
final int colorCode =
ColorTheme.getColorCode(cellValue, game.globalSettings.colorsTheme);
final Animation<double>? translation = this.animations[row][col];
final double y = yOrigin + (translation?.value ?? 0) * cellSize;
final cellPaintBackground = Paint();
cellPaintBackground.color = Color(colorCode);
cellPaintBackground.style = PaintingStyle.fill;
final Rect cellBackground = Rect.fromPoints( final double canvasSize = min(size.width, size.height);
Offset(x, y), Offset(x + cellSize + overlapping, y + cellSize + overlapping));
canvas.drawRect(cellBackground, cellPaintBackground); const double cellBorderWidth = 2;
} const double boardBorderWidth = 3;
}
switch (graphicTheme) {
case DefaultGlobalSettings.graphicThemeSolidBackground:
this.drawBoard(
canvas: canvas,
canvasSize: canvasSize,
game: game,
);
break;
case DefaultGlobalSettings.graphicThemeGradientAndBorder:
this.drawBoard(
canvas: canvas,
canvasSize: canvasSize,
game: game,
borderWidth: cellBorderWidth,
gradientFrom: -10,
gradientTo: 10,
);
break;
case DefaultGlobalSettings.graphicThemeEmojis:
this.drawBoard(
canvas: canvas,
canvasSize: canvasSize,
game: game,
contentStrings: DefaultGlobalSettings.graphicThemeContentEmojiStrings,
);
break;
case DefaultGlobalSettings.graphicThemePatterns:
this.drawBoard(
canvas: canvas,
canvasSize: canvasSize,
game: game,
contentStrings: DefaultGlobalSettings.graphicThemeContentPatternStrings,
);
break;
default:
} }
// board borders // board borders
final boardPaintBorder = Paint(); final boardPaintBorder = Paint();
boardPaintBorder.color = ColorTheme.getBorderColor(); boardPaintBorder.color = ColorTheme.getBorderColor();
boardPaintBorder.strokeWidth = borderSize; boardPaintBorder.strokeWidth = boardBorderWidth;
boardPaintBorder.strokeCap = StrokeCap.round; boardPaintBorder.strokeCap = StrokeCap.round;
final Offset boardTopLeft = Offset(0, 0); final Offset boardTopLeft = Offset(0, 0);
...@@ -72,4 +86,126 @@ class GameBoardPainter extends CustomPainter { ...@@ -72,4 +86,126 @@ class GameBoardPainter extends CustomPainter {
bool shouldRepaint(CustomPainter oldDelegate) { bool shouldRepaint(CustomPainter oldDelegate) {
return true; return true;
} }
void drawBoard({
required Canvas canvas,
required double canvasSize,
required Game game,
double overlapping = 1,
int gradientFrom = 0,
int gradientTo = 0,
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 double size = canvasSize / max(cellsCountHorizontal, cellsCountVertical);
for (int row = 0; row < cellsCountVertical; row++) {
final double yOrigin = size * row;
for (int col = 0; col < cellsCountHorizontal; col++) {
final double x = size * col;
final CellLocation cellLocation = CellLocation.go(row, col);
final int? cellValue = game.getCellValueShuffled(cellLocation);
if (cellValue != null) {
final Animation<double>? translation = this.animations[row][col];
final double y = yOrigin + (translation?.value ?? 0) * size;
this.drawCell(
canvas: canvas,
x: x,
y: y,
cellSize: size,
cellValue: cellValue,
colorsTheme: colorsTheme,
overlapping: overlapping,
gradientFrom: gradientFrom,
gradientTo: gradientTo,
borderWidth: borderWidth,
contentStrings: contentStrings,
);
}
}
}
}
void drawCell({
required Canvas canvas,
required double x,
required double y,
required double cellSize,
required int cellValue,
required int colorsTheme,
required double overlapping,
required int gradientFrom,
required int gradientTo,
required double borderWidth,
required List<String>? contentStrings,
}) {
final Color baseColor = ColorTheme.getColor(cellValue, colorsTheme);
final paint = Paint();
// draw background
final Rect rect = Rect.fromLTWH(x, y, cellSize + overlapping, cellSize + overlapping);
// solid or gradient
if (gradientFrom == 0 && gradientTo == 0) {
paint.color = baseColor;
} else {
paint.shader = ui.Gradient.linear(
rect.topLeft,
rect.bottomCenter,
[
(gradientFrom < 0)
? baseColor.lighten(-gradientFrom)
: baseColor.darken(gradientFrom),
(gradientTo < 0) ? baseColor.lighten(-gradientTo) : baseColor.darken(gradientTo),
],
);
}
paint.style = PaintingStyle.fill;
canvas.drawRect(rect, paint);
// draw border
if (borderWidth != 0) {
final Rect border = Rect.fromLTWH(x + borderWidth, y + borderWidth,
cellSize + overlapping - 2 * borderWidth, cellSize + overlapping - 2 * borderWidth);
final paintBorder = Paint();
paintBorder.color = baseColor.darken(20);
paintBorder.style = PaintingStyle.stroke;
paintBorder.strokeWidth = borderWidth;
paintBorder.strokeJoin = StrokeJoin.round;
canvas.drawRect(border, paintBorder);
}
// draw content
if (contentStrings != null) {
final textSpan = TextSpan(
text: contentStrings[cellValue - 1],
style: TextStyle(
color: Colors.black,
fontSize: 4 + cellSize / 2,
fontWeight: FontWeight.bold,
),
);
final textPainter = TextPainter(
text: textSpan,
textDirection: TextDirection.ltr,
);
textPainter.layout();
textPainter.paint(
canvas,
Offset(
x + (cellSize - textPainter.width) * 0.5,
y + (cellSize - textPainter.height) * 0.5,
),
);
}
}
} }
import 'dart:math'; import 'dart:math';
import 'dart:ui' as ui;
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:jeweled/config/default_game_settings.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_game.dart';
import 'package:jeweled/models/settings_global.dart'; import 'package:jeweled/models/settings_global.dart';
import 'package:jeweled/utils/color_extensions.dart'; import 'package:jeweled/utils/color_extensions.dart';
...@@ -50,6 +52,9 @@ class ParameterPainter extends CustomPainter { ...@@ -50,6 +52,9 @@ class ParameterPainter extends CustomPainter {
case 'colorsTheme': case 'colorsTheme':
paintColorsThemeParameterItem(value, canvas, canvasSize); paintColorsThemeParameterItem(value, canvas, canvasSize);
break; break;
case 'graphicTheme':
paintGraphicThemeParameterItem(value, canvas, canvasSize);
break;
default: default:
print('Unknown parameter: ' + code + '/' + value.toString()); print('Unknown parameter: ' + code + '/' + value.toString());
paintUnknownParameterItem(value, canvas, canvasSize); paintUnknownParameterItem(value, canvas, canvasSize);
...@@ -62,7 +67,19 @@ class ParameterPainter extends CustomPainter { ...@@ -62,7 +67,19 @@ class ParameterPainter extends CustomPainter {
} }
// "unknown" parameter -> simple bock with text // "unknown" parameter -> simple bock with text
void paintUnknownParameterItem(final int value, final Canvas canvas, final double size) { void paintUnknownParameterItem(
final int value,
final Canvas canvas,
final double size,
) {
final paint = Paint();
paint.strokeJoin = StrokeJoin.round;
paint.strokeWidth = 3 / 100 * size;
paint.color = Colors.grey;
paint.style = PaintingStyle.fill;
canvas.drawRect(Rect.fromPoints(Offset(0, 0), Offset(size, size)), paint);
final textSpan = TextSpan( final textSpan = TextSpan(
text: '?' + '\n' + value.toString(), text: '?' + '\n' + value.toString(),
style: const TextStyle( style: const TextStyle(
...@@ -85,7 +102,11 @@ class ParameterPainter extends CustomPainter { ...@@ -85,7 +102,11 @@ class ParameterPainter extends CustomPainter {
); );
} }
void paintBoardSizeParameterItem(final int value, final Canvas canvas, final double size) { void paintBoardSizeParameterItem(
final int value,
final Canvas canvas,
final double size,
) {
Color backgroundColor = Colors.grey; Color backgroundColor = Colors.grey;
int gridWidth = 1; int gridWidth = 1;
...@@ -144,7 +165,11 @@ class ParameterPainter extends CustomPainter { ...@@ -144,7 +165,11 @@ class ParameterPainter extends CustomPainter {
} }
} }
void paintColorsCountParameterItem(final int value, final Canvas canvas, final double size) { void paintColorsCountParameterItem(
final int value,
final Canvas canvas,
final double size,
) {
Color backgroundColor = Colors.grey; Color backgroundColor = Colors.grey;
switch (value) { switch (value) {
...@@ -232,7 +257,11 @@ class ParameterPainter extends CustomPainter { ...@@ -232,7 +257,11 @@ class ParameterPainter extends CustomPainter {
); );
} }
void paintColorsThemeParameterItem(final int value, final Canvas canvas, final double size) { void paintColorsThemeParameterItem(
final int value,
final Canvas canvas,
final double size,
) {
Color backgroundColor = Colors.grey; Color backgroundColor = Colors.grey;
const int gridWidth = 4; const int gridWidth = 4;
...@@ -268,4 +297,116 @@ class ParameterPainter extends CustomPainter { ...@@ -268,4 +297,116 @@ class ParameterPainter extends CustomPainter {
} }
} }
} }
void paintGraphicThemeParameterItem(
final int value,
final Canvas canvas,
final double size,
) {
Color backgroundColor = Colors.grey;
final paint = Paint();
paint.strokeJoin = StrokeJoin.round;
paint.strokeWidth = 3 / 100 * size;
// Colored background
paint.color = backgroundColor;
paint.style = PaintingStyle.fill;
canvas.drawRect(Rect.fromPoints(Offset(0, 0), Offset(size, size)), paint);
// cells preview
const List<Offset> positions = [
Offset(0, 0),
Offset(1, 0),
Offset(2, 0),
Offset(2, 1),
Offset(2, 2),
Offset(1, 2),
Offset(0, 2),
Offset(0, 1),
];
final double padding = 5 / 100 * size;
final double width = (size - 2 * padding) / 3;
bool drawBorder = false;
bool gradientColor = false;
List<String> contentStrings = [];
switch (value) {
case DefaultGlobalSettings.graphicThemeSolidBackground:
break;
case DefaultGlobalSettings.graphicThemeGradientAndBorder:
drawBorder = true;
gradientColor = true;
break;
case DefaultGlobalSettings.graphicThemeEmojis:
contentStrings = DefaultGlobalSettings.graphicThemeContentEmojiStrings;
break;
case DefaultGlobalSettings.graphicThemePatterns:
contentStrings = DefaultGlobalSettings.graphicThemeContentPatternStrings;
break;
default:
print('Wrong value for colorsCount parameter value: ' + value.toString());
}
for (int itemValue = 0; itemValue < positions.length; itemValue++) {
final Offset position = positions[itemValue];
final Offset topLeft =
Offset(padding + position.dx * width, padding + position.dy * width);
final Offset bottomRight = topLeft + Offset(width, width);
final Rect itemBox = Rect.fromPoints(topLeft, bottomRight);
final itemColor = ColorTheme.getColor(itemValue, globalSettings.colorsTheme);
paint.color = itemColor;
paint.style = PaintingStyle.fill;
canvas.drawRect(itemBox, paint);
// gradient background
if (gradientColor) {
paint.shader = ui.Gradient.linear(itemBox.topLeft, itemBox.bottomCenter, [
itemColor.lighten(10),
itemColor.darken(10),
]);
paint.style = PaintingStyle.fill;
canvas.drawRect(itemBox, paint);
}
// cell border
if (drawBorder) {
final paintBorder = Paint();
paintBorder.color = itemColor.darken(20);
paintBorder.style = PaintingStyle.stroke;
paintBorder.strokeWidth = 2;
canvas.drawRect(itemBox, paintBorder);
}
// centered text value
if (contentStrings.length != 0) {
final textSpan = TextSpan(
text: contentStrings[itemValue],
style: TextStyle(
color: Colors.black,
fontSize: width / 2,
fontWeight: FontWeight.bold,
),
);
final textPainter = TextPainter(
text: textSpan,
textDirection: TextDirection.ltr,
);
textPainter.layout();
textPainter.paint(
canvas,
Offset(
topLeft.dx + (width - textPainter.width) * 0.5,
topLeft.dy + (width - textPainter.height) * 0.5,
),
);
}
}
}
} }
...@@ -89,6 +89,10 @@ class ColorTheme { ...@@ -89,6 +89,10 @@ class ColorTheme {
return defaultItemColor | 0xFF000000; return defaultItemColor | 0xFF000000;
} }
static Color getColor(int? value, final int skin) {
return Color(getColorCode(value, skin));
}
static Color getBorderColor() { static Color getBorderColor() {
return Colors.grey; return Colors.grey;
} }
......
...@@ -3,7 +3,7 @@ description: Jeweled Game ...@@ -3,7 +3,7 @@ description: Jeweled Game
publish_to: 'none' publish_to: 'none'
version: 0.0.22+22 version: 0.0.23+23
environment: environment:
sdk: '^3.0.0' sdk: '^3.0.0'
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment