mirror of
https://github.com/venera-app/venera.git
synced 2025-09-27 07:47:24 +00:00
Add a feature to allow saving custom reader settings for each comic. (#459)
* Add a feature to allow saving custom reader settings for each comic. * Comic-specific settings disabled by default
This commit is contained in:
@@ -32,10 +32,17 @@ class _ReaderImagesState extends State<_ReaderImages> {
|
||||
inProgress = true;
|
||||
if (reader.type == ComicType.local ||
|
||||
(LocalManager().isDownloaded(
|
||||
reader.cid, reader.type, reader.chapter, reader.widget.chapters))) {
|
||||
reader.cid,
|
||||
reader.type,
|
||||
reader.chapter,
|
||||
reader.widget.chapters,
|
||||
))) {
|
||||
try {
|
||||
var images = await LocalManager()
|
||||
.getImages(reader.cid, reader.type, reader.chapter);
|
||||
var images = await LocalManager().getImages(
|
||||
reader.cid,
|
||||
reader.type,
|
||||
reader.chapter,
|
||||
);
|
||||
setState(() {
|
||||
reader.images = images;
|
||||
reader.isLoading = false;
|
||||
@@ -81,9 +88,7 @@ class _ReaderImagesState extends State<_ReaderImages> {
|
||||
Widget build(BuildContext context) {
|
||||
if (reader.isLoading) {
|
||||
load();
|
||||
return const Center(
|
||||
child: CircularProgressIndicator(),
|
||||
);
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
} else if (error != null) {
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
@@ -104,7 +109,8 @@ class _ReaderImagesState extends State<_ReaderImages> {
|
||||
} else {
|
||||
if (reader.mode.isGallery) {
|
||||
return _GalleryMode(
|
||||
key: Key('${reader.mode.key}_${reader.imagesPerPage}'));
|
||||
key: Key('${reader.mode.key}_${reader.imagesPerPage}'),
|
||||
);
|
||||
} else {
|
||||
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.
|
||||
/// More than one images can be displayed on one page.
|
||||
int get totalPages {
|
||||
if (!reader.showSingleImageOnFirstPage) {
|
||||
return (reader.images!.length / reader.imagesPerPage).ceil();
|
||||
if (!reader.showSingleImageOnFirstPage(reader.cid, reader.type)) {
|
||||
return (reader.images!.length /
|
||||
reader.imagesPerPage(reader.cid, reader.type))
|
||||
.ceil();
|
||||
} else {
|
||||
return 1 +
|
||||
((reader.images!.length - 1) / reader.imagesPerPage).ceil();
|
||||
((reader.images!.length - 1) /
|
||||
reader.imagesPerPage(reader.cid, reader.type))
|
||||
.ceil();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,19 +169,24 @@ class _GalleryModeState extends State<_GalleryMode>
|
||||
|
||||
/// Get the range of images for the given page. [page] is 1-based.
|
||||
(int start, int end) getPageImagesRange(int page) {
|
||||
if (reader.showSingleImageOnFirstPage) {
|
||||
var imagesPerPage = reader.imagesPerPage(reader.cid, reader.type);
|
||||
if (reader.showSingleImageOnFirstPage(reader.cid, reader.type)) {
|
||||
if (page == 1) {
|
||||
return (0, 1);
|
||||
} else {
|
||||
int startIndex = (page - 2) * reader.imagesPerPage + 1;
|
||||
int startIndex = (page - 2) * imagesPerPage + 1;
|
||||
int endIndex = math.min(
|
||||
startIndex + reader.imagesPerPage, reader.images!.length);
|
||||
startIndex + imagesPerPage,
|
||||
reader.images!.length,
|
||||
);
|
||||
return (startIndex, endIndex);
|
||||
}
|
||||
} else {
|
||||
int startIndex = (page - 1) * reader.imagesPerPage;
|
||||
int startIndex = (page - 1) * imagesPerPage;
|
||||
int endIndex = math.min(
|
||||
startIndex + reader.imagesPerPage, reader.images!.length);
|
||||
startIndex + imagesPerPage,
|
||||
reader.images!.length,
|
||||
);
|
||||
return (startIndex, endIndex);
|
||||
}
|
||||
}
|
||||
@@ -193,9 +208,9 @@ class _GalleryModeState extends State<_GalleryMode>
|
||||
var (startIndex, endIndex) = getPageImagesRange(page);
|
||||
for (int i = startIndex; i < endIndex; i++) {
|
||||
if (shouldPreCache) {
|
||||
_precacheImage(i+1, context);
|
||||
_precacheImage(i + 1, context);
|
||||
} else {
|
||||
_preDownloadImage(i+1, context);
|
||||
_preDownloadImage(i + 1, context);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -217,16 +232,12 @@ class _GalleryModeState extends State<_GalleryMode>
|
||||
var controller = photoViewControllers[reader.page]!;
|
||||
Offset value = event.delta;
|
||||
if (isLongPressing) {
|
||||
controller.updateMultiple(
|
||||
position: controller.position + value,
|
||||
);
|
||||
controller.updateMultiple(position: controller.position + value);
|
||||
}
|
||||
}
|
||||
},
|
||||
child: PhotoViewGallery.builder(
|
||||
backgroundDecoration: BoxDecoration(
|
||||
color: context.colorScheme.surface,
|
||||
),
|
||||
backgroundDecoration: BoxDecoration(color: context.colorScheme.surface),
|
||||
reverse: reader.mode == ReaderMode.galleryRightToLeft,
|
||||
scrollDirection: reader.mode == ReaderMode.galleryTopToBottom
|
||||
? Axis.vertical
|
||||
@@ -239,14 +250,17 @@ class _GalleryModeState extends State<_GalleryMode>
|
||||
);
|
||||
} else {
|
||||
var (startIndex, endIndex) = getPageImagesRange(index);
|
||||
List<String> pageImages =
|
||||
reader.images!.sublist(startIndex, endIndex);
|
||||
List<String> pageImages = reader.images!.sublist(
|
||||
startIndex,
|
||||
endIndex,
|
||||
);
|
||||
|
||||
cache(index);
|
||||
|
||||
photoViewControllers[index] ??= PhotoViewController();
|
||||
|
||||
if (reader.imagesPerPage == 1 || pageImages.length == 1) {
|
||||
if (reader.imagesPerPage(reader.cid, reader.type) == 1 ||
|
||||
pageImages.length == 1) {
|
||||
return PhotoViewGalleryPageOptions(
|
||||
filterQuality: FilterQuality.medium,
|
||||
controller: photoViewControllers[index],
|
||||
@@ -287,11 +301,11 @@ class _GalleryModeState extends State<_GalleryMode>
|
||||
onPageChanged: (i) {
|
||||
if (i == 0) {
|
||||
if (reader.isFirstChapterOfGroup || !reader.toPrevChapter()) {
|
||||
reader.toPage(1);
|
||||
reader.toPage(reader.cid, reader.type, 1);
|
||||
}
|
||||
} else if (i == totalPages + 1) {
|
||||
if (reader.isLastChapterOfGroup || !reader.toNextChapter()) {
|
||||
reader.toPage(totalPages);
|
||||
reader.toPage(reader.cid, reader.type, totalPages);
|
||||
}
|
||||
} else {
|
||||
reader.setPage(i);
|
||||
@@ -356,13 +370,16 @@ class _GalleryModeState extends State<_GalleryMode>
|
||||
onInit: (state) => imageStates.add(state),
|
||||
onDispose: (state) => imageStates.remove(state),
|
||||
),
|
||||
)
|
||||
),
|
||||
];
|
||||
} else {
|
||||
imageWidgets = images.map((imageKey) {
|
||||
startIndex++;
|
||||
ImageProvider imageProvider =
|
||||
_createImageProviderFromKey(imageKey, context, startIndex);
|
||||
ImageProvider imageProvider = _createImageProviderFromKey(
|
||||
imageKey,
|
||||
context,
|
||||
startIndex,
|
||||
);
|
||||
return Expanded(
|
||||
child: ComicImage(
|
||||
image: imageProvider,
|
||||
@@ -423,10 +440,7 @@ class _GalleryModeState extends State<_GalleryMode>
|
||||
} else {
|
||||
zoomPosition = Offset(0, 0);
|
||||
}
|
||||
photoViewController.animateScale?.call(
|
||||
target,
|
||||
zoomPosition,
|
||||
);
|
||||
photoViewController.animateScale?.call(target, zoomPosition);
|
||||
isLongPressing = true;
|
||||
}
|
||||
|
||||
@@ -471,14 +485,14 @@ class _GalleryModeState extends State<_GalleryMode>
|
||||
keyRepeatTimer = null;
|
||||
}
|
||||
if (forward == true) {
|
||||
reader.toPage(reader.page+1);
|
||||
reader.toPage(reader.cid, reader.type, reader.page + 1);
|
||||
} else if (forward == false) {
|
||||
reader.toPage(reader.page-1);
|
||||
reader.toPage(reader.cid, reader.type, reader.page - 1);
|
||||
}
|
||||
}
|
||||
if (event is KeyRepeatEvent && keyRepeatTimer == null) {
|
||||
keyRepeatTimer = Timer.periodic(
|
||||
reader.enablePageAnimation
|
||||
reader.enablePageAnimation(reader.cid, reader.type)
|
||||
? const Duration(milliseconds: 200)
|
||||
: const Duration(milliseconds: 50),
|
||||
(timer) {
|
||||
@@ -486,9 +500,9 @@ class _GalleryModeState extends State<_GalleryMode>
|
||||
timer.cancel();
|
||||
return;
|
||||
} else if (forward == true) {
|
||||
reader.toPage(reader.page+1);
|
||||
reader.toPage(reader.cid, reader.type, reader.page + 1);
|
||||
} else if (forward == false) {
|
||||
reader.toPage(reader.page-1);
|
||||
reader.toPage(reader.cid, reader.type, reader.page - 1);
|
||||
}
|
||||
},
|
||||
);
|
||||
@@ -512,15 +526,15 @@ class _GalleryModeState extends State<_GalleryMode>
|
||||
return await File(imageKey.substring(7)).readAsBytes();
|
||||
} else {
|
||||
return (await CacheManager().findCache(
|
||||
"$imageKey@${context.reader.type.sourceKey}@${context.reader.cid}@${context.reader.eid}"))!
|
||||
.readAsBytes();
|
||||
"$imageKey@${context.reader.type.sourceKey}@${context.reader.cid}@${context.reader.eid}",
|
||||
))!.readAsBytes();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
String? getImageKeyByOffset(Offset offset) {
|
||||
String? imageKey;
|
||||
if (reader.imagesPerPage == 1) {
|
||||
if (reader.imagesPerPage(reader.cid, reader.type) == 1) {
|
||||
imageKey = reader.images![reader.page - 1];
|
||||
} else {
|
||||
for (var imageState in imageStates) {
|
||||
@@ -538,7 +552,7 @@ const Set<PointerDeviceKind> _kTouchLikeDeviceTypes = <PointerDeviceKind>{
|
||||
PointerDeviceKind.mouse,
|
||||
PointerDeviceKind.stylus,
|
||||
PointerDeviceKind.invertedStylus,
|
||||
PointerDeviceKind.unknown
|
||||
PointerDeviceKind.unknown,
|
||||
};
|
||||
|
||||
const double _kChangeChapterOffset = 160;
|
||||
@@ -673,10 +687,12 @@ class _ContinuousModeState extends State<_ContinuousMode>
|
||||
void onScroll() {
|
||||
if (prepareToPrevChapter) {
|
||||
jumpToNextChapter = false;
|
||||
jumpToPrevChapter = scrollController.offset <
|
||||
jumpToPrevChapter =
|
||||
scrollController.offset <
|
||||
scrollController.position.minScrollExtent - _kChangeChapterOffset;
|
||||
} else if (prepareToNextChapter) {
|
||||
jumpToNextChapter = scrollController.offset >
|
||||
jumpToNextChapter =
|
||||
scrollController.offset >
|
||||
scrollController.position.maxScrollExtent + _kChangeChapterOffset;
|
||||
jumpToPrevChapter = false;
|
||||
}
|
||||
@@ -721,8 +737,8 @@ class _ContinuousModeState extends State<_ContinuousMode>
|
||||
physics: isCTRLPressed || _isMouseScrolling || disableScroll
|
||||
? const NeverScrollableScrollPhysics()
|
||||
: isZoomedIn
|
||||
? const ClampingScrollPhysics()
|
||||
: const BouncingScrollPhysics(),
|
||||
? const ClampingScrollPhysics()
|
||||
: const BouncingScrollPhysics(),
|
||||
itemBuilder: (context, index) {
|
||||
if (index == 0 || index == reader.maxPage + 1) {
|
||||
return const SizedBox();
|
||||
@@ -750,8 +766,10 @@ class _ContinuousModeState extends State<_ContinuousMode>
|
||||
),
|
||||
);
|
||||
},
|
||||
scrollBehavior: const MaterialScrollBehavior()
|
||||
.copyWith(scrollbars: false, dragDevices: _kTouchLikeDeviceTypes),
|
||||
scrollBehavior: const MaterialScrollBehavior().copyWith(
|
||||
scrollbars: false,
|
||||
dragDevices: _kTouchLikeDeviceTypes,
|
||||
),
|
||||
);
|
||||
|
||||
widget = Stack(
|
||||
@@ -895,20 +913,14 @@ class _ContinuousModeState extends State<_ContinuousMode>
|
||||
}
|
||||
|
||||
return PhotoView.customChild(
|
||||
backgroundDecoration: BoxDecoration(
|
||||
color: context.colorScheme.surface,
|
||||
),
|
||||
backgroundDecoration: BoxDecoration(color: context.colorScheme.surface),
|
||||
childSize: Size(width, height),
|
||||
minScale: 1.0,
|
||||
maxScale: 2.5,
|
||||
strictScale: true,
|
||||
controller: photoViewController,
|
||||
onScaleUpdate: onScaleUpdate,
|
||||
child: SizedBox(
|
||||
width: width,
|
||||
height: height,
|
||||
child: widget,
|
||||
),
|
||||
child: SizedBox(width: width, height: height, child: widget),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -978,10 +990,7 @@ class _ContinuousModeState extends State<_ContinuousMode>
|
||||
} else {
|
||||
zoomPosition = Offset(0, 0);
|
||||
}
|
||||
photoViewController.animateScale?.call(
|
||||
target,
|
||||
zoomPosition,
|
||||
);
|
||||
photoViewController.animateScale?.call(target, zoomPosition);
|
||||
onScaleUpdate(target);
|
||||
isLongPressing = true;
|
||||
}
|
||||
@@ -1069,8 +1078,8 @@ class _ContinuousModeState extends State<_ContinuousMode>
|
||||
return await File(imageKey.substring(7)).readAsBytes();
|
||||
} else {
|
||||
return (await CacheManager().findCache(
|
||||
"$imageKey@${context.reader.type.sourceKey}@${context.reader.cid}@${context.reader.eid}"))!
|
||||
.readAsBytes();
|
||||
"$imageKey@${context.reader.type.sourceKey}@${context.reader.cid}@${context.reader.eid}",
|
||||
))!.readAsBytes();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1114,10 +1123,7 @@ void _precacheImage(int page, BuildContext context) {
|
||||
if (page <= 0 || page > context.reader.images!.length) {
|
||||
return;
|
||||
}
|
||||
precacheImage(
|
||||
_createImageProvider(page, context),
|
||||
context,
|
||||
);
|
||||
precacheImage(_createImageProvider(page, context), context);
|
||||
}
|
||||
|
||||
/// [_preDownloadImage] is used to download the image for the given page.
|
||||
@@ -1138,10 +1144,7 @@ void _preDownloadImage(int page, BuildContext context) {
|
||||
}
|
||||
|
||||
class _SwipeChangeChapterProgress extends StatefulWidget {
|
||||
const _SwipeChangeChapterProgress({
|
||||
this.controller,
|
||||
required this.isPrev,
|
||||
});
|
||||
const _SwipeChangeChapterProgress({this.controller, required this.isPrev});
|
||||
|
||||
final ScrollController? controller;
|
||||
|
||||
@@ -1258,7 +1261,12 @@ class _ProgressPainter extends CustomPainter {
|
||||
paint.color = color;
|
||||
canvas.drawRRect(
|
||||
RRect.fromLTRBR(
|
||||
0, 0, size.width * value, size.height, Radius.circular(16)),
|
||||
0,
|
||||
0,
|
||||
size.width * value,
|
||||
size.height,
|
||||
Radius.circular(16),
|
||||
),
|
||||
paint,
|
||||
);
|
||||
}
|
||||
|
Reference in New Issue
Block a user