Skip to content

Commit b5a53d7

Browse files
committed
feat: flip vertically and horizontally feature to the editor
1 parent f5038d4 commit b5a53d7

File tree

8 files changed

+184
-1
lines changed

8 files changed

+184
-1
lines changed

packages/mini_sprite_editor/lib/app/view/app.dart

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import 'package:flutter/material.dart';
22
import 'package:flutter_bloc/flutter_bloc.dart';
33
import 'package:flutter_localizations/flutter_localizations.dart';
4+
import 'package:mini_sprite/mini_sprite.dart';
45
import 'package:mini_sprite_editor/config/config.dart';
56
import 'package:mini_sprite_editor/hub/hub.dart';
67
import 'package:mini_sprite_editor/l10n/l10n.dart';
@@ -46,9 +47,19 @@ class App extends StatelessWidget {
4647
colors.split(',').map(int.parse).map(Color.new).toList();
4748
}
4849

50+
final spriteRaw = uri.queryParameters['sprite'];
51+
MiniSprite? sprite;
52+
if (spriteRaw != null) {
53+
try {
54+
sprite = MiniSprite.fromDataString(spriteRaw);
55+
} catch (_) {
56+
// ignore on invalid sprite data
57+
}
58+
}
4959
return MaterialPageRoute(
5060
builder: (_) => WorkspaceView(
5161
colorList: colorList,
62+
sprite: sprite,
5263
),
5364
);
5465
}

packages/mini_sprite_editor/lib/l10n/arb/app_en.arb

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,5 +207,14 @@
207207
"addColor": "Add color",
208208
"@addColor": {
209209
"description": "Label for add color"
210+
},
211+
"flipVertically": "Flip vertically",
212+
"@flipVertically": {
213+
"description": "Label for flipping the sprite vertically"
214+
},
215+
"flipHorizontally": "Flip horizontally",
216+
"@flipHorizontally": {
217+
"description": "Label for flipping the sprite horizontally"
210218
}
219+
211220
}

packages/mini_sprite_editor/lib/sprite/cubit/sprite_cubit.dart

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,20 @@ class SpriteCubit extends ReplayCubit<SpriteState> {
7979
emit(state.copyWith(cursorPosition: const Offset(-1, -1)));
8080
}
8181

82+
void flipSpriteVertically() {
83+
final newPixels = [
84+
...state.pixels.reversed.map((e) => [...e]),
85+
];
86+
emit(state.copyWith(pixels: newPixels));
87+
}
88+
89+
void flipSpriteHorizontally() {
90+
final newPixels = [
91+
...state.pixels.map((e) => [...e.reversed]),
92+
];
93+
emit(state.copyWith(pixels: newPixels));
94+
}
95+
8296
Offset _projectOffset(Offset position, double pixelSize) {
8397
final projected = position / pixelSize;
8498
final x = projected.dx.floorToDouble();

packages/mini_sprite_editor/lib/sprite/view/sprite_page.dart

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,25 @@ class SpriteView extends StatelessWidget {
172172
gridActive ? Icons.grid_on : Icons.grid_off,
173173
),
174174
),
175+
IconButton(
176+
key: const Key('flip_vertically_key'),
177+
onPressed: () async {
178+
context.read<SpriteCubit>().flipSpriteVertically();
179+
},
180+
tooltip: l10n.flipVertically,
181+
icon: const Icon(Icons.transform),
182+
),
183+
IconButton(
184+
key: const Key('flip_horizontally_key'),
185+
onPressed: () async {
186+
context.read<SpriteCubit>().flipSpriteHorizontally();
187+
},
188+
tooltip: l10n.flipHorizontally,
189+
icon: Transform.rotate(
190+
angle: 1.5708,
191+
child: const Icon(Icons.transform),
192+
),
193+
),
175194
IconButton(
176195
key: const Key('copy_to_clipboard_key'),
177196
onPressed: () {

packages/mini_sprite_editor/lib/workspace/view/workspace_view.dart

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,19 @@
11
import 'package:flutter/material.dart';
22
import 'package:flutter_bloc/flutter_bloc.dart';
3+
import 'package:mini_sprite/mini_sprite.dart';
34
import 'package:mini_sprite_editor/config/cubit/config_cubit.dart';
5+
import 'package:mini_sprite_editor/sprite/sprite.dart';
46
import 'package:mini_sprite_editor/workspace/workspace.dart';
57

68
class WorkspaceView extends StatefulWidget {
79
const WorkspaceView({
810
this.colorList,
11+
this.sprite,
912
super.key,
1013
});
1114

1215
final List<Color>? colorList;
16+
final MiniSprite? sprite;
1317

1418
@override
1519
State<WorkspaceView> createState() => _WorkspaceViewState();
@@ -23,6 +27,10 @@ class _WorkspaceViewState extends State<WorkspaceView> {
2327
if (widget.colorList != null) {
2428
context.read<ConfigCubit>().setColors(widget.colorList!);
2529
}
30+
31+
if (widget.sprite != null) {
32+
context.read<SpriteCubit>().setSprite(widget.sprite!.pixels);
33+
}
2634
}
2735

2836
@override

packages/mini_sprite_editor/test/sprite/cubit/sprite_cubit_test.dart

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,46 @@ void main() {
5757
},
5858
);
5959

60+
test('flip vertically', () {
61+
final cubit = SpriteCubit();
62+
final state = SpriteState.initial().copyWith(
63+
pixels: [
64+
[1, 1],
65+
[0, 0],
66+
],
67+
);
68+
final expected = SpriteState.initial().copyWith(
69+
pixels: [
70+
[0, 0],
71+
[1, 1],
72+
],
73+
);
74+
cubit
75+
..setSprite(state.pixels)
76+
..flipSpriteVertically();
77+
expect(cubit.state, equals(expected));
78+
});
79+
80+
test('flip horizontally', () {
81+
final cubit = SpriteCubit();
82+
final state = SpriteState.initial().copyWith(
83+
pixels: [
84+
[1, 1],
85+
[0, 0],
86+
],
87+
);
88+
final expected = SpriteState.initial().copyWith(
89+
pixels: [
90+
[1, 1],
91+
[0, 0],
92+
],
93+
);
94+
cubit
95+
..setSprite(state.pixels)
96+
..flipSpriteHorizontally();
97+
expect(cubit.state, equals(expected));
98+
});
99+
60100
group('importFromClipboard', () {
61101
late GetClipboardStub stub;
62102
final sprite = MiniSprite(const [

packages/mini_sprite_editor/test/sprite/view/sprite_view_test.dart

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,48 @@ void main() async {
213213
verify(() => spriteCubit.cursorUp(any(), any())).called(1);
214214
});
215215

216+
group('flip', () {
217+
testWidgets('flips horizontally', (tester) async {
218+
_mockState(
219+
spriteState: SpriteState.initial(),
220+
toolsState: ToolsState.initial(),
221+
configState: ConfigState.initial(),
222+
libraryState: LibraryState.initial(),
223+
);
224+
await tester.pumpTest(
225+
spriteCubit: spriteCubit,
226+
toolsCubit: toolsCubit,
227+
configCubit: configCubit,
228+
libraryCubit: libraryCubit,
229+
);
230+
231+
await tester.tap(find.byKey(const Key('flip_horizontally_key')));
232+
await tester.pump();
233+
234+
verify(() => spriteCubit.flipSpriteHorizontally()).called(1);
235+
});
236+
237+
testWidgets('flips vertically', (tester) async {
238+
_mockState(
239+
spriteState: SpriteState.initial(),
240+
toolsState: ToolsState.initial(),
241+
configState: ConfigState.initial(),
242+
libraryState: LibraryState.initial(),
243+
);
244+
await tester.pumpTest(
245+
spriteCubit: spriteCubit,
246+
toolsCubit: toolsCubit,
247+
configCubit: configCubit,
248+
libraryCubit: libraryCubit,
249+
);
250+
251+
await tester.tap(find.byKey(const Key('flip_vertically_key')));
252+
await tester.pump();
253+
254+
verify(() => spriteCubit.flipSpriteVertically()).called(1);
255+
});
256+
});
257+
216258
group('tools', () {
217259
group('brush', () {
218260
testWidgets(

packages/mini_sprite_editor/test/workspace/view/workspace_view_test.dart

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import 'package:bloc_test/bloc_test.dart';
22
import 'package:flutter/material.dart';
33
import 'package:flutter_bloc/flutter_bloc.dart';
44
import 'package:flutter_test/flutter_test.dart';
5+
import 'package:mini_sprite/mini_sprite.dart';
56
import 'package:mini_sprite_editor/config/config.dart';
67
import 'package:mini_sprite_editor/hub/hub.dart';
78
import 'package:mini_sprite_editor/library/library.dart';
@@ -38,6 +39,7 @@ extension TestWidgetText on WidgetTester {
3839
required MapCubit mapCubit,
3940
required HubCubit hubCubit,
4041
List<Color>? colors,
42+
MiniSprite? sprite,
4143
}) async {
4244
await pumpApp(
4345
MultiBlocProvider(
@@ -64,7 +66,12 @@ extension TestWidgetText on WidgetTester {
6466
value: hubCubit,
6567
),
6668
],
67-
child: Scaffold(body: WorkspaceView(colorList: colors)),
69+
child: Scaffold(
70+
body: WorkspaceView(
71+
colorList: colors,
72+
sprite: sprite,
73+
),
74+
),
6875
),
6976
);
7077
}
@@ -196,5 +203,38 @@ void main() {
196203
.called(1);
197204
},
198205
);
206+
207+
testWidgets(
208+
'set the sprite when receiving one',
209+
(tester) async {
210+
_mockState(
211+
spriteState: SpriteState.initial(),
212+
toolsState: const ToolsState.initial(),
213+
configState: const ConfigState.initial(),
214+
libraryState: const LibraryState.initial(),
215+
workspaceState: const WorkspaceState.initial(),
216+
mapState: const MapState.initial(),
217+
);
218+
219+
const sprite = MiniSprite(
220+
[
221+
[1],
222+
[0],
223+
],
224+
);
225+
await tester.pumpTest(
226+
spriteCubit: spriteCubit,
227+
toolsCubit: toolsCubit,
228+
configCubit: configCubit,
229+
libraryCubit: libraryCubit,
230+
workspaceCubit: workspaceCubit,
231+
mapCubit: mapCubit,
232+
hubCubit: hubCubit,
233+
sprite: sprite,
234+
);
235+
236+
verify(() => spriteCubit.setSprite(sprite.pixels)).called(1);
237+
},
238+
);
199239
});
200240
}

0 commit comments

Comments
 (0)