144 lines
4.3 KiB
Dart
144 lines
4.3 KiB
Dart
// ============================================================
|
||
// 闲言APP — 编辑器导入操作 Mixin
|
||
// 创建时间: 2026-04-25
|
||
// 更新时间: 2026-04-25
|
||
// 作用: 图片导入、xycard导入、草稿列表、模板选择、贴纸编辑器入口
|
||
// 上次更新: 从 editor_actions_mixin.dart 分流
|
||
// ============================================================
|
||
|
||
import 'dart:typed_data';
|
||
import 'dart:ui' as ui;
|
||
|
||
import 'package:flutter/cupertino.dart';
|
||
import 'package:pro_image_editor/pro_image_editor.dart' as pro;
|
||
import 'package:pro_image_editor/designs/frosted_glass/frosted_glass.dart';
|
||
|
||
import 'package:xianyan/core/router/editor_router.dart';
|
||
import 'package:xianyan/core/utils/logger.dart' as app_log;
|
||
import 'package:xianyan/editor/models/editor_models.dart';
|
||
import 'package:xianyan/editor/services/image/image_import_service.dart';
|
||
import 'package:xianyan/editor/services/export/xycard_service.dart';
|
||
import 'package:xianyan/editor/widgets/panels/template_picker_sheet.dart';
|
||
import 'package:xianyan/shared/widgets/containers/bottom_sheet.dart';
|
||
import 'package:xianyan/editor/mixins/editor_actions_base.dart';
|
||
|
||
mixin EditorImportActions on EditorActionsBase {
|
||
void openStickerEditor(pro.ProImageEditorState editor) async {
|
||
final layer = await editor.openPage<pro.Layer>(
|
||
FrostedGlassStickerPage(
|
||
configs: editor.configs,
|
||
callbacks: editor.callbacks,
|
||
),
|
||
);
|
||
|
||
if (layer == null || !mounted) return;
|
||
|
||
if (layer is! pro.WidgetLayer) {
|
||
layer.scale = editor.configs.emojiEditor.initScale;
|
||
}
|
||
|
||
editor.addLayer(layer);
|
||
}
|
||
|
||
void showDraftList() {
|
||
context.goToDraftList();
|
||
}
|
||
|
||
void showTemplateSheet() {
|
||
AppBottomSheet.showHalf<void>(
|
||
context: context,
|
||
builder: (_) => TemplatePickerSheet(
|
||
onSelect: (template) {
|
||
Navigator.of(context).pop();
|
||
},
|
||
),
|
||
);
|
||
}
|
||
|
||
Future<void> importImage() async {
|
||
final choice = await showEditorChoice(
|
||
context: context,
|
||
title: '📥 导入',
|
||
message: '选择导入方式',
|
||
choices: [('🖼️ 图片', 'image'), ('📦 .xycard 源文件', 'xycard')],
|
||
);
|
||
if (!mounted || choice == null) return;
|
||
|
||
if (choice == 'xycard') {
|
||
await _importXycard();
|
||
} else {
|
||
await _importImageFile();
|
||
}
|
||
}
|
||
|
||
Future<void> _importImageFile() async {
|
||
try {
|
||
final bytes = await ImageImportService.showImportSheet(context);
|
||
if (bytes == null || !mounted) return;
|
||
|
||
final processed = await ImageImportService.preprocessImage(bytes);
|
||
if (processed == null || !mounted) return;
|
||
|
||
context.replaceWithEditor(processed);
|
||
} catch (e) {
|
||
app_log.Log.e('导入图片失败', e);
|
||
}
|
||
}
|
||
|
||
Future<void> _importXycard() async {
|
||
try {
|
||
final canvas = await XycardService.import();
|
||
if (canvas == null || !mounted) return;
|
||
|
||
final imageBytes = await _renderCanvasToBytes(canvas);
|
||
if (imageBytes == null || !mounted) return;
|
||
|
||
context.replaceWithEditor(
|
||
imageBytes,
|
||
initialText: canvas.textLayers.isNotEmpty
|
||
? canvas.textLayers.first.text
|
||
: null,
|
||
);
|
||
} catch (e) {
|
||
app_log.Log.e('导入.xycard失败', e);
|
||
if (mounted) {
|
||
showCupertinoDialog<void>(
|
||
context: context,
|
||
builder: (_) => CupertinoAlertDialog(
|
||
title: const Text('导入失败'),
|
||
content: const Text('无法解析.xycard文件,请检查文件格式'),
|
||
actions: [
|
||
CupertinoDialogAction(
|
||
child: const Text('确定'),
|
||
onPressed: () => Navigator.pop(context),
|
||
),
|
||
],
|
||
),
|
||
);
|
||
}
|
||
}
|
||
}
|
||
|
||
Future<Uint8List?> _renderCanvasToBytes(QuoteCanvasModel canvas) async {
|
||
try {
|
||
final recorder = ui.PictureRecorder();
|
||
final canvasObj = Canvas(recorder);
|
||
final w = canvas.canvasWidth;
|
||
final h = canvas.canvasHeight;
|
||
|
||
canvasObj.drawRect(
|
||
Rect.fromLTWH(0, 0, w, h),
|
||
Paint()..color = canvas.background.solidColor,
|
||
);
|
||
|
||
final picture = recorder.endRecording();
|
||
final image = await picture.toImage(w.toInt(), h.toInt());
|
||
final byteData = await image.toByteData(format: ui.ImageByteFormat.png);
|
||
return byteData?.buffer.asUint8List();
|
||
} catch (e) {
|
||
app_log.Log.e('画布渲染失败', e);
|
||
return null;
|
||
}
|
||
}
|
||
}
|