import 'package:flutter/material.dart'; import '../../../core/models/config.dart'; import 'pdf_page_display.dart'; /// Callback for page turn events. typedef PageTurnCallback = void Function(int delta); /// Gesture layer for touch-based navigation over PDF pages. /// /// Touch zones: /// - Top 2cm: Toggle fullscreen (or exit if in fullscreen + top-right corner) /// - Left side: Turn page backward (-1 or -2 in two-page mode) /// - Right side: Turn page forward (+1 or +2 in two-page mode) class TouchNavigationLayer extends StatelessWidget { final PdfPageDisplay pageDisplay; final Config config; final VoidCallback onToggleFullscreen; final VoidCallback onExit; final PageTurnCallback onPageTurn; const TouchNavigationLayer({ super.key, required this.pageDisplay, required this.config, required this.onToggleFullscreen, required this.onExit, required this.onPageTurn, }); @override Widget build(BuildContext context) { return GestureDetector( behavior: HitTestBehavior.opaque, onTapUp: (details) => _handleTap(context, details), child: pageDisplay, ); } void _handleTap(BuildContext context, TapUpDetails details) { final mediaQuery = MediaQuery.of(context); final position = details.localPosition; // Calculate physical measurements for consistent touch zones final pixelsPerCm = _calculatePixelsPerCm(mediaQuery.devicePixelRatio); final touchZoneHeight = 2 * pixelsPerCm; final touchZoneWidth = 2 * pixelsPerCm; final screenWidth = mediaQuery.size.width; final screenCenter = screenWidth / 2; // Get page sizes for accurate touch zone calculation final (leftPageSize, rightPageSize) = pageDisplay.calculateScaledPageSizes( mediaQuery.size, ); // Check top zone first if (position.dy < touchZoneHeight) { _handleTopZoneTap(position, screenWidth, touchZoneWidth); return; } // Handle page turning based on tap position _handlePageTurnTap(position, screenCenter, leftPageSize, rightPageSize); } void _handleTopZoneTap( Offset position, double screenWidth, double touchZoneWidth, ) { // Top-right corner in fullscreen mode = exit if (config.fullscreen && position.dx >= screenWidth - touchZoneWidth) { onExit(); } else { onToggleFullscreen(); } } void _handlePageTurnTap( Offset position, double screenCenter, Size leftPageSize, Size? rightPageSize, ) { final isLeftSide = position.dx < screenCenter; if (config.twoPageMode) { _handleTwoPageModeTap( position, screenCenter, leftPageSize, rightPageSize, ); } else { // Single page mode: simple left/right onPageTurn(isLeftSide ? -1 : 1); } } void _handleTwoPageModeTap( Offset position, double screenCenter, Size leftPageSize, Size? rightPageSize, ) { final leftEdge = screenCenter - leftPageSize.width / 2; final rightEdge = screenCenter + (rightPageSize?.width ?? 0) / 2; if (position.dx < leftEdge) { onPageTurn(-2); } else if (position.dx < screenCenter) { onPageTurn(-1); } else if (position.dx > rightEdge) { onPageTurn(2); } else { onPageTurn(1); } } double _calculatePixelsPerCm(double devicePixelRatio) { const baseDpi = 160.0; // Android baseline DPI const cmPerInch = 2.54; return (devicePixelRatio * baseDpi) / cmPerInch; } }