import 'package:fluent_reader_lite/components/list_tile_group.dart'; import 'package:fluent_reader_lite/components/my_list_tile.dart'; import 'package:fluent_reader_lite/generated/l10n.dart'; import 'package:fluent_reader_lite/utils/colors.dart'; import 'package:fluent_reader_lite/utils/store.dart'; import 'package:fluent_reader_lite/utils/global.dart'; import 'package:fluent_reader_lite/utils/font_manager.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:file_picker/file_picker.dart'; class ReadingPage extends StatefulWidget { @override _ReadingPageState createState() => _ReadingPageState(); } class _ReadingPageState extends State { int _fontSize = Store.getArticleFontSize(); List _customFontNames = []; List _availableFonts = []; @override void initState() { super.initState(); _loadCustomFonts(); } void _loadCustomFonts() async { final customFonts = await FontManager.getInstalledCustomFonts(); setState(() { _customFontNames = customFonts; _availableFonts = [...FontManager.builtInFonts, ...customFonts]; }); } void _showFontPicker(BuildContext context) { showCupertinoModalPopup( context: context, builder: (context) => CupertinoActionSheet( title: Text(S.of(context).selectFontFamily), actions: _availableFonts.map((font) { final isCustom = !FontManager.builtInFonts.contains(font); final isSelected = Global.globalModel.fontFamily == font; final isSystem = font == 'System'; return CupertinoActionSheetAction( child: Column( children: [ Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Text( FontManager.getFontDisplayName(font), style: TextStyle( fontFamily: isCustom || isSystem ? null : font, fontWeight: isSelected ? FontWeight.bold : FontWeight.normal, ), ), if (isSelected) Padding( padding: EdgeInsets.only(left: 8), child: Icon(Icons.done, size: 18, color: CupertinoColors.activeBlue), ), ], ), if (!isSystem && !isCustom) Text( 'The quick brown fox jumps over the lazy dog', style: TextStyle( fontFamily: font, fontSize: 12, color: CupertinoColors.systemGrey, ), ), if (isCustom) Text( '(${S.of(context).uploadCustomFont})', style: TextStyle( fontSize: 12, color: CupertinoColors.systemGrey, ), ), ], ), onPressed: () { Global.globalModel.fontFamily = font; Navigator.pop(context); setState(() {}); }, ); }).toList(), cancelButton: CupertinoActionSheetAction( child: Text(S.of(context).cancel), onPressed: () => Navigator.pop(context), ), ), ); } void _uploadCustomFont() async { FilePickerResult? result = await FilePicker.platform.pickFiles( type: FileType.custom, allowedExtensions: ['ttf', 'otf', 'woff', 'woff2'], ); if (result != null && result.files.single.path != null) { try { final sourcePath = result.files.single.path!; final fileName = result.files.single.name; await FontManager.installCustomFont(sourcePath, fileName); final fontName = fileName.split('.').first; Global.globalModel.fontFamily = fontName; _loadCustomFonts(); showCupertinoDialog( context: context, builder: (context) => CupertinoAlertDialog( title: Text(S.of(context).fontUploaded), content: Text(S.of(context).customFontUploaded), actions: [ CupertinoDialogAction( child: Text('OK'), onPressed: () => Navigator.pop(context), ), ], ), ); } catch (e) { showCupertinoDialog( context: context, builder: (context) => CupertinoAlertDialog( title: Text(S.of(context).fontUploadError), content: Text('$e'), actions: [ CupertinoDialogAction( child: Text('OK'), onPressed: () => Navigator.pop(context), ), ], ), ); } } } void _confirmDeleteFont(BuildContext context, String fontName) { showCupertinoDialog( context: context, builder: (context) => CupertinoAlertDialog( title: Text(S.of(context).deleteFont), content: Text(S.of(context).deleteFontConfirm), actions: [ CupertinoDialogAction( child: Text(S.of(context).cancel), onPressed: () => Navigator.pop(context), ), CupertinoDialogAction( isDestructiveAction: true, child: Text(S.of(context).confirm), onPressed: () async { Navigator.pop(context); try { await FontManager.removeCustomFont(fontName); if (Global.globalModel.fontFamily == fontName) { Global.globalModel.fontFamily = 'System'; Store.sp.remove(StoreKeys.CUSTOM_FONT_PATH); } _loadCustomFonts(); } catch (e) { showCupertinoDialog( context: context, builder: (ctx) => CupertinoAlertDialog( title: Text(S.of(ctx).fontUploadError), content: Text('$e'), actions: [ CupertinoDialogAction( child: Text('OK'), onPressed: () => Navigator.pop(ctx), ), ], ), ); } }, ), ], ), ); } @override Widget build(BuildContext context) { return CupertinoPageScaffold( backgroundColor: MyColors.background, navigationBar: CupertinoNavigationBar( middle: Text(S.of(context).reading), ), child: ListView(children: [ ListTileGroup([ MyListTile( title: Text(S.of(context).fontSize), trailing: Text(_fontSize.toString()), trailingChevron: false, withDivider: false, ), MyListTile( title: Expanded(child: CupertinoSlider( min: 10, max: 22, divisions: 13, value: _fontSize.toDouble(), onChanged: (v) { setState(() { _fontSize = v.toInt(); }); }, onChangeEnd: (v) { Store.setArticleFontSize(v.toInt()); }, )), trailingChevron: false, withDivider: false, ), ], title: S.of(context).preferences), ListTileGroup([ MyListTile( title: Text(S.of(context).fontFamily), trailing: Text(FontManager.getFontDisplayName(Global.globalModel.fontFamily)), onTap: () => _showFontPicker(context), ), MyListTile( title: Text(S.of(context).uploadCustomFont), trailing: Icon(CupertinoIcons.add), onTap: _uploadCustomFont, withDivider: _customFontNames.isNotEmpty, ), ..._customFontNames.map((fontName) => MyListTile( title: Text(fontName), trailing: GestureDetector( onTap: () => _confirmDeleteFont(context, fontName), child: Icon(CupertinoIcons.delete, color: CupertinoColors.destructiveRed, size: 20), ), trailingChevron: false, withDivider: fontName != _customFontNames.last, )), ], title: S.of(context).fontSettings), ]), ); } }