mirror of
https://github.com/kodjodevf/mangayomi.git
synced 2026-05-10 11:30:41 +00:00
- PageIndicator widget to display current page and total pages. - Created ReaderAppBar for navigation and chapter information. - ReaderBottomBar for page navigation and settings access. - Added ReaderGestureHandler for managing tap zones and gestures. - ReaderSettingsModal for user-configurable settings.
212 lines
5.5 KiB
Dart
212 lines
5.5 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:flutter/services.dart';
|
|
|
|
/// Widget providing horizontal tap zones for reader navigation.
|
|
class HorizontalTapZones extends StatelessWidget {
|
|
/// Callback for left region tap.
|
|
final VoidCallback onLeftTap;
|
|
|
|
/// Callback for center region tap.
|
|
final VoidCallback onCenterTap;
|
|
|
|
/// Callback for right region tap.
|
|
final VoidCallback onRightTap;
|
|
|
|
/// Callback for double-tap with position.
|
|
final void Function(Offset position)? onDoubleTap;
|
|
|
|
/// Whether to show overlay for failed images.
|
|
final bool showFailedOverlay;
|
|
|
|
/// Widget to show when image failed to load.
|
|
final Widget? failedWidget;
|
|
|
|
const HorizontalTapZones({
|
|
super.key,
|
|
required this.onLeftTap,
|
|
required this.onCenterTap,
|
|
required this.onRightTap,
|
|
this.onDoubleTap,
|
|
this.showFailedOverlay = false,
|
|
this.failedWidget,
|
|
});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Row(
|
|
children: [
|
|
// Left region (2 flex)
|
|
Expanded(
|
|
flex: 2,
|
|
child: _TapZone(onTap: onLeftTap, onDoubleTap: onDoubleTap),
|
|
),
|
|
// Center region (2 flex)
|
|
Expanded(
|
|
flex: 2,
|
|
child: showFailedOverlay && failedWidget != null
|
|
? failedWidget!
|
|
: _TapZone(onTap: onCenterTap, onDoubleTap: onDoubleTap),
|
|
),
|
|
// Right region (2 flex)
|
|
Expanded(
|
|
flex: 2,
|
|
child: _TapZone(onTap: onRightTap, onDoubleTap: onDoubleTap),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
}
|
|
|
|
/// Widget providing vertical tap zones for reader navigation.
|
|
class VerticalTapZones extends StatelessWidget {
|
|
/// Callback for top region tap.
|
|
final VoidCallback onTopTap;
|
|
|
|
/// Callback for center region tap.
|
|
final VoidCallback onCenterTap;
|
|
|
|
/// Callback for bottom region tap.
|
|
final VoidCallback onBottomTap;
|
|
|
|
/// Callback for double-tap with position.
|
|
final void Function(Offset position)? onDoubleTap;
|
|
|
|
const VerticalTapZones({
|
|
super.key,
|
|
required this.onTopTap,
|
|
required this.onCenterTap,
|
|
required this.onBottomTap,
|
|
this.onDoubleTap,
|
|
});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Column(
|
|
children: [
|
|
// Top region (2 flex)
|
|
Expanded(
|
|
flex: 2,
|
|
child: _TapZone(onTap: onTopTap, onDoubleTap: onDoubleTap),
|
|
),
|
|
// Center region (5 flex) - larger for viewing
|
|
const Expanded(flex: 5, child: SizedBox.shrink()),
|
|
// Bottom region (2 flex)
|
|
Expanded(
|
|
flex: 2,
|
|
child: _TapZone(onTap: onBottomTap, onDoubleTap: onDoubleTap),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
}
|
|
|
|
class _TapZone extends StatelessWidget {
|
|
final VoidCallback onTap;
|
|
final void Function(Offset position)? onDoubleTap;
|
|
|
|
const _TapZone({required this.onTap, this.onDoubleTap});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return GestureDetector(
|
|
behavior: HitTestBehavior.translucent,
|
|
onTap: onTap,
|
|
onDoubleTapDown: onDoubleTap != null
|
|
? (details) => onDoubleTap!(details.globalPosition)
|
|
: null,
|
|
onDoubleTap: onDoubleTap != null ? () {} : null,
|
|
onSecondaryTapDown: onDoubleTap != null
|
|
? (details) => onDoubleTap!(details.globalPosition)
|
|
: null,
|
|
onSecondaryTap: onDoubleTap != null ? () {} : null,
|
|
);
|
|
}
|
|
}
|
|
|
|
/// Handler for keyboard shortcuts in the reader.
|
|
class ReaderKeyboardHandler {
|
|
final VoidCallback? onEscape;
|
|
final VoidCallback? onFullScreen;
|
|
final VoidCallback? onPreviousPage;
|
|
final VoidCallback? onNextPage;
|
|
final VoidCallback? onNextChapter;
|
|
final VoidCallback? onPreviousChapter;
|
|
|
|
const ReaderKeyboardHandler({
|
|
this.onEscape,
|
|
this.onFullScreen,
|
|
this.onPreviousPage,
|
|
this.onNextPage,
|
|
this.onNextChapter,
|
|
this.onPreviousChapter,
|
|
});
|
|
|
|
/// Handles a key event and returns true if it was handled.
|
|
bool handleKeyEvent(KeyEvent event, {bool isReverseHorizontal = false}) {
|
|
if (event is! KeyDownEvent) return false;
|
|
switch (event.logicalKey) {
|
|
case LogicalKeyboardKey.f11:
|
|
onFullScreen?.call();
|
|
return true;
|
|
|
|
case LogicalKeyboardKey.escape:
|
|
case LogicalKeyboardKey.backspace:
|
|
onEscape?.call();
|
|
return true;
|
|
|
|
case LogicalKeyboardKey.arrowUp:
|
|
onPreviousPage?.call();
|
|
return true;
|
|
|
|
case LogicalKeyboardKey.arrowDown:
|
|
onNextPage?.call();
|
|
return true;
|
|
|
|
case LogicalKeyboardKey.arrowLeft:
|
|
if (isReverseHorizontal) {
|
|
onNextPage?.call();
|
|
} else {
|
|
onPreviousPage?.call();
|
|
}
|
|
return true;
|
|
|
|
case LogicalKeyboardKey.arrowRight:
|
|
if (isReverseHorizontal) {
|
|
onPreviousPage?.call();
|
|
} else {
|
|
onNextPage?.call();
|
|
}
|
|
return true;
|
|
|
|
case LogicalKeyboardKey.keyN:
|
|
case LogicalKeyboardKey.pageDown:
|
|
case LogicalKeyboardKey.shiftRight:
|
|
onNextChapter?.call();
|
|
return true;
|
|
|
|
case LogicalKeyboardKey.keyP:
|
|
case LogicalKeyboardKey.pageUp:
|
|
case LogicalKeyboardKey.shiftLeft:
|
|
onPreviousChapter?.call();
|
|
return true;
|
|
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// Creates a KeyboardListener widget with this handler.
|
|
Widget wrapWithKeyboardListener({
|
|
required Widget child,
|
|
bool isReverseHorizontal = false,
|
|
FocusNode? focusNode,
|
|
}) {
|
|
return KeyboardListener(
|
|
autofocus: true,
|
|
focusNode: focusNode ?? FocusNode(),
|
|
onKeyEvent: (event) =>
|
|
handleKeyEvent(event, isReverseHorizontal: isReverseHorizontal),
|
|
child: child,
|
|
);
|
|
}
|
|
}
|