diff --git a/lib/components/gallery_info.dart b/lib/components/gallery_info.dart index 6a90bba..ff0304a 100644 --- a/lib/components/gallery_info.dart +++ b/lib/components/gallery_info.dart @@ -1,3 +1,4 @@ +import 'dart:math'; import 'dart:ui'; import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; @@ -65,7 +66,18 @@ class _GalleryInfo extends State with ThemeModeWidget { @override Widget build(BuildContext context) { - bool useMobile = MediaQuery.of(context).size.width <= 810; + final maxWidth = MediaQuery.of(context).size.width; + bool useMobile = maxWidth <= 810; + final thumb = prefs.getInt("thumbnailSize") ?? 1; + final tsize = (thumb >= 0 && thumb < ThumbnailSize.values.length + ? ThumbnailSize.values[thumb] + : ThumbnailSize.medium) + .size; + final max = maxWidth > 810 + ? tsize + : maxWidth >= 400 + ? min(tsize, ThumbnailSize.medium.size) + : ThumbnailSize.smail.size; final firstPage = widget.gData.pages.firstOrNull; final int? firstFileId = widget.files != null && firstPage != null ? widget.files!.files[firstPage!.token]!.firstOrNull?.id @@ -134,14 +146,11 @@ class _GalleryInfo extends State with ThemeModeWidget { : SliverToBoxAdapter(child: Container()), ThumbnailGridView( widget.gData, - useMobile - ? const SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: 2) - : const SliverGridDelegateWithMaxCrossAxisExtent( - maxCrossAxisExtent: 400, - childAspectRatio: 1, - crossAxisSpacing: 10, - mainAxisSpacing: 10), + SliverGridDelegateWithMaxCrossAxisExtent( + maxCrossAxisExtent: max.toDouble(), + childAspectRatio: 1, + crossAxisSpacing: 10, + mainAxisSpacing: 10), files: widget.files, gid: widget.gData.meta.gid, isSelectMode: widget.isSelectMode, diff --git a/lib/components/thumbnail_gridview.dart b/lib/components/thumbnail_gridview.dart index b43b0ae..c9d24f4 100644 --- a/lib/components/thumbnail_gridview.dart +++ b/lib/components/thumbnail_gridview.dart @@ -15,7 +15,7 @@ class ThumbnailGridView extends StatelessWidget { final GalleryData gdata; final int? gid; final EhFiles? files; - final SliverGridDelegate gridDelegate; + final SliverGridDelegateWithMaxCrossAxisExtent gridDelegate; final bool isSelectMode; final List? selected; final Function? onSelectedChange; @@ -25,12 +25,7 @@ class ThumbnailGridView extends StatelessWidget { final displayAd = prefs.getBool("displayAd") ?? false; final npages = displayAd ? gdata.pages : gdata.pages.where((e) => !e.isAd).toList(); - final maxWidth = MediaQuery.of(context).size.width; - final baseSize = maxWidth > 810 - ? 400 - : maxWidth < 400 - ? 200 - : 300; + final baseSize = gridDelegate.maxCrossAxisExtent.toInt(); final max = (baseSize * MediaQuery.of(context).devicePixelRatio).toInt(); return SliverGrid.builder( gridDelegate: gridDelegate, diff --git a/lib/globals.dart b/lib/globals.dart index 30a6566..0d23ed7 100644 --- a/lib/globals.dart +++ b/lib/globals.dart @@ -339,6 +339,27 @@ enum Lang { } } +enum ThumbnailSize { + smail(200), + medium(300), + big(400); + + const ThumbnailSize(this.size); + final int size; + + String localText(BuildContext context) { + final i18n = AppLocalizations.of(context)!; + switch (this) { + case ThumbnailSize.smail: + return i18n.smail; + case ThumbnailSize.medium: + return i18n.medium; + case ThumbnailSize.big: + return i18n.big; + } + } +} + final _authLog = Logger("AuthLog"); void clearAllStates(BuildContext context) { diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 41c18dc..60869f5 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -244,5 +244,9 @@ "createExportZipTask": "Create export as ZIP file task", "outputDir": "Output directory", "copyOriImgUrl": "Copy original image URL to clipboard", - "importTask": "Import task" + "importTask": "Import task", + "thumbnailSize": "Thumbnail size", + "smail": "smail", + "medium": "medium", + "big": "big" } diff --git a/lib/l10n/app_zh.arb b/lib/l10n/app_zh.arb index 413a5b6..56e1a0d 100644 --- a/lib/l10n/app_zh.arb +++ b/lib/l10n/app_zh.arb @@ -244,5 +244,9 @@ "createExportZipTask": "新建导出为ZIP文件任务", "outputDir": "输出文件夹", "copyOriImgUrl": "复制原图链接到剪贴板", - "importTask": "导入任务" + "importTask": "导入任务", + "thumbnailSize": "缩略图大小", + "smail": "小", + "medium": "中", + "big": "大" } diff --git a/lib/pages/settings/display.dart b/lib/pages/settings/display.dart index 23e03d4..dd732a9 100644 --- a/lib/pages/settings/display.dart +++ b/lib/pages/settings/display.dart @@ -26,6 +26,7 @@ class _DisplaySettingsPage extends State bool _oriShowTranslatedTag = false; bool _oriUseTitleJpn = false; bool _oriDlUseAvgSpeed = false; + ThumbnailSize _oriThumbnailSize = ThumbnailSize.medium; bool _displayAd = false; Lang _lang = Lang.system; bool _preventScreenCapture = false; @@ -33,6 +34,7 @@ class _DisplaySettingsPage extends State bool _showTranslatedTag = false; bool _useTitleJpn = false; bool _dlUseAvgSpeed = false; + ThumbnailSize _thumbnailSize = ThumbnailSize.medium; @override void initState() { super.initState(); @@ -93,6 +95,15 @@ class _DisplaySettingsPage extends State _oriDlUseAvgSpeed = false; _dlUseAvgSpeed = false; } + try { + _oriThumbnailSize = + ThumbnailSize.values[prefs.getInt("thumbnailSize") ?? 1]; + _thumbnailSize = _oriThumbnailSize; + } catch (e) { + _log.warning("Failed to get thumbnailSize:", e); + _oriThumbnailSize = ThumbnailSize.medium; + _thumbnailSize = ThumbnailSize.medium; + } } void fallback(BuildContext context) { @@ -111,6 +122,7 @@ class _DisplaySettingsPage extends State _showTranslatedTag = _oriLang.toLocale().languageCode == "zh"; _preventScreenCapture = false; _dlUseAvgSpeed = false; + _thumbnailSize = ThumbnailSize.medium; }); } @@ -179,6 +191,14 @@ class _DisplaySettingsPage extends State _oriDlUseAvgSpeed = _dlUseAvgSpeed; } } + if (_thumbnailSize != _oriThumbnailSize) { + if (!await prefs.setInt("thumbnailSize", _thumbnailSize.index)) { + re = false; + _log.warning("Failed to save thumbnailSize"); + } else { + _oriThumbnailSize = _thumbnailSize; + } + } return re; } @@ -328,6 +348,27 @@ class _DisplaySettingsPage extends State child: Text(i18n.dlUseAvgSpeed), ), ), + Container( + padding: + const EdgeInsets.symmetric(vertical: 8), + child: DropdownMenu( + initialSelection: _thumbnailSize, + onSelected: (value) { + if (value != null) { + setState(() { + _thumbnailSize = value; + }); + } + }, + label: Text(i18n.thumbnailSize), + dropdownMenuEntries: ThumbnailSize.values + .map((e) => DropdownMenuEntry( + value: e, + label: e.localText(context))) + .toList(), + leadingIcon: const Icon(Icons.photo_rounded), + width: 250, + )), Row( mainAxisAlignment: MainAxisAlignment.center, children: [