From dc1304f97394676ca7257682abc4f1bf0915e299 Mon Sep 17 00:00:00 2001 From: insleker Date: Thu, 4 Sep 2025 16:59:43 +0800 Subject: [PATCH] refactor: adjust some text layout in pdf toolbar --- README.md | 4 +- integration_test/export_flow_test.dart | 2 + lib/data/services/export_providers.dart | 16 ++- lib/data/services/preferences_providers.dart | 28 +++++ lib/l10n/app_de.arb | 4 +- lib/l10n/app_en.arb | 6 +- lib/l10n/app_es.arb | 4 +- lib/l10n/app_fr.arb | 2 + lib/l10n/app_ja.arb | 4 +- lib/l10n/app_ko.arb | 4 +- lib/l10n/app_uk.arb | 4 +- lib/l10n/app_zh.arb | 6 +- lib/l10n/app_zh_CN.arb | 4 +- lib/l10n/app_zh_TW.arb | 4 +- .../features/pdf/view_model/view_model.dart | 10 +- .../pdf/widgets/image_editor_dialog.dart | 4 +- lib/ui/features/pdf/widgets/pdf_screen.dart | 5 +- lib/ui/features/pdf/widgets/pdf_toolbar.dart | 108 ++++++++---------- .../preferences/widgets/settings_screen.dart | 28 ++++- 19 files changed, 167 insertions(+), 80 deletions(-) diff --git a/README.md b/README.md index 07b3e59..575b169 100644 --- a/README.md +++ b/README.md @@ -21,13 +21,13 @@ flutter analyze # > run unit tests and widget tests flutter test # > run integration tests -flutter test integration_test/ -d linux +flutter test integration_test/ -d # dart run tool/gen_view_wireframe_md.dart # flutter pub run dead_code_analyzer # run the app -flutter run +flutter run -d ``` ### build diff --git a/integration_test/export_flow_test.dart b/integration_test/export_flow_test.dart index 71b5b30..f57162a 100644 --- a/integration_test/export_flow_test.dart +++ b/integration_test/export_flow_test.dart @@ -45,6 +45,7 @@ void main() { child: const MaterialApp( localizationsDelegates: AppLocalizations.localizationsDelegates, supportedLocales: AppLocalizations.supportedLocales, + locale: Locale('en'), home: PdfSignatureHomePage(), ), ), @@ -96,6 +97,7 @@ void main() { child: const MaterialApp( localizationsDelegates: AppLocalizations.localizationsDelegates, supportedLocales: AppLocalizations.supportedLocales, + locale: Locale('en'), home: PdfSignatureHomePage(), ), ), diff --git a/lib/data/services/export_providers.dart b/lib/data/services/export_providers.dart index 8fc4cbc..f117b6d 100644 --- a/lib/data/services/export_providers.dart +++ b/lib/data/services/export_providers.dart @@ -2,6 +2,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:path_provider/path_provider.dart' as pp; import 'package:file_selector/file_selector.dart' as fs; import 'package:pdf_signature/data/services/export_service.dart'; +import 'package:pdf_signature/data/services/preferences_providers.dart'; // Feature-scoped DI and configuration providers @@ -11,8 +12,19 @@ final useMockViewerProvider = Provider((_) => false); // Export service injection for testability final exportServiceProvider = Provider((_) => ExportService()); -// Export DPI setting (points per inch mapping), default 144 DPI -final exportDpiProvider = StateProvider((_) => 144.0); +// Export DPI setting (points per inch mapping). Reads from SharedPreferences when available, +// otherwise falls back to 144.0 to keep tests deterministic without bootstrapping prefs. +final exportDpiProvider = Provider((ref) { + final sp = ref.watch(sharedPreferencesProvider); + return sp.maybeWhen( + data: (prefs) { + const allowed = [96.0, 144.0, 200.0, 300.0]; + final v = prefs.getDouble('export_dpi'); + return (v != null && allowed.contains(v)) ? v : 144.0; + }, + orElse: () => 144.0, + ); +}); // Controls whether signature overlay is visible (used to hide on non-stamped pages during export) final signatureVisibilityProvider = StateProvider((_) => true); diff --git a/lib/data/services/preferences_providers.dart b/lib/data/services/preferences_providers.dart index df55154..f893d84 100644 --- a/lib/data/services/preferences_providers.dart +++ b/lib/data/services/preferences_providers.dart @@ -29,6 +29,7 @@ Set _supportedTags() { const _kTheme = 'theme'; // 'light'|'dark'|'system' const _kLanguage = 'language'; // BCP-47 tag like 'en', 'zh-TW', 'es' const _kPageView = 'page_view'; // now only 'continuous' +const _kExportDpi = 'export_dpi'; // double, allowed: 96,144,200,300 String _normalizeLanguageTag(String tag) { final tags = _supportedTags(); @@ -66,20 +67,24 @@ class PreferencesState { final String theme; // 'light' | 'dark' | 'system' final String language; // 'en' | 'zh-TW' | 'es' final String pageView; // only 'continuous' + final double exportDpi; // 96.0 | 144.0 | 200.0 | 300.0 const PreferencesState({ required this.theme, required this.language, required this.pageView, + required this.exportDpi, }); PreferencesState copyWith({ String? theme, String? language, String? pageView, + double? exportDpi, }) => PreferencesState( theme: theme ?? this.theme, language: language ?? this.language, pageView: pageView ?? this.pageView, + exportDpi: exportDpi ?? this.exportDpi, ); } @@ -95,12 +100,20 @@ class PreferencesNotifier extends StateNotifier { .toLanguageTag(), ), pageView: prefs.getString(_kPageView) ?? 'continuous', + exportDpi: _readDpi(prefs), ), ) { // normalize language to supported/fallback _ensureValid(); } + static double _readDpi(SharedPreferences prefs) { + final d = prefs.getDouble(_kExportDpi); + if (d == null) return 144.0; + const allowed = [96.0, 144.0, 200.0, 300.0]; + return allowed.contains(d) ? d : 144.0; + } + void _ensureValid() { final themeValid = {'light', 'dark', 'system'}; if (!themeValid.contains(state.theme)) { @@ -117,6 +130,12 @@ class PreferencesNotifier extends StateNotifier { state = state.copyWith(pageView: 'continuous'); prefs.setString(_kPageView, 'continuous'); } + // Ensure DPI is one of allowed values + const allowed = [96.0, 144.0, 200.0, 300.0]; + if (!allowed.contains(state.exportDpi)) { + state = state.copyWith(exportDpi: 144.0); + prefs.setDouble(_kExportDpi, 144.0); + } } Future setTheme(String theme) async { @@ -140,10 +159,12 @@ class PreferencesNotifier extends StateNotifier { theme: 'system', language: normalized, pageView: 'continuous', + exportDpi: 144.0, ); await prefs.setString(_kTheme, 'system'); await prefs.setString(_kLanguage, normalized); await prefs.setString(_kPageView, 'continuous'); + await prefs.setDouble(_kExportDpi, 144.0); } Future setPageView(String pageView) async { @@ -152,6 +173,13 @@ class PreferencesNotifier extends StateNotifier { state = state.copyWith(pageView: pageView); await prefs.setString(_kPageView, pageView); } + + Future setExportDpi(double dpi) async { + const allowed = [96.0, 144.0, 200.0, 300.0]; + if (!allowed.contains(dpi)) return; + state = state.copyWith(exportDpi: dpi); + await prefs.setDouble(_kExportDpi, dpi); + } } final sharedPreferencesProvider = FutureProvider(( diff --git a/lib/l10n/app_de.arb b/lib/l10n/app_de.arb index 25b533f..c033e81 100644 --- a/lib/l10n/app_de.arb +++ b/lib/l10n/app_de.arb @@ -11,7 +11,7 @@ "delete": "Löschen", "display": "Anzeige", "downloadStarted": "Download gestartet", - "dpi": "DPI:", + "dpi": "DPI", "drawSignature": "Signatur zeichnen", "errorWithMessage": "Fehler: {message}", "exportingPleaseWait": "Exportiere… Bitte warten", @@ -19,6 +19,7 @@ "failedToSavePdf": "PDF konnte nicht gespeichert werden", "general": "Allgemein", "goTo": "Gehe zu:", + "image": "Bild", "invalidOrUnsupportedFile": "Ungültige oder nicht unterstützte Datei", "language": "Sprache", "loadSignatureFromFile": "Signatur aus Datei laden", @@ -34,6 +35,7 @@ "pageViewContinuous": "Kontinuierlich", "prev": "Vorherige", "resetToDefaults": "Auf Standardwerte zurücksetzen", + "rotate": "Drehen", "save": "Speichern", "savedWithPath": "Gespeichert: {path}", "saveSignedPdf": "Signiertes PDF speichern", diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 9d95cf1..711c3c7 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -24,7 +24,7 @@ "@display": {}, "downloadStarted": "Download started", "@downloadStarted": {}, - "dpi": "DPI:", + "dpi": "DPI", "@dpi": {}, "drawSignature": "Draw Signature", "@drawSignature": {}, @@ -47,6 +47,8 @@ "@general": {}, "goTo": "Go to:", "@goTo": {}, + "image": "Image", + "@image": {}, "invalidOrUnsupportedFile": "Invalid or unsupported file", "@invalidOrUnsupportedFile": {}, "language": "Language", @@ -87,6 +89,8 @@ "@prev": {}, "resetToDefaults": "Reset to defaults", "@resetToDefaults": {}, + "rotate": "Rotate", + "@rotate": {}, "save": "Save", "@save": {}, "savedWithPath": "Saved: {path}", diff --git a/lib/l10n/app_es.arb b/lib/l10n/app_es.arb index 049c458..5aef40c 100644 --- a/lib/l10n/app_es.arb +++ b/lib/l10n/app_es.arb @@ -11,7 +11,7 @@ "delete": "Eliminar", "display": "Pantalla", "downloadStarted": "Descarga iniciada", - "dpi": "DPI:", + "dpi": "DPI", "drawSignature": "Dibujar firma", "errorWithMessage": "Error: {message}", "exportingPleaseWait": "Exportando... Por favor, espere", @@ -19,6 +19,7 @@ "failedToSavePdf": "No se pudo guardar el PDF", "general": "General", "goTo": "Ir a:", + "image": "Imagen", "invalidOrUnsupportedFile": "Archivo inválido o no compatible", "language": "Idioma", "loadSignatureFromFile": "Cargar firma desde archivo", @@ -34,6 +35,7 @@ "pageViewContinuous": "Continuo", "prev": "Anterior", "resetToDefaults": "Restablecer valores predeterminados", + "rotate": "Rotar", "save": "Guardar", "savedWithPath": "Guardado: {path}", "saveSignedPdf": "Guardar PDF firmado", diff --git a/lib/l10n/app_fr.arb b/lib/l10n/app_fr.arb index 355ac57..fe793fa 100644 --- a/lib/l10n/app_fr.arb +++ b/lib/l10n/app_fr.arb @@ -19,6 +19,7 @@ "failedToSavePdf": "Échec de l'enregistrement du PDF", "general": "Général", "goTo": "Aller à :", + "image": "Image", "invalidOrUnsupportedFile": "Fichier invalide ou non pris en charge", "language": "Langue", "loadSignatureFromFile": "Charger une signature depuis un fichier", @@ -34,6 +35,7 @@ "pageViewContinuous": "Continu", "prev": "Précédent", "resetToDefaults": "Rétablir les valeurs par défaut", + "rotate": "Rotation", "save": "Enregistrer", "savedWithPath": "Enregistré : {path}", "saveSignedPdf": "Enregistrer le PDF signé", diff --git a/lib/l10n/app_ja.arb b/lib/l10n/app_ja.arb index 2ca0379..2476988 100644 --- a/lib/l10n/app_ja.arb +++ b/lib/l10n/app_ja.arb @@ -11,7 +11,7 @@ "delete": "削除", "display": "表示", "downloadStarted": "ダウンロード開始", - "dpi": "DPI:", + "dpi": "DPI", "drawSignature": "署名をかく", "errorWithMessage": "エラー:{message}", "exportingPleaseWait": "エクスポート中…お待ちください", @@ -19,6 +19,7 @@ "failedToSavePdf": "PDFの保存に失敗しました", "general": "一般", "goTo": "移動:", + "image": "画像", "invalidOrUnsupportedFile": "無効なファイルまたはサポートされていないファイル", "language": "言語", "loadSignatureFromFile": "ファイルから署名を読み込む", @@ -34,6 +35,7 @@ "pageViewContinuous": "連続", "prev": "前へ", "resetToDefaults": "デフォルトに戻す", + "rotate": "回転", "save": "保存", "savedWithPath": "保存しました:{path}", "saveSignedPdf": "署名済みPDFを保存", diff --git a/lib/l10n/app_ko.arb b/lib/l10n/app_ko.arb index 380213c..25081a2 100644 --- a/lib/l10n/app_ko.arb +++ b/lib/l10n/app_ko.arb @@ -11,7 +11,7 @@ "delete": "삭제", "display": "표시", "downloadStarted": "다운로드 시작됨", - "dpi": "DPI:", + "dpi": "DPI", "drawSignature": "서명 그리기", "errorWithMessage": "오류: {message}", "exportingPleaseWait": "내보내는 중... 잠시 기다려주세요", @@ -19,6 +19,7 @@ "failedToSavePdf": "PDF 저장 실패", "general": "일반", "goTo": "이동:", + "image": "이미지", "invalidOrUnsupportedFile": "잘못된 파일이거나 지원되지 않는 파일입니다.", "language": "언어", "loadSignatureFromFile": "파일에서 서명 불러오기", @@ -34,6 +35,7 @@ "pageViewContinuous": "연속", "prev": "이전", "resetToDefaults": "기본값으로 재설정", + "rotate": "회전", "save": "저장", "savedWithPath": "{path}에 저장됨", "saveSignedPdf": "서명된 PDF 저장", diff --git a/lib/l10n/app_uk.arb b/lib/l10n/app_uk.arb index e5f8cdf..b287b01 100644 --- a/lib/l10n/app_uk.arb +++ b/lib/l10n/app_uk.arb @@ -11,7 +11,7 @@ "delete": "Видалити", "display": "Відображення", "downloadStarted": "Завантаження розпочато", - "dpi": "DPI:", + "dpi": "DPI", "drawSignature": "Намалювати підпис", "errorWithMessage": "Помилка: {message}", "exportingPleaseWait": "Експортування... Зачекайте", @@ -19,6 +19,7 @@ "failedToSavePdf": "Не вдалося зберегти PDF", "general": "Загальні", "goTo": "Перейти до:", + "image": "Зображення", "invalidOrUnsupportedFile": "Недійсний або непідтримуваний файл", "language": "Мова", "loadSignatureFromFile": "Завантажити підпис з файлу", @@ -34,6 +35,7 @@ "pageViewContinuous": "Безперервний", "prev": "Попередня", "resetToDefaults": "Скинути до значень за замовчуванням", + "rotate": "Повернути", "save": "Зберегти", "savedWithPath": "Збережено: {path}", "saveSignedPdf": "Зберегти підписаний PDF", diff --git a/lib/l10n/app_zh.arb b/lib/l10n/app_zh.arb index 3d25a34..4f7dc84 100644 --- a/lib/l10n/app_zh.arb +++ b/lib/l10n/app_zh.arb @@ -12,7 +12,7 @@ "delete": "刪除", "display": "顯示", "downloadStarted": "已開始下載", - "dpi": "DPI:", + "dpi": "DPI", "drawSignature": "手寫簽名", "errorWithMessage": "錯誤:{message}", "exportingPleaseWait": "匯出中…請稍候", @@ -20,6 +20,7 @@ "failedToSavePdf": "儲存 PDF 失敗", "general": "一般", "goTo": "前往:", + "image": "圖片", "invalidOrUnsupportedFile": "無效或不支援的檔案", "language": "語言", "loadSignatureFromFile": "從檔案載入簽名", @@ -27,7 +28,7 @@ "longPressOrRightClickTheSignatureToConfirmOrDelete": "長按或右鍵點擊簽名以確認或刪除。", "next": "下一頁", "noPdfLoaded": "尚未載入 PDF", - "noSignatureLoaded": "没有加载签名", + "noSignatureLoaded": "沒有加載簽名", "nothingToSaveYet": "尚無可儲存的內容", "openPdf": "開啟 PDF…", "pageInfo": "第 {current}/{total} 頁", @@ -35,6 +36,7 @@ "pageViewContinuous": "連續", "prev": "上一頁", "resetToDefaults": "重設為預設值", + "rotate": "旋轉", "save": "儲存", "savedWithPath": "已儲存:{path}", "saveSignedPdf": "儲存已簽名 PDF", diff --git a/lib/l10n/app_zh_CN.arb b/lib/l10n/app_zh_CN.arb index e2c7b55..213bb1e 100644 --- a/lib/l10n/app_zh_CN.arb +++ b/lib/l10n/app_zh_CN.arb @@ -11,7 +11,7 @@ "delete": "删除", "display": "显示", "downloadStarted": "下载已开始", - "dpi": "DPI:", + "dpi": "DPI", "drawSignature": "绘制签名", "errorWithMessage": "错误:{message}", "exportingPleaseWait": "正在导出... 请稍候", @@ -19,6 +19,7 @@ "failedToSavePdf": "PDF 保存失败", "general": "常规", "goTo": "跳转到:", + "image": "图片", "invalidOrUnsupportedFile": "无效或不支持的文件", "language": "语言", "loadSignatureFromFile": "从文件加载签名", @@ -34,6 +35,7 @@ "pageViewContinuous": "连续", "prev": "上一页", "resetToDefaults": "恢复默认值", + "rotate": "旋转", "save": "保存", "savedWithPath": "已保存:{path}", "saveSignedPdf": "保存已签名的 PDF", diff --git a/lib/l10n/app_zh_TW.arb b/lib/l10n/app_zh_TW.arb index 5c804c7..1cbf855 100644 --- a/lib/l10n/app_zh_TW.arb +++ b/lib/l10n/app_zh_TW.arb @@ -12,7 +12,7 @@ "delete": "刪除", "display": "顯示", "downloadStarted": "已開始下載", - "dpi": "DPI:", + "dpi": "DPI", "drawSignature": "手寫簽名", "errorWithMessage": "錯誤:{message}", "exportingPleaseWait": "匯出中…請稍候", @@ -20,6 +20,7 @@ "failedToSavePdf": "儲存 PDF 失敗", "general": "一般", "goTo": "前往:", + "image": "圖片", "invalidOrUnsupportedFile": "無效或不支援的檔案", "language": "語言", "loadSignatureFromFile": "從檔案載入簽名", @@ -35,6 +36,7 @@ "pageViewContinuous": "連續", "prev": "上一頁", "resetToDefaults": "重設為預設值", + "rotate": "旋轉", "save": "儲存", "savedWithPath": "已儲存:{path}", "saveSignedPdf": "儲存已簽名 PDF", diff --git a/lib/ui/features/pdf/view_model/view_model.dart b/lib/ui/features/pdf/view_model/view_model.dart index 7074e7b..3135412 100644 --- a/lib/ui/features/pdf/view_model/view_model.dart +++ b/lib/ui/features/pdf/view_model/view_model.dart @@ -4,6 +4,7 @@ import 'dart:typed_data'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:image/image.dart' as img; +import 'package:pdf_signature/l10n/app_localizations.dart'; import '../../../../data/model/model.dart'; @@ -251,7 +252,14 @@ class SignatureController extends StateNotifier { void setInvalidSelected(BuildContext context) { // Fallback message without localization to keep core logic testable ScaffoldMessenger.of(context).showSnackBar( - const SnackBar(content: Text('Invalid or unsupported file')), + SnackBar( + content: Text( + Localizations.of( + context, + AppLocalizations, + )!.invalidOrUnsupportedFile, + ), + ), ); } diff --git a/lib/ui/features/pdf/widgets/image_editor_dialog.dart b/lib/ui/features/pdf/widgets/image_editor_dialog.dart index cd1f856..1f6ae41 100644 --- a/lib/ui/features/pdf/widgets/image_editor_dialog.dart +++ b/lib/ui/features/pdf/widgets/image_editor_dialog.dart @@ -10,6 +10,8 @@ class ImageEditorDialog extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { + final l10n = Localizations.of(context, AppLocalizations)!; + final l = AppLocalizations.of(context); final sig = ref.watch(signatureProvider); return Dialog( @@ -57,7 +59,7 @@ class ImageEditorDialog extends ConsumerWidget { const SizedBox(height: 8), Row( children: [ - Text('Rotate'), + Text(l10n.rotate), Expanded( child: Slider( key: const Key('sld_rotation'), diff --git a/lib/ui/features/pdf/widgets/pdf_screen.dart b/lib/ui/features/pdf/widgets/pdf_screen.dart index 5a383bc..650959e 100644 --- a/lib/ui/features/pdf/widgets/pdf_screen.dart +++ b/lib/ui/features/pdf/widgets/pdf_screen.dart @@ -68,8 +68,9 @@ class _PdfSignatureHomePageState extends ConsumerState { } Future _loadSignatureFromFile() async { - final typeGroup = const fs.XTypeGroup( - label: 'Image', + final typeGroup = fs.XTypeGroup( + label: + Localizations.of(context, AppLocalizations)?.image, extensions: ['png', 'jpg', 'jpeg', 'webp'], ); final file = await fs.openFile(acceptedTypeGroups: [typeGroup]); diff --git a/lib/ui/features/pdf/widgets/pdf_toolbar.dart b/lib/ui/features/pdf/widgets/pdf_toolbar.dart index 9d18b6f..1a8a4a4 100644 --- a/lib/ui/features/pdf/widgets/pdf_toolbar.dart +++ b/lib/ui/features/pdf/widgets/pdf_toolbar.dart @@ -3,7 +3,6 @@ import 'package:flutter/services.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:pdf_signature/l10n/app_localizations.dart'; -import '../../../../data/services/export_providers.dart'; import '../view_model/view_model.dart'; class PdfToolbar extends ConsumerStatefulWidget { @@ -57,7 +56,6 @@ class _PdfToolbarState extends ConsumerState { @override Widget build(BuildContext context) { final pdf = ref.watch(pdfProvider); - final dpi = ref.watch(exportDpiProvider); final l = AppLocalizations.of(context); final pageInfo = l.pageInfo(pdf.currentPage, pdf.pageCount); @@ -97,25 +95,32 @@ class _PdfToolbarState extends ConsumerState { Wrap( spacing: 8, children: [ - IconButton( - key: const Key('btn_prev'), - onPressed: - widget.disabled - ? null - : () => widget.onJumpToPage(pdf.currentPage - 1), - icon: const Icon(Icons.chevron_left), - tooltip: l.prev, - ), - // Current page label - Text(pageInfo, key: const Key('lbl_page_info')), - IconButton( - key: const Key('btn_next'), - onPressed: - widget.disabled - ? null - : () => widget.onJumpToPage(pdf.currentPage + 1), - icon: const Icon(Icons.chevron_right), - tooltip: l.next, + Wrap( + crossAxisAlignment: WrapCrossAlignment.center, + children: [ + IconButton( + key: const Key('btn_prev'), + onPressed: + widget.disabled + ? null + : () => + widget.onJumpToPage(pdf.currentPage - 1), + icon: const Icon(Icons.chevron_left), + tooltip: l.prev, + ), + // Current page label + Text(pageInfo, key: const Key('lbl_page_info')), + IconButton( + key: const Key('btn_next'), + onPressed: + widget.disabled + ? null + : () => + widget.onJumpToPage(pdf.currentPage + 1), + icon: const Icon(Icons.chevron_right), + tooltip: l.next, + ), + ], ), Wrap( spacing: 6, @@ -150,48 +155,29 @@ class _PdfToolbarState extends ConsumerState { ], ), const SizedBox(width: 8), - IconButton( - key: const Key('btn_zoom_out'), - tooltip: 'Zoom out', - onPressed: widget.disabled ? null : widget.onZoomOut, - icon: const Icon(Icons.zoom_out), - ), - Text( - //if not null - widget.zoomLevel != null ? '${widget.zoomLevel}%' : '', - style: const TextStyle(fontSize: 12), - ), - IconButton( - key: const Key('btn_zoom_in'), - tooltip: 'Zoom in', - onPressed: widget.disabled ? null : widget.onZoomIn, - icon: const Icon(Icons.zoom_in), + Wrap( + crossAxisAlignment: WrapCrossAlignment.center, + children: [ + IconButton( + key: const Key('btn_zoom_out'), + tooltip: 'Zoom out', + onPressed: widget.disabled ? null : widget.onZoomOut, + icon: const Icon(Icons.zoom_out), + ), + Text( + //if not null + widget.zoomLevel != null ? '${widget.zoomLevel}%' : '', + style: const TextStyle(fontSize: 12), + ), + IconButton( + key: const Key('btn_zoom_in'), + tooltip: 'Zoom in', + onPressed: widget.disabled ? null : widget.onZoomIn, + icon: const Icon(Icons.zoom_in), + ), + ], ), SizedBox(width: 6), - // show zoom ratio - Text(l.dpi), - const SizedBox(width: 8), - DropdownButton( - key: const Key('ddl_export_dpi'), - value: dpi, - items: - const [96.0, 144.0, 200.0, 300.0] - .map( - (v) => DropdownMenuItem( - value: v, - child: Text(v.toStringAsFixed(0)), - ), - ) - .toList(), - onChanged: - widget.disabled - ? null - : (v) { - if (v != null) { - ref.read(exportDpiProvider.notifier).state = v; - } - }, - ), ], ), ], diff --git a/lib/ui/features/preferences/widgets/settings_screen.dart b/lib/ui/features/preferences/widgets/settings_screen.dart index 08c388d..a80c4be 100644 --- a/lib/ui/features/preferences/widgets/settings_screen.dart +++ b/lib/ui/features/preferences/widgets/settings_screen.dart @@ -14,6 +14,7 @@ class _SettingsDialogState extends ConsumerState { String? _theme; String? _language; // Page view removed; continuous-only + double? _exportDpi; @override void initState() { @@ -21,6 +22,7 @@ class _SettingsDialogState extends ConsumerState { final prefs = ref.read(preferencesProvider); _theme = prefs.theme; _language = prefs.language; + _exportDpi = prefs.exportDpi; // pageView no longer configurable (continuous-only) } @@ -118,6 +120,29 @@ class _SettingsDialogState extends ConsumerState { ), ], ), + Row( + children: [ + SizedBox(width: 140, child: Text('${l.dpi}:')), + const SizedBox(width: 8), + Expanded( + child: DropdownButton( + key: const Key('ddl_export_dpi'), + isExpanded: true, + value: _exportDpi, + items: + const [96.0, 144.0, 200.0, 300.0] + .map( + (v) => DropdownMenuItem( + value: v, + child: Text(v.toStringAsFixed(0)), + ), + ) + .toList(), + onChanged: (v) => setState(() => _exportDpi = v), + ), + ), + ], + ), const SizedBox(height: 16), Text(l.display, style: Theme.of(context).textTheme.titleMedium), const SizedBox(height: 8), @@ -149,7 +174,7 @@ class _SettingsDialogState extends ConsumerState { ), ], ), - // Page view setting removed (continuous-only) + const SizedBox(height: 24), Row( mainAxisAlignment: MainAxisAlignment.end, @@ -164,6 +189,7 @@ class _SettingsDialogState extends ConsumerState { final n = ref.read(preferencesProvider.notifier); if (_theme != null) await n.setTheme(_theme!); if (_language != null) await n.setLanguage(_language!); + if (_exportDpi != null) await n.setExportDpi(_exportDpi!); // pageView not configurable anymore if (mounted) Navigator.of(context).pop(true); },