mirror of
https://github.com/venera-app/venera.git
synced 2025-09-27 15:57:25 +00:00
Compare commits
8 Commits
v1.4.6
...
d308c2ac60
Author | SHA1 | Date | |
---|---|---|---|
d308c2ac60 | |||
ac13807ef4 | |||
38a5b2b8cf | |||
3a7c8d5e38 | |||
![]() |
ce0d10aeb2 | ||
![]() |
0ac857ef9a | ||
3928f5afe7 | |||
8a61a4750b |
@@ -406,7 +406,12 @@
|
|||||||
"Disable Length Limitation": "禁用长度限制",
|
"Disable Length Limitation": "禁用长度限制",
|
||||||
"Only valid for this run": "仅对本次运行有效",
|
"Only valid for this run": "仅对本次运行有效",
|
||||||
"Logs": "日志",
|
"Logs": "日志",
|
||||||
"Export logs": "导出日志"
|
"Export logs": "导出日志",
|
||||||
|
"Clear specific reader settings for all comics": "清除所有漫画的特殊阅读设置",
|
||||||
|
"Clear specific reader settings for this comic": "清除该漫画的特殊阅读设置",
|
||||||
|
"Enable comic specific settings": "启用此漫画特定设置",
|
||||||
|
"Ignore Certificate Errors": "忽略证书错误",
|
||||||
|
"Mouse scroll speed": "鼠标滚动速度"
|
||||||
},
|
},
|
||||||
"zh_TW": {
|
"zh_TW": {
|
||||||
"Home": "首頁",
|
"Home": "首頁",
|
||||||
@@ -815,6 +820,11 @@
|
|||||||
"Disable Length Limitation": "禁用長度限制",
|
"Disable Length Limitation": "禁用長度限制",
|
||||||
"Only valid for this run": "僅對本次運行有效",
|
"Only valid for this run": "僅對本次運行有效",
|
||||||
"Logs": "日誌",
|
"Logs": "日誌",
|
||||||
"Export logs": "匯出日誌"
|
"Export logs": "匯出日誌",
|
||||||
|
"Clear specific reader settings for all comics": "清除所有漫畫的特殊閱讀設定",
|
||||||
|
"Clear specific reader settings for this comic": "清除該漫畫的特殊閱讀設定",
|
||||||
|
"Enable comic specific settings": "啟用此漫畫特定設定",
|
||||||
|
"Ignore Certificate Errors": "忽略證書錯誤",
|
||||||
|
"Mouse scroll speed": "滑鼠滾動速度"
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -117,16 +117,25 @@ class _SmoothScrollProviderState extends State<SmoothScrollProvider> {
|
|||||||
_futurePosition ??= currentLocation;
|
_futurePosition ??= currentLocation;
|
||||||
double k = (_futurePosition! - currentLocation).abs() / 1600 + 1;
|
double k = (_futurePosition! - currentLocation).abs() / 1600 + 1;
|
||||||
_futurePosition = _futurePosition! + pointerSignal.scrollDelta.dy * k;
|
_futurePosition = _futurePosition! + pointerSignal.scrollDelta.dy * k;
|
||||||
|
var beforeOffset = (_futurePosition! - currentLocation).abs();
|
||||||
_futurePosition = _futurePosition!.clamp(
|
_futurePosition = _futurePosition!.clamp(
|
||||||
_controller.position.minScrollExtent,
|
_controller.position.minScrollExtent,
|
||||||
_controller.position.maxScrollExtent,
|
_controller.position.maxScrollExtent,
|
||||||
);
|
);
|
||||||
|
var afterOffset = (_futurePosition! - currentLocation).abs();
|
||||||
if (_futurePosition == old) return;
|
if (_futurePosition == old) return;
|
||||||
var target = _futurePosition!;
|
var target = _futurePosition!;
|
||||||
|
var duration = _fastAnimationDuration;
|
||||||
|
if (afterOffset < beforeOffset) {
|
||||||
|
duration = duration * (afterOffset / beforeOffset);
|
||||||
|
if (duration < Duration(milliseconds: 10)) {
|
||||||
|
duration = Duration(milliseconds: 10);
|
||||||
|
}
|
||||||
|
}
|
||||||
_controller
|
_controller
|
||||||
.animateTo(
|
.animateTo(
|
||||||
_futurePosition!,
|
_futurePosition!,
|
||||||
duration: _fastAnimationDuration,
|
duration: duration,
|
||||||
curve: Curves.linear,
|
curve: Curves.linear,
|
||||||
)
|
)
|
||||||
.then((_) {
|
.then((_) {
|
||||||
|
@@ -26,8 +26,7 @@ class Appdata with Init {
|
|||||||
var data = jsonEncode(toJson());
|
var data = jsonEncode(toJson());
|
||||||
var file = File(FilePath.join(App.dataPath, 'appdata.json'));
|
var file = File(FilePath.join(App.dataPath, 'appdata.json'));
|
||||||
await file.writeAsString(data);
|
await file.writeAsString(data);
|
||||||
}
|
} finally {
|
||||||
finally {
|
|
||||||
_isSavingData = false;
|
_isSavingData = false;
|
||||||
}
|
}
|
||||||
if (sync) {
|
if (sync) {
|
||||||
@@ -57,10 +56,7 @@ class Appdata with Init {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
Map<String, dynamic> toJson() {
|
||||||
return {
|
return {'settings': settings._data, 'searchHistory': searchHistory};
|
||||||
'settings': settings._data,
|
|
||||||
'searchHistory': searchHistory,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Following fields are related to device-specific data and should not be synced.
|
/// Following fields are related to device-specific data and should not be synced.
|
||||||
@@ -95,8 +91,7 @@ class Appdata with Init {
|
|||||||
try {
|
try {
|
||||||
var file = File(FilePath.join(App.dataPath, 'implicitData.json'));
|
var file = File(FilePath.join(App.dataPath, 'implicitData.json'));
|
||||||
await file.writeAsString(jsonEncode(implicitData));
|
await file.writeAsString(jsonEncode(implicitData));
|
||||||
}
|
} finally {
|
||||||
finally {
|
|
||||||
_isSavingData = false;
|
_isSavingData = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -104,10 +99,7 @@ class Appdata with Init {
|
|||||||
@override
|
@override
|
||||||
Future<void> doInit() async {
|
Future<void> doInit() async {
|
||||||
var dataPath = (await getApplicationSupportDirectory()).path;
|
var dataPath = (await getApplicationSupportDirectory()).path;
|
||||||
var file = File(FilePath.join(
|
var file = File(FilePath.join(dataPath, 'appdata.json'));
|
||||||
dataPath,
|
|
||||||
'appdata.json',
|
|
||||||
));
|
|
||||||
if (!await file.exists()) {
|
if (!await file.exists()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -119,8 +111,7 @@ class Appdata with Init {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
searchHistory = List.from(json['searchHistory']);
|
searchHistory = List.from(json['searchHistory']);
|
||||||
}
|
} catch (e) {
|
||||||
catch(e) {
|
|
||||||
Log.error("Appdata", "Failed to load appdata", e);
|
Log.error("Appdata", "Failed to load appdata", e);
|
||||||
Log.info("Appdata", "Resetting appdata");
|
Log.info("Appdata", "Resetting appdata");
|
||||||
file.deleteIgnoreError();
|
file.deleteIgnoreError();
|
||||||
@@ -130,8 +121,7 @@ class Appdata with Init {
|
|||||||
if (await implicitDataFile.exists()) {
|
if (await implicitDataFile.exists()) {
|
||||||
implicitData = jsonDecode(await implicitDataFile.readAsString());
|
implicitData = jsonDecode(await implicitDataFile.readAsString());
|
||||||
}
|
}
|
||||||
}
|
} catch (e) {
|
||||||
catch (e) {
|
|
||||||
Log.error("Appdata", "Failed to load implicit data", e);
|
Log.error("Appdata", "Failed to load implicit data", e);
|
||||||
Log.info("Appdata", "Resetting implicit data");
|
Log.info("Appdata", "Resetting implicit data");
|
||||||
var implicitDataFile = File(FilePath.join(dataPath, 'implicitData.json'));
|
var implicitDataFile = File(FilePath.join(dataPath, 'implicitData.json'));
|
||||||
@@ -199,6 +189,9 @@ class Settings with ChangeNotifier {
|
|||||||
'enableDoubleTapToZoom': true,
|
'enableDoubleTapToZoom': true,
|
||||||
'reverseChapterOrder': false,
|
'reverseChapterOrder': false,
|
||||||
'showSystemStatusBar': false,
|
'showSystemStatusBar': false,
|
||||||
|
'comicSpecificSettings': <String, Map<String, dynamic>>{},
|
||||||
|
'ignoreBadCertificate': false,
|
||||||
|
'readerScrollSpeed': 1.0, // 0.5 - 3.0
|
||||||
};
|
};
|
||||||
|
|
||||||
operator [](String key) {
|
operator [](String key) {
|
||||||
@@ -212,6 +205,43 @@ class Settings with ChangeNotifier {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setEnabledComicSpecificSettings(String comicId, String sourceKey, bool enabled) {
|
||||||
|
setReaderSetting(comicId, sourceKey, "enabled", enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isComicSpecificSettingsEnabled(String? comicId, String? sourceKey) {
|
||||||
|
if (comicId == null || sourceKey == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return _data['comicSpecificSettings']["$comicId@$sourceKey"]?["enabled"] == true;
|
||||||
|
}
|
||||||
|
|
||||||
|
dynamic getReaderSetting(String comicId, String sourceKey, String key) {
|
||||||
|
if (!isComicSpecificSettingsEnabled(comicId, sourceKey)) {
|
||||||
|
return _data[key];
|
||||||
|
}
|
||||||
|
return _data['comicSpecificSettings']["$comicId@$sourceKey"]?[key] ??
|
||||||
|
_data[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
void setReaderSetting(
|
||||||
|
String comicId,
|
||||||
|
String sourceKey,
|
||||||
|
String key,
|
||||||
|
dynamic value,
|
||||||
|
) {
|
||||||
|
(_data['comicSpecificSettings'] as Map<String, dynamic>).putIfAbsent(
|
||||||
|
"$comicId@$sourceKey",
|
||||||
|
() => <String, dynamic>{},
|
||||||
|
)[key] = value;
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
void resetComicReaderSettings(String key) {
|
||||||
|
(_data['comicSpecificSettings'] as Map).remove(key);
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return _data.toString();
|
return _data.toString();
|
||||||
@@ -236,4 +266,5 @@ function processImage(image, cid, eid, page, sourceKey) {
|
|||||||
}
|
}
|
||||||
''';
|
''';
|
||||||
|
|
||||||
const _defaultSourceListUrl = "https://git.nyne.dev/nyne/venera-configs/raw/branch/main/index.json";
|
const _defaultSourceListUrl =
|
||||||
|
"https://git.nyne.dev/nyne/venera-configs/raw/branch/main/index.json";
|
||||||
|
@@ -237,6 +237,27 @@ class _MyAppState extends State<MyApp> with WidgetsBindingObserver {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
if (widget != null) {
|
if (widget != null) {
|
||||||
|
/// 如果无法检测到状态栏高度设定指定高度
|
||||||
|
/// https://github.com/flutter/flutter/issues/161086
|
||||||
|
var isPaddingCheckError =
|
||||||
|
MediaQuery.of(context).viewPadding.top <= 0 ||
|
||||||
|
MediaQuery.of(context).viewPadding.top > 50;
|
||||||
|
|
||||||
|
if (isPaddingCheckError) {
|
||||||
|
widget = MediaQuery(
|
||||||
|
data: MediaQuery.of(context).copyWith(
|
||||||
|
viewPadding: const EdgeInsets.only(
|
||||||
|
top: 15,
|
||||||
|
bottom: 15,
|
||||||
|
),
|
||||||
|
padding: const EdgeInsets.only(
|
||||||
|
top: 15,
|
||||||
|
bottom: 15,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: widget);
|
||||||
|
}
|
||||||
|
|
||||||
widget = OverlayWidget(widget);
|
widget = OverlayWidget(widget);
|
||||||
if (App.isDesktop) {
|
if (App.isDesktop) {
|
||||||
widget = Shortcuts(
|
widget = Shortcuts(
|
||||||
|
@@ -173,6 +173,7 @@ class RHttpAdapter implements HttpClientAdapter {
|
|||||||
dnsSettings: rhttp.DnsSettings.static(overrides: _getOverrides()),
|
dnsSettings: rhttp.DnsSettings.static(overrides: _getOverrides()),
|
||||||
tlsSettings: rhttp.TlsSettings(
|
tlsSettings: rhttp.TlsSettings(
|
||||||
sni: appdata.settings['sni'] != false,
|
sni: appdata.settings['sni'] != false,
|
||||||
|
verifyCertificates: appdata.settings['ignoreBadCertificate'] != true,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@@ -152,7 +152,8 @@ class _ReaderGestureDetectorState extends AutomaticGlobalState<_ReaderGestureDet
|
|||||||
|
|
||||||
bool _dragInProgress = false;
|
bool _dragInProgress = false;
|
||||||
|
|
||||||
bool get _enableDoubleTapToZoom => appdata.settings["enableDoubleTapToZoom"];
|
bool get _enableDoubleTapToZoom =>
|
||||||
|
appdata.settings.getReaderSetting(reader.cid, reader.type.sourceKey, 'enableDoubleTapToZoom');
|
||||||
|
|
||||||
void onTapUp(TapUpDetails event) {
|
void onTapUp(TapUpDetails event) {
|
||||||
if (_longPressInProgress) {
|
if (_longPressInProgress) {
|
||||||
@@ -190,7 +191,8 @@ class _ReaderGestureDetectorState extends AutomaticGlobalState<_ReaderGestureDet
|
|||||||
} else if (context.readerScaffold.isOpen) {
|
} else if (context.readerScaffold.isOpen) {
|
||||||
context.readerScaffold.openOrClose();
|
context.readerScaffold.openOrClose();
|
||||||
} else {
|
} else {
|
||||||
if (appdata.settings['enableTapToTurnPages']) {
|
if (appdata.settings.getReaderSetting(
|
||||||
|
reader.cid, reader.type.sourceKey, 'enableTapToTurnPages')) {
|
||||||
bool isLeft = false, isRight = false, isTop = false, isBottom = false;
|
bool isLeft = false, isRight = false, isTop = false, isBottom = false;
|
||||||
final width = context.width;
|
final width = context.width;
|
||||||
final height = context.height;
|
final height = context.height;
|
||||||
@@ -207,11 +209,12 @@ class _ReaderGestureDetectorState extends AutomaticGlobalState<_ReaderGestureDet
|
|||||||
isBottom = true;
|
isBottom = true;
|
||||||
}
|
}
|
||||||
bool isCenter = false;
|
bool isCenter = false;
|
||||||
var prev = context.reader.toPrevPage;
|
var prev = () => context.reader.toPrevPage();
|
||||||
var next = context.reader.toNextPage;
|
var next = () => context.reader.toNextPage();
|
||||||
if (appdata.settings['reverseTapToTurnPages']) {
|
if (appdata.settings.getReaderSetting(
|
||||||
prev = context.reader.toNextPage;
|
reader.cid, reader.type.sourceKey, 'reverseTapToTurnPages')) {
|
||||||
next = context.reader.toPrevPage;
|
prev = () => context.reader.toNextPage();
|
||||||
|
next = () => context.reader.toPrevPage();
|
||||||
}
|
}
|
||||||
switch (context.reader.mode) {
|
switch (context.reader.mode) {
|
||||||
case ReaderMode.galleryLeftToRight:
|
case ReaderMode.galleryLeftToRight:
|
||||||
|
@@ -32,10 +32,17 @@ class _ReaderImagesState extends State<_ReaderImages> {
|
|||||||
inProgress = true;
|
inProgress = true;
|
||||||
if (reader.type == ComicType.local ||
|
if (reader.type == ComicType.local ||
|
||||||
(LocalManager().isDownloaded(
|
(LocalManager().isDownloaded(
|
||||||
reader.cid, reader.type, reader.chapter, reader.widget.chapters))) {
|
reader.cid,
|
||||||
|
reader.type,
|
||||||
|
reader.chapter,
|
||||||
|
reader.widget.chapters,
|
||||||
|
))) {
|
||||||
try {
|
try {
|
||||||
var images = await LocalManager()
|
var images = await LocalManager().getImages(
|
||||||
.getImages(reader.cid, reader.type, reader.chapter);
|
reader.cid,
|
||||||
|
reader.type,
|
||||||
|
reader.chapter,
|
||||||
|
);
|
||||||
setState(() {
|
setState(() {
|
||||||
reader.images = images;
|
reader.images = images;
|
||||||
reader.isLoading = false;
|
reader.isLoading = false;
|
||||||
@@ -81,9 +88,7 @@ class _ReaderImagesState extends State<_ReaderImages> {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
if (reader.isLoading) {
|
if (reader.isLoading) {
|
||||||
load();
|
load();
|
||||||
return const Center(
|
return const Center(child: CircularProgressIndicator());
|
||||||
child: CircularProgressIndicator(),
|
|
||||||
);
|
|
||||||
} else if (error != null) {
|
} else if (error != null) {
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
@@ -104,7 +109,8 @@ class _ReaderImagesState extends State<_ReaderImages> {
|
|||||||
} else {
|
} else {
|
||||||
if (reader.mode.isGallery) {
|
if (reader.mode.isGallery) {
|
||||||
return _GalleryMode(
|
return _GalleryMode(
|
||||||
key: Key('${reader.mode.key}_${reader.imagesPerPage}'));
|
key: Key('${reader.mode.key}_${reader.imagesPerPage}'),
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
return _ContinuousMode(key: Key(reader.mode.key));
|
return _ContinuousMode(key: Key(reader.mode.key));
|
||||||
}
|
}
|
||||||
@@ -132,11 +138,15 @@ class _GalleryModeState extends State<_GalleryMode>
|
|||||||
/// [totalPages] is the total number of pages in the current chapter.
|
/// [totalPages] is the total number of pages in the current chapter.
|
||||||
/// More than one images can be displayed on one page.
|
/// More than one images can be displayed on one page.
|
||||||
int get totalPages {
|
int get totalPages {
|
||||||
if (!reader.showSingleImageOnFirstPage) {
|
if (!reader.showSingleImageOnFirstPage()) {
|
||||||
return (reader.images!.length / reader.imagesPerPage).ceil();
|
return (reader.images!.length /
|
||||||
|
reader.imagesPerPage())
|
||||||
|
.ceil();
|
||||||
} else {
|
} else {
|
||||||
return 1 +
|
return 1 +
|
||||||
((reader.images!.length - 1) / reader.imagesPerPage).ceil();
|
((reader.images!.length - 1) /
|
||||||
|
reader.imagesPerPage())
|
||||||
|
.ceil();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,19 +169,24 @@ class _GalleryModeState extends State<_GalleryMode>
|
|||||||
|
|
||||||
/// Get the range of images for the given page. [page] is 1-based.
|
/// Get the range of images for the given page. [page] is 1-based.
|
||||||
(int start, int end) getPageImagesRange(int page) {
|
(int start, int end) getPageImagesRange(int page) {
|
||||||
if (reader.showSingleImageOnFirstPage) {
|
var imagesPerPage = reader.imagesPerPage();
|
||||||
|
if (reader.showSingleImageOnFirstPage()) {
|
||||||
if (page == 1) {
|
if (page == 1) {
|
||||||
return (0, 1);
|
return (0, 1);
|
||||||
} else {
|
} else {
|
||||||
int startIndex = (page - 2) * reader.imagesPerPage + 1;
|
int startIndex = (page - 2) * imagesPerPage + 1;
|
||||||
int endIndex = math.min(
|
int endIndex = math.min(
|
||||||
startIndex + reader.imagesPerPage, reader.images!.length);
|
startIndex + imagesPerPage,
|
||||||
|
reader.images!.length,
|
||||||
|
);
|
||||||
return (startIndex, endIndex);
|
return (startIndex, endIndex);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
int startIndex = (page - 1) * reader.imagesPerPage;
|
int startIndex = (page - 1) * imagesPerPage;
|
||||||
int endIndex = math.min(
|
int endIndex = math.min(
|
||||||
startIndex + reader.imagesPerPage, reader.images!.length);
|
startIndex + imagesPerPage,
|
||||||
|
reader.images!.length,
|
||||||
|
);
|
||||||
return (startIndex, endIndex);
|
return (startIndex, endIndex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -193,9 +208,9 @@ class _GalleryModeState extends State<_GalleryMode>
|
|||||||
var (startIndex, endIndex) = getPageImagesRange(page);
|
var (startIndex, endIndex) = getPageImagesRange(page);
|
||||||
for (int i = startIndex; i < endIndex; i++) {
|
for (int i = startIndex; i < endIndex; i++) {
|
||||||
if (shouldPreCache) {
|
if (shouldPreCache) {
|
||||||
_precacheImage(i+1, context);
|
_precacheImage(i + 1, context);
|
||||||
} else {
|
} else {
|
||||||
_preDownloadImage(i+1, context);
|
_preDownloadImage(i + 1, context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -217,16 +232,12 @@ class _GalleryModeState extends State<_GalleryMode>
|
|||||||
var controller = photoViewControllers[reader.page]!;
|
var controller = photoViewControllers[reader.page]!;
|
||||||
Offset value = event.delta;
|
Offset value = event.delta;
|
||||||
if (isLongPressing) {
|
if (isLongPressing) {
|
||||||
controller.updateMultiple(
|
controller.updateMultiple(position: controller.position + value);
|
||||||
position: controller.position + value,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
child: PhotoViewGallery.builder(
|
child: PhotoViewGallery.builder(
|
||||||
backgroundDecoration: BoxDecoration(
|
backgroundDecoration: BoxDecoration(color: context.colorScheme.surface),
|
||||||
color: context.colorScheme.surface,
|
|
||||||
),
|
|
||||||
reverse: reader.mode == ReaderMode.galleryRightToLeft,
|
reverse: reader.mode == ReaderMode.galleryRightToLeft,
|
||||||
scrollDirection: reader.mode == ReaderMode.galleryTopToBottom
|
scrollDirection: reader.mode == ReaderMode.galleryTopToBottom
|
||||||
? Axis.vertical
|
? Axis.vertical
|
||||||
@@ -239,14 +250,17 @@ class _GalleryModeState extends State<_GalleryMode>
|
|||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
var (startIndex, endIndex) = getPageImagesRange(index);
|
var (startIndex, endIndex) = getPageImagesRange(index);
|
||||||
List<String> pageImages =
|
List<String> pageImages = reader.images!.sublist(
|
||||||
reader.images!.sublist(startIndex, endIndex);
|
startIndex,
|
||||||
|
endIndex,
|
||||||
|
);
|
||||||
|
|
||||||
cache(index);
|
cache(index);
|
||||||
|
|
||||||
photoViewControllers[index] ??= PhotoViewController();
|
photoViewControllers[index] ??= PhotoViewController();
|
||||||
|
|
||||||
if (reader.imagesPerPage == 1 || pageImages.length == 1) {
|
if (reader.imagesPerPage() == 1 ||
|
||||||
|
pageImages.length == 1) {
|
||||||
return PhotoViewGalleryPageOptions(
|
return PhotoViewGalleryPageOptions(
|
||||||
filterQuality: FilterQuality.medium,
|
filterQuality: FilterQuality.medium,
|
||||||
controller: photoViewControllers[index],
|
controller: photoViewControllers[index],
|
||||||
@@ -356,13 +370,16 @@ class _GalleryModeState extends State<_GalleryMode>
|
|||||||
onInit: (state) => imageStates.add(state),
|
onInit: (state) => imageStates.add(state),
|
||||||
onDispose: (state) => imageStates.remove(state),
|
onDispose: (state) => imageStates.remove(state),
|
||||||
),
|
),
|
||||||
)
|
),
|
||||||
];
|
];
|
||||||
} else {
|
} else {
|
||||||
imageWidgets = images.map((imageKey) {
|
imageWidgets = images.map((imageKey) {
|
||||||
startIndex++;
|
startIndex++;
|
||||||
ImageProvider imageProvider =
|
ImageProvider imageProvider = _createImageProviderFromKey(
|
||||||
_createImageProviderFromKey(imageKey, context, startIndex);
|
imageKey,
|
||||||
|
context,
|
||||||
|
startIndex,
|
||||||
|
);
|
||||||
return Expanded(
|
return Expanded(
|
||||||
child: ComicImage(
|
child: ComicImage(
|
||||||
image: imageProvider,
|
image: imageProvider,
|
||||||
@@ -423,10 +440,7 @@ class _GalleryModeState extends State<_GalleryMode>
|
|||||||
} else {
|
} else {
|
||||||
zoomPosition = Offset(0, 0);
|
zoomPosition = Offset(0, 0);
|
||||||
}
|
}
|
||||||
photoViewController.animateScale?.call(
|
photoViewController.animateScale?.call(target, zoomPosition);
|
||||||
target,
|
|
||||||
zoomPosition,
|
|
||||||
);
|
|
||||||
isLongPressing = true;
|
isLongPressing = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -471,14 +485,14 @@ class _GalleryModeState extends State<_GalleryMode>
|
|||||||
keyRepeatTimer = null;
|
keyRepeatTimer = null;
|
||||||
}
|
}
|
||||||
if (forward == true) {
|
if (forward == true) {
|
||||||
reader.toPage(reader.page+1);
|
reader.toPage(reader.page + 1);
|
||||||
} else if (forward == false) {
|
} else if (forward == false) {
|
||||||
reader.toPage(reader.page-1);
|
reader.toPage(reader.page - 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (event is KeyRepeatEvent && keyRepeatTimer == null) {
|
if (event is KeyRepeatEvent && keyRepeatTimer == null) {
|
||||||
keyRepeatTimer = Timer.periodic(
|
keyRepeatTimer = Timer.periodic(
|
||||||
reader.enablePageAnimation
|
reader.enablePageAnimation(reader.cid, reader.type)
|
||||||
? const Duration(milliseconds: 200)
|
? const Duration(milliseconds: 200)
|
||||||
: const Duration(milliseconds: 50),
|
: const Duration(milliseconds: 50),
|
||||||
(timer) {
|
(timer) {
|
||||||
@@ -486,9 +500,9 @@ class _GalleryModeState extends State<_GalleryMode>
|
|||||||
timer.cancel();
|
timer.cancel();
|
||||||
return;
|
return;
|
||||||
} else if (forward == true) {
|
} else if (forward == true) {
|
||||||
reader.toPage(reader.page+1);
|
reader.toPage(reader.page + 1);
|
||||||
} else if (forward == false) {
|
} else if (forward == false) {
|
||||||
reader.toPage(reader.page-1);
|
reader.toPage(reader.page - 1);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@@ -512,15 +526,15 @@ class _GalleryModeState extends State<_GalleryMode>
|
|||||||
return await File(imageKey.substring(7)).readAsBytes();
|
return await File(imageKey.substring(7)).readAsBytes();
|
||||||
} else {
|
} else {
|
||||||
return (await CacheManager().findCache(
|
return (await CacheManager().findCache(
|
||||||
"$imageKey@${context.reader.type.sourceKey}@${context.reader.cid}@${context.reader.eid}"))!
|
"$imageKey@${context.reader.type.sourceKey}@${context.reader.cid}@${context.reader.eid}",
|
||||||
.readAsBytes();
|
))!.readAsBytes();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String? getImageKeyByOffset(Offset offset) {
|
String? getImageKeyByOffset(Offset offset) {
|
||||||
String? imageKey;
|
String? imageKey;
|
||||||
if (reader.imagesPerPage == 1) {
|
if (reader.imagesPerPage() == 1) {
|
||||||
imageKey = reader.images![reader.page - 1];
|
imageKey = reader.images![reader.page - 1];
|
||||||
} else {
|
} else {
|
||||||
for (var imageState in imageStates) {
|
for (var imageState in imageStates) {
|
||||||
@@ -538,7 +552,7 @@ const Set<PointerDeviceKind> _kTouchLikeDeviceTypes = <PointerDeviceKind>{
|
|||||||
PointerDeviceKind.mouse,
|
PointerDeviceKind.mouse,
|
||||||
PointerDeviceKind.stylus,
|
PointerDeviceKind.stylus,
|
||||||
PointerDeviceKind.invertedStylus,
|
PointerDeviceKind.invertedStylus,
|
||||||
PointerDeviceKind.unknown
|
PointerDeviceKind.unknown,
|
||||||
};
|
};
|
||||||
|
|
||||||
const double _kChangeChapterOffset = 160;
|
const double _kChangeChapterOffset = 160;
|
||||||
@@ -624,27 +638,52 @@ class _ContinuousModeState extends State<_ContinuousMode>
|
|||||||
cacheImages(page);
|
cacheImages(page);
|
||||||
}
|
}
|
||||||
|
|
||||||
double? futurePosition;
|
double? _futurePosition;
|
||||||
|
|
||||||
void smoothTo(double offset) {
|
void smoothTo(double offset) {
|
||||||
futurePosition ??= scrollController.offset;
|
if (HardwareKeyboard.instance.isShiftPressed) {
|
||||||
if (futurePosition! > scrollController.position.maxScrollExtent &&
|
|
||||||
offset > 0) {
|
|
||||||
return;
|
|
||||||
} else if (futurePosition! < scrollController.position.minScrollExtent &&
|
|
||||||
offset < 0) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
futurePosition = futurePosition! + offset * 1.2;
|
var currentLocation = scrollController.position.pixels;
|
||||||
futurePosition = futurePosition!.clamp(
|
var old = _futurePosition;
|
||||||
|
_futurePosition ??= currentLocation;
|
||||||
|
double k = (_futurePosition! - currentLocation).abs() / 1600 + 1;
|
||||||
|
final customSpeed = appdata.settings.getReaderSetting(
|
||||||
|
context.reader.cid,
|
||||||
|
context.reader.type.sourceKey,
|
||||||
|
"readerScrollSpeed",
|
||||||
|
);
|
||||||
|
if (customSpeed is num) {
|
||||||
|
k *= customSpeed;
|
||||||
|
}
|
||||||
|
_futurePosition = _futurePosition! + offset * k;
|
||||||
|
var beforeOffset = (_futurePosition! - currentLocation).abs();
|
||||||
|
_futurePosition = _futurePosition!.clamp(
|
||||||
scrollController.position.minScrollExtent,
|
scrollController.position.minScrollExtent,
|
||||||
scrollController.position.maxScrollExtent,
|
scrollController.position.maxScrollExtent,
|
||||||
);
|
);
|
||||||
scrollController.animateTo(
|
var afterOffset = (_futurePosition! - currentLocation).abs();
|
||||||
futurePosition!,
|
if (_futurePosition == old) return;
|
||||||
duration: const Duration(milliseconds: 200),
|
var target = _futurePosition!;
|
||||||
|
var duration = const Duration(milliseconds: 160);
|
||||||
|
if (afterOffset < beforeOffset) {
|
||||||
|
duration = duration * (afterOffset / beforeOffset);
|
||||||
|
if (duration < Duration(milliseconds: 10)) {
|
||||||
|
duration = Duration(milliseconds: 10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
scrollController
|
||||||
|
.animateTo(
|
||||||
|
_futurePosition!,
|
||||||
|
duration: duration,
|
||||||
curve: Curves.linear,
|
curve: Curves.linear,
|
||||||
);
|
)
|
||||||
|
.then((_) {
|
||||||
|
var current = scrollController.position.pixels;
|
||||||
|
if (current == target && current == _futurePosition) {
|
||||||
|
_futurePosition = null;
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void onPointerSignal(PointerSignalEvent event) {
|
void onPointerSignal(PointerSignalEvent event) {
|
||||||
@@ -673,10 +712,12 @@ class _ContinuousModeState extends State<_ContinuousMode>
|
|||||||
void onScroll() {
|
void onScroll() {
|
||||||
if (prepareToPrevChapter) {
|
if (prepareToPrevChapter) {
|
||||||
jumpToNextChapter = false;
|
jumpToNextChapter = false;
|
||||||
jumpToPrevChapter = scrollController.offset <
|
jumpToPrevChapter =
|
||||||
|
scrollController.offset <
|
||||||
scrollController.position.minScrollExtent - _kChangeChapterOffset;
|
scrollController.position.minScrollExtent - _kChangeChapterOffset;
|
||||||
} else if (prepareToNextChapter) {
|
} else if (prepareToNextChapter) {
|
||||||
jumpToNextChapter = scrollController.offset >
|
jumpToNextChapter =
|
||||||
|
scrollController.offset >
|
||||||
scrollController.position.maxScrollExtent + _kChangeChapterOffset;
|
scrollController.position.maxScrollExtent + _kChangeChapterOffset;
|
||||||
jumpToPrevChapter = false;
|
jumpToPrevChapter = false;
|
||||||
}
|
}
|
||||||
@@ -721,8 +762,8 @@ class _ContinuousModeState extends State<_ContinuousMode>
|
|||||||
physics: isCTRLPressed || _isMouseScrolling || disableScroll
|
physics: isCTRLPressed || _isMouseScrolling || disableScroll
|
||||||
? const NeverScrollableScrollPhysics()
|
? const NeverScrollableScrollPhysics()
|
||||||
: isZoomedIn
|
: isZoomedIn
|
||||||
? const ClampingScrollPhysics()
|
? const ClampingScrollPhysics()
|
||||||
: const BouncingScrollPhysics(),
|
: const BouncingScrollPhysics(),
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
if (index == 0 || index == reader.maxPage + 1) {
|
if (index == 0 || index == reader.maxPage + 1) {
|
||||||
return const SizedBox();
|
return const SizedBox();
|
||||||
@@ -750,8 +791,10 @@ class _ContinuousModeState extends State<_ContinuousMode>
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
scrollBehavior: const MaterialScrollBehavior()
|
scrollBehavior: const MaterialScrollBehavior().copyWith(
|
||||||
.copyWith(scrollbars: false, dragDevices: _kTouchLikeDeviceTypes),
|
scrollbars: false,
|
||||||
|
dragDevices: _kTouchLikeDeviceTypes,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
widget = Stack(
|
widget = Stack(
|
||||||
@@ -769,7 +812,7 @@ class _ContinuousModeState extends State<_ContinuousMode>
|
|||||||
disableScroll = true;
|
disableScroll = true;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
futurePosition = null;
|
_futurePosition = null;
|
||||||
if (_isMouseScrolling) {
|
if (_isMouseScrolling) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_isMouseScrolling = false;
|
_isMouseScrolling = false;
|
||||||
@@ -895,20 +938,14 @@ class _ContinuousModeState extends State<_ContinuousMode>
|
|||||||
}
|
}
|
||||||
|
|
||||||
return PhotoView.customChild(
|
return PhotoView.customChild(
|
||||||
backgroundDecoration: BoxDecoration(
|
backgroundDecoration: BoxDecoration(color: context.colorScheme.surface),
|
||||||
color: context.colorScheme.surface,
|
|
||||||
),
|
|
||||||
childSize: Size(width, height),
|
childSize: Size(width, height),
|
||||||
minScale: 1.0,
|
minScale: 1.0,
|
||||||
maxScale: 2.5,
|
maxScale: 2.5,
|
||||||
strictScale: true,
|
strictScale: true,
|
||||||
controller: photoViewController,
|
controller: photoViewController,
|
||||||
onScaleUpdate: onScaleUpdate,
|
onScaleUpdate: onScaleUpdate,
|
||||||
child: SizedBox(
|
child: SizedBox(width: width, height: height, child: widget),
|
||||||
width: width,
|
|
||||||
height: height,
|
|
||||||
child: widget,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -978,10 +1015,7 @@ class _ContinuousModeState extends State<_ContinuousMode>
|
|||||||
} else {
|
} else {
|
||||||
zoomPosition = Offset(0, 0);
|
zoomPosition = Offset(0, 0);
|
||||||
}
|
}
|
||||||
photoViewController.animateScale?.call(
|
photoViewController.animateScale?.call(target, zoomPosition);
|
||||||
target,
|
|
||||||
zoomPosition,
|
|
||||||
);
|
|
||||||
onScaleUpdate(target);
|
onScaleUpdate(target);
|
||||||
isLongPressing = true;
|
isLongPressing = true;
|
||||||
}
|
}
|
||||||
@@ -1000,7 +1034,7 @@ class _ContinuousModeState extends State<_ContinuousMode>
|
|||||||
@override
|
@override
|
||||||
void toPage(int page) {
|
void toPage(int page) {
|
||||||
itemScrollController.jumpTo(index: page);
|
itemScrollController.jumpTo(index: page);
|
||||||
futurePosition = null;
|
_futurePosition = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -1069,8 +1103,8 @@ class _ContinuousModeState extends State<_ContinuousMode>
|
|||||||
return await File(imageKey.substring(7)).readAsBytes();
|
return await File(imageKey.substring(7)).readAsBytes();
|
||||||
} else {
|
} else {
|
||||||
return (await CacheManager().findCache(
|
return (await CacheManager().findCache(
|
||||||
"$imageKey@${context.reader.type.sourceKey}@${context.reader.cid}@${context.reader.eid}"))!
|
"$imageKey@${context.reader.type.sourceKey}@${context.reader.cid}@${context.reader.eid}",
|
||||||
.readAsBytes();
|
))!.readAsBytes();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1114,10 +1148,7 @@ void _precacheImage(int page, BuildContext context) {
|
|||||||
if (page <= 0 || page > context.reader.images!.length) {
|
if (page <= 0 || page > context.reader.images!.length) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
precacheImage(
|
precacheImage(_createImageProvider(page, context), context);
|
||||||
_createImageProvider(page, context),
|
|
||||||
context,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// [_preDownloadImage] is used to download the image for the given page.
|
/// [_preDownloadImage] is used to download the image for the given page.
|
||||||
@@ -1138,10 +1169,7 @@ void _preDownloadImage(int page, BuildContext context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _SwipeChangeChapterProgress extends StatefulWidget {
|
class _SwipeChangeChapterProgress extends StatefulWidget {
|
||||||
const _SwipeChangeChapterProgress({
|
const _SwipeChangeChapterProgress({this.controller, required this.isPrev});
|
||||||
this.controller,
|
|
||||||
required this.isPrev,
|
|
||||||
});
|
|
||||||
|
|
||||||
final ScrollController? controller;
|
final ScrollController? controller;
|
||||||
|
|
||||||
@@ -1258,7 +1286,12 @@ class _ProgressPainter extends CustomPainter {
|
|||||||
paint.color = color;
|
paint.color = color;
|
||||||
canvas.drawRRect(
|
canvas.drawRRect(
|
||||||
RRect.fromLTRBR(
|
RRect.fromLTRBR(
|
||||||
0, 0, size.width * value, size.height, Radius.circular(16)),
|
0,
|
||||||
|
0,
|
||||||
|
size.width * value,
|
||||||
|
size.height,
|
||||||
|
Radius.circular(16),
|
||||||
|
),
|
||||||
paint,
|
paint,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@@ -115,15 +115,17 @@ class _ReaderState extends State<Reader>
|
|||||||
if (images == null) {
|
if (images == null) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
if (!showSingleImageOnFirstPage) {
|
if (!showSingleImageOnFirstPage()) {
|
||||||
return (images!.length / imagesPerPage).ceil();
|
return (images!.length / imagesPerPage()).ceil();
|
||||||
} else {
|
} else {
|
||||||
return 1 + ((images!.length - 1) / imagesPerPage).ceil();
|
return 1 + ((images!.length - 1) / imagesPerPage()).ceil();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
ComicType get type => widget.type;
|
ComicType get type => widget.type;
|
||||||
|
|
||||||
|
@override
|
||||||
String get cid => widget.cid;
|
String get cid => widget.cid;
|
||||||
|
|
||||||
String get eid => widget.chapters?.ids.elementAtOrNull(chapter - 1) ?? '0';
|
String get eid => widget.chapters?.ids.elementAtOrNull(chapter - 1) ?? '0';
|
||||||
@@ -162,12 +164,13 @@ class _ReaderState extends State<Reader>
|
|||||||
if (widget.initialPage != null) {
|
if (widget.initialPage != null) {
|
||||||
page = widget.initialPage!;
|
page = widget.initialPage!;
|
||||||
}
|
}
|
||||||
mode = ReaderMode.fromKey(appdata.settings['readerMode']);
|
// mode = ReaderMode.fromKey(appdata.settings['readerMode']);
|
||||||
|
mode = ReaderMode.fromKey(appdata.settings.getReaderSetting(cid, type.sourceKey, 'readerMode'));
|
||||||
history = widget.history;
|
history = widget.history;
|
||||||
if (!appdata.settings['showSystemStatusBar']) {
|
if (!appdata.settings.getReaderSetting(cid, type.sourceKey, 'showSystemStatusBar')) {
|
||||||
SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersive);
|
SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersive);
|
||||||
}
|
}
|
||||||
if (appdata.settings['enableTurnPageByVolumeKey']) {
|
if (appdata.settings.getReaderSetting(cid, type.sourceKey, 'enableTurnPageByVolumeKey')) {
|
||||||
handleVolumeEvent();
|
handleVolumeEvent();
|
||||||
}
|
}
|
||||||
setImageCacheSize();
|
setImageCacheSize();
|
||||||
@@ -274,13 +277,13 @@ class _ReaderState extends State<Reader>
|
|||||||
history!.page = images?.length ?? 1;
|
history!.page = images?.length ?? 1;
|
||||||
} else {
|
} else {
|
||||||
/// Record the first image of the page
|
/// Record the first image of the page
|
||||||
if (!showSingleImageOnFirstPage || imagesPerPage == 1) {
|
if (!showSingleImageOnFirstPage() || imagesPerPage() == 1) {
|
||||||
history!.page = (page - 1) * imagesPerPage + 1;
|
history!.page = (page - 1) * imagesPerPage() + 1;
|
||||||
} else {
|
} else {
|
||||||
if (page == 1) {
|
if (page == 1) {
|
||||||
history!.page = 1;
|
history!.page = 1;
|
||||||
} else {
|
} else {
|
||||||
history!.page = (page - 2) * imagesPerPage + 2;
|
history!.page = (page - 2) * imagesPerPage() + 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -363,49 +366,51 @@ abstract mixin class _ImagePerPageHandler {
|
|||||||
|
|
||||||
ReaderMode get mode;
|
ReaderMode get mode;
|
||||||
|
|
||||||
|
String get cid;
|
||||||
|
|
||||||
|
ComicType get type;
|
||||||
|
|
||||||
void initImagesPerPage(int initialPage) {
|
void initImagesPerPage(int initialPage) {
|
||||||
_lastImagesPerPage = imagesPerPage;
|
_lastImagesPerPage = imagesPerPage();
|
||||||
_lastOrientation = isPortrait;
|
_lastOrientation = isPortrait;
|
||||||
if (imagesPerPage != 1) {
|
if (imagesPerPage() != 1) {
|
||||||
if (showSingleImageOnFirstPage) {
|
if (showSingleImageOnFirstPage()) {
|
||||||
page = ((initialPage - 1) / imagesPerPage).ceil() + 1;
|
page = ((initialPage - 1) / imagesPerPage()).ceil() + 1;
|
||||||
} else {
|
} else {
|
||||||
page = (initialPage / imagesPerPage).ceil();
|
page = (initialPage / imagesPerPage()).ceil();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool get showSingleImageOnFirstPage =>
|
bool showSingleImageOnFirstPage() =>
|
||||||
appdata.settings["showSingleImageOnFirstPage"];
|
appdata.settings.getReaderSetting(cid, type.sourceKey, 'showSingleImageOnFirstPage');
|
||||||
|
|
||||||
/// The number of images displayed on one screen
|
/// The number of images displayed on one screen
|
||||||
int get imagesPerPage {
|
int imagesPerPage() {
|
||||||
if (mode.isContinuous) return 1;
|
if (mode.isContinuous) return 1;
|
||||||
if (isPortrait) {
|
if (isPortrait) {
|
||||||
return appdata.settings['readerScreenPicNumberForPortrait'] ?? 1;
|
return appdata.settings.getReaderSetting(cid, type.sourceKey, 'readerScreenPicNumberForPortrait') ?? 1;
|
||||||
} else {
|
} else {
|
||||||
return appdata.settings['readerScreenPicNumberForLandscape'] ?? 1;
|
return appdata.settings.getReaderSetting(cid, type.sourceKey, 'readerScreenPicNumberForLandscape') ?? 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if the number of images per page has changed
|
/// Check if the number of images per page has changed
|
||||||
void _checkImagesPerPageChange() {
|
void _checkImagesPerPageChange() {
|
||||||
int currentImagesPerPage = imagesPerPage;
|
int currentImagesPerPage = imagesPerPage();
|
||||||
bool currentOrientation = isPortrait;
|
bool currentOrientation = isPortrait;
|
||||||
|
|
||||||
if (_lastImagesPerPage != currentImagesPerPage || _lastOrientation != currentOrientation) {
|
if (_lastImagesPerPage != currentImagesPerPage || _lastOrientation != currentOrientation) {
|
||||||
_adjustPageForImagesPerPageChange(
|
_adjustPageForImagesPerPageChange(_lastImagesPerPage, currentImagesPerPage);
|
||||||
_lastImagesPerPage, currentImagesPerPage);
|
|
||||||
_lastImagesPerPage = currentImagesPerPage;
|
_lastImagesPerPage = currentImagesPerPage;
|
||||||
_lastOrientation = currentOrientation;
|
_lastOrientation = currentOrientation;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Adjust the page number when the number of images per page changes
|
/// Adjust the page number when the number of images per page changes
|
||||||
void _adjustPageForImagesPerPageChange(
|
void _adjustPageForImagesPerPageChange(int oldImagesPerPage, int newImagesPerPage) {
|
||||||
int oldImagesPerPage, int newImagesPerPage) {
|
|
||||||
int previousImageIndex = 1;
|
int previousImageIndex = 1;
|
||||||
if (!showSingleImageOnFirstPage || oldImagesPerPage == 1) {
|
if (!showSingleImageOnFirstPage() || oldImagesPerPage == 1) {
|
||||||
previousImageIndex = (page - 1) * oldImagesPerPage + 1;
|
previousImageIndex = (page - 1) * oldImagesPerPage + 1;
|
||||||
} else {
|
} else {
|
||||||
if (page == 1) {
|
if (page == 1) {
|
||||||
@@ -417,7 +422,7 @@ abstract mixin class _ImagePerPageHandler {
|
|||||||
|
|
||||||
int newPage;
|
int newPage;
|
||||||
if (newImagesPerPage != 1) {
|
if (newImagesPerPage != 1) {
|
||||||
if (showSingleImageOnFirstPage) {
|
if (showSingleImageOnFirstPage()) {
|
||||||
newPage = ((previousImageIndex - 1) / newImagesPerPage).ceil() + 1;
|
newPage = ((previousImageIndex - 1) / newImagesPerPage).ceil() + 1;
|
||||||
} else {
|
} else {
|
||||||
newPage = (previousImageIndex / newImagesPerPage).ceil();
|
newPage = (previousImageIndex / newImagesPerPage).ceil();
|
||||||
@@ -493,9 +498,13 @@ abstract mixin class _ReaderLocation {
|
|||||||
|
|
||||||
bool get isLoading;
|
bool get isLoading;
|
||||||
|
|
||||||
|
String get cid;
|
||||||
|
|
||||||
|
ComicType get type;
|
||||||
|
|
||||||
void update();
|
void update();
|
||||||
|
|
||||||
bool get enablePageAnimation => appdata.settings['enablePageAnimation'];
|
bool enablePageAnimation(String cid, ComicType type) => appdata.settings.getReaderSetting(cid, type.sourceKey, 'enablePageAnimation');
|
||||||
|
|
||||||
_ImageViewController? _imageViewController;
|
_ImageViewController? _imageViewController;
|
||||||
|
|
||||||
@@ -532,7 +541,7 @@ abstract mixin class _ReaderLocation {
|
|||||||
}
|
}
|
||||||
this.page = page;
|
this.page = page;
|
||||||
update();
|
update();
|
||||||
if (enablePageAnimation) {
|
if (enablePageAnimation(cid, type)) {
|
||||||
_animationCount++;
|
_animationCount++;
|
||||||
_imageViewController!.animateToPage(page).then((_) {
|
_imageViewController!.animateToPage(page).then((_) {
|
||||||
_animationCount--;
|
_animationCount--;
|
||||||
@@ -571,12 +580,12 @@ abstract mixin class _ReaderLocation {
|
|||||||
|
|
||||||
Timer? autoPageTurningTimer;
|
Timer? autoPageTurningTimer;
|
||||||
|
|
||||||
void autoPageTurning() {
|
void autoPageTurning(String cid, ComicType type) {
|
||||||
if (autoPageTurningTimer != null) {
|
if (autoPageTurningTimer != null) {
|
||||||
autoPageTurningTimer!.cancel();
|
autoPageTurningTimer!.cancel();
|
||||||
autoPageTurningTimer = null;
|
autoPageTurningTimer = null;
|
||||||
} else {
|
} else {
|
||||||
int interval = appdata.settings['autoPageTurningInterval'];
|
int interval = appdata.settings.getReaderSetting(cid, type.sourceKey, 'autoPageTurningInterval');
|
||||||
autoPageTurningTimer = Timer.periodic(Duration(seconds: interval), (_) {
|
autoPageTurningTimer = Timer.periodic(Duration(seconds: interval), (_) {
|
||||||
if (page == maxPage) {
|
if (page == maxPage) {
|
||||||
autoPageTurningTimer!.cancel();
|
autoPageTurningTimer!.cancel();
|
||||||
|
@@ -128,9 +128,7 @@ class _ReaderScaffoldState extends State<_ReaderScaffold> {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Stack(
|
return Stack(
|
||||||
children: [
|
children: [
|
||||||
Positioned.fill(
|
Positioned.fill(child: widget.child),
|
||||||
child: widget.child,
|
|
||||||
),
|
|
||||||
if (appdata.settings['showPageNumberInReader'] == true)
|
if (appdata.settings['showPageNumberInReader'] == true)
|
||||||
buildPageInfoText(),
|
buildPageInfoText(),
|
||||||
buildStatusInfo(),
|
buildStatusInfo(),
|
||||||
@@ -168,10 +166,7 @@ class _ReaderScaffoldState extends State<_ReaderScaffold> {
|
|||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: context.colorScheme.surface.toOpacity(0.92),
|
color: context.colorScheme.surface.toOpacity(0.92),
|
||||||
border: Border(
|
border: Border(
|
||||||
bottom: BorderSide(
|
bottom: BorderSide(color: Colors.grey.toOpacity(0.5), width: 0.5),
|
||||||
color: Colors.grey.toOpacity(0.5),
|
|
||||||
width: 0.5,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
@@ -216,8 +211,9 @@ class _ReaderScaffoldState extends State<_ReaderScaffold> {
|
|||||||
try {
|
try {
|
||||||
if (context.reader.images![0].contains('file://')) {
|
if (context.reader.images![0].contains('file://')) {
|
||||||
showToast(
|
showToast(
|
||||||
message: "Local comic collection is not supported at present".tl,
|
message: "Local comic collection is not supported at present".tl,
|
||||||
context: context);
|
context: context,
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
String id = context.reader.cid;
|
String id = context.reader.cid;
|
||||||
@@ -234,8 +230,10 @@ class _ReaderScaffoldState extends State<_ReaderScaffold> {
|
|||||||
List<String> tags = context.reader.widget.tags;
|
List<String> tags = context.reader.widget.tags;
|
||||||
String author = context.reader.widget.author;
|
String author = context.reader.widget.author;
|
||||||
|
|
||||||
var epName = context.reader.widget.chapters?.titles
|
var epName =
|
||||||
.elementAtOrNull(context.reader.chapter - 1) ??
|
context.reader.widget.chapters?.titles.elementAtOrNull(
|
||||||
|
context.reader.chapter - 1,
|
||||||
|
) ??
|
||||||
"E${context.reader.chapter}";
|
"E${context.reader.chapter}";
|
||||||
var translatedTags = tags.map((e) => e.translateTagsToCN).toList();
|
var translatedTags = tags.map((e) => e.translateTagsToCN).toList();
|
||||||
|
|
||||||
@@ -248,7 +246,7 @@ class _ReaderScaffoldState extends State<_ReaderScaffold> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ImageFavoriteManager().deleteImageFavorite([
|
ImageFavoriteManager().deleteImageFavorite([
|
||||||
ImageFavorite(page, imageKey, null, eid, id, ep, sourceKey, epName)
|
ImageFavorite(page, imageKey, null, eid, id, ep, sourceKey, epName),
|
||||||
]);
|
]);
|
||||||
showToast(
|
showToast(
|
||||||
message: "Uncollected the image".tl,
|
message: "Uncollected the image".tl,
|
||||||
@@ -256,7 +254,8 @@ class _ReaderScaffoldState extends State<_ReaderScaffold> {
|
|||||||
seconds: 1,
|
seconds: 1,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
var imageFavoritesComic = ImageFavoriteManager().find(id, sourceKey) ??
|
var imageFavoritesComic =
|
||||||
|
ImageFavoriteManager().find(id, sourceKey) ??
|
||||||
ImageFavoritesComic(
|
ImageFavoritesComic(
|
||||||
id,
|
id,
|
||||||
[],
|
[],
|
||||||
@@ -270,12 +269,21 @@ class _ReaderScaffoldState extends State<_ReaderScaffold> {
|
|||||||
subTitle,
|
subTitle,
|
||||||
maxPage,
|
maxPage,
|
||||||
);
|
);
|
||||||
ImageFavorite imageFavorite =
|
ImageFavorite imageFavorite = ImageFavorite(
|
||||||
ImageFavorite(page, imageKey, null, eid, id, ep, sourceKey, epName);
|
page,
|
||||||
ImageFavoritesEp? imageFavoritesEp =
|
imageKey,
|
||||||
imageFavoritesComic.imageFavoritesEp.firstWhereOrNull((e) {
|
null,
|
||||||
return e.ep == ep;
|
eid,
|
||||||
});
|
id,
|
||||||
|
ep,
|
||||||
|
sourceKey,
|
||||||
|
epName,
|
||||||
|
);
|
||||||
|
ImageFavoritesEp? imageFavoritesEp = imageFavoritesComic
|
||||||
|
.imageFavoritesEp
|
||||||
|
.firstWhereOrNull((e) {
|
||||||
|
return e.ep == ep;
|
||||||
|
});
|
||||||
if (imageFavoritesEp == null) {
|
if (imageFavoritesEp == null) {
|
||||||
if (page != firstPage) {
|
if (page != firstPage) {
|
||||||
var copy = imageFavorite.copyWith(
|
var copy = imageFavorite.copyWith(
|
||||||
@@ -285,10 +293,20 @@ class _ReaderScaffoldState extends State<_ReaderScaffold> {
|
|||||||
);
|
);
|
||||||
// 不是第一页的话, 自动塞一个封面进去
|
// 不是第一页的话, 自动塞一个封面进去
|
||||||
imageFavoritesEp = ImageFavoritesEp(
|
imageFavoritesEp = ImageFavoritesEp(
|
||||||
eid, ep, [copy, imageFavorite], epName, maxPage);
|
eid,
|
||||||
|
ep,
|
||||||
|
[copy, imageFavorite],
|
||||||
|
epName,
|
||||||
|
maxPage,
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
imageFavoritesEp =
|
imageFavoritesEp = ImageFavoritesEp(
|
||||||
ImageFavoritesEp(eid, ep, [imageFavorite], epName, maxPage);
|
eid,
|
||||||
|
ep,
|
||||||
|
[imageFavorite],
|
||||||
|
epName,
|
||||||
|
maxPage,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
imageFavoritesComic.imageFavoritesEp.add(imageFavoritesEp);
|
imageFavoritesComic.imageFavoritesEp.add(imageFavoritesEp);
|
||||||
} else {
|
} else {
|
||||||
@@ -312,7 +330,10 @@ class _ReaderScaffoldState extends State<_ReaderScaffold> {
|
|||||||
|
|
||||||
ImageFavoriteManager().addOrUpdateOrDelete(imageFavoritesComic);
|
ImageFavoriteManager().addOrUpdateOrDelete(imageFavoritesComic);
|
||||||
showToast(
|
showToast(
|
||||||
message: "Successfully collected".tl, context: context, seconds: 1);
|
message: "Successfully collected".tl,
|
||||||
|
context: context,
|
||||||
|
seconds: 1,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
update();
|
update();
|
||||||
} catch (e, stackTrace) {
|
} catch (e, stackTrace) {
|
||||||
@@ -327,155 +348,152 @@ class _ReaderScaffoldState extends State<_ReaderScaffold> {
|
|||||||
text = "P${context.reader.page}";
|
text = "P${context.reader.page}";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final buttons = [
|
||||||
|
Tooltip(
|
||||||
|
message: "Collect the image".tl,
|
||||||
|
child: IconButton(
|
||||||
|
icon: Icon(isLiked() ? Icons.favorite : Icons.favorite_border),
|
||||||
|
onPressed: addImageFavorite,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (App.isDesktop)
|
||||||
|
Tooltip(
|
||||||
|
message: "${"Full Screen".tl}(F12)",
|
||||||
|
child: IconButton(
|
||||||
|
icon: const Icon(Icons.fullscreen),
|
||||||
|
onPressed: () {
|
||||||
|
context.reader.fullscreen();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (App.isAndroid)
|
||||||
|
Tooltip(
|
||||||
|
message: "Screen Rotation".tl,
|
||||||
|
child: IconButton(
|
||||||
|
icon: () {
|
||||||
|
if (rotation == null) {
|
||||||
|
return const Icon(Icons.screen_rotation);
|
||||||
|
} else if (rotation == false) {
|
||||||
|
return const Icon(Icons.screen_lock_portrait);
|
||||||
|
} else {
|
||||||
|
return const Icon(Icons.screen_lock_landscape);
|
||||||
|
}
|
||||||
|
}.call(),
|
||||||
|
onPressed: () {
|
||||||
|
if (rotation == null) {
|
||||||
|
setState(() {
|
||||||
|
rotation = false;
|
||||||
|
});
|
||||||
|
SystemChrome.setPreferredOrientations([
|
||||||
|
DeviceOrientation.portraitUp,
|
||||||
|
DeviceOrientation.portraitDown,
|
||||||
|
]);
|
||||||
|
} else if (rotation == false) {
|
||||||
|
setState(() {
|
||||||
|
rotation = true;
|
||||||
|
});
|
||||||
|
SystemChrome.setPreferredOrientations([
|
||||||
|
DeviceOrientation.landscapeLeft,
|
||||||
|
DeviceOrientation.landscapeRight,
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
setState(() {
|
||||||
|
rotation = null;
|
||||||
|
});
|
||||||
|
SystemChrome.setPreferredOrientations(DeviceOrientation.values);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Tooltip(
|
||||||
|
message: "Auto Page Turning".tl,
|
||||||
|
child: IconButton(
|
||||||
|
icon: context.reader.autoPageTurningTimer != null
|
||||||
|
? const Icon(Icons.timer)
|
||||||
|
: const Icon(Icons.timer_sharp),
|
||||||
|
onPressed: () {
|
||||||
|
context.reader.autoPageTurning(
|
||||||
|
context.reader.cid,
|
||||||
|
context.reader.type,
|
||||||
|
);
|
||||||
|
update();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (context.reader.widget.chapters != null)
|
||||||
|
Tooltip(
|
||||||
|
message: "Chapters".tl,
|
||||||
|
child: IconButton(
|
||||||
|
icon: const Icon(Icons.library_books),
|
||||||
|
onPressed: openChapterDrawer,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Tooltip(
|
||||||
|
message: "Save Image".tl,
|
||||||
|
child: IconButton(
|
||||||
|
icon: const Icon(Icons.download),
|
||||||
|
onPressed: saveCurrentImage,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Tooltip(
|
||||||
|
message: "Share".tl,
|
||||||
|
child: IconButton(icon: const Icon(Icons.share), onPressed: share),
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
||||||
Widget child = SizedBox(
|
Widget child = SizedBox(
|
||||||
height: kBottomBarHeight,
|
height: kBottomBarHeight,
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
const SizedBox(
|
const SizedBox(height: 8),
|
||||||
height: 8,
|
|
||||||
),
|
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
IconButton.filledTonal(
|
IconButton.filledTonal(
|
||||||
onPressed: () => !isReversed
|
onPressed: () => !isReversed
|
||||||
? context.reader.chapter > 1
|
? context.reader.chapter > 1
|
||||||
? context.reader.toPrevChapter()
|
? context.reader.toPrevChapter()
|
||||||
: context.reader.toPage(1)
|
: context.reader.toPage(1)
|
||||||
: context.reader.chapter < context.reader.maxChapter
|
: context.reader.chapter < context.reader.maxChapter
|
||||||
? context.reader.toNextChapter()
|
? context.reader.toNextChapter()
|
||||||
: context.reader.toPage(context.reader.maxPage),
|
: context.reader.toPage(context.reader.maxPage),
|
||||||
icon: const Icon(Icons.first_page),
|
icon: const Icon(Icons.first_page),
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(child: buildSlider()),
|
||||||
child: buildSlider(),
|
|
||||||
),
|
|
||||||
IconButton.filledTonal(
|
IconButton.filledTonal(
|
||||||
onPressed: () => !isReversed
|
onPressed: () => !isReversed
|
||||||
? context.reader.chapter < context.reader.maxChapter
|
? context.reader.chapter < context.reader.maxChapter
|
||||||
? context.reader.toNextChapter()
|
? context.reader.toNextChapter()
|
||||||
: context.reader.toPage(context.reader.maxPage)
|
: context.reader.toPage(context.reader.maxPage)
|
||||||
: context.reader.chapter > 1
|
: context.reader.chapter > 1
|
||||||
? context.reader.toPrevChapter()
|
? context.reader.toPrevChapter()
|
||||||
: context.reader.toPage(1),
|
: context.reader.toPage(1),
|
||||||
icon: const Icon(Icons.last_page)),
|
icon: const Icon(Icons.last_page),
|
||||||
const SizedBox(
|
|
||||||
width: 8,
|
|
||||||
),
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
Row(
|
LayoutBuilder(
|
||||||
children: [
|
builder: (context, constrains) {
|
||||||
const SizedBox(
|
return Row(
|
||||||
width: 16,
|
children: [
|
||||||
),
|
if ((constrains.maxWidth - buttons.length * 42) > 80)
|
||||||
Container(
|
Container(
|
||||||
height: 24,
|
height: 24,
|
||||||
padding: const EdgeInsets.fromLTRB(6, 2, 6, 0),
|
padding: const EdgeInsets.fromLTRB(6, 2, 6, 0),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Theme.of(context).colorScheme.tertiaryContainer,
|
color: Theme.of(context).colorScheme.tertiaryContainer,
|
||||||
borderRadius: BorderRadius.circular(8),
|
borderRadius: BorderRadius.circular(8),
|
||||||
),
|
),
|
||||||
child: Center(
|
child: Center(child: Text(text)),
|
||||||
child: Text(text),
|
).paddingLeft(16),
|
||||||
),
|
const Spacer(),
|
||||||
),
|
...buttons,
|
||||||
const Spacer(),
|
const SizedBox(width: 4),
|
||||||
Tooltip(
|
],
|
||||||
message: "Collect the image".tl,
|
);
|
||||||
child: IconButton(
|
},
|
||||||
icon:
|
),
|
||||||
Icon(isLiked() ? Icons.favorite : Icons.favorite_border),
|
|
||||||
onPressed: addImageFavorite,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
if (App.isDesktop)
|
|
||||||
Tooltip(
|
|
||||||
message: "${"Full Screen".tl}(F12)",
|
|
||||||
child: IconButton(
|
|
||||||
icon: const Icon(Icons.fullscreen),
|
|
||||||
onPressed: () {
|
|
||||||
context.reader.fullscreen();
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
if (App.isAndroid)
|
|
||||||
Tooltip(
|
|
||||||
message: "Screen Rotation".tl,
|
|
||||||
child: IconButton(
|
|
||||||
icon: () {
|
|
||||||
if (rotation == null) {
|
|
||||||
return const Icon(Icons.screen_rotation);
|
|
||||||
} else if (rotation == false) {
|
|
||||||
return const Icon(Icons.screen_lock_portrait);
|
|
||||||
} else {
|
|
||||||
return const Icon(Icons.screen_lock_landscape);
|
|
||||||
}
|
|
||||||
}.call(),
|
|
||||||
onPressed: () {
|
|
||||||
if (rotation == null) {
|
|
||||||
setState(() {
|
|
||||||
rotation = false;
|
|
||||||
});
|
|
||||||
SystemChrome.setPreferredOrientations([
|
|
||||||
DeviceOrientation.portraitUp,
|
|
||||||
DeviceOrientation.portraitDown,
|
|
||||||
]);
|
|
||||||
} else if (rotation == false) {
|
|
||||||
setState(() {
|
|
||||||
rotation = true;
|
|
||||||
});
|
|
||||||
SystemChrome.setPreferredOrientations([
|
|
||||||
DeviceOrientation.landscapeLeft,
|
|
||||||
DeviceOrientation.landscapeRight
|
|
||||||
]);
|
|
||||||
} else {
|
|
||||||
setState(() {
|
|
||||||
rotation = null;
|
|
||||||
});
|
|
||||||
SystemChrome.setPreferredOrientations(
|
|
||||||
DeviceOrientation.values);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Tooltip(
|
|
||||||
message: "Auto Page Turning".tl,
|
|
||||||
child: IconButton(
|
|
||||||
icon: context.reader.autoPageTurningTimer != null
|
|
||||||
? const Icon(Icons.timer)
|
|
||||||
: const Icon(Icons.timer_sharp),
|
|
||||||
onPressed: () {
|
|
||||||
context.reader.autoPageTurning();
|
|
||||||
update();
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
if (context.reader.widget.chapters != null)
|
|
||||||
Tooltip(
|
|
||||||
message: "Chapters".tl,
|
|
||||||
child: IconButton(
|
|
||||||
icon: const Icon(Icons.library_books),
|
|
||||||
onPressed: openChapterDrawer,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Tooltip(
|
|
||||||
message: "Save Image".tl,
|
|
||||||
child: IconButton(
|
|
||||||
icon: const Icon(Icons.download),
|
|
||||||
onPressed: saveCurrentImage,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Tooltip(
|
|
||||||
message: "Share".tl,
|
|
||||||
child: IconButton(
|
|
||||||
icon: const Icon(Icons.share),
|
|
||||||
onPressed: share,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 4)
|
|
||||||
],
|
|
||||||
)
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -506,8 +524,9 @@ class _ReaderScaffoldState extends State<_ReaderScaffold> {
|
|||||||
focusNode: sliderFocus,
|
focusNode: sliderFocus,
|
||||||
value: context.reader.page.toDouble(),
|
value: context.reader.page.toDouble(),
|
||||||
min: 1,
|
min: 1,
|
||||||
max:
|
max: context.reader.maxPage
|
||||||
context.reader.maxPage.clamp(context.reader.page, 1 << 16).toDouble(),
|
.clamp(context.reader.page, 1 << 16)
|
||||||
|
.toDouble(),
|
||||||
reversed: isReversed,
|
reversed: isReversed,
|
||||||
divisions: (context.reader.maxPage - 1).clamp(2, 1 << 16),
|
divisions: (context.reader.maxPage - 1).clamp(2, 1 << 16),
|
||||||
onChanged: (i) {
|
onChanged: (i) {
|
||||||
@@ -517,8 +536,10 @@ class _ReaderScaffoldState extends State<_ReaderScaffold> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget buildPageInfoText() {
|
Widget buildPageInfoText() {
|
||||||
var epName = context.reader.widget.chapters?.titles
|
var epName =
|
||||||
.elementAtOrNull(context.reader.chapter - 1) ??
|
context.reader.widget.chapters?.titles.elementAtOrNull(
|
||||||
|
context.reader.chapter - 1,
|
||||||
|
) ??
|
||||||
"E${context.reader.chapter}";
|
"E${context.reader.chapter}";
|
||||||
if (epName.length > 8) {
|
if (epName.length > 8) {
|
||||||
epName = "${epName.substring(0, 8)}...";
|
epName = "${epName.substring(0, 8)}...";
|
||||||
@@ -594,23 +615,31 @@ class _ReaderScaffoldState extends State<_ReaderScaffold> {
|
|||||||
}
|
}
|
||||||
var fileType = detectFileType(data);
|
var fileType = detectFileType(data);
|
||||||
var filename = "${context.reader.page}${fileType.ext}";
|
var filename = "${context.reader.page}${fileType.ext}";
|
||||||
Share.shareFile(
|
Share.shareFile(data: data, filename: filename, mime: fileType.mime);
|
||||||
data: data,
|
|
||||||
filename: filename,
|
|
||||||
mime: fileType.mime,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void openSetting() {
|
void openSetting() {
|
||||||
showSideBar(
|
showSideBar(
|
||||||
context,
|
context,
|
||||||
ReaderSettings(
|
ReaderSettings(
|
||||||
|
comicId: context.reader.cid,
|
||||||
|
comicSource: context.reader.type.sourceKey,
|
||||||
onChanged: (key) {
|
onChanged: (key) {
|
||||||
if (key == "readerMode") {
|
if (key == "readerMode") {
|
||||||
context.reader.mode = ReaderMode.fromKey(appdata.settings[key]);
|
context.reader.mode = ReaderMode.fromKey(
|
||||||
|
appdata.settings.getReaderSetting(
|
||||||
|
context.reader.cid,
|
||||||
|
context.reader.type.sourceKey,
|
||||||
|
key,
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if (key == "enableTurnPageByVolumeKey") {
|
if (key == "enableTurnPageByVolumeKey") {
|
||||||
if (appdata.settings[key]) {
|
if (appdata.settings.getReaderSetting(
|
||||||
|
context.reader.cid,
|
||||||
|
context.reader.type.sourceKey,
|
||||||
|
key,
|
||||||
|
)) {
|
||||||
context.reader.handleVolumeEvent();
|
context.reader.handleVolumeEvent();
|
||||||
} else {
|
} else {
|
||||||
context.reader.stopVolumeEvent();
|
context.reader.stopVolumeEvent();
|
||||||
@@ -716,8 +745,8 @@ class _ReaderScaffoldState extends State<_ReaderScaffold> {
|
|||||||
return await File(imageKey.substring(7)).readAsBytes();
|
return await File(imageKey.substring(7)).readAsBytes();
|
||||||
} else {
|
} else {
|
||||||
return (await CacheManager().findCache(
|
return (await CacheManager().findCache(
|
||||||
"$imageKey@${context.reader.type.sourceKey}@${context.reader.cid}@${context.reader.eid}"))!
|
"$imageKey@${context.reader.type.sourceKey}@${context.reader.cid}@${context.reader.eid}",
|
||||||
.readAsBytes();
|
))!.readAsBytes();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -733,14 +762,17 @@ class _ReaderScaffoldState extends State<_ReaderScaffold> {
|
|||||||
entry = OverlayEntry(
|
entry = OverlayEntry(
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
return Positioned.fill(
|
return Positioned.fill(
|
||||||
child: _SelectImageOverlayContent(onTap: (offset) {
|
child: _SelectImageOverlayContent(
|
||||||
completer.complete(offset);
|
onTap: (offset) {
|
||||||
entry!.remove();
|
completer.complete(offset);
|
||||||
}, onDispose: () {
|
entry!.remove();
|
||||||
if (!completer.isCompleted) {
|
},
|
||||||
completer.complete(null);
|
onDispose: () {
|
||||||
}
|
if (!completer.isCompleted) {
|
||||||
}),
|
completer.complete(null);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@@ -840,20 +872,17 @@ class _BatteryWidgetState extends State<_BatteryWidget> {
|
|||||||
size: 16,
|
size: 16,
|
||||||
color: batteryColor,
|
color: batteryColor,
|
||||||
// Stroke
|
// Stroke
|
||||||
shadows: List.generate(
|
shadows: List.generate(9, (index) {
|
||||||
9,
|
if (index == 4) {
|
||||||
(index) {
|
return null;
|
||||||
if (index == 4) {
|
}
|
||||||
return null;
|
double offsetX = (index % 3 - 1) * 0.8;
|
||||||
}
|
double offsetY = ((index / 3).floor() - 1) * 0.8;
|
||||||
double offsetX = (index % 3 - 1) * 0.8;
|
return Shadow(
|
||||||
double offsetY = ((index / 3).floor() - 1) * 0.8;
|
color: context.colorScheme.onInverseSurface,
|
||||||
return Shadow(
|
offset: Offset(offsetX, offsetY),
|
||||||
color: context.colorScheme.onInverseSurface,
|
);
|
||||||
offset: Offset(offsetX, offsetY),
|
}).whereType<Shadow>().toList(),
|
||||||
);
|
|
||||||
},
|
|
||||||
).whereType<Shadow>().toList(),
|
|
||||||
),
|
),
|
||||||
Stack(
|
Stack(
|
||||||
children: [
|
children: [
|
||||||
@@ -940,10 +969,12 @@ class _SelectImageOverlayContent extends StatefulWidget {
|
|||||||
final void Function() onDispose;
|
final void Function() onDispose;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<_SelectImageOverlayContent> createState() => _SelectImageOverlayContentState();
|
State<_SelectImageOverlayContent> createState() =>
|
||||||
|
_SelectImageOverlayContentState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _SelectImageOverlayContentState extends State<_SelectImageOverlayContent> {
|
class _SelectImageOverlayContentState
|
||||||
|
extends State<_SelectImageOverlayContent> {
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
widget.onDispose();
|
widget.onDispose();
|
||||||
@@ -960,19 +991,14 @@ class _SelectImageOverlayContentState extends State<_SelectImageOverlayContent>
|
|||||||
child: Container(
|
child: Container(
|
||||||
color: Colors.black.withAlpha(50),
|
color: Colors.black.withAlpha(50),
|
||||||
child: Align(
|
child: Align(
|
||||||
alignment: Alignment(
|
alignment: Alignment(0, -0.8),
|
||||||
0,
|
|
||||||
-0.8,
|
|
||||||
),
|
|
||||||
child: Container(
|
child: Container(
|
||||||
width: 232,
|
width: 232,
|
||||||
height: 42,
|
height: 42,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: context.colorScheme.surface,
|
color: context.colorScheme.surface,
|
||||||
borderRadius: BorderRadius.circular(8),
|
borderRadius: BorderRadius.circular(8),
|
||||||
border: Border.all(
|
border: Border.all(color: context.colorScheme.outlineVariant),
|
||||||
color: context.colorScheme.outlineVariant,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
|
@@ -31,6 +31,10 @@ class DebugPageState extends State<DebugPage> {
|
|||||||
},
|
},
|
||||||
actionTitle: 'Open'.tl,
|
actionTitle: 'Open'.tl,
|
||||||
).toSliver(),
|
).toSliver(),
|
||||||
|
_SwitchSetting(
|
||||||
|
title: "Ignore Certificate Errors".tl,
|
||||||
|
settingKey: "ignoreBadCertificate",
|
||||||
|
).toSliver(),
|
||||||
SliverToBoxAdapter(
|
SliverToBoxAdapter(
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
|
@@ -1,9 +1,16 @@
|
|||||||
part of 'settings_page.dart';
|
part of 'settings_page.dart';
|
||||||
|
|
||||||
class ReaderSettings extends StatefulWidget {
|
class ReaderSettings extends StatefulWidget {
|
||||||
const ReaderSettings({super.key, this.onChanged});
|
const ReaderSettings({
|
||||||
|
super.key,
|
||||||
|
this.onChanged,
|
||||||
|
this.comicId,
|
||||||
|
this.comicSource,
|
||||||
|
});
|
||||||
|
|
||||||
final void Function(String key)? onChanged;
|
final void Function(String key)? onChanged;
|
||||||
|
final String? comicId;
|
||||||
|
final String? comicSource;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<ReaderSettings> createState() => _ReaderSettingsState();
|
State<ReaderSettings> createState() => _ReaderSettingsState();
|
||||||
@@ -12,15 +19,57 @@ class ReaderSettings extends StatefulWidget {
|
|||||||
class _ReaderSettingsState extends State<ReaderSettings> {
|
class _ReaderSettingsState extends State<ReaderSettings> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final comicId = widget.comicId;
|
||||||
|
final sourceKey = widget.comicSource;
|
||||||
|
final key = "$comicId@$sourceKey";
|
||||||
|
|
||||||
|
bool isEnabledSpecificSettings =
|
||||||
|
comicId != null &&
|
||||||
|
appdata.settings.isComicSpecificSettingsEnabled(comicId, sourceKey);
|
||||||
|
|
||||||
return SmoothCustomScrollView(
|
return SmoothCustomScrollView(
|
||||||
slivers: [
|
slivers: [
|
||||||
SliverAppbar(title: Text("Reading".tl)),
|
SliverAppbar(title: Text("Reading".tl)),
|
||||||
|
if (comicId != null && sourceKey != null)
|
||||||
|
SliverMainAxisGroup(
|
||||||
|
slivers: [
|
||||||
|
SwitchListTile(
|
||||||
|
title: Text("Enable comic specific settings".tl),
|
||||||
|
value: isEnabledSpecificSettings,
|
||||||
|
onChanged: (b) {
|
||||||
|
setState(() {
|
||||||
|
appdata.settings.setEnabledComicSpecificSettings(
|
||||||
|
comicId,
|
||||||
|
sourceKey,
|
||||||
|
b,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
).toSliver(),
|
||||||
|
if (isEnabledSpecificSettings)
|
||||||
|
Center(
|
||||||
|
child: TextButton(
|
||||||
|
onPressed: () {
|
||||||
|
setState(() {
|
||||||
|
appdata.settings.resetComicReaderSettings(key);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
child: Text(
|
||||||
|
"Clear specific reader settings for this comic".tl,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
).toSliver(),
|
||||||
|
Divider().toSliver(),
|
||||||
|
],
|
||||||
|
),
|
||||||
_SwitchSetting(
|
_SwitchSetting(
|
||||||
title: "Tap to turn Pages".tl,
|
title: "Tap to turn Pages".tl,
|
||||||
settingKey: "enableTapToTurnPages",
|
settingKey: "enableTapToTurnPages",
|
||||||
onChanged: () {
|
onChanged: () {
|
||||||
widget.onChanged?.call("enableTapToTurnPages");
|
widget.onChanged?.call("enableTapToTurnPages");
|
||||||
},
|
},
|
||||||
|
comicId: isEnabledSpecificSettings ? widget.comicId : null,
|
||||||
|
comicSource: isEnabledSpecificSettings ? widget.comicSource : null,
|
||||||
).toSliver(),
|
).toSliver(),
|
||||||
_SwitchSetting(
|
_SwitchSetting(
|
||||||
title: "Reverse tap to turn Pages".tl,
|
title: "Reverse tap to turn Pages".tl,
|
||||||
@@ -28,6 +77,8 @@ class _ReaderSettingsState extends State<ReaderSettings> {
|
|||||||
onChanged: () {
|
onChanged: () {
|
||||||
widget.onChanged?.call("reverseTapToTurnPages");
|
widget.onChanged?.call("reverseTapToTurnPages");
|
||||||
},
|
},
|
||||||
|
comicId: isEnabledSpecificSettings ? widget.comicId : null,
|
||||||
|
comicSource: isEnabledSpecificSettings ? widget.comicSource : null,
|
||||||
).toSliver(),
|
).toSliver(),
|
||||||
_SwitchSetting(
|
_SwitchSetting(
|
||||||
title: "Page animation".tl,
|
title: "Page animation".tl,
|
||||||
@@ -35,6 +86,8 @@ class _ReaderSettingsState extends State<ReaderSettings> {
|
|||||||
onChanged: () {
|
onChanged: () {
|
||||||
widget.onChanged?.call("enablePageAnimation");
|
widget.onChanged?.call("enablePageAnimation");
|
||||||
},
|
},
|
||||||
|
comicId: isEnabledSpecificSettings ? widget.comicId : null,
|
||||||
|
comicSource: isEnabledSpecificSettings ? widget.comicSource : null,
|
||||||
).toSliver(),
|
).toSliver(),
|
||||||
SelectSetting(
|
SelectSetting(
|
||||||
title: "Reading mode".tl,
|
title: "Reading mode".tl,
|
||||||
@@ -58,6 +111,8 @@ class _ReaderSettingsState extends State<ReaderSettings> {
|
|||||||
}
|
}
|
||||||
widget.onChanged?.call("readerMode");
|
widget.onChanged?.call("readerMode");
|
||||||
},
|
},
|
||||||
|
comicId: isEnabledSpecificSettings ? widget.comicId : null,
|
||||||
|
comicSource: isEnabledSpecificSettings ? widget.comicSource : null,
|
||||||
).toSliver(),
|
).toSliver(),
|
||||||
_SliderSetting(
|
_SliderSetting(
|
||||||
title: "Auto page turning interval".tl,
|
title: "Auto page turning interval".tl,
|
||||||
@@ -69,6 +124,8 @@ class _ReaderSettingsState extends State<ReaderSettings> {
|
|||||||
setState(() {});
|
setState(() {});
|
||||||
widget.onChanged?.call("autoPageTurningInterval");
|
widget.onChanged?.call("autoPageTurningInterval");
|
||||||
},
|
},
|
||||||
|
comicId: isEnabledSpecificSettings ? widget.comicId : null,
|
||||||
|
comicSource: isEnabledSpecificSettings ? widget.comicSource : null,
|
||||||
).toSliver(),
|
).toSliver(),
|
||||||
SliverAnimatedVisibility(
|
SliverAnimatedVisibility(
|
||||||
visible: appdata.settings['readerMode']!.startsWith('gallery'),
|
visible: appdata.settings['readerMode']!.startsWith('gallery'),
|
||||||
@@ -84,6 +141,8 @@ class _ReaderSettingsState extends State<ReaderSettings> {
|
|||||||
setState(() {});
|
setState(() {});
|
||||||
widget.onChanged?.call("readerScreenPicNumberForLandscape");
|
widget.onChanged?.call("readerScreenPicNumberForLandscape");
|
||||||
},
|
},
|
||||||
|
comicId: isEnabledSpecificSettings ? widget.comicId : null,
|
||||||
|
comicSource: isEnabledSpecificSettings ? widget.comicSource : null,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
SliverAnimatedVisibility(
|
SliverAnimatedVisibility(
|
||||||
@@ -99,10 +158,13 @@ class _ReaderSettingsState extends State<ReaderSettings> {
|
|||||||
onChanged: () {
|
onChanged: () {
|
||||||
widget.onChanged?.call("readerScreenPicNumberForPortrait");
|
widget.onChanged?.call("readerScreenPicNumberForPortrait");
|
||||||
},
|
},
|
||||||
|
comicId: isEnabledSpecificSettings ? widget.comicId : null,
|
||||||
|
comicSource: isEnabledSpecificSettings ? widget.comicSource : null,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
SliverAnimatedVisibility(
|
SliverAnimatedVisibility(
|
||||||
visible: appdata.settings['readerMode']!.startsWith('gallery') &&
|
visible:
|
||||||
|
appdata.settings['readerMode']!.startsWith('gallery') &&
|
||||||
(appdata.settings['readerScreenPicNumberForLandscape'] > 1 ||
|
(appdata.settings['readerScreenPicNumberForLandscape'] > 1 ||
|
||||||
appdata.settings['readerScreenPicNumberForPortrait'] > 1),
|
appdata.settings['readerScreenPicNumberForPortrait'] > 1),
|
||||||
child: _SwitchSetting(
|
child: _SwitchSetting(
|
||||||
@@ -111,6 +173,23 @@ class _ReaderSettingsState extends State<ReaderSettings> {
|
|||||||
onChanged: () {
|
onChanged: () {
|
||||||
widget.onChanged?.call("showSingleImageOnFirstPage");
|
widget.onChanged?.call("showSingleImageOnFirstPage");
|
||||||
},
|
},
|
||||||
|
comicId: isEnabledSpecificSettings ? widget.comicId : null,
|
||||||
|
comicSource: isEnabledSpecificSettings ? widget.comicSource : null,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SliverAnimatedVisibility(
|
||||||
|
visible: appdata.settings['readerMode']!.startsWith('continuous'),
|
||||||
|
child: _SliderSetting(
|
||||||
|
title: "Mouse scroll speed".tl,
|
||||||
|
settingsIndex: "readerScrollSpeed",
|
||||||
|
interval: 0.1,
|
||||||
|
min: 0.5,
|
||||||
|
max: 3,
|
||||||
|
onChanged: () {
|
||||||
|
widget.onChanged?.call("readerScrollSpeed");
|
||||||
|
},
|
||||||
|
comicId: isEnabledSpecificSettings ? widget.comicId : null,
|
||||||
|
comicSource: isEnabledSpecificSettings ? widget.comicSource : null,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
_SwitchSetting(
|
_SwitchSetting(
|
||||||
@@ -120,6 +199,8 @@ class _ReaderSettingsState extends State<ReaderSettings> {
|
|||||||
setState(() {});
|
setState(() {});
|
||||||
widget.onChanged?.call('enableDoubleTapToZoom');
|
widget.onChanged?.call('enableDoubleTapToZoom');
|
||||||
},
|
},
|
||||||
|
comicId: isEnabledSpecificSettings ? widget.comicId : null,
|
||||||
|
comicSource: isEnabledSpecificSettings ? widget.comicSource : null,
|
||||||
).toSliver(),
|
).toSliver(),
|
||||||
_SwitchSetting(
|
_SwitchSetting(
|
||||||
title: 'Long press to zoom'.tl,
|
title: 'Long press to zoom'.tl,
|
||||||
@@ -128,6 +209,8 @@ class _ReaderSettingsState extends State<ReaderSettings> {
|
|||||||
setState(() {});
|
setState(() {});
|
||||||
widget.onChanged?.call('enableLongPressToZoom');
|
widget.onChanged?.call('enableLongPressToZoom');
|
||||||
},
|
},
|
||||||
|
comicId: isEnabledSpecificSettings ? widget.comicId : null,
|
||||||
|
comicSource: isEnabledSpecificSettings ? widget.comicSource : null,
|
||||||
).toSliver(),
|
).toSliver(),
|
||||||
SliverAnimatedVisibility(
|
SliverAnimatedVisibility(
|
||||||
visible: appdata.settings['enableLongPressToZoom'] == true,
|
visible: appdata.settings['enableLongPressToZoom'] == true,
|
||||||
@@ -138,6 +221,8 @@ class _ReaderSettingsState extends State<ReaderSettings> {
|
|||||||
"press": "Press position".tl,
|
"press": "Press position".tl,
|
||||||
"center": "Screen center".tl,
|
"center": "Screen center".tl,
|
||||||
},
|
},
|
||||||
|
comicId: isEnabledSpecificSettings ? widget.comicId : null,
|
||||||
|
comicSource: isEnabledSpecificSettings ? widget.comicSource : null,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
_SwitchSetting(
|
_SwitchSetting(
|
||||||
@@ -147,6 +232,8 @@ class _ReaderSettingsState extends State<ReaderSettings> {
|
|||||||
onChanged: () {
|
onChanged: () {
|
||||||
widget.onChanged?.call('limitImageWidth');
|
widget.onChanged?.call('limitImageWidth');
|
||||||
},
|
},
|
||||||
|
comicId: isEnabledSpecificSettings ? widget.comicId : null,
|
||||||
|
comicSource: isEnabledSpecificSettings ? widget.comicSource : null,
|
||||||
).toSliver(),
|
).toSliver(),
|
||||||
if (App.isAndroid)
|
if (App.isAndroid)
|
||||||
_SwitchSetting(
|
_SwitchSetting(
|
||||||
@@ -155,6 +242,8 @@ class _ReaderSettingsState extends State<ReaderSettings> {
|
|||||||
onChanged: () {
|
onChanged: () {
|
||||||
widget.onChanged?.call('enableTurnPageByVolumeKey');
|
widget.onChanged?.call('enableTurnPageByVolumeKey');
|
||||||
},
|
},
|
||||||
|
comicId: isEnabledSpecificSettings ? widget.comicId : null,
|
||||||
|
comicSource: isEnabledSpecificSettings ? widget.comicSource : null,
|
||||||
).toSliver(),
|
).toSliver(),
|
||||||
_SwitchSetting(
|
_SwitchSetting(
|
||||||
title: "Display time & battery info in reader".tl,
|
title: "Display time & battery info in reader".tl,
|
||||||
@@ -162,6 +251,8 @@ class _ReaderSettingsState extends State<ReaderSettings> {
|
|||||||
onChanged: () {
|
onChanged: () {
|
||||||
widget.onChanged?.call("enableClockAndBatteryInfoInReader");
|
widget.onChanged?.call("enableClockAndBatteryInfoInReader");
|
||||||
},
|
},
|
||||||
|
comicId: isEnabledSpecificSettings ? widget.comicId : null,
|
||||||
|
comicSource: isEnabledSpecificSettings ? widget.comicSource : null,
|
||||||
).toSliver(),
|
).toSliver(),
|
||||||
_SwitchSetting(
|
_SwitchSetting(
|
||||||
title: "Show system status bar".tl,
|
title: "Show system status bar".tl,
|
||||||
@@ -169,6 +260,8 @@ class _ReaderSettingsState extends State<ReaderSettings> {
|
|||||||
onChanged: () {
|
onChanged: () {
|
||||||
widget.onChanged?.call("showSystemStatusBar");
|
widget.onChanged?.call("showSystemStatusBar");
|
||||||
},
|
},
|
||||||
|
comicId: isEnabledSpecificSettings ? widget.comicId : null,
|
||||||
|
comicSource: isEnabledSpecificSettings ? widget.comicSource : null,
|
||||||
).toSliver(),
|
).toSliver(),
|
||||||
SelectSetting(
|
SelectSetting(
|
||||||
title: "Quick collect image".tl,
|
title: "Quick collect image".tl,
|
||||||
@@ -184,6 +277,8 @@ class _ReaderSettingsState extends State<ReaderSettings> {
|
|||||||
help:
|
help:
|
||||||
"On the image browsing page, you can quickly collect images by sliding horizontally or vertically according to your reading mode"
|
"On the image browsing page, you can quickly collect images by sliding horizontally or vertically according to your reading mode"
|
||||||
.tl,
|
.tl,
|
||||||
|
comicId: isEnabledSpecificSettings ? widget.comicId : null,
|
||||||
|
comicSource: isEnabledSpecificSettings ? widget.comicSource : null,
|
||||||
).toSliver(),
|
).toSliver(),
|
||||||
_CallbackSetting(
|
_CallbackSetting(
|
||||||
title: "Custom Image Processing".tl,
|
title: "Custom Image Processing".tl,
|
||||||
@@ -196,6 +291,8 @@ class _ReaderSettingsState extends State<ReaderSettings> {
|
|||||||
interval: 1,
|
interval: 1,
|
||||||
min: 1,
|
min: 1,
|
||||||
max: 16,
|
max: 16,
|
||||||
|
comicId: isEnabledSpecificSettings ? widget.comicId : null,
|
||||||
|
comicSource: isEnabledSpecificSettings ? widget.comicSource : null,
|
||||||
).toSliver(),
|
).toSliver(),
|
||||||
_SwitchSetting(
|
_SwitchSetting(
|
||||||
title: "Show Page Number".tl,
|
title: "Show Page Number".tl,
|
||||||
@@ -203,6 +300,8 @@ class _ReaderSettingsState extends State<ReaderSettings> {
|
|||||||
onChanged: () {
|
onChanged: () {
|
||||||
widget.onChanged?.call("showPageNumberInReader");
|
widget.onChanged?.call("showPageNumberInReader");
|
||||||
},
|
},
|
||||||
|
comicId: isEnabledSpecificSettings ? widget.comicId : null,
|
||||||
|
comicSource: isEnabledSpecificSettings ? widget.comicSource : null,
|
||||||
).toSliver(),
|
).toSliver(),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
@@ -248,7 +347,7 @@ class __CustomImageProcessingState extends State<_CustomImageProcessing> {
|
|||||||
setState(() {});
|
setState(() {});
|
||||||
},
|
},
|
||||||
child: Text("Reset".tl),
|
child: Text("Reset".tl),
|
||||||
)
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
body: Column(
|
body: Column(
|
||||||
@@ -274,7 +373,7 @@ class __CustomImageProcessingState extends State<_CustomImageProcessing> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@@ -6,6 +6,8 @@ class _SwitchSetting extends StatefulWidget {
|
|||||||
required this.settingKey,
|
required this.settingKey,
|
||||||
this.onChanged,
|
this.onChanged,
|
||||||
this.subtitle,
|
this.subtitle,
|
||||||
|
this.comicId,
|
||||||
|
this.comicSource,
|
||||||
});
|
});
|
||||||
|
|
||||||
final String title;
|
final String title;
|
||||||
@@ -16,6 +18,10 @@ class _SwitchSetting extends StatefulWidget {
|
|||||||
|
|
||||||
final String? subtitle;
|
final String? subtitle;
|
||||||
|
|
||||||
|
final String? comicId;
|
||||||
|
|
||||||
|
final String? comicSource;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<_SwitchSetting> createState() => _SwitchSettingState();
|
State<_SwitchSetting> createState() => _SwitchSettingState();
|
||||||
}
|
}
|
||||||
@@ -23,16 +29,33 @@ class _SwitchSetting extends StatefulWidget {
|
|||||||
class _SwitchSettingState extends State<_SwitchSetting> {
|
class _SwitchSettingState extends State<_SwitchSetting> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
assert(appdata.settings[widget.settingKey] is bool);
|
var value = widget.comicId == null
|
||||||
|
? appdata.settings[widget.settingKey]
|
||||||
|
: appdata.settings.getReaderSetting(
|
||||||
|
widget.comicId!,
|
||||||
|
widget.comicSource!,
|
||||||
|
widget.settingKey,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert(value is bool);
|
||||||
|
|
||||||
return ListTile(
|
return ListTile(
|
||||||
title: Text(widget.title),
|
title: Text(widget.title),
|
||||||
subtitle: widget.subtitle == null ? null : Text(widget.subtitle!),
|
subtitle: widget.subtitle == null ? null : Text(widget.subtitle!),
|
||||||
trailing: Switch(
|
trailing: Switch(
|
||||||
value: appdata.settings[widget.settingKey],
|
value: value,
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
setState(() {
|
setState(() {
|
||||||
appdata.settings[widget.settingKey] = value;
|
if (widget.comicId == null) {
|
||||||
|
appdata.settings[widget.settingKey] = value;
|
||||||
|
} else {
|
||||||
|
appdata.settings.setReaderSetting(
|
||||||
|
widget.comicId!,
|
||||||
|
widget.comicSource!,
|
||||||
|
widget.settingKey,
|
||||||
|
value,
|
||||||
|
);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
appdata.saveData().then((_) {
|
appdata.saveData().then((_) {
|
||||||
widget.onChanged?.call();
|
widget.onChanged?.call();
|
||||||
@@ -51,6 +74,8 @@ class SelectSetting extends StatelessWidget {
|
|||||||
required this.optionTranslation,
|
required this.optionTranslation,
|
||||||
this.onChanged,
|
this.onChanged,
|
||||||
this.help,
|
this.help,
|
||||||
|
this.comicId,
|
||||||
|
this.comicSource,
|
||||||
});
|
});
|
||||||
|
|
||||||
final String title;
|
final String title;
|
||||||
@@ -63,6 +88,10 @@ class SelectSetting extends StatelessWidget {
|
|||||||
|
|
||||||
final String? help;
|
final String? help;
|
||||||
|
|
||||||
|
final String? comicId;
|
||||||
|
|
||||||
|
final String? comicSource;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return SizedBox(
|
return SizedBox(
|
||||||
@@ -76,6 +105,8 @@ class SelectSetting extends StatelessWidget {
|
|||||||
optionTranslation: optionTranslation,
|
optionTranslation: optionTranslation,
|
||||||
onChanged: onChanged,
|
onChanged: onChanged,
|
||||||
help: help,
|
help: help,
|
||||||
|
comicId: comicId,
|
||||||
|
comicSource: comicSource,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return _EndSelectorSelectSetting(
|
return _EndSelectorSelectSetting(
|
||||||
@@ -84,6 +115,8 @@ class SelectSetting extends StatelessWidget {
|
|||||||
optionTranslation: optionTranslation,
|
optionTranslation: optionTranslation,
|
||||||
onChanged: onChanged,
|
onChanged: onChanged,
|
||||||
help: help,
|
help: help,
|
||||||
|
comicId: comicId,
|
||||||
|
comicSource: comicSource,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -99,6 +132,8 @@ class _DoubleLineSelectSettings extends StatefulWidget {
|
|||||||
required this.optionTranslation,
|
required this.optionTranslation,
|
||||||
this.onChanged,
|
this.onChanged,
|
||||||
this.help,
|
this.help,
|
||||||
|
this.comicId,
|
||||||
|
this.comicSource,
|
||||||
});
|
});
|
||||||
|
|
||||||
final String title;
|
final String title;
|
||||||
@@ -111,6 +146,10 @@ class _DoubleLineSelectSettings extends StatefulWidget {
|
|||||||
|
|
||||||
final String? help;
|
final String? help;
|
||||||
|
|
||||||
|
final String? comicId;
|
||||||
|
|
||||||
|
final String? comicSource;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<_DoubleLineSelectSettings> createState() =>
|
State<_DoubleLineSelectSettings> createState() =>
|
||||||
_DoubleLineSelectSettingsState();
|
_DoubleLineSelectSettingsState();
|
||||||
@@ -119,6 +158,14 @@ class _DoubleLineSelectSettings extends StatefulWidget {
|
|||||||
class _DoubleLineSelectSettingsState extends State<_DoubleLineSelectSettings> {
|
class _DoubleLineSelectSettingsState extends State<_DoubleLineSelectSettings> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
var value = widget.comicId == null
|
||||||
|
? appdata.settings[widget.settingKey]
|
||||||
|
: appdata.settings.getReaderSetting(
|
||||||
|
widget.comicId!,
|
||||||
|
widget.comicSource!,
|
||||||
|
widget.settingKey,
|
||||||
|
);
|
||||||
|
|
||||||
return ListTile(
|
return ListTile(
|
||||||
title: Row(
|
title: Row(
|
||||||
children: [
|
children: [
|
||||||
@@ -134,9 +181,9 @@ class _DoubleLineSelectSettingsState extends State<_DoubleLineSelectSettings> {
|
|||||||
builder: (context) {
|
builder: (context) {
|
||||||
return ContentDialog(
|
return ContentDialog(
|
||||||
title: "Help".tl,
|
title: "Help".tl,
|
||||||
content: Text(widget.help!)
|
content: Text(
|
||||||
.paddingHorizontal(16)
|
widget.help!,
|
||||||
.fixWidth(double.infinity),
|
).paddingHorizontal(16).fixWidth(double.infinity),
|
||||||
actions: [
|
actions: [
|
||||||
Button.filled(
|
Button.filled(
|
||||||
onPressed: context.pop,
|
onPressed: context.pop,
|
||||||
@@ -150,9 +197,7 @@ class _DoubleLineSelectSettingsState extends State<_DoubleLineSelectSettings> {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
subtitle: Text(
|
subtitle: Text(widget.optionTranslation[value] ?? "None"),
|
||||||
widget.optionTranslation[appdata.settings[widget.settingKey]] ??
|
|
||||||
"None"),
|
|
||||||
trailing: const Icon(Icons.arrow_drop_down),
|
trailing: const Icon(Icons.arrow_drop_down),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
var renderBox = context.findRenderObject() as RenderBox;
|
var renderBox = context.findRenderObject() as RenderBox;
|
||||||
@@ -170,16 +215,27 @@ class _DoubleLineSelectSettingsState extends State<_DoubleLineSelectSettings> {
|
|||||||
Offset.zero & MediaQuery.of(context).size,
|
Offset.zero & MediaQuery.of(context).size,
|
||||||
),
|
),
|
||||||
items: widget.optionTranslation.keys
|
items: widget.optionTranslation.keys
|
||||||
.map((key) => PopupMenuItem(
|
.map(
|
||||||
value: key,
|
(key) => PopupMenuItem(
|
||||||
height: App.isMobile ? 46 : 40,
|
value: key,
|
||||||
child: Text(widget.optionTranslation[key]!),
|
height: App.isMobile ? 46 : 40,
|
||||||
))
|
child: Text(widget.optionTranslation[key]!),
|
||||||
|
),
|
||||||
|
)
|
||||||
.toList(),
|
.toList(),
|
||||||
).then((value) {
|
).then((value) {
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
setState(() {
|
setState(() {
|
||||||
appdata.settings[widget.settingKey] = value;
|
if (widget.comicId == null) {
|
||||||
|
appdata.settings[widget.settingKey] = value;
|
||||||
|
} else {
|
||||||
|
appdata.settings.setReaderSetting(
|
||||||
|
widget.comicId!,
|
||||||
|
widget.comicSource!,
|
||||||
|
widget.settingKey,
|
||||||
|
value,
|
||||||
|
);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
appdata.saveData();
|
appdata.saveData();
|
||||||
widget.onChanged?.call();
|
widget.onChanged?.call();
|
||||||
@@ -197,6 +253,8 @@ class _EndSelectorSelectSetting extends StatefulWidget {
|
|||||||
required this.optionTranslation,
|
required this.optionTranslation,
|
||||||
this.onChanged,
|
this.onChanged,
|
||||||
this.help,
|
this.help,
|
||||||
|
this.comicId,
|
||||||
|
this.comicSource,
|
||||||
});
|
});
|
||||||
|
|
||||||
final String title;
|
final String title;
|
||||||
@@ -209,6 +267,10 @@ class _EndSelectorSelectSetting extends StatefulWidget {
|
|||||||
|
|
||||||
final String? help;
|
final String? help;
|
||||||
|
|
||||||
|
final String? comicId;
|
||||||
|
|
||||||
|
final String? comicSource;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<_EndSelectorSelectSetting> createState() =>
|
State<_EndSelectorSelectSetting> createState() =>
|
||||||
_EndSelectorSelectSettingState();
|
_EndSelectorSelectSettingState();
|
||||||
@@ -218,6 +280,13 @@ class _EndSelectorSelectSettingState extends State<_EndSelectorSelectSetting> {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
var options = widget.optionTranslation;
|
var options = widget.optionTranslation;
|
||||||
|
var value = widget.comicId == null
|
||||||
|
? appdata.settings[widget.settingKey]
|
||||||
|
: appdata.settings.getReaderSetting(
|
||||||
|
widget.comicId!,
|
||||||
|
widget.comicSource!,
|
||||||
|
widget.settingKey,
|
||||||
|
);
|
||||||
return ListTile(
|
return ListTile(
|
||||||
title: Row(
|
title: Row(
|
||||||
children: [
|
children: [
|
||||||
@@ -233,9 +302,9 @@ class _EndSelectorSelectSettingState extends State<_EndSelectorSelectSetting> {
|
|||||||
builder: (context) {
|
builder: (context) {
|
||||||
return ContentDialog(
|
return ContentDialog(
|
||||||
title: "Help".tl,
|
title: "Help".tl,
|
||||||
content: Text(widget.help!)
|
content: Text(
|
||||||
.paddingHorizontal(16)
|
widget.help!,
|
||||||
.fixWidth(double.infinity),
|
).paddingHorizontal(16).fixWidth(double.infinity),
|
||||||
actions: [
|
actions: [
|
||||||
Button.filled(
|
Button.filled(
|
||||||
onPressed: context.pop,
|
onPressed: context.pop,
|
||||||
@@ -250,12 +319,22 @@ class _EndSelectorSelectSettingState extends State<_EndSelectorSelectSetting> {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
trailing: Select(
|
trailing: Select(
|
||||||
current: options[appdata.settings[widget.settingKey]],
|
current: options[value],
|
||||||
values: options.values.toList(),
|
values: options.values.toList(),
|
||||||
minWidth: 64,
|
minWidth: 64,
|
||||||
onTap: (index) {
|
onTap: (index) {
|
||||||
setState(() {
|
setState(() {
|
||||||
appdata.settings[widget.settingKey] = options.keys.elementAt(index);
|
var value = options.keys.elementAt(index);
|
||||||
|
if (widget.comicId == null) {
|
||||||
|
appdata.settings[widget.settingKey] = value;
|
||||||
|
} else {
|
||||||
|
appdata.settings.setReaderSetting(
|
||||||
|
widget.comicId!,
|
||||||
|
widget.comicSource!,
|
||||||
|
widget.settingKey,
|
||||||
|
value,
|
||||||
|
);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
appdata.saveData();
|
appdata.saveData();
|
||||||
widget.onChanged?.call();
|
widget.onChanged?.call();
|
||||||
@@ -273,6 +352,8 @@ class _SliderSetting extends StatefulWidget {
|
|||||||
required this.min,
|
required this.min,
|
||||||
required this.max,
|
required this.max,
|
||||||
this.onChanged,
|
this.onChanged,
|
||||||
|
this.comicId,
|
||||||
|
this.comicSource,
|
||||||
});
|
});
|
||||||
|
|
||||||
final String title;
|
final String title;
|
||||||
@@ -287,6 +368,10 @@ class _SliderSetting extends StatefulWidget {
|
|||||||
|
|
||||||
final VoidCallback? onChanged;
|
final VoidCallback? onChanged;
|
||||||
|
|
||||||
|
final String? comicId;
|
||||||
|
|
||||||
|
final String? comicSource;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<_SliderSetting> createState() => _SliderSettingState();
|
State<_SliderSetting> createState() => _SliderSettingState();
|
||||||
}
|
}
|
||||||
@@ -294,28 +379,52 @@ class _SliderSetting extends StatefulWidget {
|
|||||||
class _SliderSettingState extends State<_SliderSetting> {
|
class _SliderSettingState extends State<_SliderSetting> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
var value =
|
||||||
|
(widget.comicId == null
|
||||||
|
? appdata.settings[widget.settingsIndex]
|
||||||
|
: appdata.settings.getReaderSetting(
|
||||||
|
widget.comicId!,
|
||||||
|
widget.comicSource!,
|
||||||
|
widget.settingsIndex,
|
||||||
|
))
|
||||||
|
.toDouble();
|
||||||
return ListTile(
|
return ListTile(
|
||||||
title: Row(
|
title: Row(
|
||||||
children: [
|
children: [
|
||||||
Text(widget.title),
|
Text(widget.title),
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
Text(
|
Text(value.toString(), style: ts.s12),
|
||||||
appdata.settings[widget.settingsIndex].toString(),
|
|
||||||
style: ts.s12,
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
subtitle: Slider(
|
subtitle: Slider(
|
||||||
value: appdata.settings[widget.settingsIndex].toDouble(),
|
value: value,
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
if (value.toInt() == value) {
|
if (value.toInt() == value) {
|
||||||
setState(() {
|
setState(() {
|
||||||
appdata.settings[widget.settingsIndex] = value.toInt();
|
if (widget.comicId == null) {
|
||||||
|
appdata.settings[widget.settingsIndex] = value.toInt();
|
||||||
|
} else {
|
||||||
|
appdata.settings.setReaderSetting(
|
||||||
|
widget.comicId!,
|
||||||
|
widget.comicSource!,
|
||||||
|
widget.settingsIndex,
|
||||||
|
value.toInt(),
|
||||||
|
);
|
||||||
|
}
|
||||||
appdata.saveData();
|
appdata.saveData();
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
setState(() {
|
setState(() {
|
||||||
appdata.settings[widget.settingsIndex] = value;
|
if (widget.comicId == null) {
|
||||||
|
appdata.settings[widget.settingsIndex] = value;
|
||||||
|
} else {
|
||||||
|
appdata.settings.setReaderSetting(
|
||||||
|
widget.comicId!,
|
||||||
|
widget.comicSource!,
|
||||||
|
widget.settingsIndex,
|
||||||
|
value,
|
||||||
|
);
|
||||||
|
}
|
||||||
appdata.saveData();
|
appdata.saveData();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -402,10 +511,11 @@ class _MultiPagesFilterState extends State<_MultiPagesFilter> {
|
|||||||
color: Theme.of(context).colorScheme.surfaceContainer,
|
color: Theme.of(context).colorScheme.surfaceContainer,
|
||||||
boxShadow: const [
|
boxShadow: const [
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: Colors.black12,
|
color: Colors.black12,
|
||||||
blurRadius: 5,
|
blurRadius: 5,
|
||||||
offset: Offset(0, 2),
|
offset: Offset(0, 2),
|
||||||
spreadRadius: 2)
|
spreadRadius: 2,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
onReorder: (reorderFunc) {
|
onReorder: (reorderFunc) {
|
||||||
@@ -435,7 +545,7 @@ class _MultiPagesFilterState extends State<_MultiPagesFilter> {
|
|||||||
label: Text("Add".tl),
|
label: Text("Add".tl),
|
||||||
icon: const Icon(Icons.add),
|
icon: const Icon(Icons.add),
|
||||||
onPressed: showAddDialog,
|
onPressed: showAddDialog,
|
||||||
)
|
),
|
||||||
],
|
],
|
||||||
body: view,
|
body: view,
|
||||||
);
|
);
|
||||||
@@ -445,12 +555,13 @@ class _MultiPagesFilterState extends State<_MultiPagesFilter> {
|
|||||||
Widget removeButton = Padding(
|
Widget removeButton = Padding(
|
||||||
padding: const EdgeInsets.only(right: 8),
|
padding: const EdgeInsets.only(right: 8),
|
||||||
child: IconButton(
|
child: IconButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
setState(() {
|
setState(() {
|
||||||
keys.remove(key);
|
keys.remove(key);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
icon: const Icon(Icons.delete_outline)),
|
icon: const Icon(Icons.delete_outline),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
return ListTile(
|
return ListTile(
|
||||||
@@ -458,10 +569,7 @@ class _MultiPagesFilterState extends State<_MultiPagesFilter> {
|
|||||||
key: Key(key),
|
key: Key(key),
|
||||||
trailing: Row(
|
trailing: Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [removeButton, const Icon(Icons.drag_handle)],
|
||||||
removeButton,
|
|
||||||
const Icon(Icons.drag_handle),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -477,64 +585,66 @@ class _MultiPagesFilterState extends State<_MultiPagesFilter> {
|
|||||||
showDialog(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
return StatefulBuilder(builder: (context, setState) {
|
return StatefulBuilder(
|
||||||
return ContentDialog(
|
builder: (context, setState) {
|
||||||
title: "Add".tl,
|
return ContentDialog(
|
||||||
content: Column(
|
title: "Add".tl,
|
||||||
mainAxisSize: MainAxisSize.min,
|
content: Column(
|
||||||
children: canAdd.entries
|
mainAxisSize: MainAxisSize.min,
|
||||||
.map(
|
children: canAdd.entries
|
||||||
(e) => CheckboxListTile(
|
.map(
|
||||||
value: selected.contains(e.key),
|
(e) => CheckboxListTile(
|
||||||
title: Text(e.value),
|
value: selected.contains(e.key),
|
||||||
key: Key(e.key),
|
title: Text(e.value),
|
||||||
onChanged: (value) {
|
key: Key(e.key),
|
||||||
setState(() {
|
onChanged: (value) {
|
||||||
if (value!) {
|
setState(() {
|
||||||
selected.add(e.key);
|
if (value!) {
|
||||||
} else {
|
selected.add(e.key);
|
||||||
selected.remove(e.key);
|
} else {
|
||||||
}
|
selected.remove(e.key);
|
||||||
});
|
}
|
||||||
},
|
});
|
||||||
),
|
},
|
||||||
)
|
),
|
||||||
.toList(),
|
)
|
||||||
),
|
.toList(),
|
||||||
actions: [
|
|
||||||
if (selected.length < canAdd.length)
|
|
||||||
TextButton(
|
|
||||||
child: Text("Select All".tl),
|
|
||||||
onPressed: () {
|
|
||||||
setState(() {
|
|
||||||
selected = canAdd.keys.toList();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
)
|
|
||||||
else
|
|
||||||
TextButton(
|
|
||||||
child: Text("Deselect All".tl),
|
|
||||||
onPressed: () {
|
|
||||||
setState(() {
|
|
||||||
selected.clear();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
),
|
|
||||||
const SizedBox(width: 8),
|
|
||||||
FilledButton(
|
|
||||||
onPressed: selected.isNotEmpty
|
|
||||||
? () {
|
|
||||||
this.setState(() {
|
|
||||||
keys.addAll(selected);
|
|
||||||
});
|
|
||||||
Navigator.pop(context);
|
|
||||||
}
|
|
||||||
: null,
|
|
||||||
child: Text("Add".tl),
|
|
||||||
),
|
),
|
||||||
],
|
actions: [
|
||||||
);
|
if (selected.length < canAdd.length)
|
||||||
});
|
TextButton(
|
||||||
|
child: Text("Select All".tl),
|
||||||
|
onPressed: () {
|
||||||
|
setState(() {
|
||||||
|
selected = canAdd.keys.toList();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
)
|
||||||
|
else
|
||||||
|
TextButton(
|
||||||
|
child: Text("Deselect All".tl),
|
||||||
|
onPressed: () {
|
||||||
|
setState(() {
|
||||||
|
selected.clear();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
FilledButton(
|
||||||
|
onPressed: selected.isNotEmpty
|
||||||
|
? () {
|
||||||
|
this.setState(() {
|
||||||
|
keys.addAll(selected);
|
||||||
|
});
|
||||||
|
Navigator.pop(context);
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
child: Text("Add".tl),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@@ -28,6 +28,8 @@ final _resolver = MimeTypeResolver()
|
|||||||
..addMagicNumber([0x37, 0x7A, 0xBC, 0xAF, 0x27, 0x1C], 'application/x-7z-compressed')
|
..addMagicNumber([0x37, 0x7A, 0xBC, 0xAF, 0x27, 0x1C], 'application/x-7z-compressed')
|
||||||
// rar
|
// rar
|
||||||
..addMagicNumber([0x52, 0x61, 0x72, 0x21, 0x1A, 0x07], 'application/vnd.rar')
|
..addMagicNumber([0x52, 0x61, 0x72, 0x21, 0x1A, 0x07], 'application/vnd.rar')
|
||||||
|
// avif
|
||||||
|
..addMagicNumber([0x00, 0x00, 0x00, 0x18, 0x66, 0x74, 0x79, 0x70, 0x61, 0x76, 0x69, 0x66], 'image/avif')
|
||||||
;
|
;
|
||||||
|
|
||||||
FileType detectFileType(List<int> data) {
|
FileType detectFileType(List<int> data) {
|
||||||
|
Reference in New Issue
Block a user