From 00877a0223272c40f5e4fa55e3670bab51e9b662 Mon Sep 17 00:00:00 2001 From: Carlo Baratto Date: Fri, 21 Aug 2026 10:47:40 +0200 Subject: [PATCH] Lettore 0.3.3: paginazione per contenuto + versione nelle impostazioni - Tap avanti/indietro per PAGINA DI TESTO (offset in caratteri dai text node): positionJs (sopra) + charsInViewportJs (nella viewport) + scrollToCharJs; fine capitolo = offset >= chapterTextLength (C++), mai dallo scroll del WebView. window.scrollY non riflette lo scroll reale e contentHeight/ scrollPosition nativi non sono scrivibili su Qt 5.6 (0.3.1 e 0.3.2 falliti) - SettingsPage: label 'Version' in fondo (appVersion da setApplicationVersion) - Traduzione IT aggiunta; log build aggiornato --- qml/pages/ReaderPage.qml | 129 +++++++++++++++++--------- qml/pages/SettingsPage.qml | 7 ++ rpm/harbour-calibreweb.spec | 10 +- src/main.cpp | 7 +- translations/harbour-calibreweb_it.ts | 5 + 5 files changed, 113 insertions(+), 45 deletions(-) diff --git a/qml/pages/ReaderPage.qml b/qml/pages/ReaderPage.qml index b725045..62013df 100644 --- a/qml/pages/ReaderPage.qml +++ b/qml/pages/ReaderPage.qml @@ -15,6 +15,9 @@ Page { // posizione di lettura nel capitolo corrente, in caratteri di testo // (conteggio JS sui text node: stabile al variare di font/margini) property int charOffset: 0 + // caratteri di testo per pagina (stimati dalla viewport corrente): usato + // per il passo di pagina e per la label "pagina corrente/totale" + property int charsPerPage: 1 // offset da ripristinare al load del capitolo (-1 = nessuno) property int restoreCharOffset: -1 // ultimo offset assoluto salvato (evita scritture continue) @@ -144,9 +147,11 @@ Page { page.restoreCharOffset = -1 if (off >= book.chapterTextLength(page.currentChapter)) { // oltre la fine = vai all'ultima pagina (usato da - // prevPage per non saltare la coda del capitolo) - web.scrollPosition = Qt.point(0, Math.max(1, web.contentHeight - web.height)) - readScrollPosition() + // prevPage per non saltare la coda del capitolo): + // scrollTo oltre il limite porta in fondo al documento + web.runJavaScript( + "window.scrollTo(0,9999999);true", + function() { readScrollPosition() }) } else { web.runJavaScript(page.scrollToCharJs(off), function() { readScrollPosition() @@ -275,6 +280,28 @@ Page { + "return o;})()" } + // JS che conta i caratteri di testo NELLA viewport corrente (per il passo + // di pagina): stessa tecnica di positionJs, ma sulla fascia [0, innerHeight] + function charsInViewportJs() { + return "(function(){" + + "var b=document.body;" + + "var vh=window.innerHeight;" + + "var c=0;" + + "var it=document.createTreeWalker(b,NodeFilter.SHOW_TEXT);" + + "var n;" + + "while((n=it.nextNode())){" + + "var p=n.parentNode;" + + "if(!p||p.tagName==='SCRIPT'||p.tagName==='STYLE'||p.tagName==='NOSCRIPT')continue;" + + "var r=document.createRange();r.selectNodeContents(n);" + + "var rect=r.getBoundingClientRect();" + + "if(rect.bottom<=0||rect.top>=vh)continue;" + + "var top=Math.max(0,rect.top),bot=Math.min(vh,rect.bottom);" + + "var h=rect.bottom-rect.top;" + + "if(h>0)c+=Math.round(n.data.length*(bot-top)/h);" + + "}" + + "return c;})()" + } + // JS che porta in cima alla viewport il carattere 'off' del capitolo function scrollToCharJs(off) { return "(function(){" @@ -296,16 +323,17 @@ Page { function readScrollPosition() { if (!web.loaded) return - // posizione letta dalle proprietà NATIVE del WebView (affidabili), - // il JS serve solo a contare i caratteri per il bookmark - var vh = Math.max(1, web.height) - var max = Math.max(1, web.contentHeight - vh) - var y = Math.max(0, Math.min(max, web.scrollPosition.y)) - page.chapterFraction = y / max - page.pageIndex = Math.floor(y / vh) + 1 - page.pageCount = Math.max(1, Math.floor(max / vh) + 1) web.runJavaScript(page.positionJs(), function(o) { page.charOffset = o || 0 + // avanzamento per CARATTERI: stabile e indipendente dallo scroll reale + var total = book.chapterStartChar(book.chapterCount) + var start = book.chapterStartChar(page.currentChapter) + if (total > 0) + page.chapterFraction = (start + page.charOffset) / total + var len = book.chapterTextLength(page.currentChapter) + var pp = Math.max(1, page.charsPerPage) + page.pageIndex = Math.floor(page.charOffset / pp) + 1 + page.pageCount = Math.max(1, Math.ceil(len / pp)) maybeSave(false) }) } @@ -346,46 +374,61 @@ Page { loadChapter() } - // pagina successiva: una viewport avanti, usando le proprietà native del - // WebView (contentHeight/scrollPosition) — niente JS di navigazione, quindi - // nessuna dipendenza da runJavaScript/scrollY per il cambio capitolo. - // A fine capitolo (y >= max-2) passa al capitolo successivo. + // pagina successiva: avanza di una "pagina di testo" (i caratteri visibili + // nella viewport corrente). La fine del capitolo è determinata dal CONFRONTO + // CON LA LUNGHEZZA IN CARATTERI (nota in C++), non dallo scroll reale del + // WebView — quindi il passaggio al capitolo successivo è sempre affidabile. function nextPage() { if (!web.loaded || !page.ready) return - var vh = Math.max(1, web.height) - var max = Math.max(1, web.contentHeight - vh) - var y = web.scrollPosition.y - if (y >= max - 2) { - // fine capitolo (o capitolo non scrollabile) -> avanti - gotoChapter(page.currentChapter + 1) - return - } - web.scrollPosition = Qt.point(0, Math.min(max, y + vh)) - readScrollPosition() + web.runJavaScript(page.positionJs(), function(o) { + page.charOffset = o || 0 + web.runJavaScript(page.charsInViewportJs(), function(c) { + page.charsPerPage = Math.max(1, Math.round(c || 1)) + var target = page.charOffset + page.charsPerPage + if (target >= book.chapterTextLength(page.currentChapter)) { + // fine capitolo: segna la coda come letta e passa avanti + page.charOffset = Math.max(0, book.chapterTextLength(page.currentChapter) - 1) + maybeSave(true) + gotoChapter(page.currentChapter + 1) + } else { + web.runJavaScript(page.scrollToCharJs(target), function() { + readScrollPosition() + }) + } + }) + }) } - // pagina precedente: una viewport indietro; all'inizio -> capitolo prima - // SULL'ULTIMA PAGINA (non salta la coda del capitolo) + // pagina precedente: torna indietro di una pagina di testo; dall'inizio del + // capitolo va al capitolo prima SULL'ULTIMA PAGINA (non salta la coda) function prevPage() { if (!web.loaded || !page.ready) return - var vh = Math.max(1, web.height) - var y = web.scrollPosition.y - if (y > 1) { - web.scrollPosition = Qt.point(0, Math.max(0, y - vh)) - readScrollPosition() - } else if (page.currentChapter > 0) { - maybeSave(true) - page.currentChapter -= 1 - page.chapterFraction = 0 - page.charOffset = 0 - // sentinella "fine capitolo": va oltre la lunghezza, così - // onLoadedChanged porta all'ultima pagina - page.restoreCharOffset = book.chapterTextLength(page.currentChapter) + 1 - page.lastSavedAbs = -1 - loadChapter() - } + web.runJavaScript(page.positionJs(), function(o) { + page.charOffset = o || 0 + web.runJavaScript(page.charsInViewportJs(), function(c) { + page.charsPerPage = Math.max(1, Math.round(c || 1)) + var target = page.charOffset - page.charsPerPage + if (target <= 0) { + if (page.currentChapter > 0) { + maybeSave(true) + page.currentChapter -= 1 + page.chapterFraction = 0 + page.charOffset = 0 + // sentinella "fine capitolo": va oltre la lunghezza, così + // onLoadedChanged porta all'ultima pagina + page.restoreCharOffset = book.chapterTextLength(page.currentChapter) + 1 + page.lastSavedAbs = -1 + loadChapter() + } + } else { + web.runJavaScript(page.scrollToCharJs(target), function() { + readScrollPosition() + }) + } + }) + }) } function fontPx() { diff --git a/qml/pages/SettingsPage.qml b/qml/pages/SettingsPage.qml index eba925d..3ee118c 100644 --- a/qml/pages/SettingsPage.qml +++ b/qml/pages/SettingsPage.qml @@ -150,6 +150,13 @@ Page { } Item { width: 1; height: Theme.paddingLarge } + + Label { + anchors.horizontalCenter: parent.horizontalCenter + text: qsTr("Version") + " " + appVersion + color: Theme.secondaryColor + font.pixelSize: Theme.fontSizeSmall + } } } diff --git a/rpm/harbour-calibreweb.spec b/rpm/harbour-calibreweb.spec index 9bcc052..21132a0 100644 --- a/rpm/harbour-calibreweb.spec +++ b/rpm/harbour-calibreweb.spec @@ -1,6 +1,6 @@ Name: harbour-calibreweb Summary: Client OPDS per Calibre Web -Version: 0.3.2 +Version: 0.3.3 Release: 1 Group: Qt/Qt License: MIT @@ -24,6 +24,14 @@ reflow del testo, margini configurabili e posizione salvata in locale. %changelog +* Fri Aug 21 2026 Carlo Baratto - 0.3.3-1 +- Paginazione per CONTENUTO: il tap avanza di una pagina di testo (offset in + caratteri calcolato sui text node del DOM), la fine del capitolo e' decisa + confrontando l'offset con la lunghezza del capitolo nota in C++ — nessuna + dipendenza dallo scroll del WebView (window.scrollY non riflette lo scroll + reale; contentHeight/scrollPosition nativi non scrivibili su Qt 5.6) +- Impostazioni: numero di versione in fondo alla pagina (label "Version") +- Fix definitivo del cambio capitolo avanti/indietro * Fri Aug 21 2026 Carlo Baratto - 0.3.2-1 - Fix definitivo cambio capitolo: la navigazione non usa piu' window.scrollY/ scrollBy via runJavaScript (lo scroll reale del WebView Sailfish non e' sempre diff --git a/src/main.cpp b/src/main.cpp index e918269..00a7bb9 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -20,6 +21,8 @@ Q_DECL_EXPORT int main(int argc, char *argv[]) // l'unica zona config persistente della sandbox di Sailfish 5.1. app->setOrganizationName(QStringLiteral("harbour")); app->setApplicationName(QStringLiteral("calibreweb")); + // versione mostrata nelle impostazioni (tenere allineata allo spec) + app->setApplicationVersion(QStringLiteral("0.3.3")); Settings settings; @@ -53,7 +56,7 @@ Q_DECL_EXPORT int main(int argc, char *argv[]) QQuickView *view = SailfishApp::createView(); - qDebug() << "harbour-calibreweb v0.2.0 build" << __DATE__ << __TIME__; + qDebug() << "harbour-calibreweb v0.3.3 build" << __DATE__ << __TIME__; qmlRegisterType("harbour.calibreweb", 1, 0, "EpubBook"); @@ -63,6 +66,8 @@ Q_DECL_EXPORT int main(int argc, char *argv[]) view->rootContext()->setContextProperty(QStringLiteral("appSettings"), &settings); view->rootContext()->setContextProperty(QStringLiteral("apiClient"), &apiClient); view->rootContext()->setContextProperty(QStringLiteral("downloader"), &downloader); + view->rootContext()->setContextProperty(QStringLiteral("appVersion"), + QCoreApplication::applicationVersion()); view->setSource(SailfishApp::pathTo(QStringLiteral("qml/harbour-calibreweb.qml"))); view->show(); diff --git a/translations/harbour-calibreweb_it.ts b/translations/harbour-calibreweb_it.ts index 4004c57..d7f449d 100644 --- a/translations/harbour-calibreweb_it.ts +++ b/translations/harbour-calibreweb_it.ts @@ -279,6 +279,11 @@ Enter the server address Inserisci l'indirizzo del server + + + Version + Versione + Testing…