diff --git a/packages/mini_sprite_editor/lib/config/cubit/config_cubit.dart b/packages/mini_sprite_editor/lib/config/cubit/config_cubit.dart index 49eb3ea..1471b06 100644 --- a/packages/mini_sprite_editor/lib/config/cubit/config_cubit.dart +++ b/packages/mini_sprite_editor/lib/config/cubit/config_cubit.dart @@ -1,11 +1,19 @@ +import 'dart:async'; + import 'package:equatable/equatable.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:hydrated_bloc/hydrated_bloc.dart'; part 'config_state.dart'; class ConfigCubit extends HydratedCubit { - ConfigCubit() : super(const ConfigState.initial()); + ConfigCubit({ + Future Function(ClipboardData)? setClipboardData, + }) : _setClipboardData = setClipboardData ?? Clipboard.setData, + super(const ConfigState.initial()); + + final Future Function(ClipboardData) _setClipboardData; void setThemeMode(ThemeMode mode) { emit(state.copyWith(themeMode: mode)); @@ -39,6 +47,13 @@ class ConfigCubit extends HydratedCubit { emit(state.copyWith(mapGridSize: value)); } + void copyPaletteToClipboard() { + final colorsInHex = state.colors + .map((e) => e.toARGB32().toRadixString(16).padLeft(8, '0')) + .join('\n'); + unawaited(_setClipboardData(ClipboardData(text: colorsInHex))); + } + @override ConfigState? fromJson(Map json) { final themeModeRaw = json['theme_mode'] as String?; diff --git a/packages/mini_sprite_editor/lib/sprite/view/sprite_page.dart b/packages/mini_sprite_editor/lib/sprite/view/sprite_page.dart index aee43a8..5e80854 100644 --- a/packages/mini_sprite_editor/lib/sprite/view/sprite_page.dart +++ b/packages/mini_sprite_editor/lib/sprite/view/sprite_page.dart @@ -7,6 +7,7 @@ import 'package:mini_sprite_editor/l10n/l10n.dart'; import 'package:mini_sprite_editor/library/library.dart'; import 'package:mini_sprite_editor/sprite/cubit/tools_cubit.dart'; import 'package:mini_sprite_editor/sprite/sprite.dart'; +import 'package:mini_sprite_editor/widgets/composed_icon.dart'; class SpritePage extends StatelessWidget { const SpritePage({super.key}); @@ -220,6 +221,22 @@ class SpriteView extends StatelessWidget { tooltip: l10n.copyToClipboard, icon: const Icon(Icons.download), ), + Tooltip( + message: l10n.copyToClipboard, + child: GestureDetector( + key: const Key('copy_palette_clipboard_key'), + onTap: () { + context.read().copyPaletteToClipboard(); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text(l10n.copiedWithSuccess)), + ); + }, + child: const ComposedIcon( + primary: Icons.palette, + secondary: Icons.download, + ), + ), + ), IconButton( key: const Key('import_from_clipboard_key'), onPressed: () { diff --git a/packages/mini_sprite_editor/lib/widgets/composed_icon.dart b/packages/mini_sprite_editor/lib/widgets/composed_icon.dart new file mode 100644 index 0000000..df6d833 --- /dev/null +++ b/packages/mini_sprite_editor/lib/widgets/composed_icon.dart @@ -0,0 +1,41 @@ +import 'package:flame/extensions.dart'; +import 'package:flutter/material.dart'; + +class ComposedIcon extends StatelessWidget { + const ComposedIcon({ + required this.primary, + required this.secondary, + super.key, + }); + + final IconData primary; + final IconData secondary; + + @override + Widget build(BuildContext context) { + return Stack( + clipBehavior: Clip.none, + children: [ + Icon(primary), + Positioned( + left: 10, + top: 6, + child: Icon( + size: 18, + shadows: [ + Shadow( + color: + Theme.of(context).iconTheme.color?.darken( + .7, + ) ?? + Colors.black, + blurRadius: 4, + ), + ], + secondary, + ), + ), + ], + ); + } +} diff --git a/packages/mini_sprite_editor/lib/widgets/widgets.dart b/packages/mini_sprite_editor/lib/widgets/widgets.dart new file mode 100644 index 0000000..ccde6de --- /dev/null +++ b/packages/mini_sprite_editor/lib/widgets/widgets.dart @@ -0,0 +1 @@ +export 'composed_icon.dart';