79 lines
3.5 KiB
Dart
79 lines
3.5 KiB
Dart
// 보기 화면 PDF 편집 모듈: 페이지 지우기 · 그림을 페이지로 넣기 · 저장 · 닫을 때 저장 묻기
|
|
//
|
|
// 약속
|
|
// - 입력: 창을 띄울 [BuildContext] (화면의 것 - 닫혔는지 context.mounted 로 본다) · [PdfSource] · 지금 페이지 (부를 때 읽음) ·
|
|
// 알림 글을 보일 [snack]
|
|
// - 출력: 바뀐 페이지 번호 (다시 그릴 자리) 또는 null (취소 · 화면 닫힘 · 실패). 저장 · 나가기는 bool
|
|
// - 오류: 넣기 · 저장 실패는 [snack] 으로 알리고 던지지 않는다. 페이지 지우기 실패는 그대로 던진다 (예전과 같음)
|
|
// 창은 지금 장면이 끝난 뒤 (endOfFrame) 띄운다 - 28 지적 (10/20): 위 막대 단추를 누른 그 이벤트 안에서 창을 띄우면
|
|
// (장을 손으로 넘긴 뒤) 창을 닫을 때 디버그 판에서 의미 트리 assertion ('node.built') 이 났다.
|
|
// 다른 PDF 화면도 다시 쓸 수 있다.
|
|
|
|
import 'package:flutter/widgets.dart';
|
|
|
|
import '../../app/app_controller.dart';
|
|
import '../../core/reader_sources.dart';
|
|
import '../../core/vfs.dart';
|
|
import '../../l10n/tr.dart';
|
|
import 'reader_dialogs.dart';
|
|
|
|
/// 지금 페이지를 묻고 지운다. 지운 페이지 번호 (취소 · 한 페이지뿐 · 화면 닫힘이면 null)
|
|
Future<int?> deletePdfPage(BuildContext context, PdfSource pdf, int Function() currentPage, void Function(String) snack) async {
|
|
await afterFrame();
|
|
if (!context.mounted) return null;
|
|
if (pdf.length <= 1) {
|
|
snack(tr('마지막 한 페이지는 지울 수 없습니다.'));
|
|
return null;
|
|
}
|
|
final page = currentPage();
|
|
final ok = await confirmPdfPageDelete(context, page);
|
|
if (ok != true) return null;
|
|
await pdf.deletePage(page);
|
|
if (!context.mounted) return null;
|
|
return page;
|
|
}
|
|
|
|
/// 그림을 골라 지금 페이지 뒤에 넣는다. 넣은 자리 (페이지) 와 장 수 (고르지 않음 · 실패 · 화면 닫힘이면 null)
|
|
Future<({int page, int count})?> insertPdfImages(
|
|
BuildContext context, AppController c, PdfSource pdf, int Function() currentPage, void Function(String) snack) async {
|
|
await afterFrame();
|
|
if (!context.mounted) return null;
|
|
final page = currentPage();
|
|
final files = await pickPdfImages(c);
|
|
if (files.isEmpty || !context.mounted) return null;
|
|
final temp = await c.services.storage.tempDirectory();
|
|
try {
|
|
await pdf.insertImages(files, after: page, tempDir: temp);
|
|
} catch (e) {
|
|
snack(trf('넣지 못했습니다: {0}', [e]));
|
|
return null;
|
|
}
|
|
return (page: page, count: files.length);
|
|
}
|
|
|
|
/// 넣은 뒤 알림 글
|
|
String insertedPdfImagesText(({int page, int count}) r) =>
|
|
trf('그림 {0}장을 {1} 페이지 뒤에 넣었습니다 (저장해야 파일에 반영됩니다).', [r.count, r.page + 1]);
|
|
|
|
/// 저장한다 (알림 글은 [snack]). 저장했으면 true
|
|
Future<bool> savePdf(PdfSource pdf, void Function(String) snack) async {
|
|
try {
|
|
final path = await pdf.save();
|
|
snack(trf('저장했습니다: {0}', [vDisplay(path)]));
|
|
return true;
|
|
} catch (e) {
|
|
snack(trf('저장하지 못했습니다: {0}', [e]));
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// 닫아도 되는지 (저장하지 않은 변경이 있으면 묻고, [저장] 이면 [save] 를 부른다). [pdf] 가 없거나 바뀐 것이 없으면 true
|
|
Future<bool> confirmPdfLeave(BuildContext context, PdfSource? pdf, Future<void> Function() save) async {
|
|
if (pdf == null || !pdf.dirty) return true;
|
|
await afterFrame();
|
|
if (!context.mounted) return false;
|
|
final r = await askSaveChanges(context);
|
|
if (r == 'save') await save();
|
|
return r == 'save' || r == 'discard';
|
|
}
|