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
This commit is contained in:
2026-08-21 10:47:40 +02:00
parent fc2d4d9d3d
commit 00877a0223
5 changed files with 113 additions and 45 deletions
+86 -43
View File
@@ -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() {
+7
View File
@@ -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
}
}
}
+9 -1
View File
@@ -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 <carlo@carlobaratto.it> - 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 <carlo@carlobaratto.it> - 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
+6 -1
View File
@@ -1,4 +1,5 @@
#include <QDebug>
#include <QCoreApplication>
#include <QGuiApplication>
#include <QLocale>
#include <QQuickView>
@@ -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<EpubBook>("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();
+5
View File
@@ -279,6 +279,11 @@
<source>Enter the server address</source>
<translation>Inserisci l'indirizzo del server</translation>
</message>
<message>
<location filename="../qml/pages/SettingsPage.qml" line="156" />
<source>Version</source>
<translation type="finished">Versione</translation>
</message>
<message>
<location filename="../qml/pages/SettingsPage.qml" line="168" />
<source>Testing…</source>