Add a setting for long press position. Close #287

This commit is contained in:
2025-04-02 16:23:51 +08:00
parent d3c115ee0c
commit 3da00595b7
6 changed files with 117 additions and 59 deletions

View File

@@ -381,7 +381,10 @@
"A valid WebDav directory URL": "有效的WebDav目录URL", "A valid WebDav directory URL": "有效的WebDav目录URL",
"Shut Down": "关闭", "Shut Down": "关闭",
"Uploading data...": "正在上传数据...", "Uploading data...": "正在上传数据...",
"Pages": "页数" "Pages": "页数",
"Long press zoom position": "长按缩放位置",
"Press position": "按压位置",
"Screen center": "屏幕中心"
}, },
"zh_TW": { "zh_TW": {
"Home": "首頁", "Home": "首頁",
@@ -765,6 +768,9 @@
"A valid WebDav directory URL": "有效的WebDav目錄URL", "A valid WebDav directory URL": "有效的WebDav目錄URL",
"Shut Down": "關閉", "Shut Down": "關閉",
"Uploading data...": "正在上傳數據...", "Uploading data...": "正在上傳數據...",
"Pages": "頁數" "Pages": "頁數",
"Long press zoom position": "長按縮放位置",
"Press position": "按壓位置",
"Screen center": "螢幕中心"
} }
} }

View File

@@ -163,3 +163,29 @@ class SliverLazyToBoxAdapter extends StatelessWidget {
]); ]);
} }
} }
class SliverAnimatedVisibility extends StatelessWidget {
const SliverAnimatedVisibility({
super.key,
required this.visible,
required this.child,
});
final bool visible;
final Widget child;
@override
Widget build(BuildContext context) {
var child = visible ? this.child : const SizedBox.shrink();
return SliverToBoxAdapter(
child: AnimatedSize(
duration: const Duration(milliseconds: 200),
curve: Curves.easeInOut,
alignment: Alignment.topCenter,
child: child,
),
);
}
}

View File

@@ -161,6 +161,7 @@ class Settings with ChangeNotifier {
'cacheSize': 2048, // in MB 'cacheSize': 2048, // in MB
'downloadThreads': 5, 'downloadThreads': 5,
'enableLongPressToZoom': true, 'enableLongPressToZoom': true,
'longPressZoomPosition': "press", // press, center
'checkUpdateOnStart': false, 'checkUpdateOnStart': false,
'limitImageWidth': true, 'limitImageWidth': true,
'webdav': [], // empty means not configured 'webdav': [], // empty means not configured

View File

@@ -343,10 +343,19 @@ class _GalleryModeState extends State<_GalleryMode>
} }
var photoViewController = photoViewControllers[reader.page]!; var photoViewController = photoViewControllers[reader.page]!;
double target = photoViewController.getInitialScale!.call()! * 1.75; double target = photoViewController.getInitialScale!.call()! * 1.75;
var size = MediaQuery.of(context).size; var size = reader.size;
Offset zoomPosition;
if (appdata.settings['longPressZoomPosition'] != 'center') {
zoomPosition = Offset(
size.width / 2 - location.dx,
size.height / 2 - location.dy,
);
} else {
zoomPosition = Offset(0, 0);
}
photoViewController.animateScale?.call( photoViewController.animateScale?.call(
target, target,
Offset(size.width / 2 - location.dx, size.height / 2 - location.dy), zoomPosition,
); );
isLongPressing = true; isLongPressing = true;
} }
@@ -608,6 +617,13 @@ class _ContinuousModeState extends State<_ContinuousMode>
} }
bool onScaleUpdate([double? scale]) { bool onScaleUpdate([double? scale]) {
if (prepareToNextChapter || prepareToPrevChapter) {
setState(() {
prepareToPrevChapter = false;
prepareToNextChapter = false;
});
context.readerScaffold.setFloatingButton(0);
}
var isZoomedIn = (scale ?? photoViewController.scale) != 1.0; var isZoomedIn = (scale ?? photoViewController.scale) != 1.0;
if (isZoomedIn != this.isZoomedIn) { if (isZoomedIn != this.isZoomedIn) {
setState(() { setState(() {
@@ -731,7 +747,7 @@ class _ContinuousModeState extends State<_ContinuousMode>
} }
Offset offset; Offset offset;
var sp = scrollController.position; var sp = scrollController.position;
if (sp.pixels < sp.minScrollExtent || sp.pixels > sp.maxScrollExtent) { if (sp.pixels <= sp.minScrollExtent || sp.pixels >= sp.maxScrollExtent) {
offset = Offset(value.dx, value.dy); offset = Offset(value.dx, value.dy);
} else { } else {
if (reader.mode == ReaderMode.continuousTopToBottom) { if (reader.mode == ReaderMode.continuousTopToBottom) {
@@ -759,7 +775,10 @@ class _ContinuousModeState extends State<_ContinuousMode>
delayedSetIsScrolling(false); delayedSetIsScrolling(false);
} }
if (notification is ScrollUpdateNotification) { var scale = photoViewController.scale ?? 1.0;
if (notification is ScrollUpdateNotification &&
(scale - 1).abs() < 0.05) {
if (!scrollController.hasClients) return false; if (!scrollController.hasClients) return false;
if (scrollController.position.pixels <= if (scrollController.position.pixels <=
scrollController.position.minScrollExtent && scrollController.position.minScrollExtent &&
@@ -800,8 +819,8 @@ class _ContinuousModeState extends State<_ContinuousMode>
}, },
child: widget, child: widget,
); );
var width = MediaQuery.of(context).size.width; var width = reader.size.width;
var height = MediaQuery.of(context).size.height; var height = reader.size.height;
if (appdata.settings['limitImageWidth'] && if (appdata.settings['limitImageWidth'] &&
width / height > 0.7 && width / height > 0.7 &&
reader.mode == ReaderMode.continuousTopToBottom) { reader.mode == ReaderMode.continuousTopToBottom) {
@@ -882,9 +901,19 @@ class _ContinuousModeState extends State<_ContinuousMode>
return; return;
} }
double target = photoViewController.getInitialScale!.call()! * 1.75; double target = photoViewController.getInitialScale!.call()! * 1.75;
var size = reader.size;
Offset zoomPosition;
if (appdata.settings['longPressZoomPosition'] != 'center') {
zoomPosition = Offset(
size.width / 2 - location.dx,
size.height / 2 - location.dy,
);
} else {
zoomPosition = Offset(0, 0);
}
photoViewController.animateScale?.call( photoViewController.animateScale?.call(
target, target,
Offset(0, 0), zoomPosition,
); );
onScaleUpdate(target); onScaleUpdate(target);
isLongPressing = true; isLongPressing = true;

View File

@@ -309,6 +309,13 @@ class _ReaderState extends State<Reader>
} }
return chapter == maxChapter; return chapter == maxChapter;
} }
/// Get the size of the reader.
/// The size is not always the same as the size of the screen.
Size get size {
var renderBox = context.findRenderObject() as RenderBox;
return renderBox.size;
}
} }
abstract mixin class _ImagePerPageHandler { abstract mixin class _ImagePerPageHandler {

View File

@@ -48,6 +48,7 @@ class _ReaderSettingsState extends State<ReaderSettings> {
"continuousTopToBottom": "Continuous (Top to Bottom)".tl, "continuousTopToBottom": "Continuous (Top to Bottom)".tl,
}, },
onChanged: () { onChanged: () {
setState(() {});
var readerMode = appdata.settings['readerMode']; var readerMode = appdata.settings['readerMode'];
if (readerMode?.toLowerCase().startsWith('continuous') ?? false) { if (readerMode?.toLowerCase().startsWith('continuous') ?? false) {
appdata.settings['readerScreenPicNumberForLandscape'] = 1; appdata.settings['readerScreenPicNumberForLandscape'] = 1;
@@ -68,22 +69,12 @@ class _ReaderSettingsState extends State<ReaderSettings> {
widget.onChanged?.call("autoPageTurningInterval"); widget.onChanged?.call("autoPageTurningInterval");
}, },
).toSliver(), ).toSliver(),
SliverToBoxAdapter( SliverAnimatedVisibility(
child: AbsorbPointer( visible: appdata.settings['readerMode']!.startsWith('gallery'),
absorbing: (appdata.settings['readerMode']
?.toLowerCase()
.startsWith('continuous') ??
false),
child: AnimatedOpacity(
opacity: (appdata.settings['readerMode']
?.toLowerCase()
.startsWith('continuous') ??
false)
? 0.5
: 1.0,
duration: Duration(milliseconds: 300),
child: _SliderSetting( child: _SliderSetting(
title: "The number of pic in screen for landscape (Only Gallery Mode)".tl, title:
"The number of pic in screen for landscape (Only Gallery Mode)"
.tl,
settingsIndex: "readerScreenPicNumberForLandscape", settingsIndex: "readerScreenPicNumberForLandscape",
interval: 1, interval: 1,
min: 1, min: 1,
@@ -93,24 +84,12 @@ class _ReaderSettingsState extends State<ReaderSettings> {
}, },
), ),
), ),
), SliverAnimatedVisibility(
), visible: appdata.settings['readerMode']!.startsWith('gallery'),
SliverToBoxAdapter(
child: AbsorbPointer(
absorbing: (appdata.settings['readerMode']
?.toLowerCase()
.startsWith('continuous') ??
false),
child: AnimatedOpacity(
opacity: (appdata.settings['readerMode']
?.toLowerCase()
.startsWith('continuous') ??
false)
? 0.5
: 1.0,
duration: Duration(milliseconds: 300),
child: _SliderSetting( child: _SliderSetting(
title: "The number of pic in screen for portrait (Only Gallery Mode)".tl, title:
"The number of pic in screen for portrait (Only Gallery Mode)"
.tl,
settingsIndex: "readerScreenPicNumberForPortrait", settingsIndex: "readerScreenPicNumberForPortrait",
interval: 1, interval: 1,
min: 1, min: 1,
@@ -120,15 +99,25 @@ class _ReaderSettingsState extends State<ReaderSettings> {
}, },
), ),
), ),
),
),
_SwitchSetting( _SwitchSetting(
title: 'Long press to zoom'.tl, title: 'Long press to zoom'.tl,
settingKey: 'enableLongPressToZoom', settingKey: 'enableLongPressToZoom',
onChanged: () { onChanged: () {
setState(() {});
widget.onChanged?.call('enableLongPressToZoom'); widget.onChanged?.call('enableLongPressToZoom');
}, },
).toSliver(), ).toSliver(),
SliverAnimatedVisibility(
visible: appdata.settings['enableLongPressToZoom'] == true,
child: SelectSetting(
title: "Long press zoom position".tl,
settingKey: "longPressZoomPosition",
optionTranslation: {
"press": "Press position".tl,
"center": "Screen center".tl,
},
),
),
_SwitchSetting( _SwitchSetting(
title: 'Limit image width'.tl, title: 'Limit image width'.tl,
subtitle: 'When using Continuous(Top to Bottom) mode'.tl, subtitle: 'When using Continuous(Top to Bottom) mode'.tl,