diff --git a/lib/foundation/global_state.dart b/lib/foundation/global_state.dart index 400a82f..ff45aa8 100644 --- a/lib/foundation/global_state.dart +++ b/lib/foundation/global_state.dart @@ -9,7 +9,7 @@ abstract class GlobalState { static T find([Object? key]) { for (var pair in _state) { - if (pair.left == key && pair.right is T) { + if ((key == null || pair.left == key) && pair.right is T) { return pair.right as T; } } @@ -18,7 +18,7 @@ abstract class GlobalState { static T? findOrNull([Object? key]) { for (var pair in _state) { - if (pair.left == key && pair.right is T) { + if ((key == null || pair.left == key) && pair.right is T) { return pair.right as T; } } diff --git a/lib/pages/reader/comic_image.dart b/lib/pages/reader/comic_image.dart index 69fd50c..7f6d5e3 100644 --- a/lib/pages/reader/comic_image.dart +++ b/lib/pages/reader/comic_image.dart @@ -3,31 +3,30 @@ part of 'reader.dart'; class ComicImage extends StatefulWidget { /// Modified from flutter Image ComicImage({ - required ImageProvider image, - super.key, - double scale = 1.0, - this.semanticLabel, - this.excludeFromSemantics = false, - this.width, - this.height, - this.color, - this.opacity, - this.colorBlendMode, - this.fit, - this.alignment = Alignment.center, - this.repeat = ImageRepeat.noRepeat, - this.centerSlice, - this.matchTextDirection = false, - this.gaplessPlayback = false, - this.filterQuality = FilterQuality.medium, - this.isAntiAlias = false, - Map? headers, - int? cacheWidth, - int? cacheHeight, - } - ): image = ResizeImage.resizeIfNeeded(cacheWidth, cacheHeight, image), - assert(cacheWidth == null || cacheWidth > 0), - assert(cacheHeight == null || cacheHeight > 0); + required ImageProvider image, + super.key, + double scale = 1.0, + this.semanticLabel, + this.excludeFromSemantics = false, + this.width, + this.height, + this.color, + this.opacity, + this.colorBlendMode, + this.fit, + this.alignment = Alignment.center, + this.repeat = ImageRepeat.noRepeat, + this.centerSlice, + this.matchTextDirection = false, + this.gaplessPlayback = false, + this.filterQuality = FilterQuality.medium, + this.isAntiAlias = false, + Map? headers, + int? cacheWidth, + int? cacheHeight, + }) : image = ResizeImage.resizeIfNeeded(cacheWidth, cacheHeight, image), + assert(cacheWidth == null || cacheWidth > 0), + assert(cacheHeight == null || cacheHeight > 0); final ImageProvider image; @@ -138,8 +137,8 @@ class _ComicImageState extends State with WidgetsBindingObserver { } void _updateInvertColors() { - _invertColors = MediaQuery.maybeInvertColorsOf(context) - ?? SemanticsBinding.instance.accessibilityFeatures.invertColors; + _invertColors = MediaQuery.maybeInvertColorsOf(context) ?? + SemanticsBinding.instance.accessibilityFeatures.invertColors; } void _resolveImage() { @@ -148,16 +147,19 @@ class _ComicImageState extends State with WidgetsBindingObserver { imageProvider: widget.image, ); final ImageStream newStream = - provider.resolve(createLocalImageConfiguration( + provider.resolve(createLocalImageConfiguration( context, - size: widget.width != null && widget.height != null ? Size(widget.width!, widget.height!) : null, + size: widget.width != null && widget.height != null + ? Size(widget.width!, widget.height!) + : null, )); _updateSourceStream(newStream); } ImageStreamListener? _imageStreamListener; + ImageStreamListener _getListener({bool recreateListener = false}) { - if(_imageStreamListener == null || recreateListener) { + if (_imageStreamListener == null || recreateListener) { _lastException = null; _imageStreamListener = ImageStreamListener( _handleImageFrame, @@ -191,7 +193,8 @@ class _ComicImageState extends State with WidgetsBindingObserver { void _replaceImage({required ImageInfo? info}) { final ImageInfo? oldImageInfo = _imageInfo; - SchedulerBinding.instance.addPostFrameCallback((_) => oldImageInfo?.dispose()); + SchedulerBinding.instance + .addPostFrameCallback((_) => oldImageInfo?.dispose()); _imageInfo = info; } @@ -208,7 +211,9 @@ class _ComicImageState extends State with WidgetsBindingObserver { } if (!widget.gaplessPlayback) { - setState(() { _replaceImage(info: null); }); + setState(() { + _replaceImage(info: null); + }); } setState(() { @@ -247,7 +252,9 @@ class _ComicImageState extends State with WidgetsBindingObserver { return; } - if (keepStreamAlive && _completerHandle == null && _imageStream?.completer != null) { + if (keepStreamAlive && + _completerHandle == null && + _imageStream?.completer != null) { _completerHandle = _imageStream!.completer!.keepAlive(); } @@ -269,26 +276,41 @@ class _ComicImageState extends State with WidgetsBindingObserver { children: [ Expanded( child: Center( - child: Text(_lastException.toString(), maxLines: 3,), + child: Text( + _lastException.toString(), + maxLines: 3, + ), ), ), - const SizedBox(height: 4,), + const SizedBox( + height: 4, + ), MouseRegion( cursor: SystemMouseCursors.click, child: Listener( - onPointerDown: (details){ + onPointerDown: (details) { + GlobalState.find<_ReaderGestureDetectorState>().ignoreNextTap(); + setState(() { + _loadingProgress = null; + _lastException = null; + }); _resolveImage(); }, - child: const SizedBox( + child: SizedBox( width: 84, height: 36, child: Center( - child: Text("Retry", style: TextStyle(color: Colors.blue),), + child: Text( + "Retry".tl, + style: TextStyle(color: Colors.blue), + ), ), ), ), ), - const SizedBox(height: 16,), + const SizedBox( + height: 16, + ), ], ), ), @@ -300,34 +322,32 @@ class _ComicImageState extends State with WidgetsBindingObserver { var width = widget.width; var height = widget.height; - if(_imageInfo != null) { + if (_imageInfo != null) { // Record the height and the width of the image - _cache[widget.image.hashCode] = Size( - _imageInfo!.image.width.toDouble(), - _imageInfo!.image.height.toDouble() - ); + _cache[widget.image.hashCode] = Size(_imageInfo!.image.width.toDouble(), + _imageInfo!.image.height.toDouble()); } Size? cacheSize = _cache[widget.image.hashCode]; - if(cacheSize != null){ - if(width == double.infinity) { + if (cacheSize != null) { + if (width == double.infinity) { width = constrains.maxWidth; height = width * cacheSize.height / cacheSize.width; - } else if(height == double.infinity) { + } else if (height == double.infinity) { height = constrains.maxHeight; width = height * cacheSize.width / cacheSize.height; } } else { - if(width == double.infinity) { + if (width == double.infinity) { width = constrains.maxWidth; height = 300; - } else if(height == double.infinity) { + } else if (height == double.infinity) { height = constrains.maxHeight; width = 300; } } - if(_imageInfo != null){ + if (_imageInfo != null) { // build image Widget result = RawImage( // Do not clone the image, because RawImage is a stateless wrapper. @@ -379,12 +399,13 @@ class _ComicImageState extends State with WidgetsBindingObserver { height: 24, child: CircularProgressIndicator( strokeWidth: 3, - backgroundColor: context.colorScheme.surfaceContainerLow, + backgroundColor: context.colorScheme.surfaceContainer, value: (_loadingProgress != null && - _loadingProgress!.expectedTotalBytes!=null && - _loadingProgress!.expectedTotalBytes! != 0) - ?_loadingProgress!.cumulativeBytesLoaded / _loadingProgress!.expectedTotalBytes! - :0, + _loadingProgress!.expectedTotalBytes != null && + _loadingProgress!.expectedTotalBytes! != 0) + ? _loadingProgress!.cumulativeBytesLoaded / + _loadingProgress!.expectedTotalBytes! + : 0, ), ), ), @@ -398,8 +419,10 @@ class _ComicImageState extends State with WidgetsBindingObserver { super.debugFillProperties(description); description.add(DiagnosticsProperty('stream', _imageStream)); description.add(DiagnosticsProperty('pixels', _imageInfo)); - description.add(DiagnosticsProperty('loadingProgress', _loadingProgress)); + description.add(DiagnosticsProperty( + 'loadingProgress', _loadingProgress)); description.add(DiagnosticsProperty('frameNumber', _frameNumber)); - description.add(DiagnosticsProperty('wasSynchronouslyLoaded', _wasSynchronouslyLoaded)); + description.add(DiagnosticsProperty( + 'wasSynchronouslyLoaded', _wasSynchronouslyLoaded)); } } diff --git a/lib/pages/reader/gesture.dart b/lib/pages/reader/gesture.dart index 1da7f3e..3122339 100644 --- a/lib/pages/reader/gesture.dart +++ b/lib/pages/reader/gesture.dart @@ -9,7 +9,7 @@ class _ReaderGestureDetector extends StatefulWidget { State<_ReaderGestureDetector> createState() => _ReaderGestureDetectorState(); } -class _ReaderGestureDetectorState extends State<_ReaderGestureDetector> { +class _ReaderGestureDetectorState extends AutomaticGlobalState<_ReaderGestureDetector> { late TapGestureRecognizer _tapGestureRecognizer; static const _kDoubleTapMaxTime = Duration(milliseconds: 200); @@ -26,6 +26,12 @@ class _ReaderGestureDetectorState extends State<_ReaderGestureDetector> { late _ReaderState reader; + bool ignoreNextTag = false; + + void ignoreNextTap() { + ignoreNextTag = true; + } + @override void initState() { _tapGestureRecognizer = TapGestureRecognizer() @@ -44,6 +50,10 @@ class _ReaderGestureDetectorState extends State<_ReaderGestureDetector> { behavior: HitTestBehavior.translucent, onPointerDown: (event) { fingers++; + if (ignoreNextTag) { + ignoreNextTag = false; + return; + } _lastTapPointer = event.pointer; _lastTapMoveDistance = Offset.zero; _tapGestureRecognizer.addPointer(event); @@ -290,6 +300,9 @@ class _ReaderGestureDetectorState extends State<_ReaderGestureDetector> { void removeDragListener(_DragListener listener) { _dragListeners.remove(listener); } + + @override + Object? get key => "reader_gesture"; } class _DragListener { diff --git a/lib/pages/reader/reader.dart b/lib/pages/reader/reader.dart index 7d4a686..a533508 100644 --- a/lib/pages/reader/reader.dart +++ b/lib/pages/reader/reader.dart @@ -22,6 +22,7 @@ import 'package:venera/foundation/comic_source/comic_source.dart'; import 'package:venera/foundation/comic_type.dart'; import 'package:venera/foundation/consts.dart'; import 'package:venera/foundation/favorites.dart'; +import 'package:venera/foundation/global_state.dart'; import 'package:venera/foundation/history.dart'; import 'package:venera/foundation/image_provider/reader_image.dart'; import 'package:venera/foundation/local.dart';