// 보기 화면 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 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 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 confirmPdfLeave(BuildContext context, PdfSource? pdf, Future 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'; }