From 5aa45ee299c5efeb1e12d54b0548b56dce869bd9 Mon Sep 17 00:00:00 2001 From: Chmouel Boudjnah Date: Tue, 6 Oct 2026 15:45:34 +0200 Subject: [PATCH] fix: Stop text selection jumping near page tops When you held a finger on the text and dragged a selection handle near the top of a page in a paginated book, the page could slide sideways and the highlighted words kept flashing between different spots. The phone's built-in web view tried to scroll the page to follow the finger, the reader refused that scroll, and the two kept fighting over where the page should sit. This change made the reader remember where the page was when the selection started and put it back whenever the web view tried to move it sideways, until the selection was cleared. Books read in scrolling mode and books with vertical text were left alone, because following the finger is the expected behaviour there. --- .../liseur/reader/SelectionHandleFix.kt | 34 ++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/app/src/main/kotlin/com/chmouel/liseur/reader/SelectionHandleFix.kt b/app/src/main/kotlin/com/chmouel/liseur/reader/SelectionHandleFix.kt index 7f3a414e..5e18d3fc 100644 --- a/app/src/main/kotlin/com/chmouel/liseur/reader/SelectionHandleFix.kt +++ b/app/src/main/kotlin/com/chmouel/liseur/reader/SelectionHandleFix.kt @@ -5,7 +5,14 @@ import org.readium.r2.shared.ExperimentalReadiumApi import kotlin.coroutines.cancellation.CancellationException /** - * Keeps the start handle of a selection on the word that was selected. + * Keeps selection handles on the passage being selected. + * + * Near the top of a paginated column, Chromium tries to auto-scroll the + * document horizontally while a handle is dragged. Readium refuses native + * scrolling during selection, so the browser and the view repeatedly + * disagree about the offset and the range flashes between different words. + * Keep the document on its selected page too, until the selection clears. + * Scrolled and vertical-text books retain their normal selection scrolling. * * Long-pressing the *first* word of a paragraph selects it correctly, but * the WebView paints the start handle on the paragraph's last automatic @@ -85,6 +92,31 @@ internal object SelectionHandleFix { const val SCRIPT: String = """ (function () { var state = window.__liseurLead || (window.__liseurLead = {}); + if (!state.scrollGuardInstalled) { + state.scrollGuardInstalled = true; + state.selectionX = null; + document.addEventListener("selectionchange", function () { + var selection = window.getSelection(); + if (!selection || selection.isCollapsed) { + state.selectionX = null; + return; + } + var root = document.documentElement; + var view = root.style.getPropertyValue("--USER__view").trim(); + var scroll = root.style.getPropertyValue("--USER__scroll").trim(); + if (view === "readium-scroll-on" || scroll === "readium-scroll-on" || + getComputedStyle(root).writingMode.indexOf("vertical") === 0) { + state.selectionX = null; + return; + } + if (state.selectionX === null) state.selectionX = window.scrollX; + }, true); + window.addEventListener("scroll", function () { + if (state.selectionX !== null && window.scrollX !== state.selectionX) { + document.scrollingElement.scrollLeft = state.selectionX; + } + }); + } var installed = false; var tok = state.token || (state.token = "l" + Math.random().toString(36).slice(2, 10));