diff --git a/packages/app/src/components/reader/FoliateViewer.tsx b/packages/app/src/components/reader/FoliateViewer.tsx index 1179c838..333f9ecc 100644 --- a/packages/app/src/components/reader/FoliateViewer.tsx +++ b/packages/app/src/components/reader/FoliateViewer.tsx @@ -1781,7 +1781,7 @@ export const FoliateViewer = forwardRef ); // --- Hooks --- - usePagination({ bookKey, viewRef, containerRef }); + usePagination({ bookKey, viewRef, containerRef, isFixedLayout }); useBookShortcuts({ bookKey, viewRef, diff --git a/packages/app/src/components/reader/ReaderView.tsx b/packages/app/src/components/reader/ReaderView.tsx index 0605de7d..aa9f827a 100644 --- a/packages/app/src/components/reader/ReaderView.tsx +++ b/packages/app/src/components/reader/ReaderView.tsx @@ -193,6 +193,7 @@ function useAutoHideControls( keepVisible = false, isDoublePage = false, isScrollMode = false, + isFixedLayout = false, ) { const [isVisible, setIsVisible] = useState(true); const timeoutRef = useRef | null>(null); @@ -273,10 +274,23 @@ function useAutoHideControls( viewWidth, isDoublePage, isScrollMode, + isFixedLayout, leftNavEnd, rightNavStart, }); + if (isFixedLayout && !isVisible) { + console.log("[ReaderTap][reader:action]", { + bookKey, + source, + action: "show-controls", + fraction, + isDoublePage, + }); + showAndScheduleHide(); + return; + } + if (isScrollMode) { toggleControls(); return; @@ -328,6 +342,8 @@ function useAutoHideControls( showAndScheduleHide, isDoublePage, isScrollMode, + isFixedLayout, + isVisible, ]); // Mouse enter/leave handlers for toolbar area @@ -867,6 +883,7 @@ export function ReaderView({ bookId, tabId }: ReaderViewProps) { keepControlsVisible, (viewSettings.paginatedLayout ?? "double") === "double", viewSettings.viewMode === "scroll", + isFixedLayout, ); const isDoublePage = (viewSettings.paginatedLayout ?? "double") === "double"; const toolbarVisible = controlsVisible || isToolbarPinned; diff --git a/packages/core/src/hooks/reader/usePagination.ts b/packages/core/src/hooks/reader/usePagination.ts index 6b979957..7096daf1 100644 --- a/packages/core/src/hooks/reader/usePagination.ts +++ b/packages/core/src/hooks/reader/usePagination.ts @@ -11,6 +11,7 @@ interface UsePaginationOptions { bookKey: string; viewRef: React.RefObject; containerRef: React.RefObject; + isFixedLayout?: boolean; } /** Minimum cooldown after a page turn (ms) */ @@ -19,7 +20,39 @@ const WHEEL_MIN_COOLDOWN_MS = 350; /** After the last wheel event, wait this long before unlocking (ms). */ const WHEEL_IDLE_MS = 200; -export function usePagination({ bookKey, viewRef, containerRef }: UsePaginationOptions) { +function getScrollableDistance(element: HTMLElement, axis: "x" | "y") { + return axis === "x" + ? Math.max(0, element.scrollWidth - element.clientWidth) + : Math.max(0, element.scrollHeight - element.clientHeight); +} + +function scrollFixedLayoutPage(view: FoliateView, deltaY: number, deltaX = 0) { + const renderer = view.renderer as HTMLElement | undefined; + if (!renderer || renderer.getAttribute("spread") !== "none") return false; + + const maxY = getScrollableDistance(renderer, "y"); + const maxX = getScrollableDistance(renderer, "x"); + if (maxY <= 1 && maxX <= 1) return false; + + const beforeTop = renderer.scrollTop; + const beforeLeft = renderer.scrollLeft; + const top = maxY > 1 ? deltaY : 0; + const left = maxX > 1 ? deltaX : 0; + if (Math.abs(top) < 1 && Math.abs(left) < 1) return false; + + renderer.scrollBy({ top, left, behavior: "auto" }); + return ( + Math.abs(renderer.scrollTop - beforeTop) > 0.5 || + Math.abs(renderer.scrollLeft - beforeLeft) > 0.5 + ); +} + +export function usePagination({ + bookKey, + viewRef, + containerRef, + isFixedLayout = false, +}: UsePaginationOptions) { const wheelLocked = useRef(false); const idleTimer = useRef | null>(null); const lockTime = useRef(0); @@ -31,6 +64,10 @@ export function usePagination({ bookKey, viewRef, containerRef }: UsePaginationO if (view.renderer?.scrolled) return; + if (isFixedLayout && scrollFixedLayoutPage(view, deltaY, deltaX)) { + return; + } + const absDY = Math.abs(deltaY); const absDX = Math.abs(deltaX || 0); if (absDY < 2 && absDX < 2) return; @@ -69,7 +106,7 @@ export function usePagination({ bookKey, viewRef, containerRef }: UsePaginationO wheelLocked.current = false; }, WHEEL_IDLE_MS); }, - [viewRef], + [isFixedLayout, viewRef], ); useEffect(() => {