This commit is contained in:
2023-09-07 19:48:27 +08:00
parent 01764d005f
commit 1af4ed6313
3 changed files with 91 additions and 24 deletions

View File

@@ -2,19 +2,11 @@ import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:logging/logging.dart'; import 'package:logging/logging.dart';
import 'package:super_clipboard/super_clipboard.dart';
import 'package:super_context_menu/super_context_menu.dart'; import 'package:super_context_menu/super_context_menu.dart';
import '../platform/to_png_none.dart' import '../utils/clipboard.dart';
if (dart.library.html) '../platform/to_png.dart';
final _log = Logger("ImageWithContextMenu"); final _log = Logger("ImageWithContextMenu");
enum ImageFmt {
jpg,
png,
gif,
}
class ImageWithContextMenu extends StatelessWidget { class ImageWithContextMenu extends StatelessWidget {
const ImageWithContextMenu(this.data, const ImageWithContextMenu(this.data,
{Key? key, this.uri, this.fmt = ImageFmt.jpg}) {Key? key, this.uri, this.fmt = ImageFmt.jpg})
@@ -31,16 +23,7 @@ class ImageWithContextMenu extends StatelessWidget {
title: AppLocalizations.of(context)!.copyImage, title: AppLocalizations.of(context)!.copyImage,
callback: () async { callback: () async {
try { try {
final item = DataWriterItem(); await copyImageToClipboard(data, fmt);
if (!kIsWeb) {
item.add(fmt == ImageFmt.jpg
? Formats.jpeg(data)
: Formats.png(data));
} else {
item.add(Formats.png(
fmt == ImageFmt.jpg ? await jpgToPng(data) : data));
}
await ClipboardWriter.instance.write([item]);
} catch (err) { } catch (err) {
_log.warning("Failed to copy image to clipboard:", err); _log.warning("Failed to copy image to clipboard:", err);
} }
@@ -50,9 +33,7 @@ class ImageWithContextMenu extends StatelessWidget {
list.add(MenuAction( list.add(MenuAction(
title: AppLocalizations.of(context)!.copyImgUrl, title: AppLocalizations.of(context)!.copyImgUrl,
callback: () { callback: () {
final item = DataWriterItem(); copyTextToClipboard(uri!).catchError((err) {
item.add(Formats.plainText(uri!));
ClipboardWriter.instance.write([item]).catchError((err) {
_log.warning("Failed to copy image to clipboard:", err); _log.warning("Failed to copy image to clipboard:", err);
}); });
})); }));

View File

@@ -7,6 +7,7 @@ import 'package:logging/logging.dart';
import '../api/client.dart'; import '../api/client.dart';
import '../api/gallery.dart'; import '../api/gallery.dart';
import '../globals.dart'; import '../globals.dart';
import '../utils/clipboard.dart';
import 'image.dart'; import 'image.dart';
final _log = Logger("Thumbnail"); final _log = Logger("Thumbnail");
@@ -41,6 +42,11 @@ class Thumbnail extends StatefulWidget {
State<Thumbnail> createState() => _Thumbnail(); State<Thumbnail> createState() => _Thumbnail();
} }
enum _ThumbnailMenu {
copyImage,
copyImgUrl,
}
class _Thumbnail extends State<Thumbnail> { class _Thumbnail extends State<Thumbnail> {
Uint8List? _data; Uint8List? _data;
bool _isLoading = false; bool _isLoading = false;
@@ -111,11 +117,52 @@ class _Thumbnail extends State<Thumbnail> {
super.dispose(); super.dispose();
} }
Future<void> onItemSelected(_ThumbnailMenu v) async {
switch (v) {
case _ThumbnailMenu.copyImage:
try {
copyImageToClipboard(_data!, ImageFmt.jpg);
} catch (err) {
_log.warning("Failed to copy image to clipboard:", err);
}
break;
case _ThumbnailMenu.copyImgUrl:
try {
copyTextToClipboard(_uri!);
} catch (err) {
_log.warning("Failed to copy image url to clipboard:", err);
}
break;
}
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final isLoading = _data == null && _error == null; final isLoading = _data == null && _error == null;
final isNsfw = widget._pMeta.isNsfw; final isNsfw = widget._pMeta.isNsfw;
if (isLoading && !_isLoading) _fetchData(); if (isLoading && !_isLoading) _fetchData();
final iconSize = Theme.of(context).iconTheme.size;
final moreVertMenu = Positioned(
right: 0,
top: 0,
width: iconSize,
height: iconSize,
child: PopupMenuButton(
icon: const Icon(Icons.more_vert),
onSelected: (v) {
onItemSelected(v);
},
itemBuilder: (context) {
var list = <PopupMenuEntry<_ThumbnailMenu>>[
PopupMenuItem(
value: _ThumbnailMenu.copyImage,
child: Text(AppLocalizations.of(context)!.copyImage)),
PopupMenuItem(
value: _ThumbnailMenu.copyImgUrl,
child: Text(AppLocalizations.of(context)!.copyImgUrl)),
];
return list;
}));
return SizedBox( return SizedBox(
width: widget.width.toDouble(), width: widget.width.toDouble(),
height: widget.height.toDouble(), height: widget.height.toDouble(),
@@ -147,10 +194,17 @@ class _Thumbnail extends State<Thumbnail> {
}, },
icon: const Icon(Icons.visibility), icon: const Icon(Icons.visibility),
), ),
)) )),
moreVertMenu
], ],
) )
: ImageWithContextMenu(_data!, uri: _uri) : Stack(children: [
SizedBox(
width: widget.width.toDouble(),
height: widget.height.toDouble(),
child: ImageWithContextMenu(_data!, uri: _uri)),
moreVertMenu
])
: Center( : Center(
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,

32
lib/utils/clipboard.dart Normal file
View File

@@ -0,0 +1,32 @@
import 'package:flutter/foundation.dart';
import 'package:super_clipboard/super_clipboard.dart';
import '../platform/to_png_none.dart'
if (dart.library.html) '../platform/to_png.dart';
enum ImageFmt {
jpg,
png,
gif,
}
Future<void> copyImageToClipboard(Uint8List data, ImageFmt fmt) async {
final item = DataWriterItem();
if (!kIsWeb) {
item.add(fmt == ImageFmt.jpg
? Formats.jpeg(data)
: fmt == ImageFmt.gif
? Formats.gif(data)
: Formats.png(data));
} else {
item.add(fmt == ImageFmt.gif
? Formats.gif(data)
: Formats.png(fmt == ImageFmt.jpg ? await jpgToPng(data) : data));
}
await ClipboardWriter.instance.write([item]);
}
Future<void> copyTextToClipboard(String text) async {
final item = DataWriterItem();
item.add(Formats.plainText(text));
await ClipboardWriter.instance.write([item]);
}